Merge branch 'lua_refactoring' into 'master'

MWLua refactoring

See merge request OpenMW/openmw!2956
simplify_debugging
psi29a 2 years ago
commit cdc753df70

@ -1,5 +1,6 @@
#include "luabindings.hpp"
#include <components/lua/luastate.hpp>
#include <components/lua/utilpackage.hpp>
#include <components/settings/settings.hpp>
@ -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<int>(camera->getMode()); };
api["getQueuedMode"] = [camera]() -> sol::optional<int> {

@ -0,0 +1,11 @@
#ifndef MWLUA_CAMERABINDINGS_H
#define MWLUA_CAMERABINDINGS_H
#include <sol/forward.hpp>
namespace MWLua
{
sol::table initCameraPackage(sol::state_view& lua);
}
#endif // MWLUA_CAMERABINDINGS_H

@ -1,4 +1,4 @@
#include "luabindings.hpp"
#include "cellbindings.hpp"
#include <components/esm/esmbridge.hpp>
#include <components/esm/records.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

@ -1,7 +1,7 @@
#include "engineevents.hpp"
#include <components/debug/debuglog.hpp>
#include <components/settings/settings.hpp>
#include <components/settings/values.hpp>
#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();
};

@ -1,9 +1,10 @@
#include "luabindings.hpp"
#include "inputbindings.hpp"
#include <SDL_events.h>
#include <SDL_gamecontroller.h>
#include <SDL_mouse.h>
#include <components/lua/luastate.hpp>
#include <components/sdlutil/events.hpp>
#include "../mwbase/environment.hpp"

@ -0,0 +1,13 @@
#ifndef MWLUA_INPUTBINDINGS_H
#define MWLUA_INPUTBINDINGS_H
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initInputPackage(const Context&);
}
#endif // MWLUA_INPUTBINDINGS_H

@ -3,11 +3,12 @@
#include <chrono>
#include <components/esm/attr.hpp>
#include <components/esm3/activespells.hpp>
#include <components/esm3/loadalch.hpp>
#include <components/esm3/loadskil.hpp>
#include <components/lua/l10n.hpp>
#include <components/lua/luastate.hpp>
#include <components/lua/utilpackage.hpp>
#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<std::string, sol::object> 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<std::string, sol::object> 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<std::string, sol::object> 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<std::string, sol::object> 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) },
};
}
}

@ -1,49 +1,25 @@
#ifndef MWLUA_LUABINDINGS_H
#define MWLUA_LUABINDINGS_H
#include <components/lua/scriptscontainer.hpp>
#include <components/lua/storage.hpp>
#include <map>
#include <sol/forward.hpp>
#include <string>
#include "context.hpp"
namespace MWWorld
{
class CellStore;
}
namespace MWLua
{
// Initialize Lua packages that are available for all scripts.
std::map<std::string, sol::object> 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<std::string, sol::object> 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<std::string, sol::object> 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<std::string, sol::object> initPlayerPackages(const Context&);
}
#endif // MWLUA_LUABINDINGS_H

