Merge remote-tracking branch 'origin/master' into 1.13

# Conflicts:
#	Flax.flaxproj
#	Source/Engine/Level/Actors/StaticModel.cpp
#	Source/Engine/Level/Prefabs/Prefab.cpp
#	Source/Engine/Tools/ModelTool/ModelTool.cpp
This commit is contained in:
2026-06-03 17:15:38 +02:00
91 changed files with 1290 additions and 579 deletions
@@ -0,0 +1,15 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
namespace FlaxEngine
{
/// <summary>
/// Changes enum serialization to use string names instead of integer values. This makes saved data resilient to enum reordering or changes in values (but not to renaming enums). Deserialization accepts both string names and integer values for backward compatibility.
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Enum)]
public sealed class EnumStringAttribute : Attribute
{
}
}
+26 -37
View File
@@ -204,7 +204,7 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul
Struct.SetField = setField;
}
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, EnumItem* items)
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, EnumItem* items, bool stringSerialization)
: ManagedClass(nullptr)
, Module(module)
, InitRuntime(DefaultInitRuntime)
@@ -215,6 +215,7 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul
, Size(size)
{
Enum.Items = items;
Enum.StringSerialization = stringSerialization;
}
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, InitRuntimeHandler initRuntime, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable, GetInterfaceWrapper getInterfaceWrapper)
@@ -270,6 +271,7 @@ ScriptingType::ScriptingType(const ScriptingType& other)
break;
case ScriptingTypes::Enum:
Enum.Items = other.Enum.Items;
Enum.StringSerialization = other.Enum.StringSerialization;
break;
case ScriptingTypes::Interface:
Interface.SetupScriptVTable = other.Interface.SetupScriptVTable;
@@ -323,6 +325,7 @@ ScriptingType::ScriptingType(ScriptingType&& other)
break;
case ScriptingTypes::Enum:
Enum.Items = other.Enum.Items;
Enum.StringSerialization = other.Enum.StringSerialization;
break;
case ScriptingTypes::Interface:
Interface.SetupScriptVTable = other.Interface.SetupScriptVTable;
@@ -604,71 +607,57 @@ StringAnsiView ScriptingType::GetName() const
return Fullname;
}
#if BUILD_DEBUG || USE_EDITOR
#define INIT_TYPE(...) \
module->Types.AddUninitialized(); \
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, ##__VA_ARGS__); \
if (module->TypeNameToTypeIndex.ContainsKey(fullname)) \
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName())); \
module->TypeNameToTypeIndex[fullname] = TypeIndex;
#else
#define INIT_TYPE(...) \
module->Types.AddUninitialized(); \
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, ##__VA_ARGS__); \
module->TypeNameToTypeIndex[fullname] = TypeIndex;
#endif
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())
{
// Script
module->Types.AddUninitialized();
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, spawn, baseType, setupScriptVTable, setupScriptObjectVTable, interfaces);
#if BUILD_DEBUG
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
#endif
module->TypeNameToTypeIndex[fullname] = TypeIndex;
INIT_TYPE(size, initRuntime, spawn, baseType, setupScriptVTable, setupScriptObjectVTable, interfaces);
}
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType, const ScriptingType::InterfaceImplementation* interfaces)
: ScriptingTypeHandle(module, module->Types.Count())
{
// Class
module->Types.AddUninitialized();
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, baseType, interfaces);
#if BUILD_DEBUG
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
#endif
module->TypeNameToTypeIndex[fullname] = TypeIndex;
INIT_TYPE(size, initRuntime, ctor, dtor, baseType, interfaces);
}
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType, const ScriptingType::InterfaceImplementation* interfaces)
: ScriptingTypeHandle(module, module->Types.Count())
{
// Structure
module->Types.AddUninitialized();
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, copy, box, unbox, getField, setField, baseType, interfaces);
#if BUILD_DEBUG
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
#endif
module->TypeNameToTypeIndex[fullname] = TypeIndex;
INIT_TYPE(size, initRuntime, ctor, dtor, copy, box, unbox, getField, setField, baseType, interfaces);
}
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::EnumItem* items)
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::EnumItem* items, bool stringSerialization)
: ScriptingTypeHandle(module, module->Types.Count())
{
// Enum
module->Types.AddUninitialized();
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, items);
#if BUILD_DEBUG
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
#endif
module->TypeNameToTypeIndex[fullname] = TypeIndex;
INIT_TYPE(size, items, stringSerialization);
}
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable, ScriptingType::GetInterfaceWrapper getInterfaceWrapper)
: ScriptingTypeHandle(module, module->Types.Count())
{
// Interface
module->Types.AddUninitialized();
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, initRuntime, setupScriptVTable, setupScriptObjectVTable, getInterfaceWrapper);
#if BUILD_DEBUG
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
#endif
module->TypeNameToTypeIndex[fullname] = TypeIndex;
INIT_TYPE(initRuntime, setupScriptVTable, setupScriptObjectVTable, getInterfaceWrapper);
}
#undef INIT_TYPE
CriticalSection BinaryModule::Locker;
BinaryModule::BinaryModulesList& BinaryModule::GetModules()
@@ -609,6 +609,12 @@ MObject* MUtils::BoxVariant(const Variant& value)
return MCore::Object::Box((void*)&value.AsData, Double3::TypeInitializer.GetClass());
case VariantType::Double4:
return MCore::Object::Box((void*)&value.AsData, Double4::TypeInitializer.GetClass());
case VariantType::Int2:
return MCore::Object::Box((void*)&value.AsData, Int2::TypeInitializer.GetClass());
case VariantType::Int3:
return MCore::Object::Box((void*)&value.AsData, Int3::TypeInitializer.GetClass());
case VariantType::Int4:
return MCore::Object::Box((void*)&value.AsData, Int4::TypeInitializer.GetClass());
case VariantType::Color:
return MCore::Object::Box((void*)&value.AsData, stdTypes.ColorClass);
case VariantType::Guid:
@@ -880,6 +886,12 @@ MClass* MUtils::GetClass(const VariantType& value)
return Double3::TypeInitializer.GetClass();
case VariantType::Double4:
return Double4::TypeInitializer.GetClass();
case VariantType::Int2:
return Int2::TypeInitializer.GetClass();
case VariantType::Int3:
return Int3::TypeInitializer.GetClass();
case VariantType::Int4:
return Int4::TypeInitializer.GetClass();
case VariantType::Color:
return Color::TypeInitializer.GetClass();
case VariantType::Guid:
@@ -970,6 +982,12 @@ MClass* MUtils::GetClass(const Variant& value)
return Double3::TypeInitializer.GetClass();
case VariantType::Double4:
return Double4::TypeInitializer.GetClass();
case VariantType::Int2:
return Int2::TypeInitializer.GetClass();
case VariantType::Int3:
return Int3::TypeInitializer.GetClass();
case VariantType::Int4:
return Int4::TypeInitializer.GetClass();
case VariantType::Color:
return stdTypes.ColorClass;
case VariantType::Guid:
@@ -1138,6 +1156,9 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
CASE_IN_BUILD_TYPE(Double2, AsData);
CASE_IN_BUILD_TYPE(Double3, AsData);
CASE_IN_BUILD_TYPE(Double4, AsBlob.Data);
CASE_IN_BUILD_TYPE(Int2, AsData);
CASE_IN_BUILD_TYPE(Int3, AsData);
CASE_IN_BUILD_TYPE(Int4, AsData);
#undef CASE_IN_BUILD_TYPE
if (klass->IsValueType())
{
+4 -2
View File
@@ -266,6 +266,8 @@ struct FLAXENGINE_API ScriptingType
{
// Enum items table (the last item name is null)
EnumItem* Items;
// Enum uses string names serialization instead of integer values.
bool StringSerialization;
} Enum;
struct
@@ -290,7 +292,7 @@ struct FLAXENGINE_API ScriptingType
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime = DefaultInitRuntime, SpawnHandler spawn = DefaultSpawn, ScriptingTypeInitializer* baseType = nullptr, SetupScriptVTableHandler setupScriptVTable = nullptr, SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr, const InterfaceImplementation* interfaces = nullptr);
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, ScriptingTypeInitializer* baseType, const InterfaceImplementation* interfaces = nullptr);
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, Copy copy, Box box, Unbox unbox, GetField getField, SetField setField, ScriptingTypeInitializer* baseType, const InterfaceImplementation* interfaces = nullptr);
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, EnumItem* items);
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, EnumItem* items, bool stringSerialization);
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, InitRuntimeHandler initRuntime, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable, GetInterfaceWrapper getInterfaceWrapper);
ScriptingType(const ScriptingType& other);
ScriptingType(ScriptingType&& other);
@@ -339,7 +341,7 @@ struct FLAXENGINE_API ScriptingTypeInitializer : ScriptingTypeHandle
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime = ScriptingType::DefaultInitRuntime, ScriptingType::SpawnHandler spawn = ScriptingType::DefaultSpawn, ScriptingTypeInitializer* baseType = nullptr, ScriptingType::SetupScriptVTableHandler setupScriptVTable = nullptr, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr, const ScriptingType::InterfaceImplementation* interfaces = nullptr);
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType = nullptr, const ScriptingType::InterfaceImplementation* interfaces = nullptr);
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType = nullptr, const ScriptingType::InterfaceImplementation* interfaces = nullptr);
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::EnumItem* items);
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::EnumItem* items, bool stringSerialization);
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable, ScriptingType::GetInterfaceWrapper getInterfaceWrapper);
};