diff --git a/apps/openmw/mwlua/camerabindings.cpp b/apps/openmw/mwlua/camerabindings.cpp index 3b13e02936..a62f743b5d 100644 --- a/apps/openmw/mwlua/camerabindings.cpp +++ b/apps/openmw/mwlua/camerabindings.cpp @@ -1,5 +1,6 @@ #include "luabindings.hpp" +#include #include #include @@ -13,15 +14,15 @@ namespace MWLua using CameraMode = MWRender::Camera::Mode; - sol::table initCameraPackage(const Context& context) + sol::table initCameraPackage(sol::state_view& lua) { MWRender::Camera* camera = MWBase::Environment::get().getWorld()->getCamera(); MWRender::RenderingManager* renderingManager = MWBase::Environment::get().getWorld()->getRenderingManager(); - sol::table api(context.mLua->sol(), sol::create); + sol::table api(lua, sol::create); api["MODE"] = LuaUtil::makeStrictReadOnly( - context.mLua->sol().create_table_with("Static", CameraMode::Static, "FirstPerson", CameraMode::FirstPerson, - "ThirdPerson", CameraMode::ThirdPerson, "Vanity", CameraMode::Vanity, "Preview", CameraMode::Preview)); + lua.create_table_with("Static", CameraMode::Static, "FirstPerson", CameraMode::FirstPerson, "ThirdPerson", + CameraMode::ThirdPerson, "Vanity", CameraMode::Vanity, "Preview", CameraMode::Preview)); api["getMode"] = [camera]() -> int { return static_cast(camera->getMode()); }; api["getQueuedMode"] = [camera]() -> sol::optional { diff --git a/apps/openmw/mwlua/camerabindings.hpp b/apps/openmw/mwlua/camerabindings.hpp new file mode 100644 index 0000000000..be468495e1 --- /dev/null +++ b/apps/openmw/mwlua/camerabindings.hpp @@ -0,0 +1,11 @@ +#ifndef MWLUA_CAMERABINDINGS_H +#define MWLUA_CAMERABINDINGS_H + +#include + +namespace MWLua +{ + sol::table initCameraPackage(sol::state_view& lua); +} + +#endif // MWLUA_CAMERABINDINGS_H diff --git a/apps/openmw/mwlua/cellbindings.cpp b/apps/openmw/mwlua/cellbindings.cpp index d8d4e66d91..2021bca728 100644 --- a/apps/openmw/mwlua/cellbindings.cpp +++ b/apps/openmw/mwlua/cellbindings.cpp @@ -1,4 +1,4 @@ -#include "luabindings.hpp" +#include "cellbindings.hpp" #include #include diff --git a/apps/openmw/mwlua/cellbindings.hpp b/apps/openmw/mwlua/cellbindings.hpp new file mode 100644 index 0000000000..0d8e90e989 --- /dev/null +++ b/apps/openmw/mwlua/cellbindings.hpp @@ -0,0 +1,12 @@ +#ifndef MWLUA_CELLBINDINGS_H +#define MWLUA_CELLBINDINGS_H + +#include "context.hpp" + +namespace MWLua +{ + void initCellBindingsForLocalScripts(const Context&); + void initCellBindingsForGlobalScripts(const Context&); +} + +#endif // MWLUA_CELLBINDINGS_H diff --git a/apps/openmw/mwlua/engineevents.cpp b/apps/openmw/mwlua/engineevents.cpp index 2aafde2264..0c5abe6cef 100644 --- a/apps/openmw/mwlua/engineevents.cpp +++ b/apps/openmw/mwlua/engineevents.cpp @@ -1,7 +1,7 @@ #include "engineevents.hpp" #include -#include +#include #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" @@ -75,7 +75,7 @@ namespace MWLua MWWorld::Ptr getPtr(const ESM::RefNum& id) const { MWWorld::Ptr res = mWorldModel->getPtr(id); - if (res.isEmpty() && mLuaDebug) + if (res.isEmpty() && Settings::lua().mLuaDebug) Log(Debug::Verbose) << "Can not find object" << id.toString() << " when calling engine hanglers"; return res; } @@ -91,7 +91,6 @@ namespace MWLua LocalScripts* getLocalScripts(const ESM::RefNum& id) const { return getLocalScripts(getPtr(id)); } GlobalScripts& mGlobalScripts; - bool mLuaDebug = Settings::Manager::getBool("lua debug", "Lua"); MWWorld::WorldModel* mWorldModel = MWBase::Environment::get().getWorldModel(); }; diff --git a/apps/openmw/mwlua/inputbindings.cpp b/apps/openmw/mwlua/inputbindings.cpp index ae3ce82fcb..9384eccdbc 100644 --- a/apps/openmw/mwlua/inputbindings.cpp +++ b/apps/openmw/mwlua/inputbindings.cpp @@ -1,9 +1,10 @@ -#include "luabindings.hpp" +#include "inputbindings.hpp" #include #include #include +#include #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwlua/inputbindings.hpp b/apps/openmw/mwlua/inputbindings.hpp new file mode 100644 index 0000000000..195f69f5f9 --- /dev/null +++ b/apps/openmw/mwlua/inputbindings.hpp @@ -0,0 +1,13 @@ +#ifndef MWLUA_INPUTBINDINGS_H +#define MWLUA_INPUTBINDINGS_H + +#include + +#include "context.hpp" + +namespace MWLua +{ + sol::table initInputPackage(const Context&); +} + +#endif // MWLUA_INPUTBINDINGS_H diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index d62b1c10ba..d0c38f31b1 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -3,11 +3,12 @@ #include #include -#include #include #include + #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/statemanager.hpp" @@ -22,7 +23,16 @@ #include "luamanagerimp.hpp" #include "worldview.hpp" +#include "camerabindings.hpp" +#include "cellbindings.hpp" +#include "debugbindings.hpp" +#include "inputbindings.hpp" #include "magicbindings.hpp" +#include "nearbybindings.hpp" +#include "objectbindings.hpp" +#include "postprocessingbindings.hpp" +#include "types/types.hpp" +#include "uibindings.hpp" namespace MWLua { @@ -54,7 +64,7 @@ namespace MWLua // api["resume"] = []() {}; } - sol::table initCorePackage(const Context& context) + static sol::table initCorePackage(const Context& context) { auto* lua = context.mLua; sol::table api(lua->sol(), sol::create); @@ -96,7 +106,7 @@ namespace MWLua return LuaUtil::makeReadOnly(api); } - sol::table initWorldPackage(const Context& context) + static sol::table initWorldPackage(const Context& context) { sol::table api(context.mLua->sol(), sol::create); WorldView* worldView = context.mWorldView; @@ -137,33 +147,48 @@ namespace MWLua return LuaUtil::makeReadOnly(api); } - sol::table initGlobalStoragePackage(const Context& context, LuaUtil::LuaStorage* globalStorage) + std::map initCommonPackages(const Context& context) + { + sol::state_view lua = context.mLua->sol(); + WorldView* w = context.mWorldView; + return { + { "openmw.async", + LuaUtil::getAsyncPackageInitializer( + lua, [w] { return w->getSimulationTime(); }, [w] { return w->getGameTime(); }) }, + { "openmw.core", initCorePackage(context) }, + { "openmw.types", initTypesPackage(context) }, + { "openmw.util", LuaUtil::initUtilPackage(lua) }, + }; + } + + std::map initGlobalPackages(const Context& context) { - sol::table res(context.mLua->sol(), sol::create); - res["globalSection"] - = [globalStorage](std::string_view section) { return globalStorage->getMutableSection(section); }; - res["allGlobalSections"] = [globalStorage]() { return globalStorage->getAllSections(); }; - return LuaUtil::makeReadOnly(res); + initObjectBindingsForGlobalScripts(context); + initCellBindingsForGlobalScripts(context); + return { + { "openmw.world", initWorldPackage(context) }, + }; } - sol::table initLocalStoragePackage(const Context& context, LuaUtil::LuaStorage* globalStorage) + std::map initLocalPackages(const Context& context) { - sol::table res(context.mLua->sol(), sol::create); - res["globalSection"] - = [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); }; - return LuaUtil::makeReadOnly(res); + initObjectBindingsForLocalScripts(context); + initCellBindingsForLocalScripts(context); + LocalScripts::initializeSelfPackage(context); + return { + { "openmw.nearby", initNearbyPackage(context) }, + }; } - sol::table initPlayerStoragePackage( - const Context& context, LuaUtil::LuaStorage* globalStorage, LuaUtil::LuaStorage* playerStorage) + std::map initPlayerPackages(const Context& context) { - sol::table res(context.mLua->sol(), sol::create); - res["globalSection"] - = [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); }; - res["playerSection"] - = [playerStorage](std::string_view section) { return playerStorage->getMutableSection(section); }; - res["allPlayerSections"] = [playerStorage]() { return playerStorage->getAllSections(); }; - return LuaUtil::makeReadOnly(res); + return { + { "openmw.camera", initCameraPackage(context.mLua->sol()) }, + { "openmw.debug", initDebugPackage(context) }, + { "openmw.input", initInputPackage(context) }, + { "openmw.postprocessing", initPostprocessingPackage(context) }, + { "openmw.ui", initUserInterfacePackage(context) }, + }; } } diff --git a/apps/openmw/mwlua/luabindings.hpp b/apps/openmw/mwlua/luabindings.hpp index d4706c3e1e..e5d481d1eb 100644 --- a/apps/openmw/mwlua/luabindings.hpp +++ b/apps/openmw/mwlua/luabindings.hpp @@ -1,49 +1,25 @@ #ifndef MWLUA_LUABINDINGS_H #define MWLUA_LUABINDINGS_H -#include -#include +#include +#include +#include #include "context.hpp" -namespace MWWorld -{ - class CellStore; -} - namespace MWLua { + // Initialize Lua packages that are available for all scripts. + std::map initCommonPackages(const Context&); - sol::table initCorePackage(const Context&); - sol::table initWorldPackage(const Context&); - sol::table initPostprocessingPackage(const Context&); - - sol::table initGlobalStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage); - sol::table initLocalStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage); - sol::table initPlayerStoragePackage( - const Context&, LuaUtil::LuaStorage* globalStorage, LuaUtil::LuaStorage* playerStorage); - - // Implemented in nearbybindings.cpp - sol::table initNearbyPackage(const Context&); - - // Implemented in objectbindings.cpp - void initObjectBindingsForLocalScripts(const Context&); - void initObjectBindingsForGlobalScripts(const Context&); - - // Implemented in cellbindings.cpp - void initCellBindingsForLocalScripts(const Context&); - void initCellBindingsForGlobalScripts(const Context&); - - // Implemented in camerabindings.cpp - sol::table initCameraPackage(const Context&); - - // Implemented in uibindings.cpp - sol::table initUserInterfacePackage(const Context&); + // Initialize Lua packages that are available only for global scripts. + std::map initGlobalPackages(const Context&); - // Implemented in inputbindings.cpp - sol::table initInputPackage(const Context&); + // Initialize Lua packages that are available only for local scripts (including player scripts). + std::map initLocalPackages(const Context&); - // openmw.self package is implemented in localscripts.cpp + // Initialize Lua packages that are available only for local scripts on the player. + std::map initPlayerPackages(const Context&); } #endif // MWLUA_LUABINDINGS_H diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 35d1d0be4d..0447a24260 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -12,13 +12,10 @@ #include #include -#include +#include #include -#include -#include - #include #include @@ -31,7 +28,6 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/scene.hpp" -#include "debugbindings.hpp" #include "luabindings.hpp" #include "playerscripts.hpp" #include "types/types.hpp" @@ -42,12 +38,12 @@ namespace MWLua static LuaUtil::LuaStateSettings createLuaStateSettings() { - if (!Settings::Manager::getBool("lua profiler", "Lua")) + if (!Settings::lua().mLuaProfiler) LuaUtil::LuaState::disableProfiler(); - return { .mInstructionLimit = Settings::Manager::getUInt64("instruction limit per call", "Lua"), - .mMemoryLimit = Settings::Manager::getUInt64("memory limit", "Lua"), - .mSmallAllocMaxSize = Settings::Manager::getUInt64("small alloc max size", "Lua"), - .mLogMemoryUsage = Settings::Manager::getBool("log memory usage", "Lua") }; + return { .mInstructionLimit = Settings::lua().mInstructionLimitPerCall, + .mMemoryLimit = Settings::lua().mMemoryLimit, + .mSmallAllocMaxSize = Settings::lua().mSmallAllocMaxSize, + .mLogMemoryUsage = Settings::lua().mLogMemoryUsage }; } LuaManager::LuaManager(const VFS::Manager* vfs, const std::filesystem::path& libsDir) @@ -88,31 +84,21 @@ namespace MWLua localContext.mIsGlobal = false; localContext.mSerializer = mLocalSerializer.get(); - initObjectBindingsForGlobalScripts(context); - initCellBindingsForGlobalScripts(context); - initObjectBindingsForLocalScripts(localContext); - initCellBindingsForLocalScripts(localContext); - LocalScripts::initializeSelfPackage(localContext); - LuaUtil::LuaStorage::initLuaBindings(mLua.sol()); + for (const auto& [name, package] : initCommonPackages(context)) + mLua.addCommonPackage(name, package); + for (const auto& [name, package] : initGlobalPackages(context)) + mGlobalScripts.addPackage(name, package); - mLua.addCommonPackage("openmw.async", - LuaUtil::getAsyncPackageInitializer( - mLua.sol(), [this] { return mWorldView.getSimulationTime(); }, - [this] { return mWorldView.getGameTime(); })); - mLua.addCommonPackage("openmw.util", LuaUtil::initUtilPackage(mLua.sol())); - mLua.addCommonPackage("openmw.core", initCorePackage(context)); - mLua.addCommonPackage("openmw.types", initTypesPackage(context)); - mGlobalScripts.addPackage("openmw.world", initWorldPackage(context)); - mGlobalScripts.addPackage("openmw.storage", initGlobalStoragePackage(context, &mGlobalStorage)); - - mCameraPackage = initCameraPackage(localContext); - mUserInterfacePackage = initUserInterfacePackage(localContext); - mInputPackage = initInputPackage(localContext); - mNearbyPackage = initNearbyPackage(localContext); - mLocalStoragePackage = initLocalStoragePackage(localContext, &mGlobalStorage); - mPlayerStoragePackage = initPlayerStoragePackage(localContext, &mGlobalStorage, &mPlayerStorage); - mPostprocessingPackage = initPostprocessingPackage(localContext); - mDebugPackage = initDebugPackage(localContext); + mLocalPackages = initLocalPackages(localContext); + mPlayerPackages = initPlayerPackages(localContext); + mPlayerPackages.insert(mLocalPackages.begin(), mLocalPackages.end()); + + LuaUtil::LuaStorage::initLuaBindings(mLua.sol()); + mGlobalScripts.addPackage( + "openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(mLua.sol(), &mGlobalStorage)); + mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(mLua.sol(), &mGlobalStorage); + mPlayerPackages["openmw.storage"] + = LuaUtil::LuaStorage::initPlayerPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage); initConfiguration(); mInitialized = true; @@ -136,9 +122,8 @@ namespace MWLua void LuaManager::update() { - static const int gcStepCount = Settings::Manager::getInt("gc steps per frame", "Lua"); - if (gcStepCount > 0) - lua_gc(mLua.sol(), LUA_GCSTEP, gcStepCount); + if (Settings::lua().mGcStepsPerFrame > 0) + lua_gc(mLua.sol(), LUA_GCSTEP, Settings::lua().mGcStepsPerFrame); if (mPlayer.isEmpty()) return; // The game is not started yet. @@ -147,7 +132,7 @@ namespace MWLua MWWorld::Ptr newPlayerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (!(getId(mPlayer) == getId(newPlayerPtr))) - throw std::logic_error("Player Refnum was changed unexpectedly"); + throw std::logic_error("Player RefNum was changed unexpectedly"); if (!mPlayer.isInCell() || !newPlayerPtr.isInCell() || mPlayer.getCell() != newPlayerPtr.getCell()) { mPlayer = newPlayerPtr; // player was moved to another cell, update ptr in registry @@ -220,12 +205,12 @@ namespace MWLua windowManager->printToConsole(msg, "#" + color.toHex()); mInGameConsoleMessages.clear(); - for (std::unique_ptr& action : mActionQueue) - action->safeApply(); + for (DelayedAction& action : mActionQueue) + action.apply(); mActionQueue.clear(); if (mTeleportPlayerAction) - mTeleportPlayerAction->safeApply(); + mTeleportPlayerAction->apply(); mTeleportPlayerAction.reset(); } @@ -352,12 +337,8 @@ namespace MWLua { scripts = std::make_shared(&mLua, LObject(getId(ptr))); scripts->setAutoStartConf(mConfiguration.getPlayerConf()); - scripts->addPackage("openmw.ui", mUserInterfacePackage); - scripts->addPackage("openmw.camera", mCameraPackage); - scripts->addPackage("openmw.input", mInputPackage); - scripts->addPackage("openmw.storage", mPlayerStoragePackage); - scripts->addPackage("openmw.postprocessing", mPostprocessingPackage); - scripts->addPackage("openmw.debug", mDebugPackage); + for (const auto& [name, package] : mPlayerPackages) + scripts->addPackage(name, package); } else { @@ -365,9 +346,9 @@ namespace MWLua if (!autoStartConf.has_value()) autoStartConf = mConfiguration.getLocalConf(type, ptr.getCellRef().getRefId(), getId(ptr)); scripts->setAutoStartConf(std::move(*autoStartConf)); - scripts->addPackage("openmw.storage", mLocalStoragePackage); + for (const auto& [name, package] : mLocalPackages) + scripts->addPackage(name, package); } - scripts->addPackage("openmw.nearby", mNearbyPackage); scripts->setSerializer(mLocalSerializer.get()); MWWorld::RefData& refData = ptr.getRefData(); @@ -483,22 +464,23 @@ namespace MWLua "No Lua handlers for console\n", MWBase::WindowManager::sConsoleColor_Error); } - LuaManager::Action::Action(LuaUtil::LuaState* state) + LuaManager::DelayedAction::DelayedAction(LuaUtil::LuaState* state, std::function fn, std::string_view name) + : mFn(std::move(fn)) + , mName(name) { - static const bool luaDebug = Settings::Manager::getBool("lua debug", "Lua"); - if (luaDebug) + if (Settings::lua().mLuaDebug) mCallerTraceback = state->debugTraceback(); } - void LuaManager::Action::safeApply() const + void LuaManager::DelayedAction::apply() const { try { - apply(); + mFn(); } catch (const std::exception& e) { - Log(Debug::Error) << "Error in " << this->toString() << ": " << e.what(); + Log(Debug::Error) << "Error in DelayedAction " << mName << ": " << e.what(); if (mCallerTraceback.empty()) Log(Debug::Error) << "Set 'lua_debug=true' in settings.cfg to enable action tracebacks"; @@ -507,35 +489,14 @@ namespace MWLua } } - namespace - { - class FunctionAction final : public LuaManager::Action - { - public: - FunctionAction(LuaUtil::LuaState* state, std::function fn, std::string_view name) - : Action(state) - , mFn(std::move(fn)) - , mName(name) - { - } - - void apply() const override { mFn(); } - std::string toString() const override { return "FunctionAction " + mName; } - - private: - std::function mFn; - std::string mName; - }; - } - void LuaManager::addAction(std::function action, std::string_view name) { - mActionQueue.push_back(std::make_unique(&mLua, std::move(action), name)); + mActionQueue.emplace_back(&mLua, std::move(action), name); } void LuaManager::addTeleportPlayerAction(std::function action) { - mTeleportPlayerAction = std::make_unique(&mLua, std::move(action), "TeleportPlayer"); + mTeleportPlayerAction = DelayedAction(&mLua, std::move(action), "TeleportPlayer"); } void LuaManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const @@ -566,7 +527,7 @@ namespace MWLua out << (bytes / (1024 * 1024 * 1024)) << " GB"; }; - static const uint64_t smallAllocSize = Settings::Manager::getUInt64("small alloc max size", "Lua"); + const uint64_t smallAllocSize = Settings::lua().mSmallAllocMaxSize; out << "Total memory usage:"; outMemSize(mLua.getTotalMemoryUsage()); out << "\n"; diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index 7f56301edc..247772bff6 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -1,16 +1,14 @@ #ifndef MWLUA_LUAMANAGERIMP_H #define MWLUA_LUAMANAGERIMP_H +#include #include #include #include #include - #include - #include -#include #include "../mwbase/luamanager.hpp" @@ -88,24 +86,8 @@ namespace MWLua } // Some changes to the game world can not be done from the scripting thread (because it runs in parallel with - // OSG Cull), so we need to queue it and apply from the main thread. All such changes should be implemented as - // classes inherited from MWLua::Action. - class Action - { - public: - Action(LuaUtil::LuaState* state); - virtual ~Action() {} - - void safeApply() const; - virtual void apply() const = 0; - virtual std::string toString() const = 0; - - private: - std::string mCallerTraceback; - }; - + // OSG Cull), so we need to queue it and apply from the main thread. void addAction(std::function action, std::string_view name = ""); - void addAction(std::unique_ptr&& action) { mActionQueue.push_back(std::move(action)); } void addTeleportPlayerAction(std::function action); // Saving @@ -157,14 +139,8 @@ namespace MWLua LuaUtil::ScriptsConfiguration mConfiguration; LuaUtil::LuaState mLua; LuaUi::ResourceManager mUiResourceManager; - sol::table mNearbyPackage; - sol::table mUserInterfacePackage; - sol::table mCameraPackage; - sol::table mInputPackage; - sol::table mLocalStoragePackage; - sol::table mPlayerStoragePackage; - sol::table mPostprocessingPackage; - sol::table mDebugPackage; + std::map mLocalPackages; + std::map mPlayerPackages; GlobalScripts mGlobalScripts{ &mLua }; std::set mActiveLocalScripts; @@ -191,8 +167,19 @@ namespace MWLua std::vector mQueuedCallbacks; // Queued actions that should be done in main thread. Processed by applyQueuedChanges(). - std::vector> mActionQueue; - std::unique_ptr mTeleportPlayerAction; + class DelayedAction + { + public: + DelayedAction(LuaUtil::LuaState* state, std::function fn, std::string_view name); + void apply() const; + + private: + std::string mCallerTraceback; + std::function mFn; + std::string mName; + }; + std::vector mActionQueue; + std::optional mTeleportPlayerAction; std::vector mUIMessages; std::vector> mInGameConsoleMessages; diff --git a/apps/openmw/mwlua/nearbybindings.cpp b/apps/openmw/mwlua/nearbybindings.cpp index 5c2572c0e5..c9f9163648 100644 --- a/apps/openmw/mwlua/nearbybindings.cpp +++ b/apps/openmw/mwlua/nearbybindings.cpp @@ -1,4 +1,4 @@ -#include "luabindings.hpp" +#include "nearbybindings.hpp" #include #include diff --git a/apps/openmw/mwlua/nearbybindings.hpp b/apps/openmw/mwlua/nearbybindings.hpp new file mode 100644 index 0000000000..ee0022898e --- /dev/null +++ b/apps/openmw/mwlua/nearbybindings.hpp @@ -0,0 +1,13 @@ +#ifndef MWLUA_NEARBYBINDINGS_H +#define MWLUA_NEARBYBINDINGS_H + +#include + +#include "context.hpp" + +namespace MWLua +{ + sol::table initNearbyPackage(const Context&); +} + +#endif // MWLUA_NEARBYBINDINGS_H diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 5a14053fcf..1b70258541 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -1,4 +1,4 @@ -#include "luabindings.hpp" +#include "objectbindings.hpp" #include #include diff --git a/apps/openmw/mwlua/objectbindings.hpp b/apps/openmw/mwlua/objectbindings.hpp new file mode 100644 index 0000000000..5f69c54da3 --- /dev/null +++ b/apps/openmw/mwlua/objectbindings.hpp @@ -0,0 +1,12 @@ +#ifndef MWLUA_OBJECTBINDINGS_H +#define MWLUA_OBJECTBINDINGS_H + +#include "context.hpp" + +namespace MWLua +{ + void initObjectBindingsForLocalScripts(const Context&); + void initObjectBindingsForGlobalScripts(const Context&); +} + +#endif // MWLUA_OBJECTBINDINGS_H diff --git a/apps/openmw/mwlua/postprocessingbindings.cpp b/apps/openmw/mwlua/postprocessingbindings.cpp index 4d8212b749..4bdcaba1c5 100644 --- a/apps/openmw/mwlua/postprocessingbindings.cpp +++ b/apps/openmw/mwlua/postprocessingbindings.cpp @@ -1,43 +1,10 @@ -#include "luabindings.hpp" +#include "postprocessingbindings.hpp" #include "../mwbase/environment.hpp" #include "../mwrender/postprocessor.hpp" #include "luamanagerimp.hpp" -namespace -{ - template - class SetUniformShaderAction final : public MWLua::LuaManager::Action - { - public: - SetUniformShaderAction( - LuaUtil::LuaState* state, std::shared_ptr shader, const std::string& name, const T& value) - : MWLua::LuaManager::Action(state) - , mShader(std::move(shader)) - , mName(name) - , mValue(value) - { - } - - void apply() const override - { - MWBase::Environment::get().getWorld()->getPostProcessor()->setUniform(mShader, mName, mValue); - } - - std::string toString() const override - { - return std::string("SetUniformShaderAction shader=") + (mShader ? mShader->getName() : "nil") - + std::string("uniform=") + (mShader ? mName : "nil"); - } - - private: - std::shared_ptr mShader; - std::string mName; - T mValue; - }; -} - namespace MWLua { struct Shader; @@ -84,7 +51,10 @@ namespace MWLua { return [context](const Shader& shader, const std::string& name, const T& value) { context.mLuaManager->addAction( - std::make_unique>(context.mLua, shader.mShader, name, value)); + [&] { + MWBase::Environment::get().getWorld()->getPostProcessor()->setUniform(shader.mShader, name, value); + }, + "SetUniformShaderAction"); }; } @@ -114,7 +84,10 @@ namespace MWLua } context.mLuaManager->addAction( - std::make_unique>>(context.mLua, shader.mShader, name, values)); + [&] { + MWBase::Environment::get().getWorld()->getPostProcessor()->setUniform(shader.mShader, name, values); + }, + "SetUniformShaderAction"); }; } diff --git a/apps/openmw/mwlua/postprocessingbindings.hpp b/apps/openmw/mwlua/postprocessingbindings.hpp new file mode 100644 index 0000000000..50cd84fa24 --- /dev/null +++ b/apps/openmw/mwlua/postprocessingbindings.hpp @@ -0,0 +1,13 @@ +#ifndef MWLUA_POSTPROCESSINGBINDINGS_H +#define MWLUA_POSTPROCESSINGBINDINGS_H + +#include + +#include "context.hpp" + +namespace MWLua +{ + sol::table initPostprocessingPackage(const Context&); +} + +#endif // MWLUA_POSTPROCESSINGBINDINGS_H diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 54fd953b35..eddd366fd0 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -55,29 +55,17 @@ namespace namespace MWLua { - namespace + static void addStatUpdateAction(MWLua::LuaManager* manager, const SelfObject& obj) { - class StatUpdateAction final : public LuaManager::Action - { - ObjectId mId; - - public: - StatUpdateAction(LuaUtil::LuaState* state, ObjectId id) - : Action(state) - , mId(id) - { - } - - void apply() const override - { - LObject obj(mId); + if (!obj.mStatsCache.empty()) + return; // was already added before + manager->addAction( + [obj = Object(obj)] { LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts(); if (scripts) scripts->applyStatsCache(); - } - - std::string toString() const override { return "StatUpdateAction"; } - }; + }, + "StatUpdateAction"); } class LevelStat @@ -99,8 +87,7 @@ namespace MWLua void setCurrent(const Context& context, const sol::object& value) const { SelfObject* obj = mObject.asSelfObject(); - if (obj->mStatsCache.empty()) - context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); + addStatUpdateAction(context.mLuaManager, *obj); obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, 0, "current" }] = value; } @@ -158,8 +145,7 @@ namespace MWLua void cache(const Context& context, std::string_view prop, const sol::object& value) const { SelfObject* obj = mObject.asSelfObject(); - if (obj->mStatsCache.empty()) - context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); + addStatUpdateAction(context.mLuaManager, *obj); obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = value; } @@ -217,8 +203,7 @@ namespace MWLua void cache(const Context& context, std::string_view prop, const sol::object& value) const { SelfObject* obj = mObject.asSelfObject(); - if (obj->mStatsCache.empty()) - context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); + addStatUpdateAction(context.mLuaManager, *obj); obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mIndex, prop }] = value; } @@ -302,8 +287,7 @@ namespace MWLua void cache(const Context& context, std::string_view prop, const sol::object& value) const { SelfObject* obj = mObject.asSelfObject(); - if (obj->mStatsCache.empty()) - context.mLuaManager->addAction(std::make_unique(context.mLua, obj->id())); + addStatUpdateAction(context.mLuaManager, *obj); obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mIndex, prop }] = value; } diff --git a/apps/openmw/mwlua/types/actor.cpp b/apps/openmw/mwlua/types/actor.cpp index adff313f49..39bca897e8 100644 --- a/apps/openmw/mwlua/types/actor.cpp +++ b/apps/openmw/mwlua/types/actor.cpp @@ -18,107 +18,84 @@ namespace MWLua { - namespace + using EquipmentItem = std::variant; + using Equipment = std::map; + + static void setEquipment(const MWWorld::Ptr& actor, const Equipment& equipment) { - class SetEquipmentAction final : public LuaManager::Action - { - public: - using Item = std::variant; // recordId or ObjectId - using Equipment = std::map; // slot to item + MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor); + std::array usedSlots; + std::fill(usedSlots.begin(), usedSlots.end(), false); - SetEquipmentAction(LuaUtil::LuaState* state, ObjectId actor, Equipment equipment) - : Action(state) - , mActor(actor) - , mEquipment(std::move(equipment)) + static constexpr int anySlot = -1; + auto tryEquipToSlot = [&store, &usedSlots](int slot, const EquipmentItem& item) -> bool { + auto old_it = slot != anySlot ? store.getSlot(slot) : store.end(); + MWWorld::Ptr itemPtr; + if (std::holds_alternative(item)) { + itemPtr = MWBase::Environment::get().getWorldModel()->getPtr(std::get(item)); + if (old_it != store.end() && *old_it == itemPtr) + return true; // already equipped + if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0 + || itemPtr.getContainerStore() != static_cast(&store)) + { + Log(Debug::Warning) << "Object" << std::get(item).toString() << " is not in inventory"; + return false; + } } - - void apply() const override + else { - MWWorld::Ptr actor = MWBase::Environment::get().getWorldModel()->getPtr(mActor); - MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor); - std::array usedSlots; - std::fill(usedSlots.begin(), usedSlots.end(), false); - - static constexpr int anySlot = -1; - auto tryEquipToSlot = [&store, &usedSlots](int slot, const Item& item) -> bool { - auto old_it = slot != anySlot ? store.getSlot(slot) : store.end(); - MWWorld::Ptr itemPtr; - if (std::holds_alternative(item)) - { - itemPtr = MWBase::Environment::get().getWorldModel()->getPtr(std::get(item)); - if (old_it != store.end() && *old_it == itemPtr) - return true; // already equipped - if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0 - || itemPtr.getContainerStore() != static_cast(&store)) - { - Log(Debug::Warning) - << "Object" << std::get(item).toString() << " is not in inventory"; - return false; - } - } - else - { - const ESM::RefId& recordId = ESM::RefId::stringRefId(std::get(item)); - if (old_it != store.end() && old_it->getCellRef().getRefId() == recordId) - return true; // already equipped - itemPtr = store.search(recordId); - if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0) - { - Log(Debug::Warning) << "There is no object with recordId='" << recordId << "' in inventory"; - return false; - } - } - - auto [allowedSlots, _] = itemPtr.getClass().getEquipmentSlots(itemPtr); - bool requestedSlotIsAllowed - = std::find(allowedSlots.begin(), allowedSlots.end(), slot) != allowedSlots.end(); - if (!requestedSlotIsAllowed) - { - auto firstAllowed = std::find_if( - allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; }); - if (firstAllowed == allowedSlots.end()) - { - Log(Debug::Warning) << "No suitable slot for " << itemPtr.toString(); - return false; - } - slot = *firstAllowed; - } - - // TODO: Refactor InventoryStore to accept Ptr and get rid of this linear search. - MWWorld::ContainerStoreIterator it = std::find(store.begin(), store.end(), itemPtr); - if (it == store.end()) // should never happen - throw std::logic_error("Item not found in container"); - - store.equip(slot, it); - return requestedSlotIsAllowed; // return true if equipped to requested slot and false if slot was - // changed - }; + const ESM::RefId& recordId = ESM::RefId::stringRefId(std::get(item)); + if (old_it != store.end() && old_it->getCellRef().getRefId() == recordId) + return true; // already equipped + itemPtr = store.search(recordId); + if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0) + { + Log(Debug::Warning) << "There is no object with recordId='" << recordId << "' in inventory"; + return false; + } + } - for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + auto [allowedSlots, _] = itemPtr.getClass().getEquipmentSlots(itemPtr); + bool requestedSlotIsAllowed + = std::find(allowedSlots.begin(), allowedSlots.end(), slot) != allowedSlots.end(); + if (!requestedSlotIsAllowed) + { + auto firstAllowed + = std::find_if(allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; }); + if (firstAllowed == allowedSlots.end()) { - auto old_it = store.getSlot(slot); - auto new_it = mEquipment.find(slot); - if (new_it == mEquipment.end()) - { - if (old_it != store.end()) - store.unequipSlot(slot); - continue; - } - if (tryEquipToSlot(slot, new_it->second)) - usedSlots[slot] = true; + Log(Debug::Warning) << "No suitable slot for " << itemPtr.toString(); + return false; } - for (const auto& [slot, item] : mEquipment) - if (slot >= MWWorld::InventoryStore::Slots) - tryEquipToSlot(anySlot, item); + slot = *firstAllowed; } - std::string toString() const override { return "SetEquipmentAction"; } + // TODO: Refactor InventoryStore to accept Ptr and get rid of this linear search. + MWWorld::ContainerStoreIterator it = std::find(store.begin(), store.end(), itemPtr); + if (it == store.end()) // should never happen + throw std::logic_error("Item not found in container"); - private: - ObjectId mActor; - Equipment mEquipment; + store.equip(slot, it); + return requestedSlotIsAllowed; // return true if equipped to requested slot and false if slot was changed }; + + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + auto old_it = store.getSlot(slot); + auto new_it = equipment.find(slot); + if (new_it == equipment.end()) + { + if (old_it != store.end()) + store.unequipSlot(slot); + continue; + } + if (tryEquipToSlot(slot, new_it->second)) + usedSlots[slot] = true; + } + for (const auto& [slot, item] : equipment) + if (slot >= MWWorld::InventoryStore::Slots) + tryEquipToSlot(anySlot, item); } void addActorBindings(sol::table actor, const Context& context) @@ -263,13 +240,14 @@ namespace MWLua return store.isEquipped(item.ptr()); }; actor["setEquipment"] = [context](const SelfObject& obj, const sol::table& equipment) { - if (!obj.ptr().getClass().hasInventoryStore(obj.ptr())) + const MWWorld::Ptr& ptr = obj.ptr(); + if (!ptr.getClass().hasInventoryStore(ptr)) { if (!equipment.empty()) throw std::runtime_error(obj.toString() + " has no equipment slots"); return; } - SetEquipmentAction::Equipment eqp; + Equipment eqp; for (auto& [key, value] : equipment) { int slot = LuaUtil::cast(key); @@ -279,7 +257,7 @@ namespace MWLua eqp[slot] = LuaUtil::cast(value); } context.mLuaManager->addAction( - std::make_unique(context.mLua, obj.id(), std::move(eqp))); + [obj = Object(ptr), eqp = std::move(eqp)] { setEquipment(obj.ptr(), eqp); }, "SetEquipmentAction"); }; actor["getPathfindingAgentBounds"] = [](sol::this_state lua, const LObject& o) { const DetourNavigator::AgentBounds agentBounds diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index 282892213a..56bbe0d73e 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -1,3 +1,5 @@ +#include "uibindings.hpp" + #include #include #include @@ -18,71 +20,20 @@ namespace MWLua { namespace { - class UiAction final : public LuaManager::Action + template + void wrapAction(const std::shared_ptr& element, Fn&& fn) { - public: - enum Type - { - CREATE = 0, - UPDATE, - DESTROY, - }; - - UiAction(Type type, std::shared_ptr element, LuaUtil::LuaState* state) - : Action(state) - , mType{ type } - , mElement{ std::move(element) } + try { + fn(); } - - void apply() const override + catch (...) { - try - { - switch (mType) - { - case CREATE: - mElement->create(); - break; - case UPDATE: - mElement->update(); - break; - case DESTROY: - mElement->destroy(); - break; - } - } - catch (std::exception&) - { - // prevent any actions on a potentially corrupted widget - mElement->mRoot = nullptr; - throw; - } + // prevent any actions on a potentially corrupted widget + element->mRoot = nullptr; + throw; } - - std::string toString() const override - { - std::string result; - switch (mType) - { - case CREATE: - result += "Create"; - break; - case UPDATE: - result += "Update"; - break; - case DESTROY: - result += "Destroy"; - break; - } - result += " UI"; - return result; - } - - private: - Type mType; - std::shared_ptr mElement; - }; + } // Lua arrays index from 1 inline size_t fromLuaIndex(size_t i) @@ -100,17 +51,17 @@ namespace MWLua auto element = context.mLua->sol().new_usertype("Element"); element["layout"] = sol::property([](LuaUi::Element& element) { return element.mLayout; }, [](LuaUi::Element& element, const sol::table& layout) { element.mLayout = layout; }); - element["update"] = [context](const std::shared_ptr& element) { + element["update"] = [luaManager = context.mLuaManager](const std::shared_ptr& element) { if (element->mDestroy || element->mUpdate) return; element->mUpdate = true; - context.mLuaManager->addAction(std::make_unique(UiAction::UPDATE, element, context.mLua)); + luaManager->addAction([element] { wrapAction(element, [&] { element->update(); }); }, "Update UI"); }; - element["destroy"] = [context](const std::shared_ptr& element) { + element["destroy"] = [luaManager = context.mLuaManager](const std::shared_ptr& element) { if (element->mDestroy) return; element->mDestroy = true; - context.mLuaManager->addAction(std::make_unique(UiAction::DESTROY, element, context.mLua)); + luaManager->addAction([element] { wrapAction(element, [&] { element->destroy(); }); }, "Destroy UI"); }; sol::table api = context.mLua->newTable(); @@ -142,9 +93,9 @@ namespace MWLua } }; api["content"] = LuaUi::loadContentConstructor(context.mLua); - api["create"] = [context](const sol::table& layout) { + api["create"] = [luaManager = context.mLuaManager](const sol::table& layout) { auto element = LuaUi::Element::make(layout); - context.mLuaManager->addAction(std::make_unique(UiAction::CREATE, element, context.mLua)); + luaManager->addAction([element] { wrapAction(element, [&] { element->create(); }); }, "Create UI"); return element; }; api["updateAll"] = [context]() { diff --git a/apps/openmw/mwlua/uibindings.hpp b/apps/openmw/mwlua/uibindings.hpp new file mode 100644 index 0000000000..930ba7f3d8 --- /dev/null +++ b/apps/openmw/mwlua/uibindings.hpp @@ -0,0 +1,13 @@ +#ifndef MWLUA_UIBINDINGS_H +#define MWLUA_UIBINDINGS_H + +#include + +#include "context.hpp" + +namespace MWLua +{ + sol::table initUserInterfacePackage(const Context&); +} + +#endif // MWLUA_UIBINDINGS_H diff --git a/apps/openmw/mwlua/worker.cpp b/apps/openmw/mwlua/worker.cpp index 0dbe78ad4b..66fbf0d55f 100644 --- a/apps/openmw/mwlua/worker.cpp +++ b/apps/openmw/mwlua/worker.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include @@ -14,7 +14,7 @@ namespace MWLua : mManager(manager) , mViewer(viewer) { - if (Settings::Manager::getInt("lua num threads", "Lua") > 0) + if (Settings::lua().mLuaNumThreads > 0) mThread = std::thread([this] { run(); }); } diff --git a/components/lua/luastate.cpp b/components/lua/luastate.cpp index a8818ceee0..2a5769e6dd 100644 --- a/components/lua/luastate.cpp +++ b/components/lua/luastate.cpp @@ -316,7 +316,7 @@ namespace LuaUtil { if (!package.is()) package = makeReadOnly(std::move(package)); - mCommonPackages.emplace(std::move(packageName), std::move(package)); + mCommonPackages.insert_or_assign(std::move(packageName), std::move(package)); } sol::protected_function_result LuaState::runInNewSandbox(const std::string& path, const std::string& namePrefix, diff --git a/components/lua/scriptscontainer.cpp b/components/lua/scriptscontainer.cpp index e6cbfec791..3d7287d3a8 100644 --- a/components/lua/scriptscontainer.cpp +++ b/components/lua/scriptscontainer.cpp @@ -35,7 +35,7 @@ namespace LuaUtil void ScriptsContainer::addPackage(std::string packageName, sol::object package) { - mAPI.emplace(std::move(packageName), makeReadOnly(std::move(package))); + mAPI.insert_or_assign(std::move(packageName), makeReadOnly(std::move(package))); } bool ScriptsContainer::addCustomScript(int scriptId, std::string_view initData) diff --git a/components/lua/storage.cpp b/components/lua/storage.cpp index d23e1eb3d7..b96da916be 100644 --- a/components/lua/storage.cpp +++ b/components/lua/storage.cpp @@ -138,6 +138,34 @@ namespace LuaUtil }; } + sol::table LuaStorage::initGlobalPackage(lua_State* lua, LuaStorage* globalStorage) + { + sol::table res(lua, sol::create); + res["globalSection"] + = [globalStorage](std::string_view section) { return globalStorage->getMutableSection(section); }; + res["allGlobalSections"] = [globalStorage]() { return globalStorage->getAllSections(); }; + return LuaUtil::makeReadOnly(res); + } + + sol::table LuaStorage::initLocalPackage(lua_State* lua, LuaStorage* globalStorage) + { + sol::table res(lua, sol::create); + res["globalSection"] + = [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); }; + return LuaUtil::makeReadOnly(res); + } + + sol::table LuaStorage::initPlayerPackage(lua_State* lua, LuaStorage* globalStorage, LuaStorage* playerStorage) + { + sol::table res(lua, sol::create); + res["globalSection"] + = [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); }; + res["playerSection"] + = [playerStorage](std::string_view section) { return playerStorage->getMutableSection(section); }; + res["allPlayerSections"] = [playerStorage]() { return playerStorage->getAllSections(); }; + return LuaUtil::makeReadOnly(res); + } + void LuaStorage::clearTemporaryAndRemoveCallbacks() { auto it = mData.begin(); diff --git a/components/lua/storage.hpp b/components/lua/storage.hpp index 7f3c10dd0d..9998af9430 100644 --- a/components/lua/storage.hpp +++ b/components/lua/storage.hpp @@ -14,6 +14,9 @@ namespace LuaUtil { public: static void initLuaBindings(lua_State*); + static sol::table initGlobalPackage(lua_State* lua, LuaStorage* globalStorage); + static sol::table initLocalPackage(lua_State* lua, LuaStorage* globalStorage); + static sol::table initPlayerPackage(lua_State* lua, LuaStorage* globalStorage, LuaStorage* playerStorage); explicit LuaStorage(lua_State* lua) : mLua(lua)