Merge commit 'f2ecefb7ee9b9e6c5daac9f44fe40ebdccbb1c76' into 1.6
This commit is contained in:
@@ -45,6 +45,11 @@ public:
|
||||
/// </summary>
|
||||
static void UnloadEngine();
|
||||
|
||||
#if USE_EDITOR
|
||||
// Called by Scripting in a middle of hot-reload (after unloading modules but before loading them again).
|
||||
static void OnMidHotReload();
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Utilities for C# object management.
|
||||
|
||||
@@ -165,9 +165,8 @@ extern MDomain* MActiveDomain;
|
||||
extern Array<MDomain*, FixedAllocation<4>> MDomains;
|
||||
|
||||
Dictionary<String, void*> CachedFunctions;
|
||||
|
||||
Dictionary<void*, MClass*> classHandles;
|
||||
Dictionary<void*, MAssembly*> assemblyHandles;
|
||||
Dictionary<void*, MClass*> CachedClassHandles;
|
||||
Dictionary<void*, MAssembly*> CachedAssemblyHandles;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the function pointer to the managed static method in NativeInterop class.
|
||||
@@ -300,6 +299,17 @@ void MCore::UnloadEngine()
|
||||
ShutdownHostfxr();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void MCore::OnMidHotReload()
|
||||
{
|
||||
// Clear any cached class attributes (see https://github.com/FlaxEngine/FlaxEngine/issues/1108)
|
||||
for (auto e : CachedClassHandles)
|
||||
e.Value->_attributes.Clear();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
MObject* MCore::Object::Box(void* value, const MClass* klass)
|
||||
{
|
||||
static void* BoxValuePtr = GetStaticMethodPointer(TEXT("BoxValue"));
|
||||
@@ -672,7 +682,7 @@ bool MAssembly::LoadCorlib()
|
||||
return true;
|
||||
}
|
||||
_hasCachedClasses = false;
|
||||
assemblyHandles.Add(_handle, this);
|
||||
CachedAssemblyHandles.Add(_handle, this);
|
||||
|
||||
// End
|
||||
OnLoaded(startTime);
|
||||
@@ -681,6 +691,7 @@ bool MAssembly::LoadCorlib()
|
||||
|
||||
bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePath)
|
||||
{
|
||||
// TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+)
|
||||
// Open .Net assembly
|
||||
const StringAnsi assemblyPathAnsi = assemblyPath.ToStringAnsi();
|
||||
const char* name;
|
||||
@@ -696,7 +707,7 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa
|
||||
Log::CLRInnerException(TEXT(".NET assembly image is invalid at ") + assemblyPath);
|
||||
return true;
|
||||
}
|
||||
assemblyHandles.Add(_handle, this);
|
||||
CachedAssemblyHandles.Add(_handle, this);
|
||||
|
||||
// Provide new path of hot-reloaded native library path for managed DllImport
|
||||
if (nativePath.HasChars())
|
||||
@@ -722,7 +733,7 @@ bool MAssembly::UnloadImage(bool isReloading)
|
||||
CallStaticMethod<void, const void*>(CloseAssemblyPtr, _handle);
|
||||
}
|
||||
|
||||
assemblyHandles.Remove(_handle);
|
||||
CachedAssemblyHandles.Remove(_handle);
|
||||
_handle = nullptr;
|
||||
}
|
||||
return false;
|
||||
@@ -780,7 +791,7 @@ MClass::MClass(const MAssembly* parentAssembly, void* handle, const char* name,
|
||||
static void* TypeIsEnumPtr = GetStaticMethodPointer(TEXT("TypeIsEnum"));
|
||||
_isEnum = CallStaticMethod<bool, void*>(TypeIsEnumPtr, handle);
|
||||
|
||||
classHandles.Add(handle, this);
|
||||
CachedClassHandles.Add(handle, this);
|
||||
}
|
||||
|
||||
bool MAssembly::ResolveMissingFile(String& assemblyPath) const
|
||||
@@ -800,7 +811,7 @@ MClass::~MClass()
|
||||
_properties.ClearDelete();
|
||||
_events.ClearDelete();
|
||||
|
||||
classHandles.Remove(_handle);
|
||||
CachedClassHandles.Remove(_handle);
|
||||
}
|
||||
|
||||
StringAnsiView MClass::GetName() const
|
||||
@@ -1018,11 +1029,7 @@ const Array<MObject*>& MClass::GetAttributes() const
|
||||
int numAttributes;
|
||||
static void* GetClassAttributesPtr = GetStaticMethodPointer(TEXT("GetClassAttributes"));
|
||||
CallStaticMethod<void, void*, MObject***, int*>(GetClassAttributesPtr, _handle, &attributes, &numAttributes);
|
||||
_attributes.Resize(numAttributes);
|
||||
for (int i = 0; i < numAttributes; i++)
|
||||
{
|
||||
_attributes[i] = attributes[i];
|
||||
}
|
||||
_attributes.Set(attributes, numAttributes);
|
||||
MCore::GC::FreeMemory(attributes);
|
||||
|
||||
_hasCachedAttributes = true;
|
||||
@@ -1444,7 +1451,7 @@ const Array<MObject*>& MProperty::GetAttributes() const
|
||||
MAssembly* GetAssembly(void* assemblyHandle)
|
||||
{
|
||||
MAssembly* assembly;
|
||||
if (assemblyHandles.TryGet(assemblyHandle, assembly))
|
||||
if (CachedAssemblyHandles.TryGet(assemblyHandle, assembly))
|
||||
return assembly;
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1452,7 +1459,7 @@ MAssembly* GetAssembly(void* assemblyHandle)
|
||||
MClass* GetClass(MType* typeHandle)
|
||||
{
|
||||
MClass* klass = nullptr;
|
||||
classHandles.TryGet(typeHandle, klass);
|
||||
CachedClassHandles.TryGet(typeHandle, klass);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1461,7 +1468,7 @@ MClass* GetOrCreateClass(MType* typeHandle)
|
||||
if (!typeHandle)
|
||||
return nullptr;
|
||||
MClass* klass;
|
||||
if (!classHandles.TryGet(typeHandle, klass))
|
||||
if (!CachedClassHandles.TryGet(typeHandle, klass))
|
||||
{
|
||||
NativeClassDefinitions classInfo;
|
||||
void* assemblyHandle;
|
||||
@@ -1567,9 +1574,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 requried to run Flax Editor.");
|
||||
LOG(Fatal, "Missing .NET 7 SDK installation required to run Flax Editor.");
|
||||
#else
|
||||
LOG(Fatal, "Missing .NET 7 Runtime installation requried to run this application.");
|
||||
LOG(Fatal, "Missing .NET 7 Runtime installation required to run this application.");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@@ -1596,6 +1603,15 @@ 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;
|
||||
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"));
|
||||
|
||||
// Initialize hosting component
|
||||
const char_t* argv[1] = { libraryPath.Get() };
|
||||
hostfxr_initialize_parameters init_params;
|
||||
|
||||
@@ -714,6 +714,14 @@ void MCore::UnloadEngine()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void MCore::OnMidHotReload()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
MObject* MCore::Object::Box(void* value, const MClass* klass)
|
||||
{
|
||||
return mono_value_box(mono_domain_get(), klass->GetNative(), value);
|
||||
|
||||
@@ -59,6 +59,14 @@ void MCore::UnloadEngine()
|
||||
MRootDomain = nullptr;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void MCore::OnMidHotReload()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
MObject* MCore::Object::Box(void* value, const MClass* klass)
|
||||
{
|
||||
return nullptr;
|
||||
|
||||
@@ -26,6 +26,7 @@ Script::Script(const SpawnParams& params)
|
||||
, _tickFixedUpdate(false)
|
||||
, _tickUpdate(false)
|
||||
, _tickLateUpdate(false)
|
||||
, _tickLateFixedUpdate(false)
|
||||
, _wasStartCalled(false)
|
||||
, _wasEnableCalled(false)
|
||||
{
|
||||
@@ -181,6 +182,7 @@ void Script::SetupType()
|
||||
_tickUpdate |= type.Script.ScriptVTable[8] != nullptr;
|
||||
_tickLateUpdate |= type.Script.ScriptVTable[9] != nullptr;
|
||||
_tickFixedUpdate |= type.Script.ScriptVTable[10] != nullptr;
|
||||
_tickLateFixedUpdate |= type.Script.ScriptVTable[11] != nullptr;
|
||||
}
|
||||
typeHandle = type.GetBaseType();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ protected:
|
||||
int32 _tickFixedUpdate : 1;
|
||||
int32 _tickUpdate : 1;
|
||||
int32 _tickLateUpdate : 1;
|
||||
int32 _tickLateFixedUpdate : 1;
|
||||
int32 _wasStartCalled : 1;
|
||||
int32 _wasEnableCalled : 1;
|
||||
#if USE_EDITOR
|
||||
@@ -108,6 +109,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every fixed framerate frame (after FixedUpdate) if object is enabled.
|
||||
/// </summary>
|
||||
API_FUNCTION(Attributes = "NoAnimate") virtual void OnLateFixedUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called during drawing debug shapes in editor. Use <see cref="DebugDraw"/> to draw debug shapes and other visualization.
|
||||
/// </summary>
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
void Update() override;
|
||||
void LateUpdate() override;
|
||||
void FixedUpdate() override;
|
||||
void LateFixedUpdate() override;
|
||||
void Draw() override;
|
||||
void BeforeExit() override;
|
||||
void Dispose() override;
|
||||
@@ -100,6 +101,7 @@ namespace
|
||||
MMethod* _method_Update = nullptr;
|
||||
MMethod* _method_LateUpdate = nullptr;
|
||||
MMethod* _method_FixedUpdate = nullptr;
|
||||
MMethod* _method_LateFixedUpdate = nullptr;
|
||||
MMethod* _method_Draw = nullptr;
|
||||
MMethod* _method_Exit = nullptr;
|
||||
Array<BinaryModule*, InlinedAllocation<64>> _nonNativeModules;
|
||||
@@ -210,6 +212,12 @@ void ScriptingService::FixedUpdate()
|
||||
INVOKE_EVENT(FixedUpdate);
|
||||
}
|
||||
|
||||
void ScriptingService::LateFixedUpdate()
|
||||
{
|
||||
PROFILE_CPU_NAMED("Scripting::LateFixedUpdate");
|
||||
INVOKE_EVENT(LateFixedUpdate);
|
||||
}
|
||||
|
||||
void ScriptingService::Draw()
|
||||
{
|
||||
PROFILE_CPU_NAMED("Scripting::Draw");
|
||||
@@ -663,6 +671,7 @@ void Scripting::Reload(bool canTriggerSceneReload)
|
||||
modules.Clear();
|
||||
_nonNativeModules.ClearDelete();
|
||||
_hasGameModulesLoaded = false;
|
||||
MCore::OnMidHotReload();
|
||||
|
||||
// Give GC a try to cleanup old user objects and the other mess
|
||||
MCore::GC::Collect();
|
||||
|
||||
@@ -80,17 +80,22 @@ namespace FlaxEngine
|
||||
public static event Action Update;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs on scripting 'late' update.
|
||||
/// Occurs on scripting late update.
|
||||
/// </summary>
|
||||
public static event Action LateUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs on scripting `fixed` update.
|
||||
/// Occurs on scripting fixed update.
|
||||
/// </summary>
|
||||
public static event Action FixedUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs on scripting `draw` update. Called during frame rendering and can be used to invoke custom rendering with GPUDevice.
|
||||
/// Occurs on scripting late fixed update.
|
||||
/// </summary>
|
||||
public static event Action LateFixedUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs on scripting draw update. Called during frame rendering and can be used to invoke custom rendering with GPUDevice.
|
||||
/// </summary>
|
||||
public static event Action Draw;
|
||||
|
||||
@@ -302,6 +307,11 @@ namespace FlaxEngine
|
||||
FixedUpdate?.Invoke();
|
||||
}
|
||||
|
||||
internal static void Internal_LateFixedUpdate()
|
||||
{
|
||||
LateFixedUpdate?.Invoke();
|
||||
}
|
||||
|
||||
internal static void Internal_Draw()
|
||||
{
|
||||
Draw?.Invoke();
|
||||
|
||||
87
Source/Engine/Scripting/SoftTypeReference.cs
Normal file
87
Source/Engine/Scripting/SoftTypeReference.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// The soft reference to the scripting type contained in the scripting assembly.
|
||||
/// </summary>
|
||||
public struct SoftTypeReference : IComparable, IComparable<SoftTypeReference>
|
||||
{
|
||||
private string _typeName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type full name (eg. FlaxEngine.Actor).
|
||||
/// </summary>
|
||||
public string TypeName
|
||||
{
|
||||
get => _typeName;
|
||||
set => _typeName = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type (resolves soft reference).
|
||||
/// </summary>
|
||||
public Type Type
|
||||
{
|
||||
get => _typeName != null ? Type.GetType(_typeName) : null;
|
||||
set => _typeName = value?.FullName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SoftTypeReference"/>.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The type name.</param>
|
||||
public SoftTypeReference(string typeName)
|
||||
{
|
||||
_typeName = typeName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soft type reference from full name.
|
||||
/// </summary>
|
||||
/// <param name="s">The type name.</param>
|
||||
/// <returns>The soft type reference.</returns>
|
||||
public static implicit operator SoftTypeReference(string s)
|
||||
{
|
||||
return new SoftTypeReference { _typeName = s };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soft type reference from runtime type.
|
||||
/// </summary>
|
||||
/// <param name="s">The type.</param>
|
||||
/// <returns>The soft type reference.</returns>
|
||||
public static implicit operator SoftTypeReference(Type s)
|
||||
{
|
||||
return new SoftTypeReference { _typeName = s?.FullName };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return _typeName;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _typeName?.GetHashCode() ?? 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is SoftTypeReference other)
|
||||
return CompareTo(other);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(SoftTypeReference other)
|
||||
{
|
||||
return string.Compare(_typeName, other._typeName, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
151
Source/Engine/Scripting/SoftTypeReference.h
Normal file
151
Source/Engine/Scripting/SoftTypeReference.h
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Scripting.h"
|
||||
#include "ScriptingObject.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Serialization/SerializationFwd.h"
|
||||
|
||||
/// <summary>
|
||||
/// The soft reference to the scripting type contained in the scripting assembly.
|
||||
/// </summary>
|
||||
template<typename T = ScriptingObject>
|
||||
API_STRUCT(InBuild) struct SoftTypeReference
|
||||
{
|
||||
protected:
|
||||
StringAnsi _typeName;
|
||||
|
||||
public:
|
||||
SoftTypeReference() = default;
|
||||
|
||||
SoftTypeReference(const SoftTypeReference& s)
|
||||
: _typeName(s._typeName)
|
||||
{
|
||||
}
|
||||
|
||||
SoftTypeReference(SoftTypeReference&& s) noexcept
|
||||
: _typeName(MoveTemp(s._typeName))
|
||||
{
|
||||
}
|
||||
|
||||
SoftTypeReference(const StringView& s)
|
||||
: _typeName(s)
|
||||
{
|
||||
}
|
||||
|
||||
SoftTypeReference(const StringAnsiView& s)
|
||||
: _typeName(s)
|
||||
{
|
||||
}
|
||||
|
||||
SoftTypeReference(const char* s)
|
||||
: _typeName(s)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
FORCE_INLINE SoftTypeReference& operator=(SoftTypeReference&& s) noexcept
|
||||
{
|
||||
_typeName = MoveTemp(s._typeName);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE SoftTypeReference& operator=(StringAnsi&& s) noexcept
|
||||
{
|
||||
_typeName = MoveTemp(s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE SoftTypeReference& operator=(const SoftTypeReference& s)
|
||||
{
|
||||
_typeName = s._typeName;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE SoftTypeReference& operator=(const StringAnsiView& s)
|
||||
{
|
||||
_typeName = s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator==(const SoftTypeReference& other) const
|
||||
{
|
||||
return _typeName == other._typeName;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator!=(const SoftTypeReference& other) const
|
||||
{
|
||||
return _typeName != other._typeName;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator==(const StringAnsiView& other) const
|
||||
{
|
||||
return _typeName == other;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator!=(const StringAnsiView& other) const
|
||||
{
|
||||
return _typeName != other;
|
||||
}
|
||||
|
||||
FORCE_INLINE operator bool() const
|
||||
{
|
||||
return _typeName.HasChars();
|
||||
}
|
||||
|
||||
public:
|
||||
// Gets the type full name (eg. FlaxEngine.Actor).
|
||||
StringAnsiView GetTypeName() const
|
||||
{
|
||||
return StringAnsiView(_typeName);
|
||||
}
|
||||
|
||||
// Gets the type (resolves soft reference).
|
||||
ScriptingTypeHandle GetType() const
|
||||
{
|
||||
return Scripting::FindScriptingType(_typeName);
|
||||
}
|
||||
|
||||
// Creates a new objects of that type (or of type T if failed to solve typename).
|
||||
T* NewObject() const
|
||||
{
|
||||
const ScriptingTypeHandle type = Scripting::FindScriptingType(_typeName);
|
||||
auto obj = ScriptingObject::NewObject<T>(type);
|
||||
if (!obj)
|
||||
{
|
||||
if (_typeName.HasChars())
|
||||
LOG(Error, "Unknown or invalid type {0}", String(_typeName));
|
||||
obj = ScriptingObject::NewObject<T>();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
uint32 GetHash(const SoftTypeReference<T>& key)
|
||||
{
|
||||
return GetHash(key.GetTypeName());
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
namespace Serialization
|
||||
{
|
||||
template<typename T>
|
||||
bool ShouldSerialize(const SoftTypeReference<T>& v, const void* otherObj)
|
||||
{
|
||||
return !otherObj || v != *(SoftTypeReference<T>*)otherObj;
|
||||
}
|
||||
template<typename T>
|
||||
void Serialize(ISerializable::SerializeStream& stream, const SoftTypeReference<T>& v, const void* otherObj)
|
||||
{
|
||||
stream.String(v.GetTypeName());
|
||||
}
|
||||
template<typename T>
|
||||
void Deserialize(ISerializable::DeserializeStream& stream, SoftTypeReference<T>& v, ISerializeModifier* modifier)
|
||||
{
|
||||
v = stream.GetTextAnsi();
|
||||
}
|
||||
}
|
||||
// @formatter:on
|
||||
Reference in New Issue
Block a user