Serialize/Deserialize simple classes

This commit is contained in:
Simon Lübeß
2023-12-25 20:08:48 +01:00
parent 7bd69c162f
commit acefbb58bd
5 changed files with 168 additions and 72 deletions
+1 -1
View File
@@ -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);
}
}
+31 -2
View File
@@ -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);
SerializedObject newObject = new SerializedObject(context.AllObjects);
char8* rawTypeName = Mono.mono_string_to_utf8(typeName);
SerializedObject newObject = new SerializedObject(context.AllObjects, StringView(rawTypeName));
Mono.mono_free(rawTypeName);
newContext = Internal.UnsafeCastToPtr(newObject);
newId = newObject.Id;
@@ -576,6 +580,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
private static void RegisterCall<T>(String name, T method) where T : var
@@ -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<UUID, SerializedObject> AllObjects;
private List<String> _ownedString = new List<String>() ~ DeleteContainerAndItems!(_);
public append Dictionary<StringView, (SerializationType PrimitiveType, uint8[16] Data)> Fields = .();
public this(Dictionary<UUID, SerializedObject> allObjects, UUID? id = null)
[AllowAppend]
public this(Dictionary<UUID, SerializedObject> 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)
+7 -1
View File
@@ -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
}
+116 -65
View File
@@ -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<object, SerializedObject> SerializedClasses;
public Dictionary<UUID, DeserializationObject> DeserializedClasses;
public DeserializationObject(IntPtr internalContext, UUID id, Dictionary<object, SerializedObject> serializedClasses)
private object _instance;
public DeserializationObject(IntPtr internalContext, UUID id, Dictionary<UUID, DeserializationObject> deserializedClasses)
{
_internalContext = internalContext;
_id = id;
SerializedClasses = serializedClasses;
DeserializedClasses = deserializedClasses;
}
private (SerializedObject context, bool newContext) GetSerializedObject(object o)
private Dictionary<string, Type> _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;
}
ScriptGlue.Serialization_CreateObject(_internalContext, out IntPtr contextPtr, out UUID id);
return null;
}
context = new SerializedObject(contextPtr, id, SerializedClasses);
private Type GetTypeFromName(string fullName)
{
if (_fullNameToType.TryGetValue(fullName, out Type storedType))
return storedType;
SerializedClasses.Add(o, context);
Type type = FindType(fullName);
return (context, true);
_fullNameToType.Add(fullName, type);
return type;
}
private DeserializationObject GetDeserializedObject(UUID id)
{
DeserializationObject context;
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);
if (type == null)
return context;
context._instance = Activator.CreateInstance(type);
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,12 +546,12 @@ 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);
@@ -524,37 +559,53 @@ public static class EntitySerializer
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 (newContext)
// {
// context.SerializeFields(fieldValue);
// }
if (id == UUID.Zero)
return null;
// AddField(fieldName, SerializationType.ObjectReference, context._id);
// }
//}
Entity reference = new Entity(id);
if (fieldType.IsSubclassOf(typeof(Entity)))
return reference.As<Entity>();
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<Entity>();
//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<object, SerializedObject>());
DeserializationObject obj = new DeserializationObject(internalContext, UUID.Zero, new Dictionary<UUID, DeserializationObject>());
obj.Deserialize(entity);
}