mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Serialize Enums (and maybe Entitiy/Component refs)
This commit is contained in:
@@ -568,32 +568,6 @@ namespace GlitchyEditor.EditWindows
|
|||||||
ScriptEngine.ShowScriptEditor(entity, scriptComponent);
|
ScriptEngine.ShowScriptEditor(entity, scriptComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ShowEnumSelector(ScriptFieldInstance* field, StringView fieldName, ScriptClass scriptClass)
|
|
||||||
{
|
|
||||||
ScriptField scriptField = scriptClass.Fields[fieldName];
|
|
||||||
SharpEnum enumType = scriptField.SharpType as SharpEnum;
|
|
||||||
|
|
||||||
Log.EngineLogger.Assert(enumType != null, "Enum must have a SharpEnum!");
|
|
||||||
|
|
||||||
// Simply get Enum as a uint64
|
|
||||||
var fieldValue = field.GetData<uint64>();
|
|
||||||
|
|
||||||
StringView valueName = "<Invalid Value>";
|
|
||||||
|
|
||||||
if (enumType.Values.TryGetValue(fieldValue, let enumValue))
|
|
||||||
valueName = enumValue.Name;
|
|
||||||
|
|
||||||
if (ImGui.BeginCombo(fieldName.Ptr, valueName.Ptr))
|
|
||||||
{
|
|
||||||
for (let (entryValue, enumEntry) in enumType.Values)
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(enumEntry.Name.Ptr, fieldValue == entryValue))
|
|
||||||
field.SetData(entryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndCombo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Entity? ShowEntitySelector()
|
private static Entity? ShowEntitySelector()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -536,7 +536,7 @@ static class ScriptGlue
|
|||||||
#region Serialization
|
#region Serialization
|
||||||
|
|
||||||
[RegisterCall("ScriptGlue::Serialization_SerializeField")]
|
[RegisterCall("ScriptGlue::Serialization_SerializeField")]
|
||||||
static void Serialization_SerializeField(void* serializationContext, SerializationType type, MonoString* nameObject, MonoObject* valueObject)
|
static void Serialization_SerializeField(void* serializationContext, SerializationType type, MonoString* nameObject, MonoObject* valueObject, MonoString* fullTypeName)
|
||||||
{
|
{
|
||||||
SerializedObject context = Internal.UnsafeCastToObject(serializationContext) as SerializedObject;
|
SerializedObject context = Internal.UnsafeCastToObject(serializationContext) as SerializedObject;
|
||||||
|
|
||||||
@@ -544,7 +544,7 @@ static class ScriptGlue
|
|||||||
|
|
||||||
char8* name = Mono.mono_string_to_utf8(nameObject);
|
char8* name = Mono.mono_string_to_utf8(nameObject);
|
||||||
|
|
||||||
context.AddField(StringView(name), type, valueObject);
|
context.AddField(StringView(name), type, valueObject, fullTypeName);
|
||||||
|
|
||||||
Mono.mono_free(name);
|
Mono.mono_free(name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,20 @@ public enum SerializationType : int32
|
|||||||
|
|
||||||
class SerializedObject
|
class SerializedObject
|
||||||
{
|
{
|
||||||
|
[Union]
|
||||||
|
public struct FieldData
|
||||||
|
{
|
||||||
|
public uint8[16] RawData;
|
||||||
|
public StringView StringView;
|
||||||
|
public (String Type, UUID ID) EngineObject;
|
||||||
|
|
||||||
|
static this()
|
||||||
|
{
|
||||||
|
// This is important, because we expect 16 Bytes on the C# side
|
||||||
|
Compiler.Assert(sizeof(Self) == 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// ID used to identify the object represented by this SerializedObject. In case that the represented object is a
|
/// ID used to identify the object represented by this SerializedObject. In case that the represented object is a
|
||||||
/// Script Instance, the ID is the UUID of the Entity, random otherwise.
|
/// Script Instance, the ID is the UUID of the Entity, random otherwise.
|
||||||
/// Todo: This sucks because we don't know all UUIDs beforehand and might accidentally assign the ID of an Entity to some class
|
/// Todo: This sucks because we don't know all UUIDs beforehand and might accidentally assign the ID of an Entity to some class
|
||||||
@@ -81,7 +95,7 @@ class SerializedObject
|
|||||||
|
|
||||||
private List<String> _ownedString = new List<String>() ~ DeleteContainerAndItems!(_);
|
private List<String> _ownedString = new List<String>() ~ DeleteContainerAndItems!(_);
|
||||||
|
|
||||||
public append Dictionary<StringView, (SerializationType PrimitiveType, uint8[16] Data)> Fields = .();
|
public append Dictionary<StringView, (SerializationType PrimitiveType, FieldData Data)> Fields = .();
|
||||||
|
|
||||||
[AllowAppend]
|
[AllowAppend]
|
||||||
public this(Dictionary<UUID, SerializedObject> allObjects, StringView? typeName, UUID? id = null)
|
public this(Dictionary<UUID, SerializedObject> allObjects, StringView? typeName, UUID? id = null)
|
||||||
@@ -109,12 +123,13 @@ class SerializedObject
|
|||||||
TypeName = typeNameCopy;
|
TypeName = typeNameCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddField(StringView name, SerializationType primitiveType, MonoObject* value)
|
public void AddField(StringView name, SerializationType primitiveType, MonoObject* value, MonoString* fullTypeName)
|
||||||
{
|
{
|
||||||
uint8[16] data = .();
|
FieldData data = .();
|
||||||
|
|
||||||
if (primitiveType == .String)
|
switch (primitiveType)
|
||||||
{
|
{
|
||||||
|
case .String, .Enum:
|
||||||
// If the string is null, we store a nullptr and 0-length
|
// If the string is null, we store a nullptr and 0-length
|
||||||
StringView valueView = StringView(null, 0);
|
StringView valueView = StringView(null, 0);
|
||||||
|
|
||||||
@@ -132,29 +147,25 @@ class SerializedObject
|
|||||||
valueView = stringValue;
|
valueView = stringValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Internal.MemCpy(&data, &valueView, sizeof(StringView));
|
data.StringView = valueView;
|
||||||
}
|
case .EntityReference | .ComponentReference:
|
||||||
else if (primitiveType == .Enum)
|
String typeName = null;
|
||||||
{
|
|
||||||
MonoString* string = (.)value;
|
|
||||||
|
|
||||||
char8* rawEnumValue = Mono.mono_string_to_utf8(string);
|
|
||||||
|
|
||||||
String enumValue = new String(rawEnumValue);
|
if (fullTypeName != null)
|
||||||
|
{
|
||||||
|
char8* rawTypeName = Mono.mono_string_to_utf8(fullTypeName);
|
||||||
|
typeName = new String(rawTypeName);
|
||||||
|
|
||||||
_ownedString.Add(enumValue);
|
_ownedString.Add(typeName);
|
||||||
|
|
||||||
Mono.mono_free(rawEnumValue);
|
Mono.mono_free(rawTypeName);
|
||||||
|
}
|
||||||
StringView valueView = enumValue;
|
|
||||||
|
|
||||||
Internal.MemCpy(&data, &valueView, sizeof(StringView));
|
data.EngineObject = (Type: typeName, ID: *(UUID*)Mono.mono_object_unbox(value));
|
||||||
}
|
default:
|
||||||
else
|
|
||||||
{
|
|
||||||
void* rawValue = Mono.mono_object_unbox(value);
|
void* rawValue = Mono.mono_object_unbox(value);
|
||||||
|
|
||||||
Internal.MemCpy(&data, rawValue, primitiveType.GetSize());
|
Internal.MemCpy(&data.RawData, rawValue, primitiveType.GetSize());
|
||||||
|
|
||||||
String nameCopy = new String(name);
|
String nameCopy = new String(name);
|
||||||
_ownedString.Add(nameCopy);
|
_ownedString.Add(nameCopy);
|
||||||
@@ -177,19 +188,16 @@ class SerializedObject
|
|||||||
switch (field.PrimitiveType)
|
switch (field.PrimitiveType)
|
||||||
{
|
{
|
||||||
case .String, .Enum:
|
case .String, .Enum:
|
||||||
#unwarn
|
*(StringView*)target = field.Data.StringView;
|
||||||
StringView view = *(StringView*)&field.Data;
|
case .EntityReference | .ComponentReference:
|
||||||
|
char8* typeNamePtr = field.Data.EngineObject.Type.Ptr;
|
||||||
|
|
||||||
char8* stringPtr = view.Ptr;
|
*(UUID*)target = field.Data.EngineObject.ID;
|
||||||
int stringLen = view.Length;
|
*(char8**)(target + sizeof(UUID)) = typeNamePtr;
|
||||||
|
|
||||||
// We just pass the raw utf8-Pointer and length to C#
|
|
||||||
Internal.MemCpy(target, &stringPtr, sizeof(void*));
|
|
||||||
Internal.MemCpy(target + 8, &stringLen, sizeof(int));
|
|
||||||
default:
|
default:
|
||||||
// Most values can simply be copied, the conversion will be done in C#
|
// Most values can simply be copied, the conversion will be done in C#
|
||||||
#unwarn
|
#unwarn
|
||||||
Internal.MemCpy(target, &field.Data, 16);
|
Internal.MemCpy(target, &field.Data.RawData, sizeof(FieldData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+31
-1
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using GlitchyEngine.Core;
|
using GlitchyEngine.Core;
|
||||||
using GlitchyEngine.Extensions;
|
using GlitchyEngine.Extensions;
|
||||||
@@ -101,7 +102,7 @@ public class Entity : EngineObject
|
|||||||
|
|
||||||
if (HasComponent(componentType))
|
if (HasComponent(componentType))
|
||||||
{
|
{
|
||||||
return Activator.CreateInstance(componentType) as Component;
|
return Activator.CreateInstance(componentType, true, _uuid) as Component;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -291,6 +292,35 @@ public class Entity : EngineObject
|
|||||||
return scriptInstance as T;
|
return scriptInstance as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the script of the given type, or null, if the entity has no script of the given type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type of the script.</param>
|
||||||
|
/// <returns>The script instance or null.</returns>
|
||||||
|
public object As(Type type)
|
||||||
|
{
|
||||||
|
Debug.Assert(type.IsSubclassOf(typeof(Entity)));
|
||||||
|
|
||||||
|
ScriptGlue.Entity_GetScriptInstance(_uuid, out object scriptInstance);
|
||||||
|
|
||||||
|
return scriptInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the script of the given type, or null, if the entity has no script of the given type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The id of the entity whose script instance shall be returned.</param>
|
||||||
|
/// <param name="type">The type of the script.</param>
|
||||||
|
/// <returns>The script instance or null.</returns>
|
||||||
|
internal static object GetScriptReference(UUID id, Type type)
|
||||||
|
{
|
||||||
|
Debug.Assert(type.IsSubclassOf(typeof(Entity)));
|
||||||
|
|
||||||
|
ScriptGlue.Entity_GetScriptInstance(id, out object scriptInstance);
|
||||||
|
|
||||||
|
return scriptInstance;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Destroys the entity and all it's children.
|
/// Destroys the entity and all it's children.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ internal static class ScriptGlue
|
|||||||
#region Serialization
|
#region Serialization
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern void Serialization_SerializeField(IntPtr serializationContext, SerializationType type, string name, object value);
|
internal static extern void Serialization_SerializeField(IntPtr serializationContext, SerializationType type, string name, object value, string fullTypeName = null);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern void Serialization_CreateObject(IntPtr currentContext, string fullTypeName, out IntPtr context, out UUID id);
|
internal static extern void Serialization_CreateObject(IntPtr currentContext, string fullTypeName, out IntPtr context, out UUID id);
|
||||||
|
|||||||
@@ -93,11 +93,11 @@ public static class EntitySerializer
|
|||||||
_structScopeName.Remove(_structScopeName.Length - scopeToRemove.Length - 1);
|
_structScopeName.Remove(_structScopeName.Length - scopeToRemove.Length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddField(string fieldName, SerializationType serializationType, object value)
|
private void AddField(string fieldName, SerializationType serializationType, object value, string fullTypeName = null)
|
||||||
{
|
{
|
||||||
string completeFieldName = $"{_structScopeName}{fieldName}";
|
string completeFieldName = $"{_structScopeName}{fieldName}";
|
||||||
|
|
||||||
ScriptGlue.Serialization_SerializeField(_internalContext, serializationType, completeFieldName, value);
|
ScriptGlue.Serialization_SerializeField(_internalContext, serializationType, completeFieldName, value, fullTypeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Serialize(Entity entity)
|
public void Serialize(Entity entity)
|
||||||
@@ -228,11 +228,11 @@ public static class EntitySerializer
|
|||||||
{
|
{
|
||||||
if (typeof(Entity).IsAssignableFrom(fieldType))
|
if (typeof(Entity).IsAssignableFrom(fieldType))
|
||||||
{
|
{
|
||||||
AddField(fieldName, SerializationType.EntityReference, ((Entity)fieldValue)?.UUID ?? UUID.Zero);
|
AddField(fieldName, SerializationType.EntityReference, ((Entity)fieldValue)?.UUID ?? UUID.Zero, fieldValue?.GetType().FullName);
|
||||||
}
|
}
|
||||||
else if (typeof(Component).IsAssignableFrom(fieldType))
|
else if (typeof(Component).IsAssignableFrom(fieldType))
|
||||||
{
|
{
|
||||||
AddField(fieldName, SerializationType.ComponentReference, ((Component)fieldValue)?.UUID ?? UUID.Zero);
|
AddField(fieldName, SerializationType.ComponentReference, ((Component)fieldValue)?.UUID ?? UUID.Zero, fieldValue?.GetType().FullName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -363,6 +363,20 @@ public static class EntitySerializer
|
|||||||
_structScopeName.Remove(_structScopeName.Length - scopeToRemove.Length - 1);
|
_structScopeName.Remove(_structScopeName.Length - scopeToRemove.Length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
private struct DataHelper
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct EngineObjectReferenceHelper
|
||||||
|
{
|
||||||
|
public IntPtr FullTypeName;
|
||||||
|
public UUID Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public EngineObjectReferenceHelper EngineObjectReference;
|
||||||
|
}
|
||||||
|
|
||||||
private unsafe object GetFieldValue(string fieldName, SerializationType serializationType)
|
private unsafe object GetFieldValue(string fieldName, SerializationType serializationType)
|
||||||
{
|
{
|
||||||
string completeFieldName = $"{_structScopeName}{fieldName}";
|
string completeFieldName = $"{_structScopeName}{fieldName}";
|
||||||
@@ -372,6 +386,8 @@ public static class EntitySerializer
|
|||||||
byte* rawData = stackalloc byte[16];
|
byte* rawData = stackalloc byte[16];
|
||||||
//byte* rawData = (byte*)&backingFieldOnStack;
|
//byte* rawData = (byte*)&backingFieldOnStack;
|
||||||
|
|
||||||
|
ref DataHelper dataHelper = ref Unsafe.AsRef<DataHelper>(rawData);
|
||||||
|
|
||||||
ScriptGlue.Serialization_DeserializeField(_internalContext, serializationType, completeFieldName, rawData);
|
ScriptGlue.Serialization_DeserializeField(_internalContext, serializationType, completeFieldName, rawData);
|
||||||
|
|
||||||
string GetString()
|
string GetString()
|
||||||
@@ -423,12 +439,10 @@ public static class EntitySerializer
|
|||||||
return *(decimal*)rawData;
|
return *(decimal*)rawData;
|
||||||
|
|
||||||
case SerializationType.Enum:
|
case SerializationType.Enum:
|
||||||
string value = GetString();
|
return GetString();
|
||||||
// TODO!
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case SerializationType.EntityReference:
|
case SerializationType.EntityReference:
|
||||||
case SerializationType.ComponentReference:
|
case SerializationType.ComponentReference:
|
||||||
|
return dataHelper.EngineObjectReference;
|
||||||
case SerializationType.ObjectReference:
|
case SerializationType.ObjectReference:
|
||||||
return *(UUID*)rawData;
|
return *(UUID*)rawData;
|
||||||
default:
|
default:
|
||||||
@@ -476,7 +490,7 @@ public static class EntitySerializer
|
|||||||
}
|
}
|
||||||
else if (fieldType.IsEnum)
|
else if (fieldType.IsEnum)
|
||||||
{
|
{
|
||||||
// SerializeEnum(fieldName, fieldValue, fieldType);
|
newFieldValue = DeserializeEnum(field.Name, fieldType);
|
||||||
}
|
}
|
||||||
else if (fieldType.IsArray)
|
else if (fieldType.IsArray)
|
||||||
{
|
{
|
||||||
@@ -563,9 +577,21 @@ public static class EntitySerializer
|
|||||||
return GetFieldValue(fieldName, expectedType);
|
return GetFieldValue(fieldName, expectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeserializeEnum(string fieldName, object fieldValue, Type fieldType)
|
private object DeserializeEnum(string fieldName, Type enumType)
|
||||||
{
|
{
|
||||||
//AddField(fieldName, SerializationType.Enum, fieldValue.ToString());
|
if (GetFieldValue(fieldName, SerializationType.Enum) is not string valueName)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Enum.Parse(enumType, valueName);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Log.Error($"Failed to parse \"{valueName}\" as enum-type \"{enumType}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private object DeserializeStruct(string fieldName, object targetInstance)
|
private object DeserializeStruct(string fieldName, object targetInstance)
|
||||||
@@ -581,36 +607,40 @@ public static class EntitySerializer
|
|||||||
|
|
||||||
private object DeserializeClass(string fieldName, Type fieldType)
|
private object DeserializeClass(string fieldName, Type fieldType)
|
||||||
{
|
{
|
||||||
if (typeof(Entity).IsAssignableFrom(fieldType))
|
bool isEntity = typeof(Entity).IsAssignableFrom(fieldType);
|
||||||
|
bool isComponent = fieldType.IsSubclassOf(typeof(Component));
|
||||||
|
|
||||||
|
if (isEntity || isComponent)
|
||||||
{
|
{
|
||||||
UUID id = (UUID)GetFieldValue(fieldName, SerializationType.EntityReference);
|
var data = (DataHelper.EngineObjectReferenceHelper)GetFieldValue(fieldName, SerializationType.EntityReference);
|
||||||
|
|
||||||
|
UUID id = data.Id;
|
||||||
|
|
||||||
if (id == UUID.Zero)
|
if (id == UUID.Zero)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
Entity reference = new Entity(id);
|
string fullTypeName = Marshal.PtrToStringUni(data.FullTypeName);
|
||||||
|
|
||||||
if (fieldType.IsSubclassOf(typeof(Entity)))
|
Type type = GetTypeFromName(fullTypeName);
|
||||||
return reference.As<Entity>();
|
|
||||||
|
if (type == null)
|
||||||
return reference;
|
|
||||||
}
|
|
||||||
else if (typeof(Component).IsAssignableFrom(fieldType))
|
|
||||||
{
|
|
||||||
UUID id = (UUID)GetFieldValue(fieldName, SerializationType.ComponentReference);
|
|
||||||
|
|
||||||
if (id == UUID.Zero)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// TODO: Get class
|
if (isEntity)
|
||||||
|
{
|
||||||
|
// We have to differentiate between simple Entity references and script instances
|
||||||
|
// (because we decided to use the same type for both, so we could have a field Entity which contains a script instance instead of an Entity reference)
|
||||||
|
if (type == typeof(Entity))
|
||||||
|
return new Entity(id);
|
||||||
|
|
||||||
//Entity reference = new Entity(id);
|
return Entity.GetScriptReference(id, type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Entity entity = new Entity(id);
|
||||||
|
|
||||||
//if (fieldType.IsSubclassOf(typeof(Entity)))
|
return entity.GetComponent(type);
|
||||||
// return reference.As<Entity>();
|
}
|
||||||
|
|
||||||
//return reference;
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user