From cbf6c5be4ef508c2f70e5c0cf370472146a5dd56 Mon Sep 17 00:00:00 2001 From: Causeless Date: Tue, 26 Dec 2023 21:49:38 +0000 Subject: [PATCH 1/5] More accurate tracking, we shouldn't be clearing known MOs here --- Managers/MovableMan.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Managers/MovableMan.cpp b/Managers/MovableMan.cpp index 3f24a0f14..fd4d8094c 100644 --- a/Managers/MovableMan.cpp +++ b/Managers/MovableMan.cpp @@ -305,7 +305,8 @@ void MovableMan::PurgeAllMOs() m_AddedAlarmEvents.clear(); m_AlarmEvents.clear(); m_MOIDIndex.clear(); - m_KnownObjects.clear(); + // We want to keep known objects around, 'cause these can exist even when not in the simulation (they're here from creation till deletion, regardless of whether they are in sim) + //m_KnownObjects.clear(); } From e72201ec06f6b322cffd259d2eff863f8267c2de Mon Sep 17 00:00:00 2001 From: Causeless Date: Wed, 27 Dec 2023 16:08:20 +0000 Subject: [PATCH 2/5] Whole buncha fixes for mod compatibility: * Activities are in the global state again * Activity functions are continually refreshed to account for dofiles * loadfile now accounts for Data/Mods directory --- Activities/GAScripted.cpp | 28 +++++++++--- Activities/GAScripted.h | 5 +++ Entities/MovableObject.cpp | 2 +- Entities/PieSlice.cpp | 2 +- Managers/LuaMan.cpp | 88 +++++++++++++++++++++----------------- Managers/LuaMan.h | 15 +++++-- 6 files changed, 91 insertions(+), 49 deletions(-) diff --git a/Activities/GAScripted.cpp b/Activities/GAScripted.cpp index ba2c4567e..9b48d46a1 100644 --- a/Activities/GAScripted.cpp +++ b/Activities/GAScripted.cpp @@ -204,17 +204,30 @@ int GAScripted::ReloadScripts() { } } - std::unordered_map scriptFileFunctions; - if ((error = g_LuaMan.GetMasterScriptState().RunScriptFileAndRetrieveFunctions(m_ScriptPath, m_LuaClassName, GetSupportedScriptFunctionNames(), scriptFileFunctions, true)) < 0) { + if ((error = g_LuaMan.GetMasterScriptState().RunScriptFile(m_ScriptPath, true, false)) < 0) { return error; } + RefreshActivityFunctions(); + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void GAScripted::RefreshActivityFunctions() { m_ScriptFunctions.clear(); + if (m_ScriptPath.empty()) { + return; + } + + // We use m_LuaClassName here, because we ran the script file in the global state instead of in an environment + std::unordered_map scriptFileFunctions; + g_LuaMan.GetMasterScriptState().RetrieveFunctions(m_LuaClassName, GetSupportedScriptFunctionNames(), scriptFileFunctions); + for (const auto& [functionName, functionObject] : scriptFileFunctions) { m_ScriptFunctions[functionName] = std::unique_ptr(functionObject); } - - return 0; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -485,7 +498,12 @@ int GAScripted::RunLuaFunction(const std::string& functionName, const std::vecto return 0; } - return g_LuaMan.GetMasterScriptState().RunScriptFunctionObject(funcItr->second.get(), "_G", m_LuaClassName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + int error = g_LuaMan.GetMasterScriptState().RunScriptFunctionObject(funcItr->second.get(), "_G", m_LuaClassName, functionEntityArguments, functionLiteralArguments, functionObjectArguments); + + // Need to call this continually unfortunately, as something might change due to dofile() + RefreshActivityFunctions(); + + return error; } diff --git a/Activities/GAScripted.h b/Activities/GAScripted.h index e139d5fc5..e756588ec 100644 --- a/Activities/GAScripted.h +++ b/Activities/GAScripted.h @@ -139,6 +139,11 @@ ClassInfoGetters; int ReloadScripts() override; + /// + /// Refreshes our activity functions to find any changes from script. + /// + void RefreshActivityFunctions(); + ////////////////////////////////////////////////////////////////////////////////////////// // Virtual method: GetLuaClassName diff --git a/Entities/MovableObject.cpp b/Entities/MovableObject.cpp index 734e73624..89c331d39 100644 --- a/Entities/MovableObject.cpp +++ b/Entities/MovableObject.cpp @@ -587,7 +587,7 @@ int MovableObject::LoadScript(const std::string &scriptPath, bool loadAsEnabledS m_AllLoadedScripts.try_emplace(scriptPath, loadAsEnabledScript); std::unordered_map scriptFileFunctions; - if (usedState.RunScriptFileAndRetrieveFunctions(scriptPath, "", GetSupportedScriptFunctionNames(), scriptFileFunctions) < 0) { + if (usedState.RunScriptFileAndRetrieveFunctions(scriptPath, GetSupportedScriptFunctionNames(), scriptFileFunctions) < 0) { return -4; } diff --git a/Entities/PieSlice.cpp b/Entities/PieSlice.cpp index 3565900fe..6ce6d176d 100644 --- a/Entities/PieSlice.cpp +++ b/Entities/PieSlice.cpp @@ -170,7 +170,7 @@ namespace RTE { std::string filePath = m_LuabindFunctionObject->GetFilePath(); std::unordered_map scriptFileFunctions; - status = g_LuaMan.GetMasterScriptState().RunScriptFileAndRetrieveFunctions(filePath, "", { m_FunctionName }, scriptFileFunctions, true); + status = g_LuaMan.GetMasterScriptState().RunScriptFileAndRetrieveFunctions(filePath, { m_FunctionName }, scriptFileFunctions, true); if (scriptFileFunctions.find(m_FunctionName) != scriptFileFunctions.end()) { m_LuabindFunctionObject = std::unique_ptr(scriptFileFunctions.at(m_FunctionName)); } diff --git a/Managers/LuaMan.cpp b/Managers/LuaMan.cpp index ba47dbd64..8ed3a2ecb 100644 --- a/Managers/LuaMan.cpp +++ b/Managers/LuaMan.cpp @@ -9,7 +9,7 @@ namespace RTE { - const std::unordered_set LuaMan::c_FileAccessModes = { "r", "r+", "w", "w+", "a", "a+" }; + const std::unordered_set LuaMan::c_FileAccessModes = { "r", "r+", "w", "w+", "a", "a+", "rt", "wt"}; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -227,8 +227,9 @@ namespace RTE { // Override "math.random" in the lua state to use RTETools MT19937 implementation. Preserve return types of original to not break all the things. "math.random = function(lower, upper) if lower ~= nil and upper ~= nil then return LuaMan:SelectRand(lower, upper); elseif lower ~= nil then return LuaMan:SelectRand(1, lower); else return LuaMan:PosRand(); end end" "\n" - // Override "dofile" to be able to account for Data/ or Mods/ directory. + // Override "dofile"/"loadfile" to be able to account for Data/ or Mods/ directory. "OriginalDoFile = dofile; dofile = function(filePath) filePath = PresetMan:GetFullModulePath(filePath); if filePath ~= '' then return OriginalDoFile(filePath); end end;" + "OriginalLoadFile = loadfile; loadfile = function(filePath) filePath = PresetMan:GetFullModulePath(filePath); if filePath ~= '' then return OriginalLoadFile(filePath); end end;" // Internal helper functions to add callbacks for async pathing requests "_AsyncPathCallbacks = {};" "_AddAsyncPathCallback = function(id, callback) _AsyncPathCallbacks[id] = callback; end\n" @@ -636,7 +637,7 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptFile(const std::string &filePath, bool consoleErrors) { + int LuaStateWrapper::RunScriptFile(const std::string &filePath, bool consoleErrors, bool doInSandboxedEnvironment) { const std::string fullScriptPath = g_PresetMan.GetFullModulePath(filePath); if (fullScriptPath.empty()) { m_LastError = "Can't run a script file with an empty filepath!"; @@ -674,20 +675,22 @@ namespace RTE { } if (error == 0) { - // create a new environment table - lua_getglobal(m_State, filePath.c_str()); - if (lua_isnil(m_State, -1)) { - lua_pop(m_State, 1); - lua_newtable(m_State); - lua_newtable(m_State); - lua_getglobal(m_State, "_G"); - lua_setfield(m_State, -2, "__index"); - lua_setmetatable(m_State, -2); - lua_setglobal(m_State, filePath.c_str()); + if (doInSandboxedEnvironment) { + // create a new environment table lua_getglobal(m_State, filePath.c_str()); - } + if (lua_isnil(m_State, -1)) { + lua_pop(m_State, 1); + lua_newtable(m_State); + lua_newtable(m_State); + lua_getglobal(m_State, "_G"); + lua_setfield(m_State, -2, "__index"); + lua_setmetatable(m_State, -2); + lua_setglobal(m_State, filePath.c_str()); + lua_getglobal(m_State, filePath.c_str()); + } - lua_setfenv(m_State, -2); + lua_setfenv(m_State, -2); + } // execute script file with pcall. Pcall will call the file and line error handler if there's an error by pointing 2 up the stack to it. if (lua_pcall(m_State, 0, LUA_MULTRET, -2)) { @@ -710,7 +713,36 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaStateWrapper::RunScriptFileAndRetrieveFunctions(const std::string &filePath, const std::string &prefix, const std::vector &functionNamesToLookFor, std::unordered_map &outFunctionNamesAndObjects, bool forceReload) { + bool LuaStateWrapper::RetrieveFunctions(const std::string& funcObjectName, const std::vector& functionNamesToLookFor, std::unordered_map& outFunctionNamesAndObjects) { + std::lock_guard lock(m_Mutex); + s_currentLuaState = this; + + luabind::object funcHoldingObject = luabind::globals(m_State)[funcObjectName.c_str()]; + if (luabind::type(funcHoldingObject) == LUA_TNIL) { + return false; + } + + auto& newScript = m_ScriptCache[funcObjectName.c_str()]; + newScript.functionNamesAndObjects.clear(); + for (const std::string& functionName : functionNamesToLookFor) { + luabind::object functionObject = funcHoldingObject[functionName]; + if (luabind::type(functionObject) == LUA_TFUNCTION) { + luabind::object* functionObjectCopyForStoring = new luabind::object(functionObject); + newScript.functionNamesAndObjects.try_emplace(functionName, new LuabindObjectWrapper(functionObjectCopyForStoring, funcObjectName)); + } + } + + for (auto& pair : newScript.functionNamesAndObjects) { + luabind::object* functionObjectCopyForStoring = new luabind::object(*pair.second->GetLuabindObject()); + outFunctionNamesAndObjects.try_emplace(pair.first, new LuabindObjectWrapper(functionObjectCopyForStoring, funcObjectName)); + } + + return true; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int LuaStateWrapper::RunScriptFileAndRetrieveFunctions(const std::string &filePath, const std::vector &functionNamesToLookFor, std::unordered_map &outFunctionNamesAndObjects, bool forceReload) { static bool disableCaching = false; forceReload = forceReload || disableCaching; @@ -733,32 +765,10 @@ namespace RTE { return error; } - luabind::object prefixObject; - if (prefix == "") { - prefixObject = luabind::globals(m_State)[filePath.c_str()]; - } else { - prefixObject = luabind::globals(m_State)[filePath.c_str()][prefix]; - } - - if (luabind::type(prefixObject) == LUA_TNIL) { + if (!RetrieveFunctions(filePath, functionNamesToLookFor, outFunctionNamesAndObjects)) { return -1; } - auto &newScript = m_ScriptCache[filePath]; - newScript.functionNamesAndObjects.clear(); - for (const std::string& functionName : functionNamesToLookFor) { - luabind::object functionObject = prefixObject[functionName]; - if (luabind::type(functionObject) == LUA_TFUNCTION) { - luabind::object* functionObjectCopyForStoring = new luabind::object(functionObject); - newScript.functionNamesAndObjects.try_emplace(functionName, new LuabindObjectWrapper(functionObjectCopyForStoring, filePath)); - } - } - - for (auto& pair : newScript.functionNamesAndObjects) { - luabind::object* functionObjectCopyForStoring = new luabind::object(*pair.second->GetLuabindObject()); - outFunctionNamesAndObjects.try_emplace(pair.first, new LuabindObjectWrapper(functionObjectCopyForStoring, filePath)); - } - return 0; } diff --git a/Managers/LuaMan.h b/Managers/LuaMan.h index f16aae02d..9173014c1 100644 --- a/Managers/LuaMan.h +++ b/Managers/LuaMan.h @@ -148,19 +148,28 @@ namespace RTE { /// /// The path to the file to load and run. /// Whether to report any errors to the console immediately. + /// Whether to do it in a sandboxed environment, or the global environment. /// Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. - int RunScriptFile(const std::string &filePath, bool consoleErrors = true); + int RunScriptFile(const std::string &filePath, bool consoleErrors = true, bool doInSandboxedEnvironment = true); + + /// + /// Retrieves all of the specified functions that exist into the output map, and refreshes the cache. + /// + /// The path to the file to load and run. + /// The vector of strings defining the function names to be retrieved. + /// The map of function names to LuabindObjectWrappers to be retrieved from the script that was run. + /// Returns whether functions were successfully retrieved. + bool RetrieveFunctions(const std::string& functionObjectName, const std::vector& functionNamesToLookFor, std::unordered_map& outFunctionNamesAndObjects); /// /// Opens and loads a file containing a script and runs it on the state, then retrieves all of the specified functions that exist into the output map. /// /// The path to the file to load and run. - /// The prefix before each function we're looking for. With normal objects this is usually nothing (free floating), but activities expect the activity name beforehand. /// The vector of strings defining the function names to be retrieved. /// The map of function names to LuabindObjectWrappers to be retrieved from the script that was run. /// Whether caching shouldn't be used. /// Returns less than zero if any errors encountered when running this script. To get the actual error string, call GetLastError. - int RunScriptFileAndRetrieveFunctions(const std::string &filePath, const std::string &prefix, const std::vector &functionNamesToLookFor, std::unordered_map &outFunctionNamesAndObjects, bool forceReload = false); + int RunScriptFileAndRetrieveFunctions(const std::string &filePath, const std::vector &functionNamesToLookFor, std::unordered_map &outFunctionNamesAndObjects, bool forceReload = false); #pragma endregion #pragma region Concrete Methods From 0305917f3e040c2cd8a934f698ffd0b54e2ef26d Mon Sep 17 00:00:00 2001 From: Causeless Date: Wed, 27 Dec 2023 20:12:21 +0000 Subject: [PATCH 3/5] Fixed crash when removing MO --- Entities/MovableObject.cpp | 4 ++++ Managers/MovableMan.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Entities/MovableObject.cpp b/Entities/MovableObject.cpp index 89c331d39..8cf1b35b0 100644 --- a/Entities/MovableObject.cpp +++ b/Entities/MovableObject.cpp @@ -557,6 +557,10 @@ void MovableObject::Destroy(bool notInherited) { // TODO: try to make this at least reasonably workable //DestroyScriptState(); + if (m_ThreadedLuaState) { + m_ThreadedLuaState->UnregisterMO(this); + } + g_MovableMan.UnregisterObject(this); if (!notInherited) { SceneObject::Destroy(); diff --git a/Managers/MovableMan.cpp b/Managers/MovableMan.cpp index fd4d8094c..22206c9d9 100644 --- a/Managers/MovableMan.cpp +++ b/Managers/MovableMan.cpp @@ -1721,7 +1721,7 @@ void MovableMan::Update() LuaStateWrapper& luaState = luaStates[start]; g_LuaMan.SetThreadLuaStateOverride(&luaState); - for (MovableObject *mo : luaState.GetRegisteredMOs()) { + for (MovableObject *mo : luaState.GetRegisteredMOs()) { mo->RunScriptedFunctionInAppropriateScripts(threadedUpdate, false, false, {}, {}, {}); } From 9396fe8814758a5c29a305e89698577192d2e7aa Mon Sep 17 00:00:00 2001 From: Causeless Date: Wed, 27 Dec 2023 20:23:28 +0000 Subject: [PATCH 4/5] Fixed alpha blender being set too late, causing a frame of wrong alpha when starting game --- Menus/TitleScreen.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Menus/TitleScreen.cpp b/Menus/TitleScreen.cpp index d79b7ce83..c9145b503 100644 --- a/Menus/TitleScreen.cpp +++ b/Menus/TitleScreen.cpp @@ -543,6 +543,11 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void TitleScreen::DrawTitleScreenScene() { + // This only needs to be done once, but bitmaps can be reloaded which effectively undoes this, so just do it all the time to not deal with flags and checks. + set_write_alpha_blender(); + draw_trans_sprite(m_Planet.GetSpriteFrame(0), ContentFile("Base.rte/GUIs/Title/PlanetAlpha.png").GetAsBitmap(), 0, 0); + draw_trans_sprite(m_Moon.GetSpriteFrame(0), ContentFile("Base.rte/GUIs/Title/MoonAlpha.png").GetAsBitmap(), 0, 0); + Box nebulaTargetBox; m_Nebula.SetOffset(Vector(static_cast((m_TitleScreenMaxWidth - m_Nebula.GetBitmap()->w) / 2), m_ScrollOffset.GetY())); m_Nebula.Draw(g_FrameMan.GetBackBuffer32(), nebulaTargetBox, true); @@ -565,11 +570,6 @@ namespace RTE { m_Station.SetPos(m_PlanetPos + m_StationOffset); m_Station.SetRotAngle(-c_HalfPI + m_StationOrbitRotation); m_Station.Draw(g_FrameMan.GetBackBuffer32()); - - // This only needs to be done once, but bitmaps can be reloaded which effectively undoes this, so just do it all the time to not deal with flags and checks. - set_write_alpha_blender(); - draw_trans_sprite(m_Planet.GetSpriteFrame(0), ContentFile("Base.rte/GUIs/Title/PlanetAlpha.png").GetAsBitmap(), 0, 0); - draw_trans_sprite(m_Moon.GetSpriteFrame(0), ContentFile("Base.rte/GUIs/Title/MoonAlpha.png").GetAsBitmap(), 0, 0); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// From e0a4a228b9c7e9f49762b93ffa650fdec4044b76 Mon Sep 17 00:00:00 2001 From: Causeless Date: Wed, 27 Dec 2023 20:29:41 +0000 Subject: [PATCH 5/5] Allow bursting whenever again, sounds nicer I think --- Entities/AEJetpack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Entities/AEJetpack.cpp b/Entities/AEJetpack.cpp index 7115784ad..6a67efca1 100644 --- a/Entities/AEJetpack.cpp +++ b/Entities/AEJetpack.cpp @@ -143,7 +143,7 @@ namespace RTE { switch (m_JetpackType) { case JetpackType::Standard: - if (controller.IsState(BODY_JUMPSTART) && !IsOutOfFuel() && IsFullyFueled()) { + if (controller.IsState(BODY_JUMPSTART) && !IsOutOfFuel()) { Burst(parentActor, fuelUseMultiplier); } else if (controller.IsState(BODY_JUMP) && !IsOutOfFuel() && (GetJetTimeRatio() >= m_MinimumFuelRatio || wasEmittingLastFrame)) { Thrust(parentActor, fuelUseMultiplier);