diff --git a/apps/openmw/mwbase/luamanager.hpp b/apps/openmw/mwbase/luamanager.hpp index 5b4594728e..b9109518cc 100644 --- a/apps/openmw/mwbase/luamanager.hpp +++ b/apps/openmw/mwbase/luamanager.hpp @@ -59,6 +59,9 @@ namespace MWBase // Should be called before loading. The map is used to fix refnums if the order of content files was changed. virtual void setContentFileMapping(const std::map&) = 0; + + // Drops script cache and reloads all scripts. Calls `onSave` and `onLoad` for every script. + virtual void reloadAllScripts() = 0; }; } diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index b9004fcdbb..3bc2607045 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -20,6 +20,7 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/luamanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index fa885c88f5..b961550280 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -334,4 +334,30 @@ namespace MWLua scripts->setSerializer(mLocalSerializer.get()); } + void LuaManager::reloadAllScripts() + { + Log(Debug::Info) << "Reload Lua"; + mLua.dropScriptCache(); + + { // Reload global scripts + ESM::LuaScripts data; + mGlobalScripts.save(data); + mGlobalScripts.removeAllScripts(); + for (const std::string& path : mGlobalScriptList) + if (mGlobalScripts.addNewScript(path)) + Log(Debug::Info) << "Global script restarted: " << path; + mGlobalScripts.load(data, false); + } + + for (const auto& [id, ptr] : mWorldView.getObjectRegistry()->mObjectMapping) + { // Reload local scripts + LocalScripts* scripts = ptr.getRefData().getLuaScripts(); + if (scripts == nullptr) + continue; + ESM::LuaScripts data; + scripts->save(data); + scripts->load(data, true); + } + } + } diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index d9934135ec..275ee61614 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -62,6 +62,9 @@ namespace MWLua void loadLocalScripts(const MWWorld::Ptr& ptr, const ESM::LuaScripts& data) override; void setContentFileMapping(const std::map& mapping) override { mContentFileMapping = mapping; } + // Drops script cache and reloads all scripts. Calls `onSave` and `onLoad` for every script. + void reloadAllScripts() override; + private: LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr); diff --git a/apps/openmw/mwlua/object.hpp b/apps/openmw/mwlua/object.hpp index ac079fb38f..c4f1dc32f1 100644 --- a/apps/openmw/mwlua/object.hpp +++ b/apps/openmw/mwlua/object.hpp @@ -47,6 +47,7 @@ namespace MWLua friend class Object; friend class GObject; friend class LObject; + friend class LuaManager; bool mChanged = false; int64_t mUpdateCounter = 0; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 7c470fe413..aaba5e5986 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -480,5 +480,6 @@ op 0x200031d: StartScript, explicit op 0x200031e: GetDistance op 0x200031f: GetDistance, explicit op 0x2000320: Help +op 0x2000321: ReloadLua -opcodes 0x2000321-0x3ffffff unused +opcodes 0x2000322-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ca9bcbe002..0b36d8bb48 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -28,6 +28,7 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/luamanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -169,8 +170,6 @@ namespace MWScript void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); - if(!ptr.isEmpty() && !ptr.mRef->mData.isEnabled()) - ptr.mRef->mData.mPhysicsPostponed = false; MWBase::Environment::get().getWorld()->enable (ptr); } }; @@ -1599,6 +1598,17 @@ namespace MWScript } }; + class OpReloadLua : public Interpreter::Opcode0 + { + public: + + void execute (Interpreter::Runtime& runtime) override + { + MWBase::Environment::get().getLuaManager()->reloadAllScripts(); + runtime.getContext().report("All Lua scripts are reloaded"); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeMenuMode, new OpMenuMode); @@ -1719,6 +1729,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeRepairedOnMeExplicit, new OpRepairedOnMe); interpreter.installSegment5 (Compiler::Misc::opcodeToggleRecastMesh, new OpToggleRecastMesh); interpreter.installSegment5 (Compiler::Misc::opcodeHelp, new OpHelp); + interpreter.installSegment5 (Compiler::Misc::opcodeReloadLua, new OpReloadLua); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 6de35d7137..3dfcadab10 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -338,6 +338,7 @@ namespace Compiler extensions.registerFunction ("repairedonme", 'l', "S", opcodeRepairedOnMe, opcodeRepairedOnMeExplicit); extensions.registerInstruction ("togglerecastmesh", "", opcodeToggleRecastMesh); extensions.registerInstruction ("help", "", opcodeHelp); + extensions.registerInstruction ("reloadlua", "", opcodeReloadLua); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 46ee31133c..49adfbe209 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -319,6 +319,7 @@ namespace Compiler const int opcodeGetDisabledExplicit = 0x200031c; const int opcodeStartScriptExplicit = 0x200031d; const int opcodeHelp = 0x2000320; + const int opcodeReloadLua = 0x2000321; } namespace Sky diff --git a/docs/source/reference/lua-scripting/overview.rst b/docs/source/reference/lua-scripting/overview.rst index 1603529ab5..f941c190a8 100644 --- a/docs/source/reference/lua-scripting/overview.rst +++ b/docs/source/reference/lua-scripting/overview.rst @@ -114,6 +114,13 @@ Finally :ref:`register ` it in ``openmw.cfg``: Now every time the player presses "X" on a keyboard, a message is shown. +Hot reloading +============= + +It is possible to modify a script without restarting OpenMW. To apply changes open in-game console and run the command ``reloadlua``. +It will restart all Lua scripts using `onSave and onLoad`_ handlers the same way as if the game was saved and loaded. +It works only with existing ``*.lua`` files that are not packed to any archives. Adding new scripts or modifying ``*.omwscripts`` files always requires restarting the game. + Script structure ================