@ -12,13 +12,10 @@
#include <components/esm3/esmreader.hpp>
#include <components/esm3/esmwriter.hpp>
#include <components/settings/settings.hpp>
#include <components/settings/values.hpp>
#include <components/l10n/manager.hpp>
#include <components/lua/asyncpackage.hpp>
#include <components/lua/utilpackage.hpp>
#include <components/lua_ui/content.hpp>
#include <components/lua_ui/util.hpp>
@ -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>& 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<PlayerScripts>(&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<void()> 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<void()> 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<void()> mFn;
std::string mName;
};
}
void LuaManager::addAction(std::function<void()> action, std::string_view name)
{
mActionQueue.push_back(std::make_unique<FunctionAction>(&mLua, std::move(action), name));
mActionQueue.emplace_back(&mLua, std::move(action), name);
}
void LuaManager::addTeleportPlayerAction(std::function<void()> action)
{
mTeleportPlayerAction = std::make_unique<FunctionAction>(&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";

@ -1,16 +1,14 @@
#ifndef MWLUA_LUAMANAGERIMP_H
#define MWLUA_LUAMANAGERIMP_H
#include <filesystem>
#include <map>
#include <set>
#include <components/lua/luastate.hpp>
#include <components/lua/storage.hpp>
#include <components/lua_ui/resources.hpp>
#include <components/misc/color.hpp>
#include <filesystem>
#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<void()> action, std::string_view name = "");
void addAction(std::unique_ptr<Action>&& action) { mActionQueue.push_back(std::move(action)); }
void addTeleportPlayerAction(std::function<void()> 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<std::string, sol::object> mLocalPackages;
std::map<std::string, sol::object> mPlayerPackages;
GlobalScripts mGlobalScripts{ &mLua };
std::set<LocalScripts*> mActiveLocalScripts;
@ -191,8 +167,19 @@ namespace MWLua
std::vector<CallbackWithData> mQueuedCallbacks;
// Queued actions that should be done in main thread. Processed by applyQueuedChanges().
std::vector<std::unique_ptr<Action>> mActionQueue;
std::unique_ptr<Action> mTeleportPlayerAction;
class DelayedAction
{
public:
DelayedAction(LuaUtil::LuaState* state, std::function<void()> fn, std::string_view name);
void apply() const;
private:
std::string mCallerTraceback;
std::function<void()> mFn;
std::string mName;
};
std::vector<DelayedAction> mActionQueue;
std::optional<DelayedAction> mTeleportPlayerAction;
std::vector<std::string> mUIMessages;
std::vector<std::pair<std::string, Misc::Color>> mInGameConsoleMessages;

@ -1,4 +1,4 @@
#include "luabindings.hpp"
#include "nearbybindings.hpp"
#include <components/detournavigator/navigator.hpp>
#include <components/detournavigator/navigatorutils.hpp>

@ -0,0 +1,13 @@
#ifndef MWLUA_NEARBYBINDINGS_H
#define MWLUA_NEARBYBINDINGS_H
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initNearbyPackage(const Context&);
}
#endif // MWLUA_NEARBYBINDINGS_H

@ -1,4 +1,4 @@
#include "luabindings.hpp"
#include "objectbindings.hpp"
#include <components/esm3/loadfact.hpp>
#include <components/esm3/loadnpc.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

@ -1,43 +1,10 @@
#include "luabindings.hpp"
#include "postprocessingbindings.hpp"
#include "../mwbase/environment.hpp"
#include "../mwrender/postprocessor.hpp"
#include "luamanagerimp.hpp"
namespace
{
template <class T>
class SetUniformShaderAction final : public MWLua::LuaManager::Action
{
public:
SetUniformShaderAction(
LuaUtil::LuaState* state, std::shared_ptr<fx::Technique> 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<fx::Technique> 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<SetUniformShaderAction<T>>(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<SetUniformShaderAction<std::vector<T>>>(context.mLua, shader.mShader, name, values));
[&] {
MWBase::Environment::get().getWorld()->getPostProcessor()->setUniform(shader.mShader, name, values);
},
"SetUniformShaderAction");
};
}

@ -0,0 +1,13 @@
#ifndef MWLUA_POSTPROCESSINGBINDINGS_H
#define MWLUA_POSTPROCESSINGBINDINGS_H
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initPostprocessingPackage(const Context&);
}
#endif // MWLUA_POSTPROCESSINGBINDINGS_H

@ -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<StatUpdateAction>(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<StatUpdateAction>(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<StatUpdateAction>(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<StatUpdateAction>(context.mLua, obj->id()));
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mIndex, prop }] = value;
}

