Merge branch 'master' into collection-ui
This commit is contained in:
@@ -5,11 +5,11 @@ using System;
|
||||
namespace FlaxEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows property/field in the editor only if the specified member has a given value. Can be used to hide properties based on other properties (also private properties). The given member has to be bool type.
|
||||
/// Shows property/field in the editor only if the specified member has a given value. Can be used to hide properties based on other properties (also private properties). The given member has to be bool type. Multiple VisibleIf attributes can be added for additional conditions to be met.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Attribute" />
|
||||
[Serializable]
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]
|
||||
public sealed class VisibleIfAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
21
Source/Engine/Scripting/Attributes/PluginLoadOrder.cs
Normal file
21
Source/Engine/Scripting/Attributes/PluginLoadOrder.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace FlaxEngine;
|
||||
|
||||
/// <summary>
|
||||
/// This attribute allows for specifying initialization and deinitialization order for plugins.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class PluginLoadOrderAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The plugin type to initialize this plugin after.
|
||||
/// </summary>
|
||||
public Type InitializeAfter;
|
||||
|
||||
/// <summary>
|
||||
/// The plugin type to deinitialize this plugin before.
|
||||
/// </summary>
|
||||
public Type DeinitializeBefore;
|
||||
}
|
||||
@@ -693,6 +693,14 @@ void BinaryModule::Destroy(bool isReloading)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any scripting events
|
||||
for (auto i = ScriptingEvents::EventsTable.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
const ScriptingTypeHandle type = i->Key.First;
|
||||
if (type.Module == this)
|
||||
ScriptingEvents::EventsTable.Remove(i);
|
||||
}
|
||||
|
||||
// Unregister
|
||||
GetModules().RemoveKeepOrder(this);
|
||||
}
|
||||
@@ -906,6 +914,7 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly)
|
||||
#if !COMPILE_WITHOUT_CSHARP
|
||||
PROFILE_CPU();
|
||||
ASSERT(ClassToTypeIndex.IsEmpty());
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
const auto& classes = assembly->GetClasses();
|
||||
|
||||
@@ -1226,17 +1235,17 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
const bool withInterfaces = !mMethod->IsStatic() && mMethod->GetParentClass()->IsInterface();
|
||||
if (!mMethod->IsStatic())
|
||||
{
|
||||
// Box instance into C# object
|
||||
// Box instance into C# object (and validate the type)
|
||||
MObject* instanceObject = MUtils::BoxVariant(instance);
|
||||
const MClass* instanceObjectClass = MCore::Object::GetClass(instanceObject);
|
||||
|
||||
// Validate instance
|
||||
if (!instanceObject || !instanceObjectClass->IsSubClassOf(mMethod->GetParentClass(), withInterfaces))
|
||||
if (!instanceObject)
|
||||
{
|
||||
if (!instanceObject)
|
||||
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount);
|
||||
else
|
||||
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) with invalid object instance of type '{3}'", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount, String(MUtils::GetClassFullname(instanceObject)));
|
||||
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount);
|
||||
return true;
|
||||
}
|
||||
const MClass* instanceObjectClass = MCore::Object::GetClass(instanceObject);
|
||||
if (!instanceObjectClass->IsSubClassOf(mMethod->GetParentClass(), withInterfaces))
|
||||
{
|
||||
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) with invalid object instance of type '{3}'", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount, String(MUtils::GetClassFullname(instanceObject)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1270,7 +1279,11 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
|
||||
// Invoke the method
|
||||
MObject* exception = nullptr;
|
||||
#if USE_NETCORE // NetCore uses the same path for both virtual and non-virtual calls
|
||||
MObject* resultObject = mMethod->Invoke(mInstance, params, &exception);
|
||||
#else
|
||||
MObject* resultObject = withInterfaces ? mMethod->InvokeVirtual((MObject*)mInstance, params, &exception) : mMethod->Invoke(mInstance, params, &exception);
|
||||
#endif
|
||||
if (exception)
|
||||
{
|
||||
MException ex(exception);
|
||||
|
||||
@@ -108,6 +108,7 @@ namespace
|
||||
};
|
||||
|
||||
ChunkedArray<Location, 256> ManagedSourceLocations;
|
||||
ThreadLocal<uint32> ManagedEventsCount;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
@@ -145,7 +146,9 @@ DEFINE_INTERNAL_CALL(void) ProfilerInternal_BeginEvent(MString* nameObj)
|
||||
srcLoc->color = 0;
|
||||
}
|
||||
//static constexpr tracy::SourceLocationData tracySrcLoc{ nullptr, __FUNCTION__, __FILE__, (uint32_t)__LINE__, 0 };
|
||||
tracy::ScopedZone::Begin(srcLoc);
|
||||
const bool tracyActive = tracy::ScopedZone::Begin(srcLoc);
|
||||
if (tracyActive)
|
||||
ManagedEventsCount.Get()++;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
@@ -155,7 +158,12 @@ DEFINE_INTERNAL_CALL(void) ProfilerInternal_EndEvent()
|
||||
{
|
||||
#if COMPILE_WITH_PROFILER
|
||||
#if TRACY_ENABLE
|
||||
tracy::ScopedZone::End();
|
||||
uint32& tracyActive = ManagedEventsCount.Get();
|
||||
if (tracyActive > 0)
|
||||
{
|
||||
tracyActive--;
|
||||
tracy::ScopedZone::End();
|
||||
}
|
||||
#endif
|
||||
ProfilerCPU::EndEvent();
|
||||
#endif
|
||||
|
||||
@@ -18,7 +18,7 @@ private:
|
||||
#elif USE_NETCORE
|
||||
void* _handle;
|
||||
StringAnsi _name;
|
||||
StringAnsi _namespace_;
|
||||
StringAnsi _namespace;
|
||||
uint32 _types = 0;
|
||||
mutable uint32 _size = 0;
|
||||
#endif
|
||||
|
||||
@@ -1208,6 +1208,21 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
|
||||
object = nullptr;
|
||||
return object;
|
||||
}
|
||||
case MTypes::Ptr:
|
||||
switch (value.Type.Type)
|
||||
{
|
||||
case VariantType::Pointer:
|
||||
return &value.AsPointer;
|
||||
case VariantType::Object:
|
||||
return &value.AsObject;
|
||||
case VariantType::Asset:
|
||||
return &value.AsAsset;
|
||||
case VariantType::Structure:
|
||||
case VariantType::Blob:
|
||||
return &value.AsBlob.Data;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class
|
||||
|
||||
void Unbox(T& result, MObject* data)
|
||||
{
|
||||
CHECK(data);
|
||||
Platform::MemoryCopy(&result, MCore::Object::Unbox(data), sizeof(T));
|
||||
if (data)
|
||||
Platform::MemoryCopy(&result, MCore::Object::Unbox(data), sizeof(T));
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<T>& data)
|
||||
@@ -118,7 +118,7 @@ struct MConverter<String>
|
||||
{
|
||||
MString** dataPtr = MCore::Array::GetAddress<MString*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
MUtils::ToString(dataPtr[i], result[i]);
|
||||
MUtils::ToString(dataPtr[i], result.Get()[i]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -151,7 +151,7 @@ struct MConverter<StringAnsi>
|
||||
{
|
||||
MString** dataPtr = MCore::Array::GetAddress<MString*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
MUtils::ToString(dataPtr[i], result[i]);
|
||||
MUtils::ToString(dataPtr[i], result.Get()[i]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -184,7 +184,7 @@ struct MConverter<StringView>
|
||||
{
|
||||
MString** dataPtr = MCore::Array::GetAddress<MString*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
MUtils::ToString(dataPtr[i], result[i]);
|
||||
MUtils::ToString(dataPtr[i], result.Get()[i]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -217,7 +217,7 @@ struct MConverter<Variant>
|
||||
{
|
||||
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result[i] = MUtils::UnboxVariant(dataPtr[i]);
|
||||
result.Get()[i] = MUtils::UnboxVariant(dataPtr[i]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -250,7 +250,7 @@ struct MConverter<T*, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Va
|
||||
{
|
||||
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -307,7 +307,7 @@ struct MConverter<ScriptingObjectReference<T>>
|
||||
{
|
||||
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -343,7 +343,7 @@ struct MConverter<AssetReference<T>>
|
||||
{
|
||||
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ namespace FlaxEngine
|
||||
internal static partial Object Internal_Create2(string typeName);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceCreated", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial void Internal_ManagedInstanceCreated(Object managedInstance, IntPtr theKlass);
|
||||
internal static partial void Internal_ManagedInstanceCreated(Object managedInstance, IntPtr typeClass);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceDeleted", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial void Internal_ManagedInstanceDeleted(IntPtr nativeInstance);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MField.h"
|
||||
|
||||
Plugin::Plugin(const SpawnParams& params)
|
||||
: ScriptingObject(params)
|
||||
@@ -74,14 +75,57 @@ Action PluginManager::PluginsChanged;
|
||||
|
||||
namespace PluginManagerImpl
|
||||
{
|
||||
bool Initialized = false;
|
||||
Array<GamePlugin*> GamePlugins;
|
||||
Array<Plugin*> EditorPlugins;
|
||||
|
||||
void LoadPlugin(MClass* klass, bool isEditor);
|
||||
void OnAssemblyLoaded(MAssembly* assembly);
|
||||
void OnAssemblyUnloading(MAssembly* assembly);
|
||||
void OnBinaryModuleLoaded(BinaryModule* module);
|
||||
void OnScriptsReloading();
|
||||
void InitializePlugins();
|
||||
void DeinitializePlugins();
|
||||
|
||||
template<typename PluginType = Plugin>
|
||||
Array<PluginType*> SortPlugins(Array<PluginType*> plugins, MClass* pluginLoadOrderAttribute, MField* typeField)
|
||||
{
|
||||
// Sort plugins
|
||||
Array<PluginType*> newPlugins;
|
||||
for (int i = 0; i < plugins.Count(); i++)
|
||||
{
|
||||
PluginType* plugin = plugins[i];
|
||||
int32 insertIndex = -1;
|
||||
for (int j = 0; j < newPlugins.Count(); j++)
|
||||
{
|
||||
// Get first instance where a game plugin needs another one before it
|
||||
auto attribute = newPlugins[j]->GetClass()->GetAttribute(pluginLoadOrderAttribute);
|
||||
if (attribute == nullptr || MCore::Object::GetClass(attribute) != pluginLoadOrderAttribute)
|
||||
continue;
|
||||
|
||||
// Check if attribute references a valid class
|
||||
MTypeObject* refType = nullptr;
|
||||
typeField->GetValue(attribute, &refType);
|
||||
if (refType == nullptr)
|
||||
continue;
|
||||
|
||||
MType* type = INTERNAL_TYPE_OBJECT_GET(refType);
|
||||
if (type == nullptr)
|
||||
continue;
|
||||
MClass* typeClass = MCore::Type::GetClass(type);
|
||||
|
||||
if (plugin->GetClass() == typeClass)
|
||||
{
|
||||
insertIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (insertIndex == -1)
|
||||
newPlugins.Add(plugin);
|
||||
else
|
||||
newPlugins.Insert(insertIndex, plugin);
|
||||
}
|
||||
return newPlugins;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace PluginManagerImpl;
|
||||
@@ -90,7 +134,7 @@ class PluginManagerService : public EngineService
|
||||
{
|
||||
public:
|
||||
PluginManagerService()
|
||||
: EngineService(TEXT("Plugin Manager"), 130)
|
||||
: EngineService(TEXT("Plugin Manager"), 100)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -107,7 +151,7 @@ void PluginManagerService::InvokeInitialize(Plugin* plugin)
|
||||
{
|
||||
if (plugin->_initialized)
|
||||
return;
|
||||
StringAnsiView typeName = plugin->GetType().GetName();
|
||||
const StringAnsiView typeName = plugin->GetType().GetName();
|
||||
PROFILE_CPU();
|
||||
ZoneName(typeName.Get(), typeName.Length());
|
||||
|
||||
@@ -125,7 +169,7 @@ void PluginManagerService::InvokeDeinitialize(Plugin* plugin)
|
||||
{
|
||||
if (!plugin->_initialized)
|
||||
return;
|
||||
StringAnsiView typeName = plugin->GetType().GetName();
|
||||
const StringAnsiView typeName = plugin->GetType().GetName();
|
||||
PROFILE_CPU();
|
||||
ZoneName(typeName.Get(), typeName.Length());
|
||||
|
||||
@@ -139,30 +183,6 @@ void PluginManagerService::InvokeDeinitialize(Plugin* plugin)
|
||||
PluginManager::PluginUnloaded(plugin);
|
||||
}
|
||||
|
||||
void PluginManagerImpl::LoadPlugin(MClass* klass, bool isEditor)
|
||||
{
|
||||
// Create and check if use it
|
||||
auto plugin = (Plugin*)Scripting::NewObject(klass);
|
||||
if (!plugin)
|
||||
return;
|
||||
|
||||
if (!isEditor)
|
||||
{
|
||||
GamePlugins.Add((GamePlugin*)plugin);
|
||||
#if !USE_EDITOR
|
||||
PluginManagerService::InvokeInitialize(plugin);
|
||||
#endif
|
||||
}
|
||||
#if USE_EDITOR
|
||||
else
|
||||
{
|
||||
EditorPlugins.Add(plugin);
|
||||
PluginManagerService::InvokeInitialize(plugin);
|
||||
}
|
||||
#endif
|
||||
PluginManager::PluginsChanged();
|
||||
}
|
||||
|
||||
void PluginManagerImpl::OnAssemblyLoaded(MAssembly* assembly)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Load Assembly Plugins");
|
||||
@@ -183,6 +203,7 @@ void PluginManagerImpl::OnAssemblyLoaded(MAssembly* assembly)
|
||||
#endif
|
||||
|
||||
// Process all classes to find plugins
|
||||
bool loadedAnyPlugin = false;
|
||||
auto& classes = assembly->GetClasses();
|
||||
for (auto i = classes.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
@@ -192,40 +213,69 @@ void PluginManagerImpl::OnAssemblyLoaded(MAssembly* assembly)
|
||||
if (mclass->IsGeneric() || mclass->IsStatic() || mclass->IsAbstract())
|
||||
continue;
|
||||
|
||||
if (mclass->IsSubClassOf(gamePluginClass))
|
||||
{
|
||||
LoadPlugin(mclass, false);
|
||||
}
|
||||
|
||||
if (mclass->IsSubClassOf(gamePluginClass)
|
||||
#if USE_EDITOR
|
||||
if (mclass->IsSubClassOf(editorPluginClass))
|
||||
{
|
||||
LoadPlugin(mclass, true);
|
||||
}
|
||||
|| mclass->IsSubClassOf(editorPluginClass)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
auto plugin = (Plugin*)Scripting::NewObject(mclass);
|
||||
if (plugin)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
if (mclass->IsSubClassOf(editorPluginClass))
|
||||
{
|
||||
EditorPlugins.Add(plugin);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
GamePlugins.Add((GamePlugin*)plugin);
|
||||
}
|
||||
loadedAnyPlugin = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send event and initialize newly added plugins (ignore during startup)
|
||||
if (loadedAnyPlugin && Initialized)
|
||||
{
|
||||
InitializePlugins();
|
||||
PluginManager::PluginsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManagerImpl::OnAssemblyUnloading(MAssembly* assembly)
|
||||
{
|
||||
bool changed = false;
|
||||
for (int32 i = EditorPlugins.Count() - 1; i >= 0 && EditorPlugins.Count() > 0; i--)
|
||||
|
||||
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
||||
auto pluginLoadOrderAttribute = engineAssembly->GetClass("FlaxEngine.PluginLoadOrderAttribute");
|
||||
auto beforeTypeField = pluginLoadOrderAttribute->GetField("DeinitializeBefore");
|
||||
ASSERT(beforeTypeField);
|
||||
|
||||
#if USE_EDITOR
|
||||
auto editorPlugins = SortPlugins(EditorPlugins, pluginLoadOrderAttribute, beforeTypeField);
|
||||
for (int32 i = editorPlugins.Count() - 1; i >= 0 && editorPlugins.Count() > 0; i--)
|
||||
{
|
||||
auto plugin = EditorPlugins[i];
|
||||
auto plugin = editorPlugins[i];
|
||||
if (plugin->GetType().ManagedClass->GetAssembly() == assembly)
|
||||
{
|
||||
PluginManagerService::InvokeDeinitialize(plugin);
|
||||
EditorPlugins.RemoveAtKeepOrder(i);
|
||||
EditorPlugins.Remove(plugin);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
for (int32 i = GamePlugins.Count() - 1; i >= 0 && GamePlugins.Count() > 0; i--)
|
||||
#endif
|
||||
|
||||
auto gamePlugins = SortPlugins(GamePlugins, pluginLoadOrderAttribute, beforeTypeField);
|
||||
for (int32 i = gamePlugins.Count() - 1; i >= 0 && gamePlugins.Count() > 0; i--)
|
||||
{
|
||||
auto plugin = GamePlugins[i];
|
||||
auto plugin = gamePlugins[i];
|
||||
if (plugin->GetType().ManagedClass->GetAssembly() == assembly)
|
||||
{
|
||||
PluginManagerService::InvokeDeinitialize(plugin);
|
||||
GamePlugins.RemoveAtKeepOrder(i);
|
||||
GamePlugins.Remove(plugin);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
@@ -260,38 +310,83 @@ void PluginManagerImpl::OnBinaryModuleLoaded(BinaryModule* module)
|
||||
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--)
|
||||
DeinitializePlugins();
|
||||
}
|
||||
|
||||
void PluginManagerImpl::InitializePlugins()
|
||||
{
|
||||
if (EditorPlugins.Count() + GamePlugins.Count() == 0)
|
||||
return;
|
||||
PROFILE_CPU_NAMED("InitializePlugins");
|
||||
|
||||
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
||||
auto pluginLoadOrderAttribute = engineAssembly->GetClass("FlaxEngine.PluginLoadOrderAttribute");
|
||||
auto afterTypeField = pluginLoadOrderAttribute->GetField("InitializeAfter");
|
||||
ASSERT(afterTypeField);
|
||||
|
||||
#if USE_EDITOR
|
||||
auto editorPlugins = SortPlugins(EditorPlugins, pluginLoadOrderAttribute, afterTypeField);
|
||||
for (auto plugin : editorPlugins)
|
||||
{
|
||||
auto plugin = EditorPlugins[i];
|
||||
{
|
||||
PluginManagerService::InvokeDeinitialize(plugin);
|
||||
EditorPlugins.RemoveAtKeepOrder(i);
|
||||
changed = true;
|
||||
}
|
||||
PluginManagerService::InvokeInitialize(plugin);
|
||||
}
|
||||
for (int32 i = GamePlugins.Count() - 1; i >= 0 && GamePlugins.Count() > 0; i--)
|
||||
#else
|
||||
// Game plugins are managed via InitializeGamePlugins/DeinitializeGamePlugins by Editor for play mode
|
||||
auto gamePlugins = SortPlugins(GamePlugins, pluginLoadOrderAttribute, afterTypeField);
|
||||
for (auto plugin : gamePlugins)
|
||||
{
|
||||
auto plugin = GamePlugins[i];
|
||||
{
|
||||
PluginManagerService::InvokeDeinitialize(plugin);
|
||||
GamePlugins.RemoveAtKeepOrder(i);
|
||||
changed = true;
|
||||
}
|
||||
PluginManagerService::InvokeInitialize(plugin);
|
||||
}
|
||||
if (changed)
|
||||
PluginManager::PluginsChanged();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PluginManagerImpl::DeinitializePlugins()
|
||||
{
|
||||
if (EditorPlugins.Count() + GamePlugins.Count() == 0)
|
||||
return;
|
||||
PROFILE_CPU_NAMED("DeinitializePlugins");
|
||||
|
||||
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
||||
auto pluginLoadOrderAttribute = engineAssembly->GetClass("FlaxEngine.PluginLoadOrderAttribute");
|
||||
auto beforeTypeField = pluginLoadOrderAttribute->GetField("DeinitializeBefore");
|
||||
ASSERT(beforeTypeField);
|
||||
|
||||
#if USE_EDITOR
|
||||
auto editorPlugins = SortPlugins(EditorPlugins, pluginLoadOrderAttribute, beforeTypeField);
|
||||
for (int32 i = editorPlugins.Count() - 1; i >= 0 && editorPlugins.Count() > 0; i--)
|
||||
{
|
||||
auto plugin = editorPlugins[i];
|
||||
PluginManagerService::InvokeDeinitialize(plugin);
|
||||
EditorPlugins.Remove(plugin);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto gamePlugins = SortPlugins(GamePlugins, pluginLoadOrderAttribute, beforeTypeField);
|
||||
for (int32 i = gamePlugins.Count() - 1; i >= 0 && gamePlugins.Count() > 0; i--)
|
||||
{
|
||||
auto plugin = gamePlugins[i];
|
||||
PluginManagerService::InvokeDeinitialize(plugin);
|
||||
GamePlugins.Remove(plugin);
|
||||
}
|
||||
|
||||
PluginManager::PluginsChanged();
|
||||
}
|
||||
|
||||
bool PluginManagerService::Init()
|
||||
{
|
||||
Initialized = false;
|
||||
|
||||
// Process already loaded modules
|
||||
for (auto module : BinaryModule::GetModules())
|
||||
{
|
||||
OnBinaryModuleLoaded(module);
|
||||
}
|
||||
|
||||
// Invoke plugins initialization for all of them
|
||||
InitializePlugins();
|
||||
|
||||
// Register for new binary modules load actions
|
||||
Initialized = true;
|
||||
Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded);
|
||||
Scripting::ScriptsReloading.Bind(&OnScriptsReloading);
|
||||
|
||||
@@ -300,28 +395,13 @@ bool PluginManagerService::Init()
|
||||
|
||||
void PluginManagerService::Dispose()
|
||||
{
|
||||
// Unregister from new modules loading
|
||||
Initialized = false;
|
||||
Scripting::BinaryModuleLoaded.Unbind(&OnBinaryModuleLoaded);
|
||||
Scripting::ScriptsReloading.Unbind(&OnScriptsReloading);
|
||||
|
||||
// Cleanup all plugins
|
||||
PROFILE_CPU_NAMED("Dispose Plugins");
|
||||
const int32 pluginsCount = EditorPlugins.Count() + GamePlugins.Count();
|
||||
if (pluginsCount == 0)
|
||||
return;
|
||||
LOG(Info, "Unloading {0} plugins", pluginsCount);
|
||||
for (int32 i = EditorPlugins.Count() - 1; i >= 0 && EditorPlugins.Count() > 0; i--)
|
||||
{
|
||||
auto plugin = EditorPlugins[i];
|
||||
InvokeDeinitialize(plugin);
|
||||
EditorPlugins.RemoveAtKeepOrder(i);
|
||||
}
|
||||
for (int32 i = GamePlugins.Count() - 1; i >= 0 && GamePlugins.Count() > 0; i--)
|
||||
{
|
||||
auto plugin = GamePlugins[i];
|
||||
InvokeDeinitialize(plugin);
|
||||
GamePlugins.RemoveAtKeepOrder(i);
|
||||
}
|
||||
PluginManager::PluginsChanged();
|
||||
DeinitializePlugins();
|
||||
}
|
||||
|
||||
const Array<GamePlugin*>& PluginManager::GetGamePlugins()
|
||||
@@ -336,11 +416,13 @@ const Array<Plugin*>& PluginManager::GetEditorPlugins()
|
||||
|
||||
Plugin* PluginManager::GetPlugin(const StringView& name)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
for (Plugin* p : EditorPlugins)
|
||||
{
|
||||
if (p->GetDescription().Name == name)
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
for (GamePlugin* gp : GamePlugins)
|
||||
{
|
||||
if (gp->GetDescription().Name == name)
|
||||
@@ -352,11 +434,13 @@ Plugin* PluginManager::GetPlugin(const StringView& name)
|
||||
Plugin* PluginManager::GetPlugin(const MClass* type)
|
||||
{
|
||||
CHECK_RETURN(type, nullptr);
|
||||
#if USE_EDITOR
|
||||
for (Plugin* p : EditorPlugins)
|
||||
{
|
||||
if (p->GetClass()->IsSubClassOf(type))
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
for (GamePlugin* gp : GamePlugins)
|
||||
{
|
||||
if (gp->GetClass()->IsSubClassOf(type))
|
||||
@@ -368,11 +452,13 @@ Plugin* PluginManager::GetPlugin(const MClass* type)
|
||||
Plugin* PluginManager::GetPlugin(const ScriptingTypeHandle& type)
|
||||
{
|
||||
CHECK_RETURN(type, nullptr);
|
||||
#if USE_EDITOR
|
||||
for (Plugin* p : EditorPlugins)
|
||||
{
|
||||
if (p->Is(type))
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
for (GamePlugin* gp : GamePlugins)
|
||||
{
|
||||
if (gp->Is(type))
|
||||
@@ -386,18 +472,32 @@ Plugin* PluginManager::GetPlugin(const ScriptingTypeHandle& type)
|
||||
void PluginManager::InitializeGamePlugins()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
for (int32 i = 0; i < GamePlugins.Count(); i++)
|
||||
|
||||
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
||||
auto pluginLoadOrderAttribute = engineAssembly->GetClass("FlaxEngine.PluginLoadOrderAttribute");
|
||||
auto afterTypeField = pluginLoadOrderAttribute->GetField("InitializeAfter");
|
||||
ASSERT(afterTypeField);
|
||||
|
||||
auto gamePlugins = SortPlugins(GamePlugins, pluginLoadOrderAttribute, afterTypeField);
|
||||
for (int32 i = 0; i < gamePlugins.Count(); i++)
|
||||
{
|
||||
PluginManagerService::InvokeInitialize(GamePlugins[i]);
|
||||
PluginManagerService::InvokeInitialize(gamePlugins[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::DeinitializeGamePlugins()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
for (int32 i = GamePlugins.Count() - 1; i >= 0; i--)
|
||||
|
||||
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
||||
auto pluginLoadOrderAttribute = engineAssembly->GetClass("FlaxEngine.PluginLoadOrderAttribute");
|
||||
auto beforeTypeField = pluginLoadOrderAttribute->GetField("DeinitializeBefore");
|
||||
ASSERT(beforeTypeField);
|
||||
|
||||
auto gamePlugins = SortPlugins(GamePlugins, pluginLoadOrderAttribute, beforeTypeField);
|
||||
for (int32 i = gamePlugins.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
PluginManagerService::InvokeDeinitialize(GamePlugins[i]);
|
||||
PluginManagerService::InvokeDeinitialize(gamePlugins[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <mono/metadata/metadata.h>
|
||||
#include <mono/metadata/threads.h>
|
||||
#include <mono/metadata/reflection.h>
|
||||
#include <mono/metadata/mono-gc.h>
|
||||
#include <mono/metadata/mono-private-unstable.h>
|
||||
typedef char char_t;
|
||||
#define DOTNET_HOST_MONO_DEBUG 0
|
||||
@@ -182,22 +183,15 @@ Dictionary<void*, MAssembly*> CachedAssemblyHandles;
|
||||
/// </summary>
|
||||
void* GetStaticMethodPointer(const String& methodName);
|
||||
|
||||
/// <summary>
|
||||
/// Calls the managed static method in NativeInterop class with given parameters.
|
||||
/// </summary>
|
||||
template<typename RetType, typename... Args>
|
||||
FORCE_INLINE RetType CallStaticMethodByName(const String& methodName, Args... args)
|
||||
{
|
||||
typedef RetType (CORECLR_DELEGATE_CALLTYPE* fun)(Args...);
|
||||
return ((fun)GetStaticMethodPointer(methodName))(args...);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the managed static method with given parameters.
|
||||
/// </summary>
|
||||
template<typename RetType, typename... Args>
|
||||
FORCE_INLINE RetType CallStaticMethod(void* methodPtr, Args... args)
|
||||
{
|
||||
#if DOTNET_HOST_MONO
|
||||
ASSERT_LOW_LAYER(mono_domain_get()); // Ensure that Mono runtime has been attached to this thread
|
||||
#endif
|
||||
typedef RetType (CORECLR_DELEGATE_CALLTYPE* fun)(Args...);
|
||||
return ((fun)methodPtr)(args...);
|
||||
}
|
||||
@@ -273,7 +267,7 @@ bool MCore::LoadEngine()
|
||||
return true;
|
||||
|
||||
// Prepare managed side
|
||||
CallStaticMethodByName<void>(TEXT("Init"));
|
||||
CallStaticMethod<void>(GetStaticMethodPointer(TEXT("Init")));
|
||||
#ifdef MCORE_MAIN_MODULE_NAME
|
||||
// MCORE_MAIN_MODULE_NAME define is injected by Scripting.Build.cs on platforms that use separate shared library for engine symbols
|
||||
::String flaxLibraryPath(Platform::GetMainDirectory() / TEXT(MACRO_TO_STR(MCORE_MAIN_MODULE_NAME)));
|
||||
@@ -292,7 +286,8 @@ bool MCore::LoadEngine()
|
||||
MRootDomain = New<MDomain>("Root");
|
||||
MDomains.Add(MRootDomain);
|
||||
|
||||
char* buildInfo = CallStaticMethodByName<char*>(TEXT("GetRuntimeInformation"));
|
||||
void* GetRuntimeInformationPtr = GetStaticMethodPointer(TEXT("GetRuntimeInformation"));
|
||||
char* buildInfo = CallStaticMethod<char*>(GetRuntimeInformationPtr);
|
||||
LOG(Info, ".NET runtime version: {0}", ::String(buildInfo));
|
||||
MCore::GC::FreeMemory(buildInfo);
|
||||
|
||||
@@ -304,7 +299,7 @@ void MCore::UnloadEngine()
|
||||
if (!MRootDomain)
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
CallStaticMethodByName<void>(TEXT("Exit"));
|
||||
CallStaticMethod<void>(GetStaticMethodPointer(TEXT("Exit")));
|
||||
MDomains.ClearDelete();
|
||||
MRootDomain = nullptr;
|
||||
ShutdownHostfxr();
|
||||
@@ -724,6 +719,7 @@ void GetAssemblyName(void* assemblyHandle, StringAnsi& name, StringAnsi& fullnam
|
||||
|
||||
DEFINE_INTERNAL_CALL(void) NativeInterop_CreateClass(NativeClassDefinitions* managedClass, void* assemblyHandle)
|
||||
{
|
||||
ScopeLock lock(BinaryModule::Locker);
|
||||
MAssembly* assembly = GetAssembly(assemblyHandle);
|
||||
if (assembly == nullptr)
|
||||
{
|
||||
@@ -737,7 +733,18 @@ DEFINE_INTERNAL_CALL(void) NativeInterop_CreateClass(NativeClassDefinitions* man
|
||||
MClass* klass = New<MClass>(assembly, managedClass->typeHandle, managedClass->name, managedClass->fullname, managedClass->namespace_, managedClass->typeAttributes);
|
||||
if (assembly != nullptr)
|
||||
{
|
||||
const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses()).Add(klass->GetFullName(), klass);
|
||||
auto& classes = const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses());
|
||||
MClass* oldKlass;
|
||||
if (classes.TryGet(klass->GetFullName(), oldKlass))
|
||||
{
|
||||
LOG(Warning, "Class '{0}' was already added to assembly '{1}'", String(klass->GetFullName()), String(assembly->GetName()));
|
||||
Delete(klass);
|
||||
klass = oldKlass;
|
||||
}
|
||||
else
|
||||
{
|
||||
classes.Add(klass->GetFullName(), klass);
|
||||
}
|
||||
}
|
||||
managedClass->nativePointer = klass;
|
||||
}
|
||||
@@ -829,7 +836,7 @@ bool MAssembly::UnloadImage(bool isReloading)
|
||||
MClass::MClass(const MAssembly* parentAssembly, void* handle, const char* name, const char* fullname, const char* namespace_, MTypeAttributes attributes)
|
||||
: _handle(handle)
|
||||
, _name(name)
|
||||
, _namespace_(namespace_)
|
||||
, _namespace(namespace_)
|
||||
, _assembly(parentAssembly)
|
||||
, _fullname(fullname)
|
||||
, _hasCachedProperties(false)
|
||||
@@ -878,7 +885,7 @@ MClass::MClass(const MAssembly* parentAssembly, void* handle, const char* name,
|
||||
static void* TypeIsEnumPtr = GetStaticMethodPointer(TEXT("TypeIsEnum"));
|
||||
_isEnum = CallStaticMethod<bool, void*>(TypeIsEnumPtr, handle);
|
||||
|
||||
CachedClassHandles.Add(handle, this);
|
||||
CachedClassHandles[handle] = this;
|
||||
}
|
||||
|
||||
bool MAssembly::ResolveMissingFile(String& assemblyPath) const
|
||||
@@ -908,7 +915,7 @@ StringAnsiView MClass::GetName() const
|
||||
|
||||
StringAnsiView MClass::GetNamespace() const
|
||||
{
|
||||
return _namespace_;
|
||||
return _namespace;
|
||||
}
|
||||
|
||||
MType* MClass::GetType() const
|
||||
@@ -1556,6 +1563,7 @@ const Array<MObject*>& MProperty::GetAttributes() const
|
||||
|
||||
MAssembly* GetAssembly(void* assemblyHandle)
|
||||
{
|
||||
ScopeLock lock(BinaryModule::Locker);
|
||||
MAssembly* assembly;
|
||||
if (CachedAssemblyHandles.TryGet(assemblyHandle, assembly))
|
||||
return assembly;
|
||||
@@ -1564,6 +1572,7 @@ MAssembly* GetAssembly(void* assemblyHandle)
|
||||
|
||||
MClass* GetClass(MType* typeHandle)
|
||||
{
|
||||
ScopeLock lock(BinaryModule::Locker);
|
||||
MClass* klass = nullptr;
|
||||
CachedClassHandles.TryGet(typeHandle, klass);
|
||||
return nullptr;
|
||||
@@ -1573,6 +1582,7 @@ MClass* GetOrCreateClass(MType* typeHandle)
|
||||
{
|
||||
if (!typeHandle)
|
||||
return nullptr;
|
||||
ScopeLock lock(BinaryModule::Locker);
|
||||
MClass* klass;
|
||||
if (!CachedClassHandles.TryGet(typeHandle, klass))
|
||||
{
|
||||
@@ -1584,7 +1594,12 @@ MClass* GetOrCreateClass(MType* typeHandle)
|
||||
klass = New<MClass>(assembly, classInfo.typeHandle, classInfo.name, classInfo.fullname, classInfo.namespace_, classInfo.typeAttributes);
|
||||
if (assembly != nullptr)
|
||||
{
|
||||
const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses()).Add(klass->GetFullName(), klass);
|
||||
auto& classes = const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses());
|
||||
if (classes.ContainsKey(klass->GetFullName()))
|
||||
{
|
||||
LOG(Warning, "Class '{0}' was already added to assembly '{1}'", String(klass->GetFullName()), String(assembly->GetName()));
|
||||
}
|
||||
classes[klass->GetFullName()] = klass;
|
||||
}
|
||||
|
||||
if (typeHandle != classInfo.typeHandle)
|
||||
@@ -1635,14 +1650,14 @@ bool InitHostfxr()
|
||||
const ::String csharpLibraryPath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll");
|
||||
const ::String csharpRuntimeConfigPath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.runtimeconfig.json");
|
||||
if (!FileSystem::FileExists(csharpLibraryPath))
|
||||
LOG(Fatal, "Failed to initialize managed runtime, missing file: {0}", csharpLibraryPath);
|
||||
LOG(Fatal, "Failed to initialize .NET runtime, missing file: {0}", csharpLibraryPath);
|
||||
if (!FileSystem::FileExists(csharpRuntimeConfigPath))
|
||||
LOG(Fatal, "Failed to initialize managed runtime, missing file: {0}", csharpRuntimeConfigPath);
|
||||
LOG(Fatal, "Failed to initialize .NET runtime, missing file: {0}", csharpRuntimeConfigPath);
|
||||
const FLAX_CORECLR_STRING& libraryPath = FLAX_CORECLR_STRING(csharpLibraryPath);
|
||||
|
||||
// Get path to hostfxr library
|
||||
get_hostfxr_parameters get_hostfxr_params;
|
||||
get_hostfxr_params.size = sizeof(hostfxr_initialize_parameters);
|
||||
get_hostfxr_params.size = sizeof(get_hostfxr_parameters);
|
||||
get_hostfxr_params.assembly_path = libraryPath.Get();
|
||||
#if PLATFORM_MAC
|
||||
::String macOSDotnetRoot = TEXT("/usr/local/share/dotnet");
|
||||
@@ -1688,9 +1703,9 @@ bool InitHostfxr()
|
||||
Platform::OpenUrl(TEXT("https://dotnet.microsoft.com/en-us/download/dotnet/7.0"));
|
||||
#endif
|
||||
#if USE_EDITOR
|
||||
LOG(Fatal, "Missing .NET 7 SDK installation required to run Flax Editor.");
|
||||
LOG(Fatal, "Missing .NET 7 or later SDK installation required to run Flax Editor.");
|
||||
#else
|
||||
LOG(Fatal, "Missing .NET 7 Runtime installation required to run this application.");
|
||||
LOG(Fatal, "Missing .NET 7 or later Runtime installation required to run this application.");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@@ -1720,14 +1735,13 @@ bool InitHostfxr()
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Implement picking different version of hostfxr, currently prefers highest available version.
|
||||
// Allow future and preview versions of .NET
|
||||
String dotnetRollForward;
|
||||
// TODO: Implement support for picking RC/beta updates of .NET runtime
|
||||
// Uncomment for enabling support for upcoming .NET major release candidates
|
||||
#if 0
|
||||
String dotnetRollForwardPr;
|
||||
if (Platform::GetEnvironmentVariable(TEXT("DOTNET_ROLL_FORWARD"), dotnetRollForward))
|
||||
Platform::SetEnvironmentVariable(TEXT("DOTNET_ROLL_FORWARD"), TEXT("LatestMajor"));
|
||||
if (Platform::GetEnvironmentVariable(TEXT("DOTNET_ROLL_FORWARD_TO_PRERELEASE"), dotnetRollForwardPr))
|
||||
Platform::SetEnvironmentVariable(TEXT("DOTNET_ROLL_FORWARD_TO_PRERELEASE"), TEXT("1"));
|
||||
#endif
|
||||
|
||||
// Initialize hosting component
|
||||
const char_t* argv[1] = { libraryPath.Get() };
|
||||
@@ -1791,9 +1805,10 @@ void* GetStaticMethodPointer(const String& methodName)
|
||||
void* fun;
|
||||
if (CachedFunctions.TryGet(methodName, fun))
|
||||
return fun;
|
||||
PROFILE_CPU();
|
||||
const int rc = get_function_pointer(NativeInteropTypeName, FLAX_CORECLR_STRING(methodName).Get(), UNMANAGEDCALLERSONLY_METHOD, nullptr, nullptr, &fun);
|
||||
if (rc != 0)
|
||||
LOG(Fatal, "Failed to get unmanaged function pointer for method {0}: 0x{1:x}", methodName.Get(), (unsigned int)rc);
|
||||
LOG(Fatal, "Failed to get unmanaged function pointer for method '{0}': 0x{1:x}", methodName, (unsigned int)rc);
|
||||
CachedFunctions.Add(methodName, fun);
|
||||
return fun;
|
||||
}
|
||||
@@ -2012,6 +2027,9 @@ bool InitHostfxr()
|
||||
//Platform::SetEnvironmentVariable(TEXT("MONO_GC_DEBUG"), TEXT("6:gc-log.txt,check-remset-consistency,nursery-canaries"));
|
||||
#endif
|
||||
|
||||
// Adjust GC threads suspending mode to not block attached native threads (eg. Job System)
|
||||
Platform::SetEnvironmentVariable(TEXT("MONO_THREADS_SUSPEND"), TEXT("preemptive"));
|
||||
|
||||
#if defined(USE_MONO_AOT_MODE)
|
||||
// Enable AOT mode (per-platform)
|
||||
mono_jit_set_aot_mode(USE_MONO_AOT_MODE);
|
||||
@@ -2056,7 +2074,7 @@ bool InitHostfxr()
|
||||
// Setup debugger
|
||||
{
|
||||
int32 debuggerLogLevel = 0;
|
||||
if (CommandLine::Options.MonoLog.IsTrue())
|
||||
if (CommandLine::Options.MonoLog.IsTrue() || DOTNET_HOST_MONO_DEBUG)
|
||||
{
|
||||
LOG(Info, "Using detailed Mono logging");
|
||||
mono_trace_set_level_string("debug");
|
||||
@@ -2139,6 +2157,7 @@ bool InitHostfxr()
|
||||
LOG(Fatal, "Failed to initialize Mono.");
|
||||
return true;
|
||||
}
|
||||
mono_gc_init_finalizer_thread();
|
||||
|
||||
// Log info
|
||||
char* buildInfo = mono_get_runtime_build_info();
|
||||
@@ -2163,6 +2182,7 @@ void* GetStaticMethodPointer(const String& methodName)
|
||||
void* fun;
|
||||
if (CachedFunctions.TryGet(methodName, fun))
|
||||
return fun;
|
||||
PROFILE_CPU();
|
||||
|
||||
static MonoClass* nativeInteropClass = nullptr;
|
||||
if (!nativeInteropClass)
|
||||
@@ -2187,7 +2207,7 @@ void* GetStaticMethodPointer(const String& methodName)
|
||||
{
|
||||
const unsigned short errorCode = mono_error_get_error_code(&error);
|
||||
const char* errorMessage = mono_error_get_message(&error);
|
||||
LOG(Fatal, "mono_method_get_unmanaged_callers_only_ftnptr failed with error code 0x{0:x}, {1}", errorCode, String(errorMessage));
|
||||
LOG(Fatal, "Failed to get unmanaged function pointer for method '{0}': 0x{1:x}, {2}", methodName, errorCode, String(errorMessage));
|
||||
}
|
||||
mono_error_cleanup(&error);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ Script::Script(const SpawnParams& params)
|
||||
, _tickUpdate(false)
|
||||
, _tickLateUpdate(false)
|
||||
, _tickLateFixedUpdate(false)
|
||||
, _wasAwakeCalled(false)
|
||||
, _wasStartCalled(false)
|
||||
, _wasEnableCalled(false)
|
||||
{
|
||||
@@ -86,7 +87,7 @@ void Script::SetParent(Actor* value, bool canBreakPrefabLink)
|
||||
// Unlink from the old one
|
||||
if (_parent)
|
||||
{
|
||||
if (!value && _parent->IsDuringPlay() && _parent->IsActiveInHierarchy() && GetEnabled())
|
||||
if (!value && _parent->IsDuringPlay() && _parent->IsActiveInHierarchy() && GetEnabled() && _wasEnableCalled)
|
||||
{
|
||||
// Call disable when script is removed from actor (new actor is null)
|
||||
Disable();
|
||||
@@ -241,19 +242,31 @@ String Script::ToString() const
|
||||
|
||||
void Script::OnDeleteObject()
|
||||
{
|
||||
// Ensure to unlink from the parent (it will call Disable event if required)
|
||||
SetParent(nullptr);
|
||||
|
||||
// Check if remove object from game
|
||||
if (IsDuringPlay())
|
||||
// Call OnDisable
|
||||
if (_wasEnableCalled)
|
||||
{
|
||||
Disable();
|
||||
}
|
||||
|
||||
// Call OnDestroy
|
||||
if (_wasAwakeCalled)
|
||||
{
|
||||
_wasAwakeCalled = false;
|
||||
CHECK_EXECUTE_IN_EDITOR
|
||||
{
|
||||
OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
// End play
|
||||
if (IsDuringPlay())
|
||||
{
|
||||
EndPlay();
|
||||
}
|
||||
|
||||
// Unlink from parent
|
||||
SetParent(nullptr);
|
||||
|
||||
// Base
|
||||
SceneObject::OnDeleteObject();
|
||||
}
|
||||
@@ -274,9 +287,14 @@ void Script::Initialize()
|
||||
if (!IsRegistered())
|
||||
RegisterObject();
|
||||
|
||||
CHECK_EXECUTE_IN_EDITOR
|
||||
// Call OnAwake
|
||||
if (!_wasAwakeCalled)
|
||||
{
|
||||
OnAwake();
|
||||
_wasAwakeCalled = true;
|
||||
CHECK_EXECUTE_IN_EDITOR
|
||||
{
|
||||
OnAwake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,15 +15,16 @@ API_CLASS(Abstract) class FLAXENGINE_API Script : public SceneObject
|
||||
friend SceneTicking;
|
||||
friend class PrefabInstanceData;
|
||||
protected:
|
||||
int32 _enabled : 1;
|
||||
int32 _tickFixedUpdate : 1;
|
||||
int32 _tickUpdate : 1;
|
||||
int32 _tickLateUpdate : 1;
|
||||
int32 _tickLateFixedUpdate : 1;
|
||||
int32 _wasStartCalled : 1;
|
||||
int32 _wasEnableCalled : 1;
|
||||
uint16 _enabled : 1;
|
||||
uint16 _tickFixedUpdate : 1;
|
||||
uint16 _tickUpdate : 1;
|
||||
uint16 _tickLateUpdate : 1;
|
||||
uint16 _tickLateFixedUpdate : 1;
|
||||
uint16 _wasAwakeCalled : 1;
|
||||
uint16 _wasStartCalled : 1;
|
||||
uint16 _wasEnableCalled : 1;
|
||||
#if USE_EDITOR
|
||||
int32 _executeInEditor : 1;
|
||||
uint16 _executeInEditor : 1;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -20,10 +20,7 @@ public class Scripting : EngineModule
|
||||
void AddFrameworkDefines(string template, int major, int latestMinor)
|
||||
{
|
||||
for (int minor = latestMinor; minor >= 0; minor--)
|
||||
{
|
||||
options.ScriptingAPI.Defines.Add(string.Format(template, major, minor));
|
||||
options.ScriptingAPI.Defines.Add(string.Format($"{template}_OR_GREATER", major, minor));
|
||||
}
|
||||
}
|
||||
|
||||
// .NET
|
||||
@@ -31,14 +28,15 @@ public class Scripting : EngineModule
|
||||
options.ScriptingAPI.Defines.Add("USE_NETCORE");
|
||||
|
||||
// .NET SDK
|
||||
AddFrameworkDefines("NET{0}_{1}", 7, 0); // "NET7_0" and "NET7_0_OR_GREATER"
|
||||
AddFrameworkDefines("NET{0}_{1}", 6, 0);
|
||||
AddFrameworkDefines("NET{0}_{1}", 5, 0);
|
||||
var dotnetSdk = DotNetSdk.Instance;
|
||||
options.ScriptingAPI.Defines.Add("NET");
|
||||
AddFrameworkDefines("NETCOREAPP{0}_{1}", 3, 1); // "NETCOREAPP3_1" and "NETCOREAPP3_1_OR_GREATER"
|
||||
AddFrameworkDefines("NETCOREAPP{0}_{1}", 2, 2);
|
||||
AddFrameworkDefines("NETCOREAPP{0}_{1}", 1, 1);
|
||||
AddFrameworkDefines("NET{0}_{1}", dotnetSdk.Version.Major, 0); // "NET7_0"
|
||||
for (int i = 5; i <= dotnetSdk.Version.Major; i++)
|
||||
AddFrameworkDefines("NET{0}_{1}_OR_GREATER", dotnetSdk.Version.Major, 0); // "NET7_0_OR_GREATER"
|
||||
options.ScriptingAPI.Defines.Add("NETCOREAPP");
|
||||
AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 3, 1); // "NETCOREAPP3_1_OR_GREATER"
|
||||
AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 2, 2);
|
||||
AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 1, 1);
|
||||
|
||||
if (options.Target is EngineTarget engineTarget && engineTarget.UseSeparateMainExecutable(options))
|
||||
{
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Platform/MemoryStats.h"
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
|
||||
extern void registerFlaxEngineInternalCalls();
|
||||
@@ -193,6 +193,14 @@ void ScriptingService::Update()
|
||||
{
|
||||
PROFILE_CPU_NAMED("Scripting::Update");
|
||||
INVOKE_EVENT(Update);
|
||||
|
||||
#ifdef USE_NETCORE
|
||||
// Force GC to run in background periodically to avoid large blocking collections causing hitches
|
||||
if (Time::Update.TicksCount % 60 == 0)
|
||||
{
|
||||
MCore::GC::Collect(MCore::GC::MaxGeneration(), MGCCollectionMode::Forced, false, false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ScriptingService::LateUpdate()
|
||||
@@ -428,6 +436,7 @@ bool Scripting::Load()
|
||||
PROFILE_CPU();
|
||||
// Note: this action can be called from main thread (due to Mono problems with assemblies actions from other threads)
|
||||
ASSERT(IsInMainThread());
|
||||
ScopeLock lock(BinaryModule::Locker);
|
||||
|
||||
#if USE_CSHARP
|
||||
// Load C# core assembly
|
||||
@@ -1017,30 +1026,28 @@ bool Scripting::IsTypeFromGameScripts(MClass* type)
|
||||
|
||||
void Scripting::RegisterObject(ScriptingObject* obj)
|
||||
{
|
||||
const Guid id = obj->GetID();
|
||||
ScopeLock lock(_objectsLocker);
|
||||
|
||||
//ASSERT(!_objectsDictionary.ContainsValue(obj));
|
||||
#if ENABLE_ASSERTION
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
ScriptingObjectData other;
|
||||
if (_objectsDictionary.TryGet(obj->GetID(), other))
|
||||
if (_objectsDictionary.TryGet(id, other))
|
||||
#else
|
||||
ScriptingObject* other;
|
||||
if (_objectsDictionary.TryGet(obj->GetID(), other))
|
||||
if (_objectsDictionary.TryGet(id, other))
|
||||
#endif
|
||||
{
|
||||
// Something went wrong...
|
||||
LOG(Error, "Objects registry already contains object with ID={0} (type '{3}')! Trying to register object {1} (type '{2}').", obj->GetID(), obj->ToString(), String(obj->GetClass()->GetFullName()), String(other->GetClass()->GetFullName()));
|
||||
_objectsDictionary.Remove(obj->GetID());
|
||||
LOG(Error, "Objects registry already contains object with ID={0} (type '{3}')! Trying to register object {1} (type '{2}').", id, obj->ToString(), String(obj->GetClass()->GetFullName()), String(other->GetClass()->GetFullName()));
|
||||
}
|
||||
#else
|
||||
ASSERT(!_objectsDictionary.ContainsKey(obj->_id));
|
||||
#endif
|
||||
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
LOG(Info, "[RegisterObject] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName));
|
||||
#endif
|
||||
_objectsDictionary.Add(obj->GetID(), obj);
|
||||
_objectsDictionary[id] = obj;
|
||||
}
|
||||
|
||||
void Scripting::UnregisterObject(ScriptingObject* obj)
|
||||
|
||||
@@ -135,13 +135,10 @@ namespace FlaxEngine
|
||||
{
|
||||
if (e.ExceptionObject is Exception exception)
|
||||
{
|
||||
Debug.LogError($"Unhandled Exception: {exception.Message}");
|
||||
Debug.LogException(exception);
|
||||
if (e.IsTerminating && !System.Diagnostics.Debugger.IsAttached)
|
||||
Platform.Fatal($"Unhandled Exception: {exception}");
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Unhandled Exception: {exception.Message}");
|
||||
Debug.LogException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +180,8 @@ namespace FlaxEngine
|
||||
|
||||
private static void OnLocalizationChanged()
|
||||
{
|
||||
// iOS uses globalization-invariant mode so ignore it
|
||||
#if !PLATFORM_IOS
|
||||
var currentThread = Thread.CurrentThread;
|
||||
var language = Localization.CurrentLanguage;
|
||||
if (language != null)
|
||||
@@ -190,6 +189,7 @@ namespace FlaxEngine
|
||||
var culture = Localization.CurrentCulture;
|
||||
if (culture != null)
|
||||
currentThread.CurrentCulture = culture;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -271,6 +271,7 @@ namespace FlaxEngine
|
||||
Foreground = Color.FromBgra(0xFFFFFFFF),
|
||||
ForegroundGrey = Color.FromBgra(0xFFA9A9B3),
|
||||
ForegroundDisabled = Color.FromBgra(0xFF787883),
|
||||
ForegroundViewport = Color.FromBgra(0xFFFFFFFF),
|
||||
BackgroundHighlighted = Color.FromBgra(0xFF54545C),
|
||||
BorderHighlighted = Color.FromBgra(0xFF6A6A75),
|
||||
BackgroundSelected = Color.FromBgra(0xFF007ACC),
|
||||
|
||||
@@ -47,8 +47,12 @@ public:
|
||||
/// </summary>
|
||||
~ScriptingObjectReferenceBase()
|
||||
{
|
||||
if (_object)
|
||||
_object->Deleted.Unbind<ScriptingObjectReferenceBase, &ScriptingObjectReferenceBase::OnDeleted>(this);
|
||||
ScriptingObject* obj = _object;
|
||||
if (obj)
|
||||
{
|
||||
_object = nullptr;
|
||||
obj->Deleted.Unbind<ScriptingObjectReferenceBase, &ScriptingObjectReferenceBase::OnDeleted>(this);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -38,8 +38,12 @@ public:
|
||||
/// </summary>
|
||||
~SoftObjectReferenceBase()
|
||||
{
|
||||
if (_object)
|
||||
_object->Deleted.Unbind<SoftObjectReferenceBase, &SoftObjectReferenceBase::OnDeleted>(this);
|
||||
ScriptingObject* obj = _object;
|
||||
if (obj)
|
||||
{
|
||||
_object = nullptr;
|
||||
obj->Deleted.Unbind<SoftObjectReferenceBase, &SoftObjectReferenceBase::OnDeleted>(this);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user