From acefbb58bd574ff09066868eece036eab521647f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Mon, 25 Dec 2023 20:08:48 +0100 Subject: [PATCH] Serialize/Deserialize simple classes --- GlitchyEngine/src/Scripting/ScriptEngine.bf | 2 +- GlitchyEngine/src/Scripting/ScriptGlue.bf | 33 +++- .../src/Scripting/ScriptInstanceSerializer.bf | 12 +- ScriptCore/ScriptGlue.cs | 8 +- ScriptCore/Serialization/EntitySerializer.cs | 185 +++++++++++------- 5 files changed, 168 insertions(+), 72 deletions(-) diff --git a/GlitchyEngine/src/Scripting/ScriptEngine.bf b/GlitchyEngine/src/Scripting/ScriptEngine.bf index dcf8398..823be96 100644 --- a/GlitchyEngine/src/Scripting/ScriptEngine.bf +++ b/GlitchyEngine/src/Scripting/ScriptEngine.bf @@ -857,7 +857,7 @@ static class ScriptEngine for (let (id, script) in _entityScriptInstances) { - SerializedObject object = new SerializedObject(allObjects, script.EntityId); + SerializedObject object = new SerializedObject(allObjects, script.ScriptClass.FullName, script.EntityId); object.Serialize(script); } } diff --git a/GlitchyEngine/src/Scripting/ScriptGlue.bf b/GlitchyEngine/src/Scripting/ScriptGlue.bf index a4ff512..27a1642 100644 --- a/GlitchyEngine/src/Scripting/ScriptGlue.bf +++ b/GlitchyEngine/src/Scripting/ScriptGlue.bf @@ -550,13 +550,17 @@ static class ScriptGlue } [RegisterCall("ScriptGlue::Serialization_CreateObject")] - static void Serialization_CreateObject(void* currentContext, out void* newContext, out UUID newId) + static void Serialization_CreateObject(void* currentContext, MonoString* typeName, out void* newContext, out UUID newId) { SerializedObject context = Internal.UnsafeCastToObject(currentContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); + + char8* rawTypeName = Mono.mono_string_to_utf8(typeName); + + SerializedObject newObject = new SerializedObject(context.AllObjects, StringView(rawTypeName)); - SerializedObject newObject = new SerializedObject(context.AllObjects); + Mono.mono_free(rawTypeName); newContext = Internal.UnsafeCastToPtr(newObject); newId = newObject.Id; @@ -575,6 +579,31 @@ static class ScriptGlue Mono.mono_free(name); } + + [RegisterCall("ScriptGlue::Serialization_GetObject")] + public static void Serialization_GetObject(void* internalContext, UUID id, out void* objectContext) + { + SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject; + + Log.EngineLogger.AssertDebug(context != null); + + objectContext = null; + + if (!context.AllObjects.TryGetValue(id, let foundObject)) + return; + + objectContext = Internal.UnsafeCastToPtr(foundObject); + } + + [RegisterCall("ScriptGlue::Serialization_GetObjectTypeName")] + public static void Serialization_GetObjectTypeName(void* internalContext, out MonoString* fullTypeName) + { + SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject; + + Log.EngineLogger.AssertDebug(context != null); + + fullTypeName = Mono.mono_string_new(ScriptEngine.[Friend]s_AppDomain, context.TypeName); + } #endregion diff --git a/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf b/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf index 613a90c..da244a6 100644 --- a/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf +++ b/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf @@ -70,16 +70,24 @@ public enum SerializationType : int32 class SerializedObject { + /// 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. + /// Todo: This sucks because we don't know all UUIDs beforehand and might accidentally assign the ID of an Entity to some class public UUID Id; + public String TypeName ~ delete:append _; + public Dictionary AllObjects; private List _ownedString = new List() ~ DeleteContainerAndItems!(_); public append Dictionary Fields = .(); - public this(Dictionary allObjects, UUID? id = null) + [AllowAppend] + public this(Dictionary allObjects, StringView? typeName, UUID? id = null) { + String typeNameCopy = append String(typeName.Value); + AllObjects = allObjects; if (id == null) @@ -97,6 +105,8 @@ class SerializedObject } AllObjects.Add(Id, this); + + TypeName = typeNameCopy; } public void AddField(StringView name, SerializationType primitiveType, MonoObject* value) diff --git a/ScriptCore/ScriptGlue.cs b/ScriptCore/ScriptGlue.cs index bdc4553..6c58a1e 100644 --- a/ScriptCore/ScriptGlue.cs +++ b/ScriptCore/ScriptGlue.cs @@ -143,10 +143,16 @@ internal static class ScriptGlue internal static extern void Serialization_SerializeField(IntPtr serializationContext, SerializationType type, string name, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void Serialization_CreateObject(IntPtr currentContext, out IntPtr context, out UUID id); + internal static extern void Serialization_CreateObject(IntPtr currentContext, string fullTypeName, out IntPtr context, out UUID id); [MethodImpl(MethodImplOptions.InternalCall)] public static extern unsafe void Serialization_DeserializeField(IntPtr internalContext, SerializationType expectedType, string fieldName, byte* value); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void Serialization_GetObject(IntPtr internalContext, UUID id, out IntPtr objectContext); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void Serialization_GetObjectTypeName(IntPtr internalContext, out string fullTypeName); #endregion } diff --git a/ScriptCore/Serialization/EntitySerializer.cs b/ScriptCore/Serialization/EntitySerializer.cs index 7b84aea..4b037a7 100644 --- a/ScriptCore/Serialization/EntitySerializer.cs +++ b/ScriptCore/Serialization/EntitySerializer.cs @@ -3,6 +3,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -70,7 +71,7 @@ public static class EntitySerializer if (SerializedClasses.TryGetValue(o, out context)) return (context, false); - ScriptGlue.Serialization_CreateObject(_internalContext, out IntPtr contextPtr, out UUID id); + ScriptGlue.Serialization_CreateObject(_internalContext, o.GetType().FullName, out IntPtr contextPtr, out UUID id); context = new SerializedObject(contextPtr, id, SerializedClasses); @@ -231,7 +232,7 @@ public static class EntitySerializer } else if (typeof(Component).IsAssignableFrom(fieldType)) { - AddField(fieldName, SerializationType.ComponentReference, ((Component)fieldValue)?.UUID ?? UUID.Zero); + AddField(fieldName, SerializationType.ComponentReference, ((Component)fieldValue)?.UUID ?? UUID.Zero); } else { @@ -264,29 +265,72 @@ public static class EntitySerializer private string _structScopeName; - public Dictionary SerializedClasses; + public Dictionary DeserializedClasses; - public DeserializationObject(IntPtr internalContext, UUID id, Dictionary serializedClasses) + private object _instance; + + public DeserializationObject(IntPtr internalContext, UUID id, Dictionary deserializedClasses) { _internalContext = internalContext; _id = id; - SerializedClasses = serializedClasses; + DeserializedClasses = deserializedClasses; } - - private (SerializedObject context, bool newContext) GetSerializedObject(object o) + + private Dictionary _fullNameToType = new(); + + private Type FindType(string fullName) { - SerializedObject context; + foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies().Reverse()) + { + Type type = assembly.GetType(fullName); - if (SerializedClasses.TryGetValue(o, out context)) - return (context, false); + if (type != null) + return type; + } + + return null; + } + + private Type GetTypeFromName(string fullName) + { + if (_fullNameToType.TryGetValue(fullName, out Type storedType)) + return storedType; + + Type type = FindType(fullName); + + _fullNameToType.Add(fullName, type); + + return type; + } + + private DeserializationObject GetDeserializedObject(UUID id) + { + DeserializationObject context; - ScriptGlue.Serialization_CreateObject(_internalContext, out IntPtr contextPtr, out UUID id); + if (DeserializedClasses.TryGetValue(id, out context)) + return context; + + ScriptGlue.Serialization_GetObject(_internalContext, id, out IntPtr contextPtr); + + context = new DeserializationObject(contextPtr, id, DeserializedClasses); + + DeserializedClasses.Add(id, context); + + ScriptGlue.Serialization_GetObjectTypeName(context._internalContext, out string fullTypeName); + + Type type = GetTypeFromName(fullTypeName); - context = new SerializedObject(contextPtr, id, SerializedClasses); + if (type == null) + return context; - SerializedClasses.Add(o, context); + context._instance = Activator.CreateInstance(type); - return (context, true); + if (context._instance == null) + return context; + + context.DeserializeFields(context._instance); + + return context; } private void PushScope(string name) @@ -377,6 +421,8 @@ public static class EntitySerializer public void Deserialize(Entity entity) { + _instance = entity; + DeserializeFields(entity); } @@ -401,25 +447,15 @@ public static class EntitySerializer { Type fieldType = field.FieldType; + object newFieldValue = null; + if (fieldType.IsPrimitive) { - object value = DeserializePrimitive(field.Name, fieldType); - - if (value != null) - { - field.SetValue(targetInstance, value); - return true; - } + newFieldValue = DeserializePrimitive(field.Name, fieldType); } else if (fieldType == typeof(string)) { - object value = GetFieldValue(field.Name, SerializationType.String); - - if (value != null) - { - field.SetValue(targetInstance, value); - return true; - } + newFieldValue = GetFieldValue(field.Name, SerializationType.String); } else if (fieldType.IsEnum) { @@ -450,24 +486,23 @@ public static class EntitySerializer { object structValue = field.GetValue(targetInstance); - bool changed = DeserializeStruct(field.Name, structValue); - - if (changed) - { - field.SetValue(targetInstance, structValue); - return true; - } + newFieldValue = DeserializeStruct(field.Name, structValue); } else if (fieldType.IsClass) { - //DeserializeClass(fieldName, fieldValue, fieldType); - Log.Error("Class is not yet implemented."); + newFieldValue = DeserializeClass(field.Name, fieldType); } else { Log.Error($"Encountered unhandled type \"{fieldType}\" while serializing."); } + if (newFieldValue != null) + { + field.SetValue(targetInstance, newFieldValue); + return true; + } + return false; } @@ -511,50 +546,66 @@ public static class EntitySerializer return GetFieldValue(fieldName, expectedType); } - private void SerializeEnum(string fieldName, object fieldValue, Type fieldType) + private void DeserializeEnum(string fieldName, object fieldValue, Type fieldType) { //AddField(fieldName, SerializationType.Enum, fieldValue.ToString()); } - private bool DeserializeStruct(string fieldName, object targetInstance) + private object DeserializeStruct(string fieldName, object targetInstance) { PushScope(fieldName); bool changed = DeserializeFields(targetInstance); - + PopScope(); - return changed; + return changed ? targetInstance : null; } - private void SerializeClass(string fieldName, object fieldValue, Type fieldType) + private object DeserializeClass(string fieldName, Type fieldType) { - //if (typeof(Entity).IsAssignableFrom(fieldType)) - //{ - // AddField(fieldName, SerializationType.EntityReference, ((Entity)fieldValue)?.UUID ?? UUID.Zero); - //} - //else if (typeof(Component).IsAssignableFrom(fieldType)) - //{ - // AddField(fieldName, SerializationType.ComponentReference, ((Component)fieldValue)?.UUID ?? UUID.Zero); - //} - //else - //{ - // if (fieldValue == null) - // { - // AddField(fieldName, SerializationType.ObjectReference, UUID.Zero); - // } - // else - // { - // var (context, newContext) = GetSerializedObject(fieldValue); + if (typeof(Entity).IsAssignableFrom(fieldType)) + { + UUID id = (UUID)GetFieldValue(fieldName, SerializationType.EntityReference); + + if (id == UUID.Zero) + return null; - // if (newContext) - // { - // context.SerializeFields(fieldValue); - // } + Entity reference = new Entity(id); - // AddField(fieldName, SerializationType.ObjectReference, context._id); - // } - //} + if (fieldType.IsSubclassOf(typeof(Entity))) + return reference.As(); + + return reference; + } + else if (typeof(Component).IsAssignableFrom(fieldType)) + { + UUID id = (UUID)GetFieldValue(fieldName, SerializationType.ComponentReference); + + if (id == UUID.Zero) + return null; + + // TODO: Get class + + //Entity reference = new Entity(id); + + //if (fieldType.IsSubclassOf(typeof(Entity))) + // return reference.As(); + + //return reference; + return null; + } + else + { + UUID id = (UUID)GetFieldValue(fieldName, SerializationType.ObjectReference); + + if (id == UUID.Zero) + return null; + + DeserializationObject deserializedObject = GetDeserializedObject(id); + + return deserializedObject._instance; + } } } @@ -567,7 +618,7 @@ public static class EntitySerializer public static void Deserialize(Entity entity, IntPtr internalContext) { - DeserializationObject obj = new DeserializationObject(internalContext, UUID.Zero, new Dictionary()); + DeserializationObject obj = new DeserializationObject(internalContext, UUID.Zero, new Dictionary()); obj.Deserialize(entity); }