From 007a33404ecfe518a2b9365de888d19933ecbcc9 Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 15 Feb 2022 20:26:37 +0000 Subject: [PATCH] Close plugin's windows gracefully when stopped When a plugin is stopped, close all windows created by that plugin. Ensure the close event on the window can not open a new window if the plugin is being stopped. --- src/openrct2-ui/scripting/CustomWindow.cpp | 28 ++++++++++++++++++++-- src/openrct2-ui/scripting/CustomWindow.h | 1 + src/openrct2-ui/scripting/ScUi.hpp | 2 ++ src/openrct2-ui/scripting/UiExtensions.cpp | 2 ++ src/openrct2/scripting/Plugin.cpp | 16 ++++++++++++- src/openrct2/scripting/Plugin.h | 11 ++++++++- src/openrct2/scripting/ScriptEngine.cpp | 20 ++++++---------- 7 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/openrct2-ui/scripting/CustomWindow.cpp b/src/openrct2-ui/scripting/CustomWindow.cpp index 3a6718d5bc..47cfd3a372 100644 --- a/src/openrct2-ui/scripting/CustomWindow.cpp +++ b/src/openrct2-ui/scripting/CustomWindow.cpp @@ -9,16 +9,17 @@ #ifdef ENABLE_SCRIPTING +# include "../UiContext.h" # include "../interface/Dropdown.h" +# include "../interface/Widget.h" # include "../scripting/ScGraphicsContext.hpp" # include "../scripting/ScWidget.hpp" +# include "../windows/Window.h" # include "CustomListView.h" # include "ScUi.hpp" # include "ScWindow.hpp" # include -# include -# include # include # include # include @@ -1433,6 +1434,29 @@ namespace OpenRCT2::Ui::Windows } } + void CloseWindowsOwnedByPlugin(std::shared_ptr plugin) + { + // Get all the windows that need closing + std::vector> customWindows; + for (const auto& window : g_window_list) + { + if (window->classification == WC_CUSTOM) + { + auto customWindow = reinterpret_cast(window.get()); + auto customInfo = reinterpret_cast(customWindow->custom_info); + if (customInfo != nullptr && customInfo->Owner == plugin) + { + customWindows.push_back(window); + } + } + } + + for (auto& window : customWindows) + { + window_close(window.get()); + } + } + } // namespace OpenRCT2::Ui::Windows #endif diff --git a/src/openrct2-ui/scripting/CustomWindow.h b/src/openrct2-ui/scripting/CustomWindow.h index c30d6249c7..15dc17d56d 100644 --- a/src/openrct2-ui/scripting/CustomWindow.h +++ b/src/openrct2-ui/scripting/CustomWindow.h @@ -36,6 +36,7 @@ namespace OpenRCT2::Ui::Windows CustomListView* GetCustomListView(rct_window* w, rct_widgetindex widgetIndex); int32_t GetWidgetMaxLength(rct_window* w, rct_widgetindex widgetIndex); void SetWidgetMaxLength(rct_window* w, rct_widgetindex widgetIndex, int32_t value); + void CloseWindowsOwnedByPlugin(std::shared_ptr plugin); } // namespace OpenRCT2::Ui::Windows #endif diff --git a/src/openrct2-ui/scripting/ScUi.hpp b/src/openrct2-ui/scripting/ScUi.hpp index 4eaf35c062..5f8a55c85a 100644 --- a/src/openrct2-ui/scripting/ScUi.hpp +++ b/src/openrct2-ui/scripting/ScUi.hpp @@ -162,6 +162,8 @@ namespace OpenRCT2::Scripting auto& execInfo = _scriptEngine.GetExecInfo(); auto owner = execInfo.GetCurrentPlugin(); + owner->ThrowIfStopping(); + std::shared_ptr scWindow = nullptr; auto w = window_custom_open(owner, desc); if (w != nullptr) diff --git a/src/openrct2-ui/scripting/UiExtensions.cpp b/src/openrct2-ui/scripting/UiExtensions.cpp index 6edbff05c7..9d43a6430d 100644 --- a/src/openrct2-ui/scripting/UiExtensions.cpp +++ b/src/openrct2-ui/scripting/UiExtensions.cpp @@ -56,6 +56,8 @@ void UiScriptExtensions::Extend(ScriptEngine& scriptEngine) ScWindow::Register(ctx); InitialiseCustomMenuItems(scriptEngine); + scriptEngine.SubscribeToPluginStoppedEvent( + [](std::shared_ptr plugin) -> void { CloseWindowsOwnedByPlugin(plugin); }); } std::shared_ptr ScWidget::window_get() const diff --git a/src/openrct2/scripting/Plugin.cpp b/src/openrct2/scripting/Plugin.cpp index 854c08da66..1ac86345e8 100644 --- a/src/openrct2/scripting/Plugin.cpp +++ b/src/openrct2/scripting/Plugin.cpp @@ -96,11 +96,25 @@ void Plugin::Start() _hasStarted = true; } -void Plugin::Stop() +void Plugin::StopBegin() { + _isStopping = true; +} + +void Plugin::StopEnd() +{ + _isStopping = false; _hasStarted = false; } +void Plugin::ThrowIfStopping() const +{ + if (IsStopping()) + { + duk_error(_context, DUK_ERR_ERROR, "Plugin is stopping."); + } +} + void Plugin::LoadCodeFromFile() { _code = File::ReadAllText(_path); diff --git a/src/openrct2/scripting/Plugin.h b/src/openrct2/scripting/Plugin.h index b6fcb41027..6bda824c37 100644 --- a/src/openrct2/scripting/Plugin.h +++ b/src/openrct2/scripting/Plugin.h @@ -54,6 +54,7 @@ namespace OpenRCT2::Scripting PluginMetadata _metadata{}; std::string _code; bool _hasStarted{}; + bool _isStopping{}; public: std::string GetPath() const @@ -81,6 +82,11 @@ namespace OpenRCT2::Scripting return _hasStarted; } + bool IsStopping() const + { + return _isStopping; + } + int32_t GetTargetAPIVersion() const; Plugin() = default; @@ -91,7 +97,10 @@ namespace OpenRCT2::Scripting void SetCode(std::string_view code); void Load(); void Start(); - void Stop(); + void StopBegin(); + void StopEnd(); + + void ThrowIfStopping() const; private: void LoadCodeFromFile(); diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 024cec05bd..f3a2132541 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -509,24 +509,18 @@ void ScriptEngine::StopPlugin(std::shared_ptr plugin) { if (plugin->HasStarted()) { - RemoveCustomGameActions(plugin); - RemoveIntervals(plugin); - RemoveSockets(plugin); - _hookEngine.UnsubscribeAll(plugin); + plugin->StopBegin(); + for (const auto& callback : _pluginStoppedSubscriptions) { callback(plugin); } + RemoveCustomGameActions(plugin); + RemoveIntervals(plugin); + RemoveSockets(plugin); + _hookEngine.UnsubscribeAll(plugin); - ScriptExecutionInfo::PluginScope scope(_execInfo, plugin, false); - try - { - plugin->Stop(); - } - catch (const std::exception& e) - { - _console.WriteLineError(e.what()); - } + plugin->StopEnd(); } }