Implement lifetime logic of transient plugins

This commit is contained in:
Ted John 2021-03-02 22:55:41 +00:00
parent 6fea0b5025
commit dbf83f018c
3 changed files with 76 additions and 97 deletions

View File

@ -502,14 +502,14 @@ void game_load_init()
void game_load_scripts()
{
#ifdef ENABLE_SCRIPTING
GetContext()->GetScriptEngine().LoadPlugins();
GetContext()->GetScriptEngine().LoadTransientPlugins();
#endif
}
void game_unload_scripts()
{
#ifdef ENABLE_SCRIPTING
GetContext()->GetScriptEngine().UnloadPlugins();
GetContext()->GetScriptEngine().UnloadTransientPlugins();
#endif
}

View File

@ -436,10 +436,10 @@ void ScriptEngine::Initialise()
dukglue_register_global(ctx, std::make_shared<ScScenario>(), "scenario");
_initialised = true;
_pluginsLoaded = false;
_pluginsStarted = false;
_transientPluginsEnabled = false;
_transientPluginsStarted = false;
InitSharedStorage();
LoadSharedStorage();
ClearParkStorage();
}
@ -477,7 +477,7 @@ void ScriptEngine::RefreshPlugins()
}
// Turn on hot reload if not already enabled
if (!_hotReloading && gConfigPlugin.enable_hot_reloading && network_get_mode() == NETWORK_MODE_NONE)
if (!_hotReloadingInitialised && gConfigPlugin.enable_hot_reloading && network_get_mode() == NETWORK_MODE_NONE)
{
SetupHotReloading();
}
@ -493,8 +493,8 @@ std::vector<std::string> ScriptEngine::GetPluginFiles() const
auto base = _env.GetDirectoryPath(DIRBASE::USER, DIRID::PLUGIN);
if (Path::DirectoryExists(base))
{
auto pattern = Path::Combine(base, "*.js");
auto scanner = std::unique_ptr<IFileScanner>(Path::ScanDirectory(pattern, true));
auto pattern = Path::Combine(base, u8"*.js");
auto scanner = Path::ScanDirectory(pattern, true);
while (scanner->Next())
{
auto path = std::string(scanner->GetPath());
@ -558,6 +558,8 @@ void ScriptEngine::RegisterPlugin(std::string_view path)
void ScriptEngine::StartIntransientPlugins()
{
LoadSharedStorage();
for (auto& plugin : _plugins)
{
if (!plugin->HasStarted() && !plugin->IsTransient())
@ -586,38 +588,9 @@ void ScriptEngine::StopUnloadRegisterAllPlugins()
}
}
void ScriptEngine::LoadPlugins()
void ScriptEngine::LoadTransientPlugins()
{
if (!_initialised)
{
Initialise();
}
if (_pluginsLoaded)
{
UnloadPlugins();
}
auto base = _env.GetDirectoryPath(DIRBASE::USER, DIRID::PLUGIN);
if (Path::DirectoryExists(base))
{
auto pattern = Path::Combine(base, u8"*.js");
auto scanner = Path::ScanDirectory(pattern, true);
while (scanner->Next())
{
auto path = std::string(scanner->GetPath());
if (ShouldLoadScript(path))
{
LoadPlugin(path);
}
}
if (gConfigPlugin.enable_hot_reloading && network_get_mode() == NETWORK_MODE_NONE)
{
SetupHotReloading();
}
}
_pluginsLoaded = true;
_pluginsStarted = false;
_transientPluginsEnabled = true;
}
void ScriptEngine::LoadPlugin(const std::string& path)
@ -705,7 +678,7 @@ void ScriptEngine::SetupHotReloading()
std::lock_guard<std::mutex> guard(_changedPluginFilesMutex);
_changedPluginFiles.emplace(path);
};
_hotReloading = true;
_hotReloadingInitialised = true;
}
}
catch (const std::exception& e)
@ -714,6 +687,19 @@ void ScriptEngine::SetupHotReloading()
}
}
void ScriptEngine::DoAutoReloadPluginCheck()
{
if (_hotReloadingInitialised)
{
auto tick = Platform::GetTicks();
if (tick - _lastHotReloadCheckTick > 1000)
{
AutoReloadPlugins();
_lastHotReloadCheckTick = tick;
}
}
}
void ScriptEngine::AutoReloadPlugins()
{
if (_changedPluginFiles.size() > 0)
@ -746,39 +732,54 @@ void ScriptEngine::AutoReloadPlugins()
}
}
void ScriptEngine::UnloadPlugins()
void ScriptEngine::UnloadTransientPlugins()
{
StopPlugins();
// Stop them all first
for (auto& plugin : _plugins)
{
LogPluginInfo(plugin, "Unloaded");
}
_plugins.clear();
_pluginsLoaded = false;
_pluginsStarted = false;
}
void ScriptEngine::StartPlugins()
{
LoadSharedStorage();
for (auto& plugin : _plugins)
{
if (!plugin->HasStarted() && ShouldStartPlugin(plugin))
if (plugin->IsTransient())
{
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin, false);
try
if (plugin->HasStarted())
{
LogPluginInfo(plugin, "Started");
plugin->Start();
}
catch (const std::exception& e)
{
_console.WriteLineError(e.what());
StopPlugin(plugin);
LogPluginInfo(plugin, "Stopped");
}
}
}
_pluginsStarted = true;
// Now unload them
for (auto& plugin : _plugins)
{
UnloadPlugin(plugin);
}
_transientPluginsEnabled = false;
_transientPluginsStarted = false;
}
void ScriptEngine::StartTransientPlugins()
{
LoadSharedStorage();
// Load transient plugins
for (auto& plugin : _plugins)
{
if (plugin->IsTransient() && !plugin->IsLoaded() && ShouldStartPlugin(plugin))
{
LoadPlugin(plugin);
}
}
// Start transient plugins
for (auto& plugin : _plugins)
{
if (plugin->IsTransient() && plugin->IsLoaded() && !plugin->HasStarted())
{
StartPlugin(plugin);
}
}
_transientPluginsStarted = true;
}
bool ScriptEngine::ShouldStartPlugin(const std::shared_ptr<Plugin>& plugin)
@ -797,19 +798,6 @@ bool ScriptEngine::ShouldStartPlugin(const std::shared_ptr<Plugin>& plugin)
return true;
}
void ScriptEngine::StopPlugins()
{
for (auto& plugin : _plugins)
{
if (plugin->HasStarted())
{
StopPlugin(plugin);
LogPluginInfo(plugin, "Stopped");
}
}
_pluginsStarted = false;
}
void ScriptEngine::Tick()
{
PROFILED_FUNCTION();
@ -820,26 +808,15 @@ void ScriptEngine::Tick()
RefreshPlugins();
}
if (_pluginsLoaded)
if (_transientPluginsEnabled && !_transientPluginsStarted)
{
if (!_pluginsStarted)
{
StartPlugins();
}
else
{
auto tick = Platform::GetTicks();
if (tick - _lastHotReloadCheckTick > 1000)
{
AutoReloadPlugins();
_lastHotReloadCheckTick = tick;
}
}
StartTransientPlugins();
}
UpdateIntervals();
UpdateSockets();
ProcessREPL();
DoAutoReloadPluginCheck();
}
void ScriptEngine::ProcessREPL()

