Allow menu scripts to read global sections while a game is loaded

ini_importer_tests
uramer 12 months ago
parent 9b54f479e8
commit 962ecc4329

@ -54,6 +54,7 @@ namespace MWBase
virtual void newGameStarted() = 0; virtual void newGameStarted() = 0;
virtual void gameLoaded() = 0; virtual void gameLoaded() = 0;
virtual void gameEnded() = 0;
virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0; virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0;
virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0; virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0;
virtual void objectTeleported(const MWWorld::Ptr& ptr) = 0; virtual void objectTeleported(const MWWorld::Ptr& ptr) = 0;

@ -105,11 +105,14 @@ namespace MWLua
LuaUtil::LuaStorage::initLuaBindings(mLua.sol()); LuaUtil::LuaStorage::initLuaBindings(mLua.sol());
mGlobalScripts.addPackage( mGlobalScripts.addPackage(
"openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(mLua.sol(), &mGlobalStorage)); "openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(mLua.sol(), &mGlobalStorage));
mMenuScripts.addPackage("openmw.storage", LuaUtil::LuaStorage::initMenuPackage(mLua.sol(), &mPlayerStorage)); mMenuScripts.addPackage(
"openmw.storage", LuaUtil::LuaStorage::initMenuPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage));
mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(mLua.sol(), &mGlobalStorage); mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(mLua.sol(), &mGlobalStorage);
mPlayerPackages["openmw.storage"] mPlayerPackages["openmw.storage"]
= LuaUtil::LuaStorage::initPlayerPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage); = LuaUtil::LuaStorage::initPlayerPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage);
mPlayerStorage.setActive(true);
initConfiguration(); initConfiguration();
mInitialized = true; mInitialized = true;
mMenuScripts.addAutoStartedScripts(); mMenuScripts.addAutoStartedScripts();
@ -301,6 +304,7 @@ namespace MWLua
mPlayer = MWWorld::Ptr(); mPlayer = MWWorld::Ptr();
} }
mGlobalStorage.clearTemporaryAndRemoveCallbacks(); mGlobalStorage.clearTemporaryAndRemoveCallbacks();
mGlobalStorage.setActive(false);
mPlayerStorage.clearTemporaryAndRemoveCallbacks(); mPlayerStorage.clearTemporaryAndRemoveCallbacks();
mInputActions.clear(); mInputActions.clear();
mInputTriggers.clear(); mInputTriggers.clear();
@ -329,6 +333,7 @@ namespace MWLua
void LuaManager::newGameStarted() void LuaManager::newGameStarted()
{ {
mGlobalStorage.setActive(true);
mInputEvents.clear(); mInputEvents.clear();
mGlobalScripts.addAutoStartedScripts(); mGlobalScripts.addAutoStartedScripts();
mGlobalScriptsStarted = true; mGlobalScriptsStarted = true;
@ -338,12 +343,20 @@ namespace MWLua
void LuaManager::gameLoaded() void LuaManager::gameLoaded()
{ {
mGlobalStorage.setActive(true);
if (!mGlobalScriptsStarted) if (!mGlobalScriptsStarted)
mGlobalScripts.addAutoStartedScripts(); mGlobalScripts.addAutoStartedScripts();
mGlobalScriptsStarted = true; mGlobalScriptsStarted = true;
mMenuScripts.stateChanged(); mMenuScripts.stateChanged();
} }
void LuaManager::gameEnded()
{
// TODO: disable scripts and global storage when the game is actually unloaded
// mGlobalStorage.setActive(false);
mMenuScripts.stateChanged();
}
void LuaManager::uiModeChanged(const MWWorld::Ptr& arg) void LuaManager::uiModeChanged(const MWWorld::Ptr& arg)
{ {
if (mPlayer.isEmpty()) if (mPlayer.isEmpty())
@ -492,6 +505,10 @@ namespace MWLua
throw std::runtime_error("Last generated RefNum is invalid"); throw std::runtime_error("Last generated RefNum is invalid");
MWBase::Environment::get().getWorldModel()->setLastGeneratedRefNum(lastGenerated); MWBase::Environment::get().getWorldModel()->setLastGeneratedRefNum(lastGenerated);
// TODO: don't execute scripts right away, it will be necessary in multiplayer where global storage requires
// initialization. For now just set global storage as active slightly before it would be set by gameLoaded()
mGlobalStorage.setActive(true);
ESM::LuaScripts globalScripts; ESM::LuaScripts globalScripts;
globalScripts.load(reader); globalScripts.load(reader);
mLuaEvents.load(mLua.sol(), reader, mContentFileMapping, mGlobalLoader.get()); mLuaEvents.load(mLua.sol(), reader, mContentFileMapping, mGlobalLoader.get());
@ -540,29 +557,49 @@ namespace MWLua
mInputTriggers.clear(); mInputTriggers.clear();
initConfiguration(); initConfiguration();
{ // Reload global scripts ESM::LuaScripts globalData;
if (mGlobalScriptsStarted)
{
mGlobalScripts.setSavedDataDeserializer(mGlobalSerializer.get()); mGlobalScripts.setSavedDataDeserializer(mGlobalSerializer.get());
ESM::LuaScripts data; mGlobalScripts.save(globalData);
mGlobalScripts.save(data);
mGlobalStorage.clearTemporaryAndRemoveCallbacks(); mGlobalStorage.clearTemporaryAndRemoveCallbacks();
mGlobalScripts.load(data);
} }
std::unordered_map<ESM::RefNum, ESM::LuaScripts> localData;
for (const auto& [id, ptr] : MWBase::Environment::get().getWorldModel()->getPtrRegistryView()) for (const auto& [id, ptr] : MWBase::Environment::get().getWorldModel()->getPtrRegistryView())
{ // Reload local scripts {
LocalScripts* scripts = ptr.getRefData().getLuaScripts(); LocalScripts* scripts = ptr.getRefData().getLuaScripts();
if (scripts == nullptr) if (scripts == nullptr)
continue; continue;
scripts->setSavedDataDeserializer(mLocalSerializer.get()); scripts->setSavedDataDeserializer(mLocalSerializer.get());
ESM::LuaScripts data; ESM::LuaScripts data;
scripts->save(data); scripts->save(data);
scripts->load(data); localData[id] = data;
} }
for (LocalScripts* scripts : mActiveLocalScripts)
scripts->setActive(true);
mMenuScripts.removeAllScripts(); mMenuScripts.removeAllScripts();
mPlayerStorage.clearTemporaryAndRemoveCallbacks();
mMenuScripts.addAutoStartedScripts(); mMenuScripts.addAutoStartedScripts();
for (const auto& [id, ptr] : MWBase::Environment::get().getWorldModel()->getPtrRegistryView())
{
LocalScripts* scripts = ptr.getRefData().getLuaScripts();
if (scripts == nullptr)
continue;
scripts->load(localData[id]);
}
for (LocalScripts* scripts : mActiveLocalScripts)
scripts->setActive(true);
if (mGlobalScriptsStarted)
{
mGlobalScripts.load(globalData);
}
} }
void LuaManager::handleConsoleCommand( void LuaManager::handleConsoleCommand(

@ -68,6 +68,7 @@ namespace MWLua
// LuaManager queues these events and propagates to scripts on the next `update` call. // LuaManager queues these events and propagates to scripts on the next `update` call.
void newGameStarted() override; void newGameStarted() override;
void gameLoaded() override; void gameLoaded() override;
void gameEnded() override;
void objectAddedToScene(const MWWorld::Ptr& ptr) override; void objectAddedToScene(const MWWorld::Ptr& ptr) override;
void objectRemovedFromScene(const MWWorld::Ptr& ptr) override; void objectRemovedFromScene(const MWWorld::Ptr& ptr) override;
void inputEvent(const InputEvent& event) override; void inputEvent(const InputEvent& event) override;

@ -157,10 +157,10 @@ void MWState::StateManager::newGame(bool bypass)
{ {
Log(Debug::Info) << "Starting a new game"; Log(Debug::Info) << "Starting a new game";
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
MWBase::Environment::get().getLuaManager()->newGameStarted();
MWBase::Environment::get().getWorld()->startNewGame(bypass); MWBase::Environment::get().getWorld()->startNewGame(bypass);
mState = State_Running; mState = State_Running;
MWBase::Environment::get().getLuaManager()->newGameStarted();
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0);
MWBase::Environment::get().getWindowManager()->fadeScreenIn(1); MWBase::Environment::get().getWindowManager()->fadeScreenIn(1);
@ -184,11 +184,13 @@ void MWState::StateManager::newGame(bool bypass)
void MWState::StateManager::endGame() void MWState::StateManager::endGame()
{ {
mState = State_Ended; mState = State_Ended;
MWBase::Environment::get().getLuaManager()->gameEnded();
} }
void MWState::StateManager::resumeGame() void MWState::StateManager::resumeGame()
{ {
mState = State_Running; mState = State_Running;
MWBase::Environment::get().getLuaManager()->gameLoaded();
} }
void MWState::StateManager::saveGame(std::string_view description, const Slot* slot) void MWState::StateManager::saveGame(std::string_view description, const Slot* slot)

@ -22,6 +22,7 @@ namespace
sol::state_view& mLua = luaState.sol(); sol::state_view& mLua = luaState.sol();
LuaUtil::LuaStorage::initLuaBindings(mLua); LuaUtil::LuaStorage::initLuaBindings(mLua);
LuaUtil::LuaStorage storage(mLua); LuaUtil::LuaStorage storage(mLua);
storage.setActive(true);
std::vector<std::string> callbackCalls; std::vector<std::string> callbackCalls;
sol::table callbackHiddenData(mLua, sol::create); sol::table callbackHiddenData(mLua, sol::create);
@ -65,6 +66,7 @@ namespace
sol::state mLua; sol::state mLua;
LuaUtil::LuaStorage::initLuaBindings(mLua); LuaUtil::LuaStorage::initLuaBindings(mLua);
LuaUtil::LuaStorage storage(mLua); LuaUtil::LuaStorage storage(mLua);
storage.setActive(true);
mLua["mutable"] = storage.getMutableSection("test"); mLua["mutable"] = storage.getMutableSection("test");
mLua["ro"] = storage.getReadOnlySection("test"); mLua["ro"] = storage.getReadOnlySection("test");
@ -82,6 +84,7 @@ namespace
sol::state mLua; sol::state mLua;
LuaUtil::LuaStorage::initLuaBindings(mLua); LuaUtil::LuaStorage::initLuaBindings(mLua);
LuaUtil::LuaStorage storage(mLua); LuaUtil::LuaStorage storage(mLua);
storage.setActive(true);
mLua["permanent"] = storage.getMutableSection("permanent"); mLua["permanent"] = storage.getMutableSection("permanent");
mLua["temporary"] = storage.getMutableSection("temporary"); mLua["temporary"] = storage.getMutableSection("temporary");
@ -104,6 +107,7 @@ namespace
mLua.safe_script("permanent:set('z', 4)"); mLua.safe_script("permanent:set('z', 4)");
LuaUtil::LuaStorage storage2(mLua); LuaUtil::LuaStorage storage2(mLua);
storage2.setActive(true);
storage2.load(tmpFile); storage2.load(tmpFile);
mLua["permanent"] = storage2.getMutableSection("permanent"); mLua["permanent"] = storage2.getMutableSection("permanent");
mLua["temporary"] = storage2.getMutableSection("temporary"); mLua["temporary"] = storage2.getMutableSection("temporary");

@ -31,6 +31,7 @@ namespace LuaUtil
const LuaStorage::Value& LuaStorage::Section::get(std::string_view key) const const LuaStorage::Value& LuaStorage::Section::get(std::string_view key) const
{ {
checkIfActive();
auto it = mValues.find(key); auto it = mValues.find(key);
if (it != mValues.end()) if (it != mValues.end())
return it->second; return it->second;
@ -72,6 +73,7 @@ namespace LuaUtil
void LuaStorage::Section::set(std::string_view key, const sol::object& value) void LuaStorage::Section::set(std::string_view key, const sol::object& value)
{ {
checkIfActive();
throwIfCallbackRecursionIsTooDeep(); throwIfCallbackRecursionIsTooDeep();
if (value != sol::nil) if (value != sol::nil)
mValues[std::string(key)] = Value(value); mValues[std::string(key)] = Value(value);
@ -88,6 +90,7 @@ namespace LuaUtil
void LuaStorage::Section::setAll(const sol::optional<sol::table>& values) void LuaStorage::Section::setAll(const sol::optional<sol::table>& values)
{ {
checkIfActive();
throwIfCallbackRecursionIsTooDeep(); throwIfCallbackRecursionIsTooDeep();
mValues.clear(); mValues.clear();
if (values) if (values)
@ -102,6 +105,7 @@ namespace LuaUtil
sol::table LuaStorage::Section::asTable() sol::table LuaStorage::Section::asTable()
{ {
checkIfActive();
sol::table res(mStorage->mLua, sol::create); sol::table res(mStorage->mLua, sol::create);
for (const auto& [k, v] : mValues) for (const auto& [k, v] : mValues)
res[k] = v.getCopy(mStorage->mLua); res[k] = v.getCopy(mStorage->mLua);
@ -175,12 +179,14 @@ namespace LuaUtil
return LuaUtil::makeReadOnly(res); return LuaUtil::makeReadOnly(res);
} }
sol::table LuaStorage::initMenuPackage(lua_State* lua, LuaStorage* playerStorage) sol::table LuaStorage::initMenuPackage(lua_State* lua, LuaStorage* globalStorage, LuaStorage* playerStorage)
{ {
sol::table res(lua, sol::create); sol::table res(lua, sol::create);
res["playerSection"] = [playerStorage](std::string_view section) { res["playerSection"] = [playerStorage](std::string_view section) {
return playerStorage->getMutableSection(section, /*forMenuScripts=*/true); return playerStorage->getMutableSection(section, /*forMenuScripts=*/true);
}; };
res["globalSection"]
= [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); };
res["allPlayerSections"] = [playerStorage]() { return playerStorage->getAllSections(); }; res["allPlayerSections"] = [playerStorage]() { return playerStorage->getAllSections(); };
return LuaUtil::makeReadOnly(res); return LuaUtil::makeReadOnly(res);
} }
@ -244,6 +250,7 @@ namespace LuaUtil
const std::shared_ptr<LuaStorage::Section>& LuaStorage::getSection(std::string_view sectionName) const std::shared_ptr<LuaStorage::Section>& LuaStorage::getSection(std::string_view sectionName)
{ {
checkIfActive();
auto it = mData.find(sectionName); auto it = mData.find(sectionName);
if (it != mData.end()) if (it != mData.end())
return it->second; return it->second;
@ -255,12 +262,14 @@ namespace LuaUtil
sol::object LuaStorage::getSection(std::string_view sectionName, bool readOnly, bool forMenuScripts) sol::object LuaStorage::getSection(std::string_view sectionName, bool readOnly, bool forMenuScripts)
{ {
checkIfActive();
const std::shared_ptr<Section>& section = getSection(sectionName); const std::shared_ptr<Section>& section = getSection(sectionName);
return sol::make_object<SectionView>(mLua, SectionView{ section, readOnly, forMenuScripts }); return sol::make_object<SectionView>(mLua, SectionView{ section, readOnly, forMenuScripts });
} }
sol::table LuaStorage::getAllSections(bool readOnly) sol::table LuaStorage::getAllSections(bool readOnly)
{ {
checkIfActive();
sol::table res(mLua, sol::create); sol::table res(mLua, sol::create);
for (const auto& [sectionName, _] : mData) for (const auto& [sectionName, _] : mData)
res[sectionName] = getSection(sectionName, readOnly); res[sectionName] = getSection(sectionName, readOnly);

@ -3,6 +3,7 @@
#include <map> #include <map>
#include <sol/sol.hpp> #include <sol/sol.hpp>
#include <stdexcept>
#include "asyncpackage.hpp" #include "asyncpackage.hpp"
#include "serialization.hpp" #include "serialization.hpp"
@ -17,10 +18,11 @@ namespace LuaUtil
static sol::table initGlobalPackage(lua_State* lua, LuaStorage* globalStorage); static sol::table initGlobalPackage(lua_State* lua, LuaStorage* globalStorage);
static sol::table initLocalPackage(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); static sol::table initPlayerPackage(lua_State* lua, LuaStorage* globalStorage, LuaStorage* playerStorage);
static sol::table initMenuPackage(lua_State* lua, LuaStorage* playerStorage); static sol::table initMenuPackage(lua_State* lua, LuaStorage* globalStorage, LuaStorage* playerStorage);
explicit LuaStorage(lua_State* lua) explicit LuaStorage(lua_State* lua)
: mLua(lua) : mLua(lua)
, mActive(false)
{ {
} }
@ -55,6 +57,7 @@ namespace LuaUtil
virtual void sectionReplaced(std::string_view section, const sol::optional<sol::table>& values) const = 0; virtual void sectionReplaced(std::string_view section, const sol::optional<sol::table>& values) const = 0;
}; };
void setListener(const Listener* listener) { mListener = listener; } void setListener(const Listener* listener) { mListener = listener; }
void setActive(bool active) { mActive = active; }
private: private:
class Value class Value
@ -95,6 +98,8 @@ namespace LuaUtil
// remove them in clear() // remove them in clear()
bool mPermanent = true; bool mPermanent = true;
static Value sEmpty; static Value sEmpty;
void checkIfActive() const { mStorage->checkIfActive(); }
}; };
struct SectionView struct SectionView
{ {
@ -109,6 +114,12 @@ namespace LuaUtil
std::map<std::string_view, std::shared_ptr<Section>> mData; std::map<std::string_view, std::shared_ptr<Section>> mData;
const Listener* mListener = nullptr; const Listener* mListener = nullptr;
std::set<const Section*> mRunningCallbacks; std::set<const Section*> mRunningCallbacks;
bool mActive;
void checkIfActive() const
{
if (!mActive)
throw std::logic_error("Trying to access inactive storage");
}
}; };
} }

@ -17,13 +17,14 @@
--- ---
-- Get a section of the global storage; can be used by any script, but only global scripts can change values. -- Get a section of the global storage; can be used by any script, but only global scripts can change values.
-- Menu scripts can only access it when a game is running.
-- Creates the section if it doesn't exist. -- Creates the section if it doesn't exist.
-- @function [parent=#storage] globalSection -- @function [parent=#storage] globalSection
-- @param #string sectionName -- @param #string sectionName
-- @return #StorageSection -- @return #StorageSection
--- ---
-- Get a section of the player storage; can be used by player scripts only. -- Get a section of the player storage; can only be used by player and menu scripts.
-- Creates the section if it doesn't exist. -- Creates the section if it doesn't exist.
-- @function [parent=#storage] playerSection -- @function [parent=#storage] playerSection
-- @param #string sectionName -- @param #string sectionName
@ -36,7 +37,7 @@
-- @return #table -- @return #table
--- ---
-- Get all global sections as a table; can be used by player scripts only. -- Get all player sections as a table; can only be used by player and menu scripts.
-- Note that adding/removing items to the returned table doesn't create or remove sections. -- Note that adding/removing items to the returned table doesn't create or remove sections.
-- @function [parent=#storage] allPlayerSections -- @function [parent=#storage] allPlayerSections
-- @return #table -- @return #table

Loading…
Cancel
Save