mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-20 15:40:32 +00:00
Add lua package 'openmw.query'
This commit is contained in:
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
|
||||
|
30
apps/openmw/mwlua/context.hpp
Normal file
30
apps/openmw/mwlua/context.hpp
Normal file
@ -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>
|
||||
|
150
apps/openmw/mwlua/query.cpp
Normal file
150
apps/openmw/mwlua/query.cpp
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
38
apps/openmw/mwlua/query.hpp
Normal file
38
apps/openmw/mwlua/query.hpp
Normal file
@ -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…
x
Reference in New Issue
Block a user