Refactor Plugins system to support plugins in C++ scripts
This commit is contained in:
@@ -128,7 +128,9 @@ namespace FlaxEditor
|
|||||||
// Plugin assets
|
// Plugin assets
|
||||||
foreach (var plugin in PluginManager.GamePlugins)
|
foreach (var plugin in PluginManager.GamePlugins)
|
||||||
{
|
{
|
||||||
plugin.OnCollectAssets(list);
|
var pluginRefs = plugin.GetReferences();
|
||||||
|
if (pluginRefs != null)
|
||||||
|
list.AddRange(pluginRefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.Count == 0)
|
if (list.Count == 0)
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ namespace FlaxEditor.States
|
|||||||
_duplicateScenes.GatherSceneData();
|
_duplicateScenes.GatherSceneData();
|
||||||
Editor.Internal_SetPlayMode(true);
|
Editor.Internal_SetPlayMode(true);
|
||||||
IsPaused = false;
|
IsPaused = false;
|
||||||
PluginManager.InitializeGamePlugins();
|
PluginManager.Internal_InitializeGamePlugins();
|
||||||
_duplicateScenes.CreateScenes();
|
_duplicateScenes.CreateScenes();
|
||||||
SceneDuplicated?.Invoke();
|
SceneDuplicated?.Invoke();
|
||||||
RestoreSelection();
|
RestoreSelection();
|
||||||
@@ -180,7 +180,7 @@ namespace FlaxEditor.States
|
|||||||
// Restore editor scene
|
// Restore editor scene
|
||||||
SceneRestoring?.Invoke();
|
SceneRestoring?.Invoke();
|
||||||
_duplicateScenes.DeletedScenes();
|
_duplicateScenes.DeletedScenes();
|
||||||
PluginManager.DeinitializeGamePlugins();
|
PluginManager.Internal_DeinitializeGamePlugins();
|
||||||
Editor.Internal_SetPlayMode(false);
|
Editor.Internal_SetPlayMode(false);
|
||||||
_duplicateScenes.RestoreSceneData();
|
_duplicateScenes.RestoreSceneData();
|
||||||
SceneRestored?.Invoke();
|
SceneRestored?.Invoke();
|
||||||
|
|||||||
@@ -190,9 +190,11 @@ namespace FlaxEditor.Windows
|
|||||||
private void OnPluginsChanged()
|
private void OnPluginsChanged()
|
||||||
{
|
{
|
||||||
List<PluginEntry> toRemove = null;
|
List<PluginEntry> toRemove = null;
|
||||||
|
var gamePlugins = PluginManager.GamePlugins;
|
||||||
|
var editorPlugins = PluginManager.EditorPlugins;
|
||||||
foreach (var e in _entries)
|
foreach (var e in _entries)
|
||||||
{
|
{
|
||||||
if (!PluginManager.EditorPlugins.Contains(e.Key) && !PluginManager.GamePlugins.Contains(e.Key))
|
if (!editorPlugins.Contains(e.Key) && !gamePlugins.Contains(e.Key))
|
||||||
{
|
{
|
||||||
if (toRemove == null)
|
if (toRemove == null)
|
||||||
toRemove = new List<PluginEntry>();
|
toRemove = new List<PluginEntry>();
|
||||||
@@ -204,9 +206,9 @@ namespace FlaxEditor.Windows
|
|||||||
foreach (var plugin in toRemove)
|
foreach (var plugin in toRemove)
|
||||||
OnPluginRemove(plugin);
|
OnPluginRemove(plugin);
|
||||||
}
|
}
|
||||||
foreach (var plugin in PluginManager.GamePlugins)
|
foreach (var plugin in gamePlugins)
|
||||||
OnPluginAdd(plugin);
|
OnPluginAdd(plugin);
|
||||||
foreach (var plugin in PluginManager.EditorPlugins)
|
foreach (var plugin in editorPlugins)
|
||||||
OnPluginAdd(plugin);
|
OnPluginAdd(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +290,7 @@ namespace FlaxEditor.Windows
|
|||||||
|
|
||||||
foreach (var e in _entries.Keys)
|
foreach (var e in _entries.Keys)
|
||||||
{
|
{
|
||||||
if (e.GetType() == pluginType)
|
if (e.GetType() == pluginType && _entries.ContainsKey(e))
|
||||||
return _entries[e];
|
return _entries[e];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,30 @@
|
|||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
#include "Engine/Core/Types/StringBuilder.h"
|
#include "Engine/Core/Types/StringBuilder.h"
|
||||||
|
|
||||||
|
Version::Version(int32 major, int32 minor, int32 build, int32 revision)
|
||||||
|
{
|
||||||
|
_major = Math::Max(major, 0);
|
||||||
|
_minor = Math::Max(minor, 0);
|
||||||
|
_build = Math::Max(build, 0);
|
||||||
|
_revision = Math::Max(revision, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Version::Version(int32 major, int32 minor, int32 build)
|
||||||
|
{
|
||||||
|
_major = Math::Max(major, 0);
|
||||||
|
_minor = Math::Max(minor, 0);
|
||||||
|
_build = Math::Max(build, 0);
|
||||||
|
_revision = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Version::Version(int32 major, int32 minor)
|
||||||
|
{
|
||||||
|
_major = Math::Max(major, 0);
|
||||||
|
_minor = Math::Max(minor, 0);
|
||||||
|
_build = -1;
|
||||||
|
_revision = -1;
|
||||||
|
}
|
||||||
|
|
||||||
int32 Version::CompareTo(const Version& value) const
|
int32 Version::CompareTo(const Version& value) const
|
||||||
{
|
{
|
||||||
if (_major != value._major)
|
if (_major != value._major)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the version number made of major, minor, build and revision numbers.
|
/// Represents the version number made of major, minor, build and revision numbers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
struct FLAXENGINE_API Version
|
API_STRUCT(InBuild) struct FLAXENGINE_API Version
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int32 _major;
|
int32 _major;
|
||||||
@@ -25,13 +25,7 @@ public:
|
|||||||
/// <param name="minor">The minor version number.</param>
|
/// <param name="minor">The minor version number.</param>
|
||||||
/// <param name="build">The build number.</param>
|
/// <param name="build">The build number.</param>
|
||||||
/// <param name="revision">The revision number.</param>
|
/// <param name="revision">The revision number.</param>
|
||||||
Version(int32 major, int32 minor, int32 build, int32 revision)
|
Version(int32 major, int32 minor, int32 build, int32 revision);
|
||||||
{
|
|
||||||
_major = Math::Max(major, 0);
|
|
||||||
_minor = Math::Max(minor, 0);
|
|
||||||
_build = Math::Max(build, 0);
|
|
||||||
_revision = Math::Max(revision, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the Version class using the specified major, minor, and build values.
|
/// Initializes a new instance of the Version class using the specified major, minor, and build values.
|
||||||
@@ -39,26 +33,14 @@ public:
|
|||||||
/// <param name="major">The major version number.</param>
|
/// <param name="major">The major version number.</param>
|
||||||
/// <param name="minor">The minor version number.</param>
|
/// <param name="minor">The minor version number.</param>
|
||||||
/// <param name="build">The build number.</param>
|
/// <param name="build">The build number.</param>
|
||||||
Version(int32 major, int32 minor, int32 build)
|
Version(int32 major, int32 minor, int32 build);
|
||||||
{
|
|
||||||
_major = Math::Max(major, 0);
|
|
||||||
_minor = Math::Max(minor, 0);
|
|
||||||
_build = Math::Max(build, 0);
|
|
||||||
_revision = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the Version class using the specified major and minor values.
|
/// Initializes a new instance of the Version class using the specified major and minor values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="major">The major version number.</param>
|
/// <param name="major">The major version number.</param>
|
||||||
/// <param name="minor">The minor version number.</param>
|
/// <param name="minor">The minor version number.</param>
|
||||||
Version(int32 major, int32 minor)
|
Version(int32 major, int32 minor);
|
||||||
{
|
|
||||||
_major = Math::Max(major, 0);
|
|
||||||
_minor = Math::Max(minor, 0);
|
|
||||||
_build = -1;
|
|
||||||
_revision = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the Version class.
|
/// Initializes a new instance of the Version class.
|
||||||
@@ -67,8 +49,8 @@ public:
|
|||||||
{
|
{
|
||||||
_major = 0;
|
_major = 0;
|
||||||
_minor = 0;
|
_minor = 0;
|
||||||
_revision = 0;
|
_revision = -1;
|
||||||
_build = 0;
|
_build = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "Engine/Platform/MessageBox.h"
|
#include "Engine/Platform/MessageBox.h"
|
||||||
#include "Engine/Engine/CommandLine.h"
|
#include "Engine/Engine/CommandLine.h"
|
||||||
#include "Engine/Serialization/JsonSerializer.h"
|
#include "Engine/Serialization/JsonSerializer.h"
|
||||||
|
#include "Editor/Scripting/ScriptsBuilder.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if USE_LARGE_WORLDS
|
#if USE_LARGE_WORLDS
|
||||||
@@ -950,7 +951,12 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
|||||||
LOG(Error, "Cannot load scene without game modules loaded.");
|
LOG(Error, "Cannot load scene without game modules loaded.");
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
if (!CommandLine::Options.Headless.IsTrue())
|
if (!CommandLine::Options.Headless.IsTrue())
|
||||||
MessageBox::Show(TEXT("Cannot load scene without game script modules. Please fix the compilation issues. See logs for more info."), TEXT("Missing game modules"), MessageBoxButtons::OK, MessageBoxIcon::Error);
|
{
|
||||||
|
if (ScriptsBuilder::LastCompilationFailed())
|
||||||
|
MessageBox::Show(TEXT("Scripts compilation failed. Cannot load scene without game script modules. Please fix the compilation issues. See logs for more info."), TEXT("Failed to compile scripts"), MessageBoxButtons::OK, MessageBoxIcon::Error);
|
||||||
|
else
|
||||||
|
MessageBox::Show(TEXT("Failed to load scripts. Cannot load scene without game script modules. See logs for more info."), TEXT("Missing game modules"), MessageBoxButtons::OK, MessageBoxIcon::Error);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -571,6 +571,17 @@ String ScriptingType::ToString() const
|
|||||||
return String(Fullname.Get(), Fullname.Length());
|
return String(Fullname.Get(), Fullname.Length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringAnsiView ScriptingType::GetName() const
|
||||||
|
{
|
||||||
|
int32 lastDotIndex = Fullname.FindLast('.');
|
||||||
|
if (lastDotIndex != -1)
|
||||||
|
{
|
||||||
|
lastDotIndex++;
|
||||||
|
return StringAnsiView(Fullname.Get() + lastDotIndex, Fullname.Length() - lastDotIndex);
|
||||||
|
}
|
||||||
|
return Fullname;
|
||||||
|
}
|
||||||
|
|
||||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SpawnHandler spawn, ScriptingTypeInitializer* baseType, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable, const ScriptingType::InterfaceImplementation* interfaces)
|
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SpawnHandler spawn, ScriptingTypeInitializer* baseType, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable, const ScriptingType::InterfaceImplementation* interfaces)
|
||||||
: ScriptingTypeHandle(module, module->Types.Count())
|
: ScriptingTypeHandle(module, module->Types.Count())
|
||||||
{
|
{
|
||||||
@@ -686,7 +697,7 @@ ManagedBinaryModule::ManagedBinaryModule(MAssembly* assembly)
|
|||||||
// Bind for C# assembly events
|
// Bind for C# assembly events
|
||||||
assembly->Loading.Bind<ManagedBinaryModule, &ManagedBinaryModule::OnLoading>(this);
|
assembly->Loading.Bind<ManagedBinaryModule, &ManagedBinaryModule::OnLoading>(this);
|
||||||
assembly->Loaded.Bind<ManagedBinaryModule, &ManagedBinaryModule::OnLoaded>(this);
|
assembly->Loaded.Bind<ManagedBinaryModule, &ManagedBinaryModule::OnLoaded>(this);
|
||||||
assembly->Unloading.Bind<ManagedBinaryModule, &ManagedBinaryModule::OnUnloading>(this);
|
assembly->Unloaded.Bind<ManagedBinaryModule, &ManagedBinaryModule::OnUnloaded>(this);
|
||||||
|
|
||||||
if (Assembly->IsLoaded())
|
if (Assembly->IsLoaded())
|
||||||
{
|
{
|
||||||
@@ -1084,7 +1095,7 @@ void ManagedBinaryModule::InitType(MClass* mclass)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ManagedBinaryModule::OnUnloading(MAssembly* assembly)
|
void ManagedBinaryModule::OnUnloaded(MAssembly* assembly)
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
|
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ private:
|
|||||||
void OnLoading(MAssembly* assembly);
|
void OnLoading(MAssembly* assembly);
|
||||||
void OnLoaded(MAssembly* assembly);
|
void OnLoaded(MAssembly* assembly);
|
||||||
void InitType(MClass* mclass);
|
void InitType(MClass* mclass);
|
||||||
void OnUnloading(MAssembly* assembly);
|
void OnUnloaded(MAssembly* assembly);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "MType.h"
|
#include "MType.h"
|
||||||
#include "Engine/Core/Log.h"
|
#include "Engine/Core/Log.h"
|
||||||
#include "Engine/Core/Types/DataContainer.h"
|
#include "Engine/Core/Types/DataContainer.h"
|
||||||
|
#include "Engine/Core/Types/Version.h"
|
||||||
#include "Engine/Core/Math/BoundingBox.h"
|
#include "Engine/Core/Math/BoundingBox.h"
|
||||||
#include "Engine/Core/Math/BoundingSphere.h"
|
#include "Engine/Core/Math/BoundingSphere.h"
|
||||||
#include "Engine/Core/Math/Rectangle.h"
|
#include "Engine/Core/Math/Rectangle.h"
|
||||||
@@ -1279,4 +1280,18 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& fa
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MonoObject* MUtils::ToManaged(const Version& value)
|
||||||
|
{
|
||||||
|
auto obj = mono_object_new(mono_domain_get(), Scripting::FindClassNative("System.Version"));
|
||||||
|
Platform::MemoryCopy((byte*)obj + sizeof(MonoObject), &value, sizeof(Version));
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Version MUtils::ToNative(MonoObject* value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
return *(Version*)((byte*)value + sizeof(MonoObject));
|
||||||
|
return Version();
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
#include <ThirdParty/mono-2.0/mono/metadata/object.h>
|
#include <ThirdParty/mono-2.0/mono/metadata/object.h>
|
||||||
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
|
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
|
||||||
|
|
||||||
|
struct Version;
|
||||||
|
|
||||||
namespace MUtils
|
namespace MUtils
|
||||||
{
|
{
|
||||||
extern FLAXENGINE_API StringView ToString(MonoString* str);
|
extern FLAXENGINE_API StringView ToString(MonoString* str);
|
||||||
@@ -500,6 +502,9 @@ namespace MUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern void* VariantToManagedArgPtr(Variant& value, const MType& type, bool& failed);
|
extern void* VariantToManagedArgPtr(Variant& value, const MType& type, bool& failed);
|
||||||
|
extern MonoObject* ToManaged(const Version& value);
|
||||||
|
extern Version ToNative(MonoObject* value);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
|
||||||
|
|
||||||
namespace FlaxEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Base class for all plugins used at runtime in game.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Plugins should have a public and parameter-less constructor.
|
|
||||||
/// </remarks>
|
|
||||||
/// <seealso cref="FlaxEngine.Plugin" />
|
|
||||||
public abstract class GamePlugin : Plugin
|
|
||||||
{
|
|
||||||
#if FLAX_EDITOR
|
|
||||||
/// <summary>
|
|
||||||
/// Event called during game cooking in Editor to collect any assets that this plugin uses. Can be used to inject content for plugins.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="assets">The result assets list (always valid).</param>
|
|
||||||
public virtual void OnCollectAssets(System.Collections.Generic.List<System.Guid> assets)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25
Source/Engine/Scripting/Plugins/GamePlugin.h
Normal file
25
Source/Engine/Scripting/Plugins/GamePlugin.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Plugin.h"
|
||||||
|
#include "Engine/Core/Collections/Array.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for all plugins used at runtime in game.
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS(Abstract) class FLAXENGINE_API GamePlugin : public Plugin
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE(GamePlugin);
|
||||||
|
public:
|
||||||
|
#if USE_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// Function called during game cooking in Editor to collect any assets that this plugin uses. Can be used to inject content for plugins.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The result assets list.</returns>
|
||||||
|
API_FUNCTION() virtual Array<Guid> GetReferences() const
|
||||||
|
{
|
||||||
|
return Array<Guid>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FlaxEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Base class for game engine editor plugins.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Plugins should have a public and parameter-less constructor.
|
|
||||||
/// </remarks>
|
|
||||||
public abstract class Plugin
|
|
||||||
{
|
|
||||||
internal bool _initialized;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the description.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Plugin description should be a constant part of the plugin created in constructor and valid before calling <see cref="Initialize"/>.
|
|
||||||
/// </remarks>
|
|
||||||
public virtual PluginDescription Description => new PluginDescription
|
|
||||||
{
|
|
||||||
Name = GetType().Name,
|
|
||||||
Category = "Other",
|
|
||||||
Version = new Version(1, 0),
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialization method called when this plugin is loaded to the memory and can be used.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void Initialize()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Cleanup method called when this plugin is being unloaded or reloaded or engine is closing.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void Deinitialize()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
46
Source/Engine/Scripting/Plugins/Plugin.h
Normal file
46
Source/Engine/Scripting/Plugins/Plugin.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "PluginDescription.h"
|
||||||
|
#include "Engine/Scripting/ScriptingObject.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for game engine editor plugins.
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS(Abstract) class FLAXENGINE_API Plugin : public ScriptingObject
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE(Plugin);
|
||||||
|
private:
|
||||||
|
friend class PluginManagerService;
|
||||||
|
bool _initialized = false;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// <remarks>
|
||||||
|
/// Plugin description. Should be a constant part of the plugin created in constructor and valid before calling <see cref="Initialize"/>.
|
||||||
|
/// </remarks>
|
||||||
|
API_FIELD() PluginDescription _description;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the description.
|
||||||
|
/// </summary>
|
||||||
|
API_PROPERTY() const PluginDescription& GetDescription() const
|
||||||
|
{
|
||||||
|
return _description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialization method called when this plugin is loaded to the memory and can be used.
|
||||||
|
/// </summary>
|
||||||
|
API_FUNCTION() virtual void Initialize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleanup method called when this plugin is being unloaded or reloaded or engine is closing.
|
||||||
|
/// </summary>
|
||||||
|
API_FUNCTION() virtual void Deinitialize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FlaxEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The engine plugin description container.
|
|
||||||
/// </summary>
|
|
||||||
public struct PluginDescription
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the plugin.
|
|
||||||
/// </summary>
|
|
||||||
public string Name;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The version of the plugin.
|
|
||||||
/// </summary>
|
|
||||||
public Version Version;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the author of the plugin.
|
|
||||||
/// </summary>
|
|
||||||
public string Author;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The plugin author website URL.
|
|
||||||
/// </summary>
|
|
||||||
public string AuthorUrl;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The homepage URL for the plugin.
|
|
||||||
/// </summary>
|
|
||||||
public string HomepageUrl;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The plugin repository URL (for open-source plugins).
|
|
||||||
/// </summary>
|
|
||||||
public string RepositoryUrl;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The plugin description.
|
|
||||||
/// </summary>
|
|
||||||
public string Description;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The plugin category.
|
|
||||||
/// </summary>
|
|
||||||
public string Category;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// True if plugin is during Beta tests (before release).
|
|
||||||
/// </summary>
|
|
||||||
public bool IsBeta;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// True if plugin is during Alpha tests (early version).
|
|
||||||
/// </summary>
|
|
||||||
public bool IsAlpha;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
65
Source/Engine/Scripting/Plugins/PluginDescription.h
Normal file
65
Source/Engine/Scripting/Plugins/PluginDescription.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Engine/Core/Types/String.h"
|
||||||
|
#include "Engine/Core/Types/Version.h"
|
||||||
|
#include "Engine/Scripting/ScriptingType.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The engine plugin description container.
|
||||||
|
/// </summary>
|
||||||
|
API_STRUCT() struct PluginDescription
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_MINIMAL(PluginDescription);
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the plugin.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() String Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The version of the plugin.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() Version Version;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the author of the plugin.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() String Author;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The plugin author website URL.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() String AuthorUrl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The homepage URL for the plugin.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() String HomepageUrl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The plugin repository URL (for open-source plugins).
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() String RepositoryUrl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The plugin description.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() String Description;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The plugin category.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() String Category;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if plugin is during Beta tests (before release).
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() bool IsBeta = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if plugin is during Alpha tests (early version).
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() bool IsAlpha = false;
|
||||||
|
};
|
||||||
@@ -2,22 +2,41 @@
|
|||||||
|
|
||||||
#include "PluginManager.h"
|
#include "PluginManager.h"
|
||||||
#include "FlaxEngine.Gen.h"
|
#include "FlaxEngine.Gen.h"
|
||||||
|
#include "GamePlugin.h"
|
||||||
#include "Engine/Scripting/Scripting.h"
|
#include "Engine/Scripting/Scripting.h"
|
||||||
#include "Engine/Scripting/ScriptingType.h"
|
#include "Engine/Scripting/ScriptingType.h"
|
||||||
#include "Engine/Scripting/BinaryModule.h"
|
#include "Engine/Scripting/BinaryModule.h"
|
||||||
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
|
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
|
||||||
#include "Engine/Scripting/ManagedCLR/MMethod.h"
|
|
||||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||||
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
||||||
#include "Engine/Platform/FileSystem.h"
|
#include "Engine/Platform/FileSystem.h"
|
||||||
#include "Engine/Debug/DebugLog.h"
|
|
||||||
#include "Engine/Profiler/ProfilerCPU.h"
|
#include "Engine/Profiler/ProfilerCPU.h"
|
||||||
#include "Engine/Engine/EngineService.h"
|
#include "Engine/Engine/EngineService.h"
|
||||||
#include "Engine/Core/Log.h"
|
#include "Engine/Core/Log.h"
|
||||||
|
|
||||||
|
Plugin::Plugin(const SpawnParams& params)
|
||||||
|
: ScriptingObject(params)
|
||||||
|
{
|
||||||
|
_description.Name = String(GetType().GetName());
|
||||||
|
_description.Category = TEXT("Other");
|
||||||
|
_description.Version = Version(1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GamePlugin::GamePlugin(const SpawnParams& params)
|
||||||
|
: Plugin(params)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Delegate<Plugin*> PluginManager::PluginLoading;
|
||||||
|
Delegate<Plugin*> PluginManager::PluginLoaded;
|
||||||
|
Delegate<Plugin*> PluginManager::PluginUnloading;
|
||||||
|
Delegate<Plugin*> PluginManager::PluginUnloaded;
|
||||||
|
Action PluginManager::PluginsChanged;
|
||||||
|
|
||||||
namespace PluginManagerImpl
|
namespace PluginManagerImpl
|
||||||
{
|
{
|
||||||
MMethod* Internal_LoadPlugin = nullptr;
|
Array<GamePlugin*> GamePlugins;
|
||||||
|
Array<Plugin*> EditorPlugins;
|
||||||
|
|
||||||
void LoadPlugin(MClass* klass, bool isEditor);
|
void LoadPlugin(MClass* klass, bool isEditor);
|
||||||
void OnAssemblyLoaded(MAssembly* assembly);
|
void OnAssemblyLoaded(MAssembly* assembly);
|
||||||
@@ -30,7 +49,6 @@ using namespace PluginManagerImpl;
|
|||||||
class PluginManagerService : public EngineService
|
class PluginManagerService : public EngineService
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
PluginManagerService()
|
PluginManagerService()
|
||||||
: EngineService(TEXT("Plugin Manager"), 130)
|
: EngineService(TEXT("Plugin Manager"), 130)
|
||||||
{
|
{
|
||||||
@@ -38,50 +56,79 @@ public:
|
|||||||
|
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
void Dispose() override;
|
void Dispose() override;
|
||||||
|
|
||||||
|
static void InvokeInitialize(Plugin* plugin);
|
||||||
|
static void InvokeDeinitialize(Plugin* plugin);
|
||||||
};
|
};
|
||||||
|
|
||||||
PluginManagerService PluginManagerServiceInstance;
|
PluginManagerService PluginManagerServiceInstance;
|
||||||
|
|
||||||
|
void PluginManagerService::InvokeInitialize(Plugin* plugin)
|
||||||
|
{
|
||||||
|
if (plugin->_initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG(Info, "Loading plugin {}", plugin->ToString());
|
||||||
|
|
||||||
|
PluginManager::PluginLoading(plugin);
|
||||||
|
|
||||||
|
plugin->Initialize();
|
||||||
|
plugin->_initialized = true;
|
||||||
|
|
||||||
|
PluginManager::PluginLoaded(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManagerService::InvokeDeinitialize(Plugin* plugin)
|
||||||
|
{
|
||||||
|
if (!plugin->_initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG(Info, "Unloading plugin {}", plugin->ToString());
|
||||||
|
|
||||||
|
PluginManager::PluginUnloading(plugin);
|
||||||
|
|
||||||
|
plugin->Deinitialize();
|
||||||
|
plugin->_initialized = false;
|
||||||
|
|
||||||
|
PluginManager::PluginUnloaded(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
void PluginManagerImpl::LoadPlugin(MClass* klass, bool isEditor)
|
void PluginManagerImpl::LoadPlugin(MClass* klass, bool isEditor)
|
||||||
{
|
{
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
// Create and check if use it
|
||||||
if (Internal_LoadPlugin == nullptr)
|
auto plugin = (Plugin*)Scripting::NewObject(klass);
|
||||||
{
|
if (!plugin)
|
||||||
Internal_LoadPlugin = PluginManager::GetStaticClass()->GetMethod("Internal_LoadPlugin", 2);
|
return;
|
||||||
ASSERT(Internal_LoadPlugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
MObject* exception = nullptr;
|
if (!isEditor)
|
||||||
void* params[2];
|
|
||||||
params[0] = MUtils::GetType(klass->GetNative());
|
|
||||||
params[1] = &isEditor;
|
|
||||||
Internal_LoadPlugin->Invoke(nullptr, params, &exception);
|
|
||||||
if (exception)
|
|
||||||
{
|
{
|
||||||
DebugLog::LogException(exception);
|
GamePlugins.Add((GamePlugin*)plugin);
|
||||||
|
#if !USE_EDITOR
|
||||||
|
PluginManagerService::InvokeInitialize(plugin);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if USE_EDITOR
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorPlugins.Add(plugin);
|
||||||
|
PluginManagerService::InvokeInitialize(plugin);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
PluginManager::PluginsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginManagerImpl::OnAssemblyLoaded(MAssembly* assembly)
|
void PluginManagerImpl::OnAssemblyLoaded(MAssembly* assembly)
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Load Assembly Plugins");
|
PROFILE_CPU_NAMED("Load Assembly Plugins");
|
||||||
|
|
||||||
// Prepare FlaxEngine
|
const auto gamePluginClass = GamePlugin::GetStaticClass();
|
||||||
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
|
||||||
if (!engineAssembly->IsLoaded())
|
|
||||||
{
|
|
||||||
LOG(Warning, "Cannot find plugin class types for assembly {0} because FlaxEngine is not loaded.", assembly->ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto gamePluginClass = engineAssembly->GetClass("FlaxEngine.GamePlugin");
|
|
||||||
if (gamePluginClass == nullptr)
|
if (gamePluginClass == nullptr)
|
||||||
{
|
{
|
||||||
LOG(Warning, "Missing GamePlugin class.");
|
LOG(Warning, "Missing GamePlugin class.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
const auto editorPluginClass = engineAssembly->GetClass("FlaxEditor.EditorPlugin");
|
const auto editorPluginClass = ((ManagedBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.EditorPlugin");
|
||||||
if (editorPluginClass == nullptr)
|
if (editorPluginClass == nullptr)
|
||||||
{
|
{
|
||||||
LOG(Warning, "Missing EditorPlugin class.");
|
LOG(Warning, "Missing EditorPlugin class.");
|
||||||
@@ -115,19 +162,29 @@ void PluginManagerImpl::OnAssemblyLoaded(MAssembly* assembly)
|
|||||||
|
|
||||||
void PluginManagerImpl::OnAssemblyUnloading(MAssembly* assembly)
|
void PluginManagerImpl::OnAssemblyUnloading(MAssembly* assembly)
|
||||||
{
|
{
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
bool changed = false;
|
||||||
// Cleanup plugins from this assembly
|
for (int32 i = EditorPlugins.Count() - 1; i >= 0 && EditorPlugins.Count() > 0; i--)
|
||||||
const auto disposeMethod = PluginManager::GetStaticClass()->GetMethod("Internal_Dispose", 1);
|
|
||||||
ASSERT(disposeMethod);
|
|
||||||
MObject* exception = nullptr;
|
|
||||||
void* params[1];
|
|
||||||
params[0] = assembly->GetNative();
|
|
||||||
disposeMethod->Invoke(nullptr, params, &exception);
|
|
||||||
if (exception)
|
|
||||||
{
|
{
|
||||||
DebugLog::LogException(exception);
|
auto plugin = EditorPlugins[i];
|
||||||
|
if (plugin->GetType().ManagedClass->GetAssembly() == assembly)
|
||||||
|
{
|
||||||
|
PluginManagerService::InvokeDeinitialize(plugin);
|
||||||
|
EditorPlugins.RemoveAtKeepOrder(i);
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
|
for (int32 i = GamePlugins.Count() - 1; i >= 0 && GamePlugins.Count() > 0; i--)
|
||||||
|
{
|
||||||
|
auto plugin = GamePlugins[i];
|
||||||
|
if (plugin->GetType().ManagedClass->GetAssembly() == assembly)
|
||||||
|
{
|
||||||
|
PluginManagerService::InvokeDeinitialize(plugin);
|
||||||
|
GamePlugins.RemoveAtKeepOrder(i);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed)
|
||||||
|
PluginManager::PluginsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginManagerImpl::OnBinaryModuleLoaded(BinaryModule* module)
|
void PluginManagerImpl::OnBinaryModuleLoaded(BinaryModule* module)
|
||||||
@@ -138,6 +195,7 @@ void PluginManagerImpl::OnBinaryModuleLoaded(BinaryModule* module)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Skip non-managed modules
|
// Skip non-managed modules
|
||||||
|
// TODO: search native-only modules too
|
||||||
auto managedModule = dynamic_cast<ManagedBinaryModule*>(module);
|
auto managedModule = dynamic_cast<ManagedBinaryModule*>(module);
|
||||||
if (!managedModule)
|
if (!managedModule)
|
||||||
return;
|
return;
|
||||||
@@ -171,17 +229,86 @@ void PluginManagerService::Dispose()
|
|||||||
{
|
{
|
||||||
Scripting::BinaryModuleLoaded.Unbind(&OnBinaryModuleLoaded);
|
Scripting::BinaryModuleLoaded.Unbind(&OnBinaryModuleLoaded);
|
||||||
|
|
||||||
PROFILE_CPU_NAMED("Dispose Plugins");
|
|
||||||
|
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
|
||||||
// Cleanup all plugins
|
// Cleanup all plugins
|
||||||
const auto disposeMethod = PluginManager::GetStaticClass()->GetMethod("Internal_Dispose");
|
PROFILE_CPU_NAMED("Dispose Plugins");
|
||||||
ASSERT(disposeMethod);
|
const int32 pluginsCount = EditorPlugins.Count() + GamePlugins.Count();
|
||||||
MObject* exception = nullptr;
|
if (pluginsCount == 0)
|
||||||
disposeMethod->Invoke(nullptr, nullptr, &exception);
|
return;
|
||||||
if (exception)
|
LOG(Info, "Unloading {0} plugins", pluginsCount);
|
||||||
|
for (int32 i = EditorPlugins.Count() - 1; i >= 0 && EditorPlugins.Count() > 0; i--)
|
||||||
{
|
{
|
||||||
DebugLog::LogException(exception);
|
auto plugin = EditorPlugins[i];
|
||||||
|
InvokeDeinitialize(plugin);
|
||||||
|
EditorPlugins.RemoveAtKeepOrder(i);
|
||||||
}
|
}
|
||||||
#endif
|
for (int32 i = GamePlugins.Count() - 1; i >= 0 && GamePlugins.Count() > 0; i--)
|
||||||
|
{
|
||||||
|
auto plugin = GamePlugins[i];
|
||||||
|
InvokeDeinitialize(plugin);
|
||||||
|
GamePlugins.RemoveAtKeepOrder(i);
|
||||||
|
}
|
||||||
|
PluginManager::PluginsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Array<GamePlugin*>& PluginManager::GetGamePlugins()
|
||||||
|
{
|
||||||
|
return GamePlugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Array<Plugin*>& PluginManager::GetEditorPlugins()
|
||||||
|
{
|
||||||
|
return EditorPlugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin* PluginManager::GetPlugin(const StringView& name)
|
||||||
|
{
|
||||||
|
for (Plugin* p : EditorPlugins)
|
||||||
|
{
|
||||||
|
if (p->GetDescription().Name == name)
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
for (GamePlugin* gp : GamePlugins)
|
||||||
|
{
|
||||||
|
if (gp->GetDescription().Name == name)
|
||||||
|
return gp;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin* PluginManager::GetPlugin(const MClass* type)
|
||||||
|
{
|
||||||
|
CHECK_RETURN(type, nullptr);
|
||||||
|
for (Plugin* p : EditorPlugins)
|
||||||
|
{
|
||||||
|
if (p->GetClass()->IsSubClassOf(type))
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
for (GamePlugin* gp : GamePlugins)
|
||||||
|
{
|
||||||
|
if (gp->GetClass()->IsSubClassOf(type))
|
||||||
|
return gp;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
|
void PluginManager::InitializeGamePlugins()
|
||||||
|
{
|
||||||
|
PROFILE_CPU();
|
||||||
|
for (int32 i = 0; i < GamePlugins.Count(); i++)
|
||||||
|
{
|
||||||
|
PluginManagerService::InvokeInitialize(GamePlugins[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManager::DeinitializeGamePlugins()
|
||||||
|
{
|
||||||
|
PROFILE_CPU();
|
||||||
|
for (int32 i = GamePlugins.Count() - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
PluginManagerService::InvokeDeinitialize(GamePlugins[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,282 +1,17 @@
|
|||||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace FlaxEngine
|
namespace FlaxEngine
|
||||||
{
|
{
|
||||||
/// <summary>
|
partial class PluginManager
|
||||||
/// Plugin related event delegate type.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin">The plugin.</param>
|
|
||||||
[HideInEditor]
|
|
||||||
public delegate void PluginDelegate(Plugin plugin);
|
|
||||||
|
|
||||||
public static partial class PluginManager
|
|
||||||
{
|
{
|
||||||
private static readonly List<GamePlugin> _gamePlugins = new List<GamePlugin>();
|
|
||||||
private static readonly List<Plugin> _editorPlugins = new List<Plugin>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the game plugins.
|
|
||||||
/// </summary>
|
|
||||||
public static IReadOnlyList<GamePlugin> GamePlugins => _gamePlugins;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the editor plugins.
|
|
||||||
/// </summary>
|
|
||||||
public static IReadOnlyList<Plugin> EditorPlugins => _editorPlugins;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs before loading plugin.
|
|
||||||
/// </summary>
|
|
||||||
public static event PluginDelegate PluginLoading;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when plugin gets loaded and initialized.
|
|
||||||
/// </summary>
|
|
||||||
public static event PluginDelegate PluginLoaded;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs before unloading plugin.
|
|
||||||
/// </summary>
|
|
||||||
public static event PluginDelegate PluginUnloading;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when plugin gets unloaded. It should not be used anymore.
|
|
||||||
/// </summary>
|
|
||||||
public static event PluginDelegate PluginUnloaded;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when plugins collection gets edited (added or removed plugin).
|
|
||||||
/// </summary>
|
|
||||||
public static event Action PluginsChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether can load the specified plugin.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pluginDesc">The plugin description.</param>
|
|
||||||
/// <returns>True if load it, otherwise false.</returns>
|
|
||||||
public delegate bool CanLoadPluginDelegate(ref PluginDescription pluginDesc);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether can load the specified plugin.
|
|
||||||
/// </summary>
|
|
||||||
public static CanLoadPluginDelegate CanLoadPlugin = DefaultCanLoadPlugin;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Plugin related event delegate type.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin">The plugin.</param>
|
|
||||||
public delegate void PluginDelegate(Plugin plugin);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The default implementation for <see cref="CanLoadPlugin"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pluginDesc">The plugin description.</param>
|
|
||||||
/// <returns>True if load it, otherwise false.</returns>
|
|
||||||
public static bool DefaultCanLoadPlugin(ref PluginDescription pluginDesc)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
//return !pluginDesc.DisabledByDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FLAX_EDITOR
|
|
||||||
internal static void InitializeGamePlugins()
|
|
||||||
{
|
|
||||||
Profiler.BeginEvent("PluginManager.InitializeGamePlugins");
|
|
||||||
for (var i = 0; i < _gamePlugins.Count; i++)
|
|
||||||
{
|
|
||||||
InvokeInitialize(_gamePlugins[i]);
|
|
||||||
}
|
|
||||||
Profiler.EndEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void DeinitializeGamePlugins()
|
|
||||||
{
|
|
||||||
Profiler.BeginEvent("PluginManager.DeinitializeGamePlugins");
|
|
||||||
for (var i = _gamePlugins.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
InvokeDeinitialize(_gamePlugins[i]);
|
|
||||||
}
|
|
||||||
Profiler.EndEvent();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private static void InvokeInitialize(Plugin plugin)
|
|
||||||
{
|
|
||||||
if (plugin._initialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Debug.Write(LogType.Info, "Loading plugin " + plugin);
|
|
||||||
|
|
||||||
PluginLoading?.Invoke(plugin);
|
|
||||||
|
|
||||||
plugin.Initialize();
|
|
||||||
plugin._initialized = true;
|
|
||||||
|
|
||||||
PluginLoaded?.Invoke(plugin);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.LogException(ex);
|
|
||||||
Debug.LogErrorFormat("Failed to initialize plugin {0}. {1}", plugin, ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void InvokeDeinitialize(Plugin plugin)
|
|
||||||
{
|
|
||||||
if (!plugin._initialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Debug.Write(LogType.Info, "Unloading plugin " + plugin);
|
|
||||||
|
|
||||||
PluginUnloading?.Invoke(plugin);
|
|
||||||
|
|
||||||
plugin.Deinitialize();
|
|
||||||
plugin._initialized = false;
|
|
||||||
|
|
||||||
PluginUnloaded?.Invoke(plugin);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.LogException(ex);
|
|
||||||
Debug.LogErrorFormat("Failed to deinitialize plugin {0}. {1}", plugin, ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the first plugin of the provided type.
|
/// Returns the first plugin of the provided type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The plugin type.</typeparam>
|
/// <typeparam name="T">The plugin type.</typeparam>
|
||||||
/// <returns>The plugin, or null if not loaded.</returns>
|
/// <returns>The plugin, or null if not loaded.</returns>
|
||||||
public static T GetPlugin<T>() where T : class
|
public static T GetPlugin<T>() where T : Plugin
|
||||||
{
|
{
|
||||||
foreach (Plugin p in _editorPlugins)
|
return (T)GetPlugin(typeof(T));
|
||||||
{
|
|
||||||
if (p.GetType() == typeof(T))
|
|
||||||
return (T)Convert.ChangeType(p, typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (GamePlugin gp in _gamePlugins)
|
|
||||||
{
|
|
||||||
if (gp.GetType() == typeof(T))
|
|
||||||
return (T)Convert.ChangeType(gp, typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return the first plugin using the provided name.
|
|
||||||
/// </summary>
|
|
||||||
/// /// <param name="name">The plugin name.</param>
|
|
||||||
/// <returns>The plugin, or null if not loaded.</returns>
|
|
||||||
public static Plugin GetPlugin(string name)
|
|
||||||
{
|
|
||||||
foreach (Plugin p in _editorPlugins)
|
|
||||||
{
|
|
||||||
if (p.Description.Name.Equals(name))
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (GamePlugin gp in _gamePlugins)
|
|
||||||
{
|
|
||||||
if (gp.Description.Name.Equals(name))
|
|
||||||
return gp;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void Internal_LoadPlugin(Type type, bool isEditor)
|
|
||||||
{
|
|
||||||
if (type == null)
|
|
||||||
throw new ArgumentNullException();
|
|
||||||
|
|
||||||
// Create and check if use it
|
|
||||||
var plugin = (Plugin)Activator.CreateInstance(type);
|
|
||||||
var desc = plugin.Description;
|
|
||||||
if (!CanLoadPlugin(ref desc))
|
|
||||||
{
|
|
||||||
Debug.Write(LogType.Info, "Skip loading plugin " + plugin);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isEditor)
|
|
||||||
{
|
|
||||||
_gamePlugins.Add((GamePlugin)plugin);
|
|
||||||
#if !FLAX_EDITOR
|
|
||||||
InvokeInitialize(plugin);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#if FLAX_EDITOR
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_editorPlugins.Add(plugin);
|
|
||||||
InvokeInitialize(plugin);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
PluginsChanged?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void Internal_Dispose(Assembly assembly)
|
|
||||||
{
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
for (int i = _editorPlugins.Count - 1; i >= 0 && _editorPlugins.Count > 0; i--)
|
|
||||||
{
|
|
||||||
var plugin = _editorPlugins[i];
|
|
||||||
if (plugin.GetType().Assembly == assembly)
|
|
||||||
{
|
|
||||||
InvokeDeinitialize(plugin);
|
|
||||||
_editorPlugins.RemoveAt(i);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = _gamePlugins.Count - 1; i >= 0 && _gamePlugins.Count > 0; i--)
|
|
||||||
{
|
|
||||||
var plugin = _gamePlugins[i];
|
|
||||||
if (plugin.GetType().Assembly == assembly)
|
|
||||||
{
|
|
||||||
InvokeDeinitialize(plugin);
|
|
||||||
_gamePlugins.RemoveAt(i);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed)
|
|
||||||
PluginsChanged?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void Internal_Dispose()
|
|
||||||
{
|
|
||||||
int pluginsCount = _editorPlugins.Count + _gamePlugins.Count;
|
|
||||||
if (pluginsCount == 0)
|
|
||||||
return;
|
|
||||||
Debug.Write(LogType.Info, string.Format("Unloading {0} plugins", pluginsCount));
|
|
||||||
|
|
||||||
for (int i = _editorPlugins.Count - 1; i >= 0 && _editorPlugins.Count > 0; i--)
|
|
||||||
{
|
|
||||||
var plugin = _editorPlugins[i];
|
|
||||||
InvokeDeinitialize(plugin);
|
|
||||||
_editorPlugins.RemoveAt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = _gamePlugins.Count - 1; i >= 0 && _gamePlugins.Count > 0; i--)
|
|
||||||
{
|
|
||||||
var plugin = _gamePlugins[i];
|
|
||||||
InvokeDeinitialize(plugin);
|
|
||||||
_gamePlugins.RemoveAt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
PluginsChanged?.Invoke();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,80 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Engine/Scripting/ScriptingType.h"
|
#include "GamePlugin.h"
|
||||||
#include "Engine/Core/Collections/Array.h"
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Game and Editor plugins management service.
|
/// Game and Editor plugins management service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Static) class PluginManager
|
API_CLASS(Static) class PluginManager
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(PluginManager);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(PluginManager);
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the game plugins.
|
||||||
|
/// </summary>
|
||||||
|
API_PROPERTY() static const Array<GamePlugin*>& GetGamePlugins();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the editor plugins.
|
||||||
|
/// </summary>
|
||||||
|
API_PROPERTY() static const Array<Plugin*>& GetEditorPlugins();
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs before loading plugin.
|
||||||
|
/// </summary>
|
||||||
|
API_EVENT() static Delegate<Plugin*> PluginLoading;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when plugin gets loaded and initialized.
|
||||||
|
/// </summary>
|
||||||
|
API_EVENT() static Delegate<Plugin*> PluginLoaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs before unloading plugin.
|
||||||
|
/// </summary>
|
||||||
|
API_EVENT() static Delegate<Plugin*> PluginUnloading;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when plugin gets unloaded. It should not be used anymore.
|
||||||
|
/// </summary>
|
||||||
|
API_EVENT() static Delegate<Plugin*> PluginUnloaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when plugins collection gets edited (added or removed plugin).
|
||||||
|
/// </summary>
|
||||||
|
API_EVENT() static Action PluginsChanged;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Return the first plugin using the provided name.
|
||||||
|
/// </summary>
|
||||||
|
// <param name="name">The plugin name.</param>
|
||||||
|
/// <returns>The plugin, or null if not loaded.</returns>
|
||||||
|
API_FUNCTION() static Plugin* GetPlugin(const StringView& name);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the first plugin of the provided type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type of the plugin to search for. Includes any plugin base class derived from the type.</param>
|
||||||
|
/// <returns>The plugin or null.</returns>
|
||||||
|
API_FUNCTION() static Plugin* GetPlugin(API_PARAM(Attributes="TypeReference(typeof(Plugin))") const MClass* type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the first plugin of the provided type.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The plugin or null.</returns>
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE static T* GetPlugin()
|
||||||
|
{
|
||||||
|
return (T*)GetPlugin(T::GetStaticClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if USE_EDITOR
|
||||||
|
// Internal bindings
|
||||||
|
API_FUNCTION(NoProxy) static void InitializeGamePlugins();
|
||||||
|
API_FUNCTION(NoProxy) static void DeinitializeGamePlugins();
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "Engine/Platform/File.h"
|
#include "Engine/Platform/File.h"
|
||||||
#include "Engine/Debug/DebugLog.h"
|
#include "Engine/Debug/DebugLog.h"
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
|
#include "Engine/Level/Level.h"
|
||||||
#include "Editor/Scripting/ScriptsBuilder.h"
|
#include "Editor/Scripting/ScriptsBuilder.h"
|
||||||
#endif
|
#endif
|
||||||
#include "ManagedCLR/MAssembly.h"
|
#include "ManagedCLR/MAssembly.h"
|
||||||
@@ -20,7 +21,6 @@
|
|||||||
#include "ManagedCLR/MDomain.h"
|
#include "ManagedCLR/MDomain.h"
|
||||||
#include "ManagedCLR/MCore.h"
|
#include "ManagedCLR/MCore.h"
|
||||||
#include "MException.h"
|
#include "MException.h"
|
||||||
#include "Engine/Level/Level.h"
|
|
||||||
#include "Engine/Core/ObjectsRemovalService.h"
|
#include "Engine/Core/ObjectsRemovalService.h"
|
||||||
#include "Engine/Core/Types/TimeSpan.h"
|
#include "Engine/Core/Types/TimeSpan.h"
|
||||||
#include "Engine/Profiler/ProfilerCPU.h"
|
#include "Engine/Profiler/ProfilerCPU.h"
|
||||||
@@ -40,7 +40,6 @@ extern void registerFlaxEngineInternalCalls();
|
|||||||
class ScriptingService : public EngineService
|
class ScriptingService : public EngineService
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ScriptingService()
|
ScriptingService()
|
||||||
: EngineService(TEXT("Scripting"), -20)
|
: EngineService(TEXT("Scripting"), -20)
|
||||||
{
|
{
|
||||||
@@ -733,6 +732,48 @@ ScriptingTypeHandle Scripting::FindScriptingType(const StringAnsiView& fullname)
|
|||||||
return ScriptingTypeHandle();
|
return ScriptingTypeHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScriptingObject* Scripting::NewObject(const MClass* type)
|
||||||
|
{
|
||||||
|
if (type == nullptr)
|
||||||
|
{
|
||||||
|
LOG(Error, "Invalid type.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
MonoClass* typeClass = type->GetNative();
|
||||||
|
|
||||||
|
#if USE_MONO
|
||||||
|
// Get the assembly with that class
|
||||||
|
auto module = ManagedBinaryModule::FindModule(typeClass);
|
||||||
|
if (module == nullptr)
|
||||||
|
{
|
||||||
|
LOG(Error, "Cannot find scripting assembly for type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find the scripting type for this class
|
||||||
|
int32 typeIndex;
|
||||||
|
if (!module->ClassToTypeIndex.TryGet(typeClass, typeIndex))
|
||||||
|
{
|
||||||
|
LOG(Error, "Cannot spawn objects of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const ScriptingType& scriptingType = module->Types[typeIndex];
|
||||||
|
|
||||||
|
// Create unmanaged object
|
||||||
|
const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex));
|
||||||
|
ScriptingObject* obj = scriptingType.Script.Spawn(params);
|
||||||
|
if (obj == nullptr)
|
||||||
|
{
|
||||||
|
LOG(Error, "Failed to spawn object of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
LOG(Error, "Not supported object creation from Managed class.");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
FLAXENGINE_API ScriptingObject* FindObject(const Guid& id, MClass* type)
|
FLAXENGINE_API ScriptingObject* FindObject(const Guid& id, MClass* type)
|
||||||
{
|
{
|
||||||
return Scripting::FindObject(id, type);
|
return Scripting::FindObject(id, type);
|
||||||
|
|||||||
@@ -109,6 +109,13 @@ public:
|
|||||||
/// <returns>The scripting type or invalid type if missing.</returns>
|
/// <returns>The scripting type or invalid type if missing.</returns>
|
||||||
static ScriptingTypeHandle FindScriptingType(const StringAnsiView& fullname);
|
static ScriptingTypeHandle FindScriptingType(const StringAnsiView& fullname);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the given class object (native construction).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The Managed type class.</param>
|
||||||
|
/// <returns>The created object or null if failed.</returns>
|
||||||
|
static ScriptingObject* NewObject(const MClass* type);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
typedef Dictionary<Guid, Guid, HeapAllocation> IdsMappingTable;
|
typedef Dictionary<Guid, Guid, HeapAllocation> IdsMappingTable;
|
||||||
|
|||||||
@@ -319,6 +319,7 @@ struct FLAXENGINE_API ScriptingType
|
|||||||
void SetupScriptObjectVTable(void* object, ScriptingTypeHandle baseTypeHandle, int32 wrapperIndex);
|
void SetupScriptObjectVTable(void* object, ScriptingTypeHandle baseTypeHandle, int32 wrapperIndex);
|
||||||
void HackObjectVTable(void* object, ScriptingTypeHandle baseTypeHandle, int32 wrapperIndex);
|
void HackObjectVTable(void* object, ScriptingTypeHandle baseTypeHandle, int32 wrapperIndex);
|
||||||
String ToString() const;
|
String ToString() const;
|
||||||
|
StringAnsiView GetName() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -418,6 +418,9 @@ namespace Flax.Build.Bindings
|
|||||||
case "CultureInfo":
|
case "CultureInfo":
|
||||||
type = "void*";
|
type = "void*";
|
||||||
return "MUtils::ToManaged({0})";
|
return "MUtils::ToManaged({0})";
|
||||||
|
case "Version":
|
||||||
|
type = "MonoObject*";
|
||||||
|
return "MUtils::ToManaged({0})";
|
||||||
default:
|
default:
|
||||||
// Object reference property
|
// Object reference property
|
||||||
if ((typeInfo.Type == "ScriptingObjectReference" ||
|
if ((typeInfo.Type == "ScriptingObjectReference" ||
|
||||||
@@ -590,6 +593,9 @@ namespace Flax.Build.Bindings
|
|||||||
case "CultureInfo":
|
case "CultureInfo":
|
||||||
type = "void*";
|
type = "void*";
|
||||||
return "MUtils::ToNative({0})";
|
return "MUtils::ToNative({0})";
|
||||||
|
case "Version":
|
||||||
|
type = "MonoObject*";
|
||||||
|
return "MUtils::ToNative({0})";
|
||||||
default:
|
default:
|
||||||
// Object reference property
|
// Object reference property
|
||||||
if ((typeInfo.Type == "ScriptingObjectReference" ||
|
if ((typeInfo.Type == "ScriptingObjectReference" ||
|
||||||
|
|||||||
Reference in New Issue
Block a user