From fba60f05a0bf0ab0a0903dd7599864d1eb057836 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 2 Sep 2022 09:52:41 +0200 Subject: [PATCH] Refactor EditorPlugins to properly initialize and deinitialize --- Source/Editor/Editor.cs | 8 ++- Source/Editor/Plugins/EditorPlugin.cs | 29 ++++---- .../Engine/Scripting/Plugins/EditorPlugin.h | 24 +++++++ .../Scripting/Plugins/PluginManager.cpp | 68 +++++++++++++++++++ 4 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 Source/Engine/Scripting/Plugins/EditorPlugin.h diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index fabea9033..94cbf4ce1 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -571,7 +571,13 @@ namespace FlaxEditor // Deinitialize Editor Plugins foreach (var plugin in PluginManager.EditorPlugins) - ((EditorPlugin)plugin).DeinitializeEditor(); + { + if (plugin is EditorPlugin editorPlugin && editorPlugin._isEditorInitialized) + { + editorPlugin._isEditorInitialized = false; + editorPlugin.DeinitializeEditor(); + } + } // Start exit StateMachine.GoToState(); diff --git a/Source/Editor/Plugins/EditorPlugin.cs b/Source/Editor/Plugins/EditorPlugin.cs index cf22ff7af..bfc4e037a 100644 --- a/Source/Editor/Plugins/EditorPlugin.cs +++ b/Source/Editor/Plugins/EditorPlugin.cs @@ -5,15 +5,10 @@ using FlaxEngine; namespace FlaxEditor { - /// - /// Base class for all plugins used in Editor. - /// - /// - /// Plugins should have a public and parameter-less constructor. - /// - /// - public abstract class EditorPlugin : Plugin + partial class EditorPlugin { + internal bool _isEditorInitialized; + /// /// Gets the type of the that is related to this plugin. Some plugins may be used only in editor while others want to gave a runtime representation. Use this property to link the related game plugin. /// @@ -24,14 +19,12 @@ namespace FlaxEditor /// public Editor Editor { get; private set; } - /// - public override void Initialize() + internal void Initialize_Internal() { - base.Initialize(); - Editor = Editor.Instance; if (Editor.IsInitialized) { + _isEditorInitialized = true; InitializeEditor(); } else @@ -40,9 +33,21 @@ namespace FlaxEditor } } + internal void Deinitialize_Internal() + { + if (_isEditorInitialized) + { + _isEditorInitialized = false; + DeinitializeEditor(); + } + } + private void OnEditorInitializationEnd() { Editor.InitializationEnd -= OnEditorInitializationEnd; + if (_isEditorInitialized) + return; + _isEditorInitialized = true; InitializeEditor(); } diff --git a/Source/Engine/Scripting/Plugins/EditorPlugin.h b/Source/Engine/Scripting/Plugins/EditorPlugin.h new file mode 100644 index 000000000..7a519ee0d --- /dev/null +++ b/Source/Engine/Scripting/Plugins/EditorPlugin.h @@ -0,0 +1,24 @@ +// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Plugin.h" + +#if USE_EDITOR + +/// +/// Base class for all plugins used in Editor. +/// +/// +/// Plugins should have a public and parameter-less constructor. +/// +/// +API_CLASS(Abstract, Namespace="FlaxEditor") class FLAXENGINE_API EditorPlugin : public Plugin +{ + DECLARE_SCRIPTING_TYPE(EditorPlugin); +public: + void Initialize() override; + void Deinitialize() override; +}; + +#endif diff --git a/Source/Engine/Scripting/Plugins/PluginManager.cpp b/Source/Engine/Scripting/Plugins/PluginManager.cpp index 0d0fdcb12..0347fc6b2 100644 --- a/Source/Engine/Scripting/Plugins/PluginManager.cpp +++ b/Source/Engine/Scripting/Plugins/PluginManager.cpp @@ -27,6 +27,45 @@ GamePlugin::GamePlugin(const SpawnParams& params) { } +#if USE_EDITOR + +#include "EditorPlugin.h" +#include "Engine/Scripting/MException.h" +#include "Engine/Scripting/ManagedCLR/MMethod.h" + +EditorPlugin::EditorPlugin(const SpawnParams& params) + : Plugin(params) +{ +} + +void EditorPlugin::Initialize() +{ + Plugin::Initialize(); + + MObject* exception = nullptr; + TypeInitializer.GetType().ManagedClass->GetMethod("Initialize_Internal")->Invoke(GetOrCreateManagedInstance(), nullptr, &exception); + if (exception) + { + MException ex(exception); + ex.Log(LogType::Error,TEXT("EditorPlugin")); + } +} + +void EditorPlugin::Deinitialize() +{ + MObject* exception = nullptr; + TypeInitializer.GetType().ManagedClass->GetMethod("Deinitialize_Internal")->Invoke(GetOrCreateManagedInstance(), nullptr, &exception); + if (exception) + { + MException ex(exception); + ex.Log(LogType::Error,TEXT("EditorPlugin")); + } + + Plugin::Deinitialize(); +} + +#endif + Delegate PluginManager::PluginLoading; Delegate PluginManager::PluginLoaded; Delegate PluginManager::PluginUnloading; @@ -42,6 +81,7 @@ namespace PluginManagerImpl void OnAssemblyLoaded(MAssembly* assembly); void OnAssemblyUnloading(MAssembly* assembly); void OnBinaryModuleLoaded(BinaryModule* module); + void OnScriptsReloading(); } using namespace PluginManagerImpl; @@ -211,6 +251,32 @@ void PluginManagerImpl::OnBinaryModuleLoaded(BinaryModule* module) managedModule->Assembly->Unloading.Bind(OnAssemblyUnloading); } +void PluginManagerImpl::OnScriptsReloading() +{ + // When scripting is reloading (eg. for hot-reload in Editor) we have to deinitialize plugins (Scripting service destroys C# objects later on) + bool changed = false; + for (int32 i = EditorPlugins.Count() - 1; i >= 0 && EditorPlugins.Count() > 0; i--) + { + auto plugin = EditorPlugins[i]; + { + PluginManagerService::InvokeDeinitialize(plugin); + EditorPlugins.RemoveAtKeepOrder(i); + changed = true; + } + } + for (int32 i = GamePlugins.Count() - 1; i >= 0 && GamePlugins.Count() > 0; i--) + { + auto plugin = GamePlugins[i]; + { + PluginManagerService::InvokeDeinitialize(plugin); + GamePlugins.RemoveAtKeepOrder(i); + changed = true; + } + } + if (changed) + PluginManager::PluginsChanged(); +} + bool PluginManagerService::Init() { // Process already loaded modules @@ -221,6 +287,7 @@ bool PluginManagerService::Init() // Register for new binary modules load actions Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded); + Scripting::ScriptsReloading.Bind(&OnScriptsReloading); return false; } @@ -228,6 +295,7 @@ bool PluginManagerService::Init() void PluginManagerService::Dispose() { Scripting::BinaryModuleLoaded.Unbind(&OnBinaryModuleLoaded); + Scripting::ScriptsReloading.Unbind(&OnScriptsReloading); // Cleanup all plugins PROFILE_CPU_NAMED("Dispose Plugins");