diff --git a/include/openspace/scripting/scriptengine.h b/include/openspace/scripting/scriptengine.h index 7ad56ca38a..9b721617bf 100644 --- a/include/openspace/scripting/scriptengine.h +++ b/include/openspace/scripting/scriptengine.h @@ -102,7 +102,6 @@ public: void addLibrary(LuaLibrary library); bool hasLibrary(const std::string& name); - virtual void preSync(bool isMaster) override; virtual void encode(SyncBuffer* syncBuffer) override; virtual void decode(SyncBuffer* syncBuffer) override; @@ -111,6 +110,11 @@ public: void queueScript(Script script); void queueScript(std::string script); + // Runs the `script` every `timeout` seconds wallclock time + void registerRepeatedScript(std::string identifier, std::string script, + double timeout, std::string preScript = "", std::string postScript = ""); + void removeRepeatedScript(std::string_view identifier); + std::vector allLuaFunctions() const; const std::vector& allLuaLibraries() const; @@ -141,6 +145,19 @@ private: std::vector _scriptsToSync; + struct RepeatedScriptInfo { + /// This script is run everytime `timeout` seconds have passed + std::string script; + + /// This script is run when the repeated script is unregistered + std::string postScript; + + std::string identifier; + double timeout = 0.0; + double lastRun = 0.0; + }; + std::vector _repeatedScripts; + // Logging variables bool _logFileExists = false; bool _logScripts = true; diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index f6ee986123..3ade730808 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -103,6 +104,12 @@ void ScriptEngine::deinitialize() { ZoneScoped; _registeredLibraries.clear(); + for (const RepeatedScriptInfo& info : _repeatedScripts) { + if (info.postScript.empty()) { + queueScript(info.postScript); + } + } + _repeatedScripts.clear(); } void ScriptEngine::initializeLuaState(lua_State* state) { @@ -553,6 +560,17 @@ void ScriptEngine::postSync(bool isMaster) { } } } + + double now = + global::sessionRecording->isSavingFramesDuringPlayback() ? + global::sessionRecording->currentApplicationInterpolationTime() : + global::windowDelegate->applicationTime(); + for (RepeatedScriptInfo& info : _repeatedScripts) { + if (now - info.lastRun >= info.timeout) { + runScript({ info.script }); + info.lastRun = now; + } + } } void ScriptEngine::queueScript(Script script) { @@ -568,6 +586,55 @@ void ScriptEngine::queueScript(std::string script) { queueScript({ .code = std::move(script) }); } +void ScriptEngine::registerRepeatedScript(std::string identifier, std::string script, + double timeout, std::string preScript, + std::string postScript) +{ + auto it = std::find_if( + _repeatedScripts.begin(), + _repeatedScripts.end(), + [&identifier](const RepeatedScriptInfo& info) { + return info.identifier == identifier; + } + ); + if (it != _repeatedScripts.end()) { + throw ghoul::RuntimeError( + std::format("Script with identifier '{}' already registered", identifier), + "ScriptEngine" + ); + } + + if (!preScript.empty()) { + runScript({ std::move(preScript) }); + } + _repeatedScripts.emplace_back( + std::move(script), + std::move(postScript), + std::move(identifier), + timeout + ); +} + +void ScriptEngine::removeRepeatedScript(std::string_view identifier) { + auto it = std::find_if( + _repeatedScripts.begin(), + _repeatedScripts.end(), + [&identifier](const RepeatedScriptInfo& info) { + return info.identifier == identifier; + } + ); + if (it != _repeatedScripts.end()) { + if (!it->postScript.empty()) { + queueScript(it->postScript); + } + + _repeatedScripts.erase(it); + } + else { + LERROR(std::format("Could not find script with identifier '{}'", identifier)); + } +} + void ScriptEngine::addBaseLibrary() { ZoneScoped; @@ -645,7 +712,9 @@ void ScriptEngine::addBaseLibrary() { codegen::lua::WalkDirectoryFiles, codegen::lua::WalkDirectoryFolders, codegen::lua::DirectoryForPath, - codegen::lua::UnzipFile + codegen::lua::UnzipFile, + codegen::lua::RegisterRepeatedScript, + codegen::lua::RemoveRepeatedScript } }; addLibrary(lib); diff --git a/src/scripting/scriptengine_lua.inl b/src/scripting/scriptengine_lua.inl index c1c130f480..e66bc3baf3 100644 --- a/src/scripting/scriptengine_lua.inl +++ b/src/scripting/scriptengine_lua.inl @@ -279,6 +279,39 @@ std::vector walkCommon(const std::filesystem::path& path, } } +/** + * This function registers another Lua script that will be periodically executed as long + * as the application is running. The `identifier` is used to later remove the script. The + * `script` is being executed every `timeout` seconds. This timeout is only as accurate as + * the framerate at which the application is running. Optionally the `preScript` Lua + * script is run when registering the repeated script and the `postScript` is run when + * unregistering it or when the application closes. + * If the `timeout` is 0, the script will be executed every frame. + * The `identifier` has to be a unique name that cannot have been used to register a + * repeated script before. A registered script is removed with the #removeRepeatedScript + * function. + */ +[[codegen::luawrap]] void registerRepeatedScript(std::string identifier, + std::string script, double timeout = 0.0, + std::string preScript = "", + std::string postScript = "") +{ + openspace::global::scriptEngine->registerRepeatedScript( + std::move(identifier), + std::move(script), + timeout, + std::move(preScript), + std::move(postScript) + ); +} + +/** + * Removes a previously registered repeated script (see #registerRepeatedScript) + */ +[[codegen::luawrap]] void removeRepeatedScript(std::string identifier) { + openspace::global::scriptEngine->removeRepeatedScript(identifier); +} + #include "scriptengine_lua_codegen.cpp" } // namespace