View File

@ -145,9 +145,9 @@ namespace OpenRCT2::Scripting
IPlatformEnvironment& _env;
DukContext _context;
bool _initialised{};
bool _hotReloading{};
bool _pluginsLoaded{};
bool _pluginsStarted{};
bool _hotReloadingInitialised{};
bool _transientPluginsEnabled{};
bool _transientPluginsStarted{};
std::queue<std::tuple<std::promise<void>, std::string>> _evalQueue;
std::vector<std::shared_ptr<Plugin>> _plugins;
uint32_t _lastHotReloadCheckTick{};
@ -212,6 +212,8 @@ namespace OpenRCT2::Scripting
void LoadPlugins();
void UnloadPlugins();
void LoadTransientPlugins();
void UnloadTransientPlugins();
void Tick();
std::future<void> Eval(const std::string& s);
DukValue ExecutePluginCall(
@ -253,8 +255,7 @@ namespace OpenRCT2::Scripting
void UnregisterPlugin(std::string_view path);
void RegisterPlugin(std::string_view path);
void StartIntransientPlugins();
void StartPlugins();
void StopPlugins();
void StartTransientPlugins();
void LoadPlugin(const std::string& path);
void LoadPlugin(std::shared_ptr<Plugin>& plugin);
void UnloadPlugin(std::shared_ptr<Plugin>& plugin);
@ -264,6 +265,7 @@ namespace OpenRCT2::Scripting
static bool ShouldLoadScript(std::string_view path);
bool ShouldStartPlugin(const std::shared_ptr<Plugin>& plugin);
void SetupHotReloading();
void DoAutoReloadPluginCheck();
void AutoReloadPlugins();
void ProcessREPL();
void RemoveCustomGameActions(const std::shared_ptr<Plugin>& plugin);