From fa115418eb97568b118d0c0851d7b4e1bbfb44ee Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sat, 5 Mar 2022 20:11:17 +0100 Subject: [PATCH] [Lua] Remove queries --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwlua/luabindings.cpp | 50 ----- apps/openmw/mwlua/luabindings.hpp | 4 - apps/openmw/mwlua/luamanagerimp.cpp | 1 - apps/openmw/mwlua/nearbybindings.cpp | 19 -- apps/openmw/mwlua/objectbindings.cpp | 8 +- apps/openmw/mwlua/query.cpp | 191 ------------------ apps/openmw/mwlua/query.hpp | 39 ---- apps/openmw_test_suite/CMakeLists.txt | 1 - .../lua/test_querypackage.cpp | 29 --- components/CMakeLists.txt | 4 - components/queries/luabindings.cpp | 118 ----------- components/queries/luabindings.hpp | 9 - components/queries/query.cpp | 185 ----------------- components/queries/query.hpp | 99 --------- docs/source/reference/lua-scripting/api.rst | 3 - .../reference/lua-scripting/openmw_query.rst | 5 - .../reference/lua-scripting/overview.rst | 94 --------- files/lua_api/CMakeLists.txt | 1 - files/lua_api/openmw/core.lua | 7 - files/lua_api/openmw/nearby.lua | 6 - files/lua_api/openmw/query.lua | 116 ----------- files/lua_api/openmw/world.lua | 6 - 23 files changed, 2 insertions(+), 995 deletions(-) delete mode 100644 apps/openmw/mwlua/query.cpp delete mode 100644 apps/openmw/mwlua/query.hpp delete mode 100644 apps/openmw_test_suite/lua/test_querypackage.cpp delete mode 100644 components/queries/luabindings.cpp delete mode 100644 components/queries/luabindings.hpp delete mode 100644 components/queries/query.cpp delete mode 100644 components/queries/query.hpp delete mode 100644 docs/source/reference/lua-scripting/openmw_query.rst delete mode 100644 files/lua_api/openmw/query.lua diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index c29b33b28c..9a83a5cdb7 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -57,7 +57,7 @@ add_openmw_dir (mwscript ) add_openmw_dir (mwlua - luamanagerimp actions object worldview userdataserializer eventqueue query + luamanagerimp actions object worldview userdataserializer eventqueue luabindings localscripts playerscripts objectbindings cellbindings asyncbindings settingsbindings camerabindings uibindings inputbindings nearbybindings types/types types/door types/actor types/container diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 655c7f6989..a46435b2ca 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/statemanager.hpp" @@ -90,59 +89,10 @@ namespace MWLua return sol::nullopt; }; 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)}; - }; // TODO: add world.placeNewObject(recordId, cell, pos, [rot]) return LuaUtil::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; - if (field->path().empty()) - throw std::logic_error("Empty path in Queries::Field"); - for (size_t i = 0; i < field->path().size() - 1; ++i) - { - const std::string& name = field->path()[i]; - if (subgroup[name] == sol::nil) - subgroup[name] = LuaUtil::makeReadOnly(context.mLua->newTable()); - subgroup = LuaUtil::getMutableFromReadOnly(subgroup[name]); - } - subgroup[field->path().back()] = field; - } - return LuaUtil::makeReadOnly(res); - } - sol::table initGlobalStoragePackage(const Context& context, LuaUtil::LuaStorage* globalStorage) { sol::table res(context.mLua->sol(), sol::create); diff --git a/apps/openmw/mwlua/luabindings.hpp b/apps/openmw/mwlua/luabindings.hpp index f7a405b437..41bc8b29ae 100644 --- a/apps/openmw/mwlua/luabindings.hpp +++ b/apps/openmw/mwlua/luabindings.hpp @@ -9,7 +9,6 @@ #include "context.hpp" #include "eventqueue.hpp" #include "object.hpp" -#include "query.hpp" #include "worldview.hpp" namespace MWWorld @@ -22,9 +21,6 @@ namespace MWLua sol::table initCorePackage(const Context&); sol::table initWorldPackage(const Context&); - sol::table initQueryPackage(const Context&); - - sol::table initFieldGroup(const Context&, const QueryFieldGroup&); sol::table initGlobalStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage); sol::table initLocalStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage); diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 9c5c210e4d..df49862df6 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -82,7 +82,6 @@ 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)); mLua.addCommonPackage("openmw.types", initTypesPackage(context)); mGlobalScripts.addPackage("openmw.world", initWorldPackage(context)); mGlobalScripts.addPackage("openmw.settings", initGlobalSettingsPackage(context)); diff --git a/apps/openmw/mwlua/nearbybindings.cpp b/apps/openmw/mwlua/nearbybindings.cpp index 624cf69da6..05bc52c5cc 100644 --- a/apps/openmw/mwlua/nearbybindings.cpp +++ b/apps/openmw/mwlua/nearbybindings.cpp @@ -1,7 +1,6 @@ #include "luabindings.hpp" #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -98,24 +97,6 @@ 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 LuaUtil::makeReadOnly(api); } } diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 6bcf355fde..08435583df 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -1,10 +1,8 @@ #include "luabindings.hpp" #include -#include - -#include "../mwclass/door.hpp" +#include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" @@ -77,10 +75,6 @@ namespace MWLua }); listT[sol::meta_function::ipairs] = [iter](const LObjectList& list) { 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 diff --git a/apps/openmw/mwlua/query.cpp b/apps/openmw/mwlua/query.cpp deleted file mode 100644 index c357e56884..0000000000 --- a/apps/openmw/mwlua/query.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "query.hpp" - -#include - -#include - -#include "../mwclass/container.hpp" -#include "../mwworld/cellstore.hpp" - -#include "worldview.hpp" - -namespace MWLua -{ - - static std::vector initBasicFieldGroups() - { - auto createGroup = [](std::string name, const auto& arr) -> QueryFieldGroup - { - std::vector 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({"cell", "name"}, typeid(std::string)), - Queries::Field({"cell", "region"}, typeid(std::string)), - Queries::Field({"cell", "isExterior"}, typeid(bool)), - Queries::Field({"count"}, typeid(int32_t)), - }; - static std::array doorFields = { - Queries::Field({"isTeleport"}, typeid(bool)), - Queries::Field({"destCell", "name"}, typeid(std::string)), - Queries::Field({"destCell", "region"}, typeid(std::string)), - Queries::Field({"destCell", "isExterior"}, typeid(bool)), - }; - return std::vector{ - createGroup("OBJECT", objectFields), - createGroup("DOOR", doorFields), - }; - } - - const std::vector& getBasicQueryFieldGroups() - { - static std::vector fieldGroups = initBasicFieldGroups(); - return fieldGroups; - } - - bool checkQueryConditions(const Queries::Query& query, const ObjectId& id, const Context& context) - { - 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"); - } - }; - sol::object obj; - MWWorld::Ptr ptr; - if (context.mIsGlobal) - { - GObject g(id, context.mWorldView->getObjectRegistry()); - if (!g.isValid()) - return false; - ptr = g.ptr(); - obj = sol::make_object(context.mLua->sol(), g); - } - else - { - LObject l(id, context.mWorldView->getObjectRegistry()); - if (!l.isValid()) - return false; - ptr = l.ptr(); - obj = sol::make_object(context.mLua->sol(), l); - } - if (ptr.getRefData().getCount() == 0) - return false; - - // It is important to exclude all markers before checking what class it is. - // For example "prisonmarker" has class "Door" despite that it is only an invisible marker. - if (isMarker(ptr)) - return false; - - const MWWorld::Class& cls = ptr.getClass(); - if (cls.isActivator() != (query.mQueryType == ObjectQueryTypes::ACTIVATORS)) - return false; - if (cls.isActor() != (query.mQueryType == ObjectQueryTypes::ACTORS)) - return false; - if (cls.isDoor() != (query.mQueryType == ObjectQueryTypes::DOORS)) - return false; - if ((typeid(cls) == typeid(MWClass::Container)) != (query.mQueryType == ObjectQueryTypes::CONTAINERS)) - return false; - - std::vector condStack; - 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 = LuaUtil::getFieldOrNil(fieldObj, field); - bool c; - if (fieldObj == sol::nil) - c = false; - else if (cond.mField->type() == typeid(std::string)) - c = compareFn(fieldObj.as(), std::get(cond.mValue), cond.mType); - else if (cond.mField->type() == typeid(float)) - c = compareFn(fieldObj.as(), std::get(cond.mValue), cond.mType); - else if (cond.mField->type() == typeid(double)) - c = compareFn(fieldObj.as(), std::get(cond.mValue), cond.mType); - else if (cond.mField->type() == typeid(bool)) - c = compareFn(fieldObj.as(), std::get(cond.mValue), cond.mType); - else if (cond.mField->type() == typeid(int32_t)) - c = compareFn(fieldObj.as(), std::get(cond.mValue), cond.mType); - else if (cond.mField->type() == typeid(int64_t)) - c = compareFn(fieldObj.as(), std::get(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; - } - } - } - return condStack.empty() || condStack.back() != 0; - } - - 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>(); - for (const ObjectId& id : *list) - { - if (static_cast(res->size()) == query.mLimit) - break; - if (checkQueryConditions(query, id, context)) - res->push_back(id); - } - return res; - } - - ObjectIdList selectObjectsFromCellStore(const Queries::Query& query, MWWorld::CellStore* store, 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>(); - auto visitor = [&](const MWWorld::Ptr& ptr) - { - if (static_cast(res->size()) == query.mLimit) - return false; - context.mWorldView->getObjectRegistry()->registerPtr(ptr); - if (checkQueryConditions(query, getId(ptr), context)) - res->push_back(getId(ptr)); - return static_cast(res->size()) != query.mLimit; - }; - store->forEach(std::move(visitor)); // TODO: maybe use store->forEachType depending on query.mType - return res; - } - -} diff --git a/apps/openmw/mwlua/query.hpp b/apps/openmw/mwlua/query.hpp deleted file mode 100644 index 65bf0c5105..0000000000 --- a/apps/openmw/mwlua/query.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef MWLUA_QUERY_H -#define MWLUA_QUERY_H - -#include - -#include - -#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 mFields; - }; - const std::vector& getBasicQueryFieldGroups(); - - // TODO: Implement custom fields. QueryFieldGroup registerCustomFields(...); - - ObjectIdList selectObjectsFromList(const Queries::Query& query, const ObjectIdList& list, const Context&); - ObjectIdList selectObjectsFromCellStore(const Queries::Query& query, MWWorld::CellStore* store, const Context&); - -} - -#endif // MWLUA_QUERY_H diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 73e7af1d9e..767eebe6c8 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -21,7 +21,6 @@ if (GTEST_FOUND AND GMOCK_FOUND) lua/test_scriptscontainer.cpp lua/test_utilpackage.cpp lua/test_serialization.cpp - lua/test_querypackage.cpp lua/test_configuration.cpp lua/test_i18n.cpp lua/test_storage.cpp diff --git a/apps/openmw_test_suite/lua/test_querypackage.cpp b/apps/openmw_test_suite/lua/test_querypackage.cpp deleted file mode 100644 index aeaf992db0..0000000000 --- a/apps/openmw_test_suite/lua/test_querypackage.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "gmock/gmock.h" -#include - -#include - -namespace -{ - using namespace testing; - - TEST(LuaQueryPackageTest, basic) - { - sol::state lua; - lua.open_libraries(sol::lib::base, sol::lib::string); - Queries::registerQueryBindings(lua); - lua["query"] = Queries::Query("test"); - lua["fieldX"] = Queries::Field({ "x" }, typeid(std::string)); - lua["fieldY"] = Queries::Field({ "y" }, typeid(int)); - lua.safe_script("t = query:where(fieldX:eq('abc') + fieldX:like('%abcd%'))"); - lua.safe_script("t = t:where(fieldY:gt(5))"); - lua.safe_script("t = t:orderBy(fieldX)"); - lua.safe_script("t = t:orderByDesc(fieldY)"); - lua.safe_script("t = t:groupBy(fieldY)"); - lua.safe_script("t = t:limit(10):offset(5)"); - EXPECT_EQ( - lua.safe_script("return tostring(t)").get(), - "SELECT test WHERE ((x == \"abc\") OR (x LIKE \"%abcd%\")) AND (y > 5) ORDER BY x, y DESC GROUP BY y LIMIT 10 OFFSET 5"); - } -} - diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e183d54a95..efc80081e3 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -253,10 +253,6 @@ add_component_dir (fallback fallback validate ) -add_component_dir (queries - query luabindings - ) - add_component_dir (lua_ui registerscriptsettings scriptsettings properties widget element util layers content alignment resources diff --git a/components/queries/luabindings.cpp b/components/queries/luabindings.cpp deleted file mode 100644 index c830a140f7..0000000000 --- a/components/queries/luabindings.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "luabindings.hpp" - -namespace sol -{ - template <> - struct is_automagical : std::false_type {}; - - template <> - struct is_automagical : std::false_type {}; - - template <> - struct is_automagical : std::false_type {}; -} - -namespace Queries -{ - template - struct CondBuilder - { - Filter operator()(const Field& field, const sol::object& o) - { - FieldValue value; - if (field.type() == typeid(bool) && o.is()) - value = o.as(); - else if (field.type() == typeid(int32_t) && o.is()) - value = o.as(); - else if (field.type() == typeid(int64_t) && o.is()) - value = o.as(); - else if (field.type() == typeid(float) && o.is()) - value = o.as(); - else if (field.type() == typeid(double) && o.is()) - value = o.as(); - else if (field.type() == typeid(std::string) && o.is()) - value = o.as(); - else - throw std::logic_error("Invalid value for field " + field.toString()); - Filter filter; - filter.add({&field, type, value}); - return filter; - } - }; - - void registerQueryBindings(sol::state& lua) - { - sol::usertype field = lua.new_usertype("QueryField"); - sol::usertype filter = lua.new_usertype("QueryFilter"); - sol::usertype query = lua.new_usertype("Query"); - - field[sol::meta_function::to_string] = [](const Field& f) { return f.toString(); }; - field["eq"] = CondBuilder(); - field["neq"] = CondBuilder(); - field["lt"] = CondBuilder(); - field["lte"] = CondBuilder(); - field["gt"] = CondBuilder(); - field["gte"] = CondBuilder(); - field["like"] = CondBuilder(); - - filter[sol::meta_function::to_string] = [](const Filter& filter) { return filter.toString(); }; - filter[sol::meta_function::multiplication] = [](const Filter& a, const Filter& b) - { - Filter res = a; - res.add(b, Operation::AND); - return res; - }; - filter[sol::meta_function::addition] = [](const Filter& a, const Filter& b) - { - Filter res = a; - res.add(b, Operation::OR); - return res; - }; - filter[sol::meta_function::unary_minus] = [](const Filter& a) - { - Filter res = a; - if (!a.mConditions.empty()) - res.mOperations.push_back({Operation::NOT, 0}); - return res; - }; - - query[sol::meta_function::to_string] = [](const Query& q) { return q.toString(); }; - query["where"] = [](const Query& q, const Filter& filter) - { - Query res = q; - res.mFilter.add(filter, Operation::AND); - return res; - }; - query["orderBy"] = [](const Query& q, const Field& field) - { - Query res = q; - res.mOrderBy.push_back({&field, false}); - return res; - }; - query["orderByDesc"] = [](const Query& q, const Field& field) - { - Query res = q; - res.mOrderBy.push_back({&field, true}); - return res; - }; - query["groupBy"] = [](const Query& q, const Field& field) - { - Query res = q; - res.mGroupBy.push_back(&field); - return res; - }; - query["offset"] = [](const Query& q, int64_t offset) - { - Query res = q; - res.mOffset = offset; - return res; - }; - query["limit"] = [](const Query& q, int64_t limit) - { - Query res = q; - res.mLimit = limit; - return res; - }; - } -} - diff --git a/components/queries/luabindings.hpp b/components/queries/luabindings.hpp deleted file mode 100644 index 16d1a822be..0000000000 --- a/components/queries/luabindings.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#include // missing from sol/sol.hpp -#include - -#include "query.hpp" - -namespace Queries -{ - void registerQueryBindings(sol::state& lua); -} diff --git a/components/queries/query.cpp b/components/queries/query.cpp deleted file mode 100644 index 3c7f1517ee..0000000000 --- a/components/queries/query.cpp +++ /dev/null @@ -1,185 +0,0 @@ -#include "query.hpp" - -#include -#include - -namespace Queries -{ - Field::Field(std::vector path, std::type_index type) - : mPath(std::move(path)) - , mType(type) {} - - std::string Field::toString() const - { - std::string result; - for (const std::string& segment : mPath) - { - if (!result.empty()) - result += "."; - result += segment; - } - return result; - } - - std::string toString(const FieldValue& value) - { - return std::visit([](auto&& arg) -> std::string - { - using T = std::decay_t; - if constexpr (std::is_same_v) - { - std::ostringstream oss; - oss << std::quoted(arg); - return oss.str(); - } - else if constexpr (std::is_same_v) - return arg ? "true" : "false"; - else - return std::to_string(arg); - }, value); - } - - std::string Condition::toString() const - { - std::string res; - res += mField->toString(); - switch (mType) - { - case Condition::EQUAL: res += " == "; break; - case Condition::NOT_EQUAL: res += " != "; break; - case Condition::LESSER: res += " < "; break; - case Condition::LESSER_OR_EQUAL: res += " <= "; break; - case Condition::GREATER: res += " > "; break; - case Condition::GREATER_OR_EQUAL: res += " >= "; break; - case Condition::LIKE: res += " LIKE "; break; - } - res += Queries::toString(mValue); - return res; - } - - void Filter::add(const Condition& c, Operation::Type op) - { - mOperations.push_back({Operation::PUSH, mConditions.size()}); - mConditions.push_back(c); - if (mConditions.size() > 1) - mOperations.push_back({op, 0}); - } - - void Filter::add(const Filter& f, Operation::Type op) - { - size_t conditionOffset = mConditions.size(); - size_t operationsBefore = mOperations.size(); - mConditions.insert(mConditions.end(), f.mConditions.begin(), f.mConditions.end()); - mOperations.insert(mOperations.end(), f.mOperations.begin(), f.mOperations.end()); - for (size_t i = operationsBefore; i < mOperations.size(); ++i) - mOperations[i].mConditionIndex += conditionOffset; - if (conditionOffset > 0 && !f.mConditions.empty()) - mOperations.push_back({op, 0}); - } - - std::string Filter::toString() const - { - if(mOperations.empty()) - return ""; - std::vector stack; - auto pop = [&stack](){ auto v = stack.back(); stack.pop_back(); return v; }; - auto push = [&stack](const std::string& s) { stack.push_back(s); }; - for (const Operation& op : mOperations) - { - if(op.mType == Operation::PUSH) - push(mConditions[op.mConditionIndex].toString()); - else if(op.mType == Operation::AND) - { - auto rhs = pop(); - auto lhs = pop(); - std::string res; - res += "("; - res += lhs; - res += ") AND ("; - res += rhs; - res += ")"; - push(res); - } - else if (op.mType == Operation::OR) - { - auto rhs = pop(); - auto lhs = pop(); - std::string res; - res += "("; - res += lhs; - res += ") OR ("; - res += rhs; - res += ")"; - push(res); - } - else if (op.mType == Operation::NOT) - { - std::string res; - res += "NOT ("; - res += pop(); - res += ")"; - push(res); - } - else - throw std::logic_error("Unknown operation type!"); - } - return pop(); - } - - std::string Query::toString() const - { - std::string res; - res += "SELECT "; - res += mQueryType; - - std::string filter = mFilter.toString(); - if(!filter.empty()) - { - res += " WHERE "; - res += filter; - } - - std::string order; - for(const OrderBy& ord : mOrderBy) - { - if(!order.empty()) - order += ", "; - order += ord.mField->toString(); - if(ord.mDescending) - order += " DESC"; - } - if (!order.empty()) - { - res += " ORDER BY "; - res += order; - } - - std::string group; - for (const Field* f: mGroupBy) - { - if (!group.empty()) - group += " ,"; - group += f->toString(); - } - if (!group.empty()) - { - res += " GROUP BY "; - res += group; - } - - if (mLimit != sNoLimit) - { - res += " LIMIT "; - res += std::to_string(mLimit); - } - - if (mOffset != 0) - { - res += " OFFSET "; - res += std::to_string(mOffset); - } - - return res; - } -} - diff --git a/components/queries/query.hpp b/components/queries/query.hpp deleted file mode 100644 index 45144fed62..0000000000 --- a/components/queries/query.hpp +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef COMPONENTS_QUERIES_QUERY -#define COMPONENTS_QUERIES_QUERY - -#include -#include -#include -#include -#include - -namespace Queries -{ - class Field - { - public: - Field(std::vector path, std::type_index type); - - const std::vector& path() const { return mPath; } - const std::type_index type() const { return mType; } - - std::string toString() const; - - private: - std::vector mPath; - std::type_index mType; - }; - - struct OrderBy - { - const Field* mField; - bool mDescending; - }; - - using FieldValue = std::variant; - std::string toString(const FieldValue& value); - - struct Condition - { - enum Type - { - EQUAL = 0, - NOT_EQUAL = 1, - GREATER = 2, - GREATER_OR_EQUAL = 3, - LESSER = 4, - LESSER_OR_EQUAL = 5, - LIKE = 6, - }; - - std::string toString() const; - - const Field* mField; - Type mType; - FieldValue mValue; - }; - - struct Operation - { - enum Type - { - PUSH = 0, // push condition on stack - NOT = 1, // invert top condition on stack - AND = 2, // logic AND for two top conditions - OR = 3, // logic OR for two top conditions - }; - - Type mType; - size_t mConditionIndex; // used only if mType == PUSH - }; - - struct Filter - { - std::string toString() const; - - // combines with given condition or filter using operation `AND` or `OR`. - void add(const Condition& c, Operation::Type op = Operation::AND); - void add(const Filter& f, Operation::Type op = Operation::AND); - - std::vector mConditions; - std::vector mOperations; // operations on conditions in reverse polish notation - }; - - struct Query - { - static constexpr int64_t sNoLimit = -1; - - Query(std::string type) : mQueryType(std::move(type)) {} - std::string toString() const; - - std::string mQueryType; - Filter mFilter; - std::vector mOrderBy; - std::vector mGroupBy; - int64_t mOffset = 0; - int64_t mLimit = sNoLimit; - }; -} - -#endif // !COMPONENTS_QUERIES_QUERY - diff --git a/docs/source/reference/lua-scripting/api.rst b/docs/source/reference/lua-scripting/api.rst index 4518028073..cf071534d9 100644 --- a/docs/source/reference/lua-scripting/api.rst +++ b/docs/source/reference/lua-scripting/api.rst @@ -14,7 +14,6 @@ Lua API reference openmw_core openmw_types openmw_async - openmw_query openmw_world openmw_self openmw_nearby @@ -61,8 +60,6 @@ Player scripts are local scripts that are attached to a player. +---------------------------------------------------------+--------------------+---------------------------------------------------------------+ |:ref:`openmw.async ` | everywhere | | Timers (implemented) and coroutine utils (not implemented) | +---------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.query ` | everywhere | | Tools for constructing queries: base queries and fields. | -+---------------------------------------------------------+--------------------+---------------------------------------------------------------+ |:ref:`openmw.world ` | by global scripts | | Read-write access to the game world. | +---------------------------------------------------------+--------------------+---------------------------------------------------------------+ |:ref:`openmw.self ` | by local scripts | | Full access to the object the script is attached to. | diff --git a/docs/source/reference/lua-scripting/openmw_query.rst b/docs/source/reference/lua-scripting/openmw_query.rst deleted file mode 100644 index ea0932e8f0..0000000000 --- a/docs/source/reference/lua-scripting/openmw_query.rst +++ /dev/null @@ -1,5 +0,0 @@ -Package openmw.query -==================== - -.. raw:: html - :file: generated_html/openmw_query.html diff --git a/docs/source/reference/lua-scripting/overview.rst b/docs/source/reference/lua-scripting/overview.rst index a819235e2a..91c0a071e0 100644 --- a/docs/source/reference/lua-scripting/overview.rst +++ b/docs/source/reference/lua-scripting/overview.rst @@ -357,8 +357,6 @@ Player scripts are local scripts that are attached to a player. +---------------------------------------------------------+--------------------+---------------------------------------------------------------+ |:ref:`openmw.async ` | everywhere | | Timers (implemented) and coroutine utils (not implemented) | +---------------------------------------------------------+--------------------+---------------------------------------------------------------+ -|:ref:`openmw.query ` | everywhere | | Tools for constructing queries: base queries and fields. | -+---------------------------------------------------------+--------------------+---------------------------------------------------------------+ |:ref:`openmw.world ` | by global scripts | | Read-write access to the game world. | +---------------------------------------------------------+--------------------+---------------------------------------------------------------+ |:ref:`openmw.self ` | by local scripts | | Full access to the object the script is attached to. | @@ -632,98 +630,6 @@ Also in `openmw_aux`_ is the helper function ``runRepeatedly``, it is implemente }) -Queries -======= - -`openmw.query` contains base queries of each type (e.g. `query.doors`, `query.containers`...), which return all of the objects of given type in no particular order. You can then modify that query to filter the results, sort them, group them, etc. Queries are immutable, so any operations on them return a new copy, leaving the original unchanged. - -`openmw.world.selectObjects` and `openmw.nearby.selectObjects` both accept a query and return objects that match it. However, `nearby.selectObjects` is only available in local scripts, and returns only objects from currently active cells, while `world.selectObjects` is only available in global scripts, and returns objects regardless of them being in active cells. -**TODO:** describe how to filter out inactive objects from world queries - -An example of an object query: - -.. code-block:: Lua - - local query = require('openmw.query') - local nearby = require('openmw.nearby') - local ui = require('openmw.ui') - - local function selectDoors(namePattern) - local query = query.doors:where(query.DOOR.destCell.name:like(namePattern)) - return nearby.selectObjects(query) - end - - local function showGuildDoors() - ui.showMessage('Here are all the entrances to guilds!') - for _, door in selectDoors("%Guild%"):ipairs() do - local pos = door.position - local message = string.format("%.0f;%.0f;%.0f", pos.x, pos.y, pos.z) - ui.showMessage(message) - end - end - - return { - engineHandlers = { - onKeyPress = function(key) - if key.symbol == 'e' then - showGuildDoors() - end - end - } - } - -.. warning:: - The example above uses operation `like` that is not implemented yet. - -**TODO:** add non-object queries, explain how relations work, and define what a field is - -Queries are constructed through the following method calls: (if you've used SQL before, you will find them familiar) - -- `:where(filter)` - filters the results to match the combination of conditions passed as the argument -- `:orderBy(field)` and `:orderByDesc(field)` sort the result by the `field` argument. Sorts in descending order in case of `:orderByDesc`. Multiple calls can be chained, with the first call having priority. (i. e. if the first field is equal, objects are sorted by the second one...) **(not implemented yet)** -- `:groupBy(field)` returns only one result for each value of the `field` argument. The choice of the result is arbitrary. Useful for counting only unique objects, or checking if certain objects exist. **(not implemented yet)** -- `:limit(number)` will only return `number` of results (or fewer) -- `:offset(number)` skips the first `number` results. Particularly useful in combination with `:limit` **(not implemented yet)** - -Filters consist of conditions, which are combined with "and" (operator `*`), "or" (operator `+`), "not" (operator `-`) and braces `()`. - -To make a condition, take a field from the `openmw.query` package and call any of the following methods: - -- `:eq` equal to -- `:neq` not equal to -- `:gt` greater than -- `:gte` greater or equal to -- `:lt` less than -- `:lte` less or equal to -- `:like` matches a pattern. Only applicable to text (strings) **(not implemented yet)** - -**TODO:** describe the pattern format - -All the condition methods are type sensitive, and will throw an error if you pass a value of the wrong type into them. - -A few examples of filters: - -.. warning:: - `openmw.query.ACTOR` is not implemented yet - -.. code-block:: Lua - - local query = require('openmw.query') - local ACTOR = query.ACTOR - - local strong_guys_from_capital = (ACTOR.stats.level:gt(10) + ACTOR.stats.strength:gt(70)) - * ACTOR.cell.name:eq("Default city") - - -- could also write like this: - local strong_guys = ACTOR.stats.level:gt(10) + ACTOR.stats.strength:gt(70) - local guys_from_capital = ACTOR.cell.name:eq("Default city") - local strong_guys_from_capital_2 = strong_guys * guys_from_capital - - local DOOR = query.DOOR - - local interestingDoors = -DOOR.name:eq("") * DOOR.isTeleport:eq(true) * Door.destCell.isExterior:eq(false) - - Using IDE for Lua scripting =========================== diff --git a/files/lua_api/CMakeLists.txt b/files/lua_api/CMakeLists.txt index 2988f38552..88efc8195c 100644 --- a/files/lua_api/CMakeLists.txt +++ b/files/lua_api/CMakeLists.txt @@ -8,7 +8,6 @@ set(LUA_API_FILES openmw/async.lua openmw/core.lua openmw/nearby.lua - openmw/query.lua openmw/self.lua openmw/ui.lua openmw/util.lua diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index afb0d2f5bb..43b2d29a70 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -159,13 +159,6 @@ -- @type ObjectList -- @list <#GameObject> ---- --- Filter list with a Query. --- @function [parent=#ObjectList] select --- @param self --- @param openmw.query#Query query --- @return #ObjectList - --- -- A cell of the game world. diff --git a/files/lua_api/openmw/nearby.lua b/files/lua_api/openmw/nearby.lua index c3403ded0c..3ba3d7e6fb 100644 --- a/files/lua_api/openmw/nearby.lua +++ b/files/lua_api/openmw/nearby.lua @@ -26,12 +26,6 @@ -- Everything that can be picked up in the nearby. -- @field [parent=#nearby] openmw.core#ObjectList items ---- --- Evaluates a Query. --- @function [parent=#nearby] selectObjects --- @param openmw.query#Query query --- @return openmw.core#ObjectList - --- -- @type COLLISION_TYPE -- @field [parent=#COLLISION_TYPE] #number World diff --git a/files/lua_api/openmw/query.lua b/files/lua_api/openmw/query.lua deleted file mode 100644 index b5cea061ff..0000000000 --- a/files/lua_api/openmw/query.lua +++ /dev/null @@ -1,116 +0,0 @@ ---- --- `openmw.query` constructs queries that can be used in `world.selectObjects` and `nearby.selectObjects`. --- @module query --- @usage local query = require('openmw.query') - - - ---- --- Query. A Query itself can no return objects. It only holds search conditions. --- @type Query - ---- --- Add condition. --- @function [parent=#Query] where --- @param self --- @param condition --- @return #Query - ---- --- Limit result size. --- @function [parent=#Query] limit --- @param self --- @param #number maxCount --- @return #Query - - ---- --- A field that can be used in a condition --- @type Field - ---- --- Equal --- @function [parent=#Field] eq --- @param self --- @param value - ---- --- Not equal --- @function [parent=#Field] neq --- @param self --- @param value - ---- --- Lesser --- @function [parent=#Field] lt --- @param self --- @param value - ---- --- Lesser or equal --- @function [parent=#Field] lte --- @param self --- @param value - ---- --- Greater --- @function [parent=#Field] gt --- @param self --- @param value - ---- --- Greater or equal --- @function [parent=#Field] gte --- @param self --- @param value - - ---- --- @type OBJECT --- @field [parent=#OBJECT] #Field type --- @field [parent=#OBJECT] #Field recordId --- @field [parent=#OBJECT] #Field count --- @field [parent=#OBJECT] #CellFields cell - ---- --- Fields that can be used with any object. --- @field [parent=#query] #OBJECT OBJECT - ---- --- @type DOOR --- @field [parent=#DOOR] #Field isTeleport --- @field [parent=#DOOR] #CellFields destCell - ---- --- Fields that can be used only when search for doors. --- @field [parent=#query] #DOOR DOOR - ---- --- @type CellFields --- @field [parent=#CellFields] #Field name --- @field [parent=#CellFields] #Field region --- @field [parent=#CellFields] #Field isExterior - - ---- --- Base Query to select activators. --- @field [parent=#query] #Query activators - ---- --- Base Query to select actors. --- @field [parent=#query] #Query actors - ---- --- Base Query to select containers. --- @field [parent=#query] #Query containers - ---- --- Base Query to select doors. --- @field [parent=#query] #Query doors - ---- --- Base Query to select items. --- @field [parent=#query] #Query items - -return nil - diff --git a/files/lua_api/openmw/world.lua b/files/lua_api/openmw/world.lua index 127c41c064..1f5d9a539b 100644 --- a/files/lua_api/openmw/world.lua +++ b/files/lua_api/openmw/world.lua @@ -10,12 +10,6 @@ -- List of currently active actors. -- @field [parent=#world] openmw.core#ObjectList activeActors ---- --- Evaluates a Query. --- @function [parent=#world] selectObjects --- @param openmw.query#Query query --- @return openmw.core#ObjectList - --- -- Loads a named cell -- @function [parent=#world] getCellByName