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 gameLoaded() = 0;
virtual void gameEnded() = 0;
virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0;
virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0;
virtual void objectTeleported(const MWWorld::Ptr& ptr) = 0;

@ -105,11 +105,14 @@ namespace MWLua
LuaUtil::LuaStorage::initLuaBindings(mLua.sol());
mGlobalScripts.addPackage(
"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);
mPlayerPackages["openmw.storage"]
= LuaUtil::LuaStorage::initPlayerPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage);
mPlayerStorage.setActive(true);
initConfiguration();
mInitialized = true;
mMenuScripts.addAutoStartedScripts();
@ -301,6 +304,7 @@ namespace MWLua
mPlayer = MWWorld::Ptr();
}
mGlobalStorage.clearTemporaryAndRemoveCallbacks();
mGlobalStorage.setActive(false);
mPlayerStorage.clearTemporaryAndRemoveCallbacks();
mInputActions.clear();
mInputTriggers.clear();
@ -329,6 +333,7 @@ namespace MWLua
void LuaManager::newGameStarted()
{
mGlobalStorage.setActive(true);
mInputEvents.clear();
mGlobalScripts.addAutoStartedScripts();
mGlobalScriptsStarted = true;
@ -338,12 +343,20 @@ namespace MWLua
void LuaManager::gameLoaded()
{
mGlobalStorage.setActive(true);
if (!mGlobalScriptsStarted)
mGlobalScripts.addAutoStartedScripts();
mGlobalScriptsStarted = true;
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)
{
if (mPlayer.isEmpty())
@ -492,6 +505,10 @@ namespace MWLua
throw std::runtime_error("Last generated RefNum is invalid");
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;
globalScripts.load(reader);
mLuaEvents.load(mLua.sol(), reader, mContentFileMapping, mGlobalLoader.get());
@ -540,29 +557,49 @@ namespace MWLua
mInputTriggers.clear();
initConfiguration();
{ // Reload global scripts
ESM::LuaScripts globalData;
if (mGlobalScriptsStarted)
{
mGlobalScripts.setSavedDataDeserializer(mGlobalSerializer.get());
ESM::LuaScripts data;
mGlobalScripts.save(data);
mGlobalScripts.save(globalData);
mGlobalStorage.clearTemporaryAndRemoveCallbacks();
mGlobalScripts.load(data);
}
std::unordered_map<ESM::RefNum, ESM::LuaScripts> localData;
for (const auto& [id, ptr] : MWBase::Environment::get().getWorldModel()->getPtrRegistryView())
{ // Reload local scripts
{
LocalScripts* scripts = ptr.getRefData().getLuaScripts();
if (scripts == nullptr)
continue;
scripts->setSavedDataDeserializer(mLocalSerializer.get());
ESM::LuaScripts data;
scripts->save(data);
scripts->load(data);
localData[id] = data;
}
for (LocalScripts* scripts : mActiveLocalScripts)
scripts->setActive(true);
mMenuScripts.removeAllScripts();
mPlayerStorage.clearTemporaryAndRemoveCallbacks();
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(

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

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

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

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

@ -3,6 +3,7 @@
#include <map>
#include <sol/sol.hpp>
#include <stdexcept>
#include "asyncpackage.hpp"
#include "serialization.hpp"
@ -17,10 +18,11 @@ namespace LuaUtil
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);
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)
: 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;
};
void setListener(const Listener* listener) { mListener = listener; }
void setActive(bool active) { mActive = active; }
private:
class Value
@ -95,6 +98,8 @@ namespace LuaUtil
// remove them in clear()
bool mPermanent = true;
static Value sEmpty;
void checkIfActive() const { mStorage->checkIfActive(); }
};
struct SectionView
{
@ -109,6 +114,12 @@ namespace LuaUtil
std::map<std::string_view, std::shared_ptr<Section>> mData;
const Listener* mListener = nullptr;
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.
-- Menu scripts can only access it when a game is running.
-- Creates the section if it doesn't exist.
-- @function [parent=#storage] globalSection
-- @param #string sectionName
-- @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.
-- @function [parent=#storage] playerSection
-- @param #string sectionName
@ -36,7 +37,7 @@
-- @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.
-- @function [parent=#storage] allPlayerSections
-- @return #table

Loading…
Cancel
Save