From a3de5ddc9c07bd8d3580c956c4c7a94c78c57d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sat, 13 Apr 2024 00:50:09 +0200 Subject: [PATCH] Serialization: Support sharing object references --- .../src/Scripting/EntitySerializerWrapper.bf | 116 ++++++++++++++---- .../src/Scripting/ScriptInstanceSerializer.bf | 10 ++ GlitchyEngine/src/World/Scene.bf | 4 +- .../Serialization/DeserializationObject.cs | 5 + ScriptCore/Serialization/EntitySerializer.cs | 43 +++++-- 5 files changed, 142 insertions(+), 36 deletions(-) diff --git a/GlitchyEngine/src/Scripting/EntitySerializerWrapper.bf b/GlitchyEngine/src/Scripting/EntitySerializerWrapper.bf index e158858..5478886 100644 --- a/GlitchyEngine/src/Scripting/EntitySerializerWrapper.bf +++ b/GlitchyEngine/src/Scripting/EntitySerializerWrapper.bf @@ -9,10 +9,18 @@ using internal GlitchyEngine.Scripting; class EntitySerializerWrapper : ScriptClass { - function MonoObject* SerializeMethod(MonoObject* entity, void* contextPtr, MonoException** exception); - function MonoObject* DeserializeMethod(MonoObject* entity, void* contextPtr, MonoException** exception); - function MonoObject* SerializeStaticMethod(MonoReflectionType* type, void* contextPtr, MonoException** exception); - function MonoObject* DeserializeStaticMethod(MonoReflectionType* type, void* contextPtr, MonoException** exception); + function void CreateSerializationContextMethod(void* serializerPtr, MonoException** exception); + function void ClearSerializationContextMethod(void* serializerPtr, MonoException** exception); + function void DestroySerializationContextMethod(void* serializerPtr, MonoException** exception); + + function void SerializeMethod(MonoObject* entity, void* serializedObjPtr, void* serializerPtr, MonoException** exception); + function void DeserializeMethod(MonoObject* entity, void* serializedObjPtr, void* serializerPtr, MonoException** exception); + function void SerializeStaticMethod(MonoReflectionType* type, void* serializedObjPtr, void* serializerPtr, MonoException** exception); + function void DeserializeStaticMethod(MonoReflectionType* type, void* serializedObjPtr, void* serializerPtr, MonoException** exception); + + private CreateSerializationContextMethod _createSerializationContextMethod; + private ClearSerializationContextMethod _clearSerializationContextMethod; + private DestroySerializationContextMethod _destroySerializationContextMethod; private SerializeMethod _serializeMethod; private DeserializeMethod _deserializeMethod; @@ -22,82 +30,138 @@ class EntitySerializerWrapper : ScriptClass [AllowAppend] public this(StringView classNamespace, StringView className, MonoImage* image) : base(classNamespace, className, image) { - _serializeMethod = (SerializeMethod)GetMethodThunk("Serialize", 2); + _serializeMethod = (SerializeMethod)GetMethodThunk("Serialize", 3); if (_serializeMethod == null) { Log.EngineLogger.Error("EntitySerializer has no \"Serialize\" method."); } - _deserializeMethod = (DeserializeMethod)GetMethodThunk("Deserialize", 2); + _deserializeMethod = (DeserializeMethod)GetMethodThunk("Deserialize", 3); if (_serializeMethod == null) { Log.EngineLogger.Error("EntitySerializer has no \"Deserialize\" method."); } - _serializeStaticMethod = (SerializeStaticMethod)GetMethodThunk("SerializeStaticFields", 2); + _serializeStaticMethod = (SerializeStaticMethod)GetMethodThunk("SerializeStaticFields", 3); if (_serializeStaticMethod == null) { Log.EngineLogger.Error("EntitySerializer has no \"SerializeStaticStatic\" method."); } - _deserializeStaticMethod = (SerializeStaticMethod)GetMethodThunk("DeserializeStaticFields", 2); + _deserializeStaticMethod = (SerializeStaticMethod)GetMethodThunk("DeserializeStaticFields", 3); if (_deserializeStaticMethod == null) { Log.EngineLogger.Error("EntitySerializer has no \"DeserializeStaticStatic\" method."); } + + _createSerializationContextMethod = (CreateSerializationContextMethod)GetMethodThunk("CreateSerializationContext", 1); + + if (_createSerializationContextMethod == null) + { + Log.EngineLogger.Error("EntitySerializer has no \"CreateSerializationContext\" method."); + } + + _clearSerializationContextMethod = (ClearSerializationContextMethod)GetMethodThunk("ClearSerializationContext", 1); + + if (_clearSerializationContextMethod == null) + { + Log.EngineLogger.Error("EntitySerializer has no \"ClearSerializationContext\" method."); + } + + _destroySerializationContextMethod = (DestroySerializationContextMethod)GetMethodThunk("DestroySerializationContext", 1); + + if (_destroySerializationContextMethod == null) + { + Log.EngineLogger.Error("EntitySerializer has no \"DestroySerializationContext\" method."); + } } - public void Serialize(ScriptInstance instance, SerializedObject context) + public void CreateSerializationContext(ScriptInstanceSerializer serializer) { - void* thisPtr = Internal.UnsafeCastToPtr(context); + void* serializerPtr = Internal.UnsafeCastToPtr(serializer); MonoException* exception = null; - _serializeMethod(instance.[Friend]_instance, thisPtr, &exception); + _createSerializationContextMethod(serializerPtr, &exception); if (exception != null) - ScriptEngine.[Friend]HandleMonoException(exception, instance); + ScriptEngine.[Friend]HandleMonoException(exception, null); } - public void SerializeStatic(ScriptClass @class, SerializedObject context) + public void ClearSerializationContext(ScriptInstanceSerializer serializer) { - void* thisPtr = Internal.UnsafeCastToPtr(context); + void* serializerPtr = Internal.UnsafeCastToPtr(serializer); MonoException* exception = null; - - MonoType* type = Mono.mono_class_get_type(@class.[Friend]_monoClass); - MonoReflectionType* reflectionType = Mono.mono_type_get_object(ScriptEngine.[Friend]s_AppDomain, type); - - _serializeStaticMethod(reflectionType, thisPtr, &exception); + _clearSerializationContextMethod(serializerPtr, &exception); if (exception != null) ScriptEngine.[Friend]HandleMonoException(exception, null); } - public void Deserialize(ScriptInstance instance, SerializedObject context) + public void DestroySerializationContext(ScriptInstanceSerializer serializer) { - void* thisPtr = Internal.UnsafeCastToPtr(context); + void* serializerPtr = Internal.UnsafeCastToPtr(serializer); MonoException* exception = null; - _deserializeMethod(instance.[Friend]_instance, thisPtr, &exception); + _destroySerializationContextMethod(serializerPtr, &exception); + + if (exception != null) + ScriptEngine.[Friend]HandleMonoException(exception, null); + } + + public void Serialize(ScriptInstance instance, SerializedObject serializedObject) + { + void* serializedObjectPtr = Internal.UnsafeCastToPtr(serializedObject); + void* serializerPtr = Internal.UnsafeCastToPtr(serializedObject.Serializer); + + MonoException* exception = null; + _serializeMethod(instance.[Friend]_instance, serializedObjectPtr, serializerPtr, &exception); if (exception != null) ScriptEngine.[Friend]HandleMonoException(exception, instance); } - public void DeserializeStatic(ScriptClass @class, SerializedObject context) + public void SerializeStatic(ScriptClass @class, SerializedObject serializedObject) { - void* thisPtr = Internal.UnsafeCastToPtr(context); + void* serializedObjectPtr = Internal.UnsafeCastToPtr(serializedObject); + void* serializerPtr = Internal.UnsafeCastToPtr(serializedObject.Serializer); + + MonoType* type = Mono.mono_class_get_type(@class.[Friend]_monoClass); + MonoReflectionType* reflectionType = Mono.mono_type_get_object(ScriptEngine.[Friend]s_AppDomain, type); + + MonoException* exception = null; + _serializeStaticMethod(reflectionType, serializedObjectPtr, serializerPtr, &exception); + + if (exception != null) + ScriptEngine.[Friend]HandleMonoException(exception, null); + } + + public void Deserialize(ScriptInstance instance, SerializedObject serializedObject) + { + void* serializedObjectPtr = Internal.UnsafeCastToPtr(serializedObject); + void* serializerPtr = Internal.UnsafeCastToPtr(serializedObject.Serializer); MonoException* exception = null; + _deserializeMethod(instance.[Friend]_instance, serializedObjectPtr, serializerPtr, &exception); + + if (exception != null) + ScriptEngine.[Friend]HandleMonoException(exception, instance); + } + + public void DeserializeStatic(ScriptClass @class, SerializedObject serializedObject) + { + void* serializedObjectPtr = Internal.UnsafeCastToPtr(serializedObject); + void* serializerPtr = Internal.UnsafeCastToPtr(serializedObject.Serializer); MonoType* type = Mono.mono_class_get_type(@class.[Friend]_monoClass); MonoReflectionType* reflectionType = Mono.mono_type_get_object(ScriptEngine.[Friend]s_AppDomain, type); - - _deserializeStaticMethod(reflectionType, thisPtr, &exception); + + MonoException* exception = null; + _deserializeStaticMethod(reflectionType, serializedObjectPtr, serializerPtr, &exception); if (exception != null) ScriptEngine.[Friend]HandleMonoException(exception, null); diff --git a/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf b/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf index 2aee668..bc9e58b 100644 --- a/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf +++ b/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf @@ -24,6 +24,12 @@ public class ScriptInstanceSerializer public void Clear() { ClearDictionaryAndDeleteValues!(_serializedData); + ScriptEngine.Classes.EntitySerializer.DestroySerializationContext(this); + } + + public void Init() + { + ScriptEngine.Classes.EntitySerializer.CreateSerializationContext(this); } /// Serializes all script instances that are currently managed by the ScriptEngine. @@ -31,6 +37,8 @@ public class ScriptInstanceSerializer { Debug.Profiler.ProfileFunction!(); + Init(); + for (let (id, scriptInstance) in ScriptEngine._entityScriptInstances) { SerializeScriptInstance(scriptInstance); @@ -61,6 +69,8 @@ public class ScriptInstanceSerializer { Debug.Profiler.ProfileFunction!(); + Init(); + for (let (id, script) in ScriptEngine._entityScriptInstances) { DeserializeScriptInstance(id, script); diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index 3fe7d17..4e8afd6 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -123,6 +123,7 @@ namespace GlitchyEngine.World }); _font = new Font(@"C:\Windows\Fonts\arial.ttf", 24); + //_font = new Font(@"C:\Windows\Fonts\Cascadia Code.ttf", 24); } public ~this() @@ -958,9 +959,8 @@ namespace GlitchyEngine.World */ public Entity CreateInstance(Entity entity) { - //Dictionary serializedData = scope .(); ScriptInstanceSerializer scriptSerializer = scope .(); - //defer { ClearDictionaryAndDeleteValues!(serializedData); } + scriptSerializer.Init(); List newEntities = scope .(); Dictionary sourceToTargetEntity = scope .(); diff --git a/ScriptCore/Serialization/DeserializationObject.cs b/ScriptCore/Serialization/DeserializationObject.cs index 78c7a1a..cf71344 100644 --- a/ScriptCore/Serialization/DeserializationObject.cs +++ b/ScriptCore/Serialization/DeserializationObject.cs @@ -603,6 +603,11 @@ public class DeserializationObject if (type == null) return NoValueDeserialized; + if (deserializedObject._instance != null) + { + return deserializedObject._instance; + } + deserializedObject._instance = ActivatorExtension.CreateInstanceSafe(type); if (deserializedObject._instance == null) diff --git a/ScriptCore/Serialization/EntitySerializer.cs b/ScriptCore/Serialization/EntitySerializer.cs index 29ad427..df98025 100644 --- a/ScriptCore/Serialization/EntitySerializer.cs +++ b/ScriptCore/Serialization/EntitySerializer.cs @@ -9,27 +9,54 @@ namespace GlitchyEngine.Serialization; internal static class EntitySerializer { - internal static void Serialize(Entity entity, IntPtr internalContext) + private static Dictionary> SerializationObjects = new(); + private static Dictionary> DeserializationObjects = new(); + + internal static void CreateSerializationContext(IntPtr engineSerializer) { - SerializedObject obj = new SerializedObject(internalContext, false, UUID.Zero, new Dictionary()); + if (SerializationObjects.ContainsKey(engineSerializer)) + { + ClearSerializationContext(engineSerializer); + return; + } + + SerializationObjects.Add(engineSerializer, new Dictionary()); + DeserializationObjects.Add(engineSerializer, new Dictionary()); + } + + internal static void ClearSerializationContext(IntPtr engineSerializer) + { + SerializationObjects[engineSerializer].Clear(); + DeserializationObjects[engineSerializer].Clear(); + } + + internal static void DestroySerializationContext(IntPtr engineSerializer) + { + SerializationObjects.Remove(engineSerializer); + DeserializationObjects.Remove(engineSerializer); + } + + internal static void Serialize(Entity entity, IntPtr engineObject, IntPtr engineSerializer) + { + SerializedObject obj = new SerializedObject(engineObject, false, UUID.Zero, SerializationObjects[engineSerializer]); obj.Serialize(entity); } - internal static void Deserialize(Entity entity, IntPtr internalContext) + internal static void Deserialize(Entity entity, IntPtr engineObject, IntPtr engineSerializer) { - DeserializationObject obj = new DeserializationObject(internalContext, false, UUID.Zero, new Dictionary()); + DeserializationObject obj = new DeserializationObject(engineObject, false, UUID.Zero, DeserializationObjects[engineSerializer]); obj.Deserialize(entity); } - internal static void SerializeStaticFields(Type type, IntPtr internalContext) + internal static void SerializeStaticFields(Type type, IntPtr engineObject, IntPtr engineSerializer) { - SerializedObject obj = new SerializedObject(internalContext, true, UUID.Zero, new Dictionary()); + SerializedObject obj = new SerializedObject(engineObject, true, UUID.Zero, SerializationObjects[engineSerializer]); obj.Serialize(type); } - internal static void DeserializeStaticFields(Type type, IntPtr internalContext) + internal static void DeserializeStaticFields(Type type, IntPtr engineObject, IntPtr engineSerializer) { - DeserializationObject obj = new DeserializationObject(internalContext, true, UUID.Zero, new Dictionary()); + DeserializationObject obj = new DeserializationObject(engineObject, true, UUID.Zero, DeserializationObjects[engineSerializer]); obj.Deserialize(type); }