@ -18,107 +18,84 @@
namespace MWLua
{
namespace
using EquipmentItem = std::variant<std::string, ObjectId>;
using Equipment = std::map<int, EquipmentItem>;
static void setEquipment(const MWWorld::Ptr& actor, const Equipment& equipment)
{
class SetEquipmentAction final : public LuaManager::Action
{
public:
using Item = std::variant<std::string, ObjectId>; // recordId or ObjectId
using Equipment = std::map<int, Item>; // slot to item
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
std::array<bool, MWWorld::InventoryStore::Slots> 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<ObjectId>(item))
{
itemPtr = MWBase::Environment::get().getWorldModel()->getPtr(std::get<ObjectId>(item));
if (old_it != store.end() && *old_it == itemPtr)
return true; // already equipped
if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0
|| itemPtr.getContainerStore() != static_cast<const MWWorld::ContainerStore*>(&store))
{
Log(Debug::Warning) << "Object" << std::get<ObjectId>(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<bool, MWWorld::InventoryStore::Slots> 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<ObjectId>(item))
{
itemPtr = MWBase::Environment::get().getWorldModel()->getPtr(std::get<ObjectId>(item));
if (old_it != store.end() && *old_it == itemPtr)
return true; // already equipped
if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0
|| itemPtr.getContainerStore() != static_cast<const MWWorld::ContainerStore*>(&store))
{
Log(Debug::Warning)
<< "Object" << std::get<ObjectId>(item).toString() << " is not in inventory";
return false;
}
}
else
{
const ESM::RefId& recordId = ESM::RefId::stringRefId(std::get<std::string>(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<std::string>(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<int>(key);
@ -279,7 +257,7 @@ namespace MWLua
eqp[slot] = LuaUtil::cast<std::string>(value);
}
context.mLuaManager->addAction(
std::make_unique<SetEquipmentAction>(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

@ -1,3 +1,5 @@
#include "uibindings.hpp"
#include <components/lua_ui/alignment.hpp>
#include <components/lua_ui/content.hpp>
#include <components/lua_ui/element.hpp>
@ -18,71 +20,20 @@ namespace MWLua
{
namespace
{
class UiAction final : public LuaManager::Action
template <typename Fn>
void wrapAction(const std::shared_ptr<LuaUi::Element>& element, Fn&& fn)
{
public:
enum Type
{
CREATE = 0,
UPDATE,
DESTROY,
};
UiAction(Type type, std::shared_ptr<LuaUi::Element> 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<LuaUi::Element> 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<LuaUi::Element>("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<LuaUi::Element>& element) {
element["update"] = [luaManager = context.mLuaManager](const std::shared_ptr<LuaUi::Element>& element) {
if (element->mDestroy || element->mUpdate)
return;
element->mUpdate = true;
context.mLuaManager->addAction(std::make_unique<UiAction>(UiAction::UPDATE, element, context.mLua));
luaManager->addAction([element] { wrapAction(element, [&] { element->update(); }); }, "Update UI");
};
element["destroy"] = [context](const std::shared_ptr<LuaUi::Element>& element) {
element["destroy"] = [luaManager = context.mLuaManager](const std::shared_ptr<LuaUi::Element>& element) {
if (element->mDestroy)
return;
element->mDestroy = true;
context.mLuaManager->addAction(std::make_unique<UiAction>(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>(UiAction::CREATE, element, context.mLua));
luaManager->addAction([element] { wrapAction(element, [&] { element->create(); }); }, "Create UI");
return element;
};
api["updateAll"] = [context]() {

@ -0,0 +1,13 @@
#ifndef MWLUA_UIBINDINGS_H
#define MWLUA_UIBINDINGS_H
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initUserInterfacePackage(const Context&);
}
#endif // MWLUA_UIBINDINGS_H

@ -4,7 +4,7 @@
#include <apps/openmw/profile.hpp>
#include <components/settings/settings.hpp>
#include <components/settings/values.hpp>
#include <osgViewer/Viewer>
@ -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(); });
}

@ -316,7 +316,7 @@ namespace LuaUtil
{
if (!package.is<sol::function>())
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,

@ -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)

@ -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();

@ -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)

Loading…
Cancel
Save