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() void game_load_scripts()
{ {
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
GetContext()->GetScriptEngine().LoadPlugins(); GetContext()->GetScriptEngine().LoadTransientPlugins();
#endif #endif
} }
void game_unload_scripts() void game_unload_scripts()
{ {
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
GetContext()->GetScriptEngine().UnloadPlugins(); GetContext()->GetScriptEngine().UnloadTransientPlugins();
#endif #endif
} }

View File

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

View File

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