Add lua package 'openmw.query'

dont-compose-content
Petr Mikheev 4 years ago
parent 32218f6dd5
commit bdccf161c4

@ -56,8 +56,8 @@ add_openmw_dir (mwscript
)
add_openmw_dir (mwlua
luamanagerimp localscripts object worldview luabindings userdataserializer eventqueue
objectbindings asyncbindings camerabindings uibindings
luamanagerimp localscripts object worldview userdataserializer eventqueue query
luabindings objectbindings asyncbindings camerabindings uibindings
)
add_openmw_dir (mwsound

@ -0,0 +1,30 @@
#ifndef MWLUA_CONTEXT_H
#define MWLUA_CONTEXT_H
#include "eventqueue.hpp"
namespace LuaUtil
{
class LuaState;
class UserdataSerializer;
}
namespace MWLua
{
class LuaManager;
class WorldView;
struct Context
{
bool mIsGlobal;
LuaManager* mLuaManager;
LuaUtil::LuaState* mLua;
LuaUtil::UserdataSerializer* mSerializer;
WorldView* mWorldView;
LocalEventQueue* mLocalEventQueue;
GlobalEventQueue* mGlobalEventQueue;
};
}
#endif // MWLUA_CONTEXT_H

@ -1,6 +1,7 @@
#include "luabindings.hpp"
#include <components/lua/luastate.hpp>
#include <components/queries/luabindings.hpp>
#include "eventqueue.hpp"
#include "worldview.hpp"
@ -38,6 +39,24 @@ namespace MWLua
sol::table api(context.mLua->sol(), sol::create);
WorldView* worldView = context.mWorldView;
api["activeActors"] = GObjectList{worldView->getActorsInScene()};
api["selectObjects"] = [context](const Queries::Query& query)
{
ObjectIdList list;
WorldView* worldView = context.mWorldView;
if (query.mQueryType == "activators")
list = worldView->getActivatorsInScene();
else if (query.mQueryType == "actors")
list = worldView->getActorsInScene();
else if (query.mQueryType == "containers")
list = worldView->getContainersInScene();
else if (query.mQueryType == "doors")
list = worldView->getDoorsInScene();
else if (query.mQueryType == "items")
list = worldView->getItemsInScene();
return GObjectList{selectObjectsFromList(query, list, context)};
// TODO: Use sqlite to search objects that are not in the scene
// return GObjectList{worldView->selectObjects(query, false)};
};
return context.mLua->makeReadOnly(api);
}
@ -50,8 +69,55 @@ namespace MWLua
api["containers"] = LObjectList{worldView->getContainersInScene()};
api["doors"] = LObjectList{worldView->getDoorsInScene()};
api["items"] = LObjectList{worldView->getItemsInScene()};
api["selectObjects"] = [context](const Queries::Query& query)
{
ObjectIdList list;
WorldView* worldView = context.mWorldView;
if (query.mQueryType == "activators")
list = worldView->getActivatorsInScene();
else if (query.mQueryType == "actors")
list = worldView->getActorsInScene();
else if (query.mQueryType == "containers")
list = worldView->getContainersInScene();
else if (query.mQueryType == "doors")
list = worldView->getDoorsInScene();
else if (query.mQueryType == "items")
list = worldView->getItemsInScene();
return LObjectList{selectObjectsFromList(query, list, context)};
// TODO: Maybe use sqlite
// return LObjectList{worldView->selectObjects(query, true)};
};
return context.mLua->makeReadOnly(api);
}
sol::table initQueryPackage(const Context& context)
{
Queries::registerQueryBindings(context.mLua->sol());
sol::table query(context.mLua->sol(), sol::create);
for (std::string_view t : ObjectQueryTypes::types)
query[t] = Queries::Query(std::string(t));
for (const QueryFieldGroup& group : getBasicQueryFieldGroups())
query[group.mName] = initFieldGroup(context, group);
return query; // makeReadonly is applied by LuaState::addCommonPackage
}
sol::table initFieldGroup(const Context& context, const QueryFieldGroup& group)
{
sol::table res(context.mLua->sol(), sol::create);
for (const Queries::Field* field : group.mFields)
{
sol::table subgroup = res;
for (int i = 0; i < static_cast<int>(field->path().size()) - 1; ++i)
{
const std::string& name = field->path()[i];
if (subgroup[name] == sol::nil)
subgroup[name] = context.mLua->makeReadOnly(context.mLua->newTable());
subgroup = context.mLua->getMutableFromReadOnly(subgroup[name]);
}
subgroup[field->path().back()] = field;
}
return context.mLua->makeReadOnly(res);
}
}

@ -5,27 +5,21 @@
#include <components/lua/serialization.hpp>
#include <components/lua/scriptscontainer.hpp>
#include "context.hpp"
#include "eventqueue.hpp"
#include "object.hpp"
#include "query.hpp"
#include "worldview.hpp"
namespace MWLua
{
class LuaManager;
struct Context
{
LuaManager* mLuaManager;
LuaUtil::LuaState* mLua;
LuaUtil::UserdataSerializer* mSerializer;
WorldView* mWorldView;
LocalEventQueue* mLocalEventQueue;
GlobalEventQueue* mGlobalEventQueue;
};
sol::table initCorePackage(const Context&);
sol::table initWorldPackage(const Context&);
sol::table initNearbyPackage(const Context&);
sol::table initQueryPackage(const Context&);
sol::table initFieldGroup(const Context&, const QueryFieldGroup&);
// Implemented in objectbindings.cpp
void initObjectBindingsForLocalScripts(const Context&);

@ -31,6 +31,7 @@ namespace MWLua
mGlobalScripts.setSerializer(mGlobalSerializer.get());
Context context;
context.mIsGlobal = true;
context.mLuaManager = this;
context.mLua = &mLua;
context.mWorldView = &mWorldView;
@ -39,6 +40,7 @@ namespace MWLua
context.mSerializer = mGlobalSerializer.get();
Context localContext = context;
localContext.mIsGlobal = false;
localContext.mSerializer = mLocalSerializer.get();
initObjectBindingsForGlobalScripts(context);
@ -48,6 +50,7 @@ namespace MWLua
mLua.addCommonPackage("openmw.async", getAsyncPackageInitializer(context));
mLua.addCommonPackage("openmw.util", LuaUtil::initUtilPackage(mLua.sol()));
mLua.addCommonPackage("openmw.core", initCorePackage(context));
mLua.addCommonPackage("openmw.query", initQueryPackage(context));
mGlobalScripts.addPackage("openmw.world", initWorldPackage(context));
mCameraPackage = initCameraPackage(localContext);
mUserInterfacePackage = initUserInterfacePackage(localContext);

@ -1,6 +1,7 @@
#include "luabindings.hpp"
#include <components/lua/luastate.hpp>
#include <components/queries/query.hpp>
#include "../mwclass/door.hpp"
@ -64,6 +65,10 @@ namespace MWLua
};
return std::make_tuple(iter, list, 0);
};
listT["select"] = [context](const ListT& list, const Queries::Query& query)
{
return ListT{selectObjectsFromList(query, list.mIds, context)};
};
}
template <class ObjectT>

@ -0,0 +1,150 @@
#include "query.hpp"
#include <sol/sol.hpp>
#include <components/lua/luastate.hpp>
#include "../mwclass/container.hpp"
#include "worldview.hpp"
namespace MWLua
{
static std::vector<QueryFieldGroup> initBasicFieldGroups()
{
auto createGroup = [](std::string name, const auto& arr) -> QueryFieldGroup
{
std::vector<const Queries::Field*> fieldPtrs;
fieldPtrs.reserve(arr.size());
for (const Queries::Field& field : arr)
fieldPtrs.push_back(&field);
return {std::move(name), std::move(fieldPtrs)};
};
static std::array objectFields = {
Queries::Field({"type"}, typeid(std::string)),
Queries::Field({"recordId"}, typeid(std::string)),
Queries::Field({"count"}, typeid(int32_t)),
};
static std::array doorFields = {
Queries::Field({"isTeleport"}, typeid(bool)),
Queries::Field({"destCell"}, typeid(std::string)),
};
return std::vector<QueryFieldGroup>{
createGroup("OBJECT", objectFields),
createGroup("DOOR", doorFields),
};
}
const std::vector<QueryFieldGroup>& getBasicQueryFieldGroups()
{
static std::vector<QueryFieldGroup> fieldGroups = initBasicFieldGroups();
return fieldGroups;
}
ObjectIdList selectObjectsFromList(const Queries::Query& query, const ObjectIdList& list, const Context& context)
{
if (!query.mOrderBy.empty() || !query.mGroupBy.empty() || query.mOffset > 0)
throw std::runtime_error("OrderBy, GroupBy, and Offset are not supported");
ObjectIdList res = std::make_shared<std::vector<ObjectId>>();
std::vector<char> condStack;
auto compareFn = [](auto&& a, auto&& b, Queries::Condition::Type t)
{
switch (t)
{
case Queries::Condition::EQUAL: return a == b;
case Queries::Condition::NOT_EQUAL: return a != b;
case Queries::Condition::GREATER: return a > b;
case Queries::Condition::GREATER_OR_EQUAL: return a >= b;
case Queries::Condition::LESSER: return a < b;
case Queries::Condition::LESSER_OR_EQUAL: return a <= b;
default:
throw std::runtime_error("Unsupported condition type");
}
};
for (const ObjectId& id : *list)
{
if (static_cast<int64_t>(res->size()) == query.mLimit)
break;
sol::object obj;
MWWorld::Ptr ptr;
if (context.mIsGlobal)
{
GObject g(id, context.mWorldView->getObjectRegistry());
if (!g.isValid()) continue;
ptr = g.ptr();
obj = sol::make_object(context.mLua->sol(), g);
}
else
{
LObject l(id, context.mWorldView->getObjectRegistry());
if (!l.isValid()) continue;
ptr = l.ptr();
obj = sol::make_object(context.mLua->sol(), l);
}
const MWWorld::Class& cls = ptr.getClass();
if (cls.isActivator() && query.mQueryType != ObjectQueryTypes::ACTIVATORS)
continue;
if (cls.isActor() && query.mQueryType != ObjectQueryTypes::ACTORS)
continue;
if (cls.isDoor() && query.mQueryType != ObjectQueryTypes::DOORS)
continue;
if (typeid(cls) == typeid(MWClass::Container) && query.mQueryType != ObjectQueryTypes::CONTAINERS)
continue;
condStack.clear();
for (const Queries::Operation& op : query.mFilter.mOperations)
{
switch(op.mType)
{
case Queries::Operation::PUSH:
{
const Queries::Condition& cond = query.mFilter.mConditions[op.mConditionIndex];
sol::object fieldObj = obj;
for (const std::string& field : cond.mField->path())
fieldObj = sol::table(fieldObj)[field];
bool c;
if (cond.mField->type() == typeid(std::string))
c = compareFn(fieldObj.as<std::string_view>(), std::get<std::string>(cond.mValue), cond.mType);
else if (cond.mField->type() == typeid(float))
c = compareFn(fieldObj.as<float>(), std::get<float>(cond.mValue), cond.mType);
else if (cond.mField->type() == typeid(double))
c = compareFn(fieldObj.as<double>(), std::get<double>(cond.mValue), cond.mType);
else if (cond.mField->type() == typeid(bool))
c = compareFn(fieldObj.as<bool>(), std::get<bool>(cond.mValue), cond.mType);
else if (cond.mField->type() == typeid(int32_t))
c = compareFn(fieldObj.as<int32_t>(), std::get<int32_t>(cond.mValue), cond.mType);
else if (cond.mField->type() == typeid(int64_t))
c = compareFn(fieldObj.as<int64_t>(), std::get<int64_t>(cond.mValue), cond.mType);
else
throw std::runtime_error("Unknown field type");
condStack.push_back(c);
break;
}
case Queries::Operation::NOT:
condStack.back() = !condStack.back();
break;
case Queries::Operation::AND:
{
bool v = condStack.back();
condStack.pop_back();
condStack.back() = condStack.back() && v;
break;
}
case Queries::Operation::OR:
{
bool v = condStack.back();
condStack.pop_back();
condStack.back() = condStack.back() || v;
break;
}
}
}
if (condStack.empty() || condStack.back() != 0)
res->push_back(id);
}
return res;
}
}

@ -0,0 +1,38 @@
#ifndef MWLUA_QUERY_H
#define MWLUA_QUERY_H
#include <string>
#include <components/queries/query.hpp>
#include "context.hpp"
#include "object.hpp"
namespace MWLua
{
struct ObjectQueryTypes
{
static constexpr std::string_view ACTIVATORS = "activators";
static constexpr std::string_view ACTORS = "actors";
static constexpr std::string_view CONTAINERS = "containers";
static constexpr std::string_view DOORS = "doors";
static constexpr std::string_view ITEMS = "items";
static constexpr std::string_view types[] = {ACTIVATORS, ACTORS, CONTAINERS, DOORS, ITEMS};
};
struct QueryFieldGroup
{
std::string mName;
std::vector<const Queries::Field*> mFields;
};
const std::vector<QueryFieldGroup>& getBasicQueryFieldGroups();
// TODO: Implement custom fields. QueryFieldGroup registerCustomFields(...);
ObjectIdList selectObjectsFromList(const Queries::Query& query, const ObjectIdList& list, const Context&);
}
#endif // MWLUA_QUERY_H

@ -40,6 +40,10 @@ namespace MWLua
void objectAddedToScene(const MWWorld::Ptr& ptr);
void objectRemovedFromScene(const MWWorld::Ptr& ptr);
// Returns list of objects that meets the `query` criteria.
// If onlyActive = true, then search only among the objects that are currently in the scene.
// TODO: ObjectIdList selectObjects(const Queries::Query& query, bool onlyActive);
void load(ESM::ESMReader& esm);
void save(ESM::ESMWriter& esm) const;

Loading…
Cancel
Save