diff --git a/GlitchyEngine/src/Scripting/ScriptGlue.bf b/GlitchyEngine/src/Scripting/ScriptGlue.bf index cac1788..f974daa 100644 --- a/GlitchyEngine/src/Scripting/ScriptGlue.bf +++ b/GlitchyEngine/src/Scripting/ScriptGlue.bf @@ -196,7 +196,9 @@ static class ScriptGlue for (uint i < length) { MonoReflectionType* reflectionType = Mono.mono_array_get(componentTypes, i); - Entity_AddComponent(entityId, reflectionType); + + if (reflectionType != null) + Entity_AddComponent(entityId, reflectionType); } } @@ -286,6 +288,8 @@ static class ScriptGlue return scriptComponent.Instance.MonoInstance; } + Log.EngineLogger.AssertDebug(false, "Failed to set script."); + return null; } @@ -528,27 +532,6 @@ static class ScriptGlue id = UUID.Create(); } - [RegisterCall("ScriptGlue::Print_Decimal")] - static void Print_Decimal(MonoDecimal monoDecimal) - { - Log.ClientLogger.Info(scope $"{monoDecimal}"); - } - - [RegisterCall("ScriptGlue::Get_Decimal")] - static void Get_Decimal(MonoString* string, out MonoDecimal monoDecimal) - { - char8* str = Mono.mono_string_to_utf8(string); - - Result decimalo = MonoDecimal.Parse(StringView(str)); - - if (!(decimalo case .Ok(out monoDecimal))) - { - monoDecimal = default; - } - - Mono.mono_free(str); - } - #region Application [RegisterCall("ScriptGlue::Application_IsEditor")] diff --git a/ScriptCore/Editor/DictionaryEditor.cs b/ScriptCore/Editor/DictionaryEditor.cs index 61c3934..7c2e077 100644 --- a/ScriptCore/Editor/DictionaryEditor.cs +++ b/ScriptCore/Editor/DictionaryEditor.cs @@ -11,9 +11,9 @@ namespace GlitchyEngine.Editor; [CustomEditor(typeof(Dictionary<,>))] public class DictionaryEditor { - private static object? dictionaryForNewValue = null; - private static DictionaryEntry newDictionaryValue = new(); - private static object? keyLastCreated = null; + private static object? _dictionaryForNewValue = null; + private static DictionaryEntry _newDictionaryValue = new(); + private static object? _keyLastCreated = null; public static object? ShowEditor(object? reference, Type fieldType, string fieldName) { @@ -46,9 +46,9 @@ public class DictionaryEditor Debug.Assert(dictionary != null); } - dictionaryForNewValue = dictionary; + _dictionaryForNewValue = dictionary; - newDictionaryValue = new DictionaryEntry(); + _newDictionaryValue = new DictionaryEntry(); } ImGuiExtension.AttachTooltip("Add a new Entry to the dictionary."); @@ -67,11 +67,11 @@ public class DictionaryEditor id++; ImGui.PushID(id); - if (entry.Key == keyLastCreated) + if (entry.Key == _keyLastCreated) { // The current key is the one that was last created. Open the tree node. ImGui.SetNextItemOpen(true); - keyLastCreated = null; + _keyLastCreated = null; } bool isEntryOpen = ImGui.TreeNode(""); @@ -110,7 +110,7 @@ public class DictionaryEditor } - if (ReferenceEquals(dictionaryForNewValue, dictionary)) + if (ReferenceEquals(_dictionaryForNewValue, dictionary)) { ImGui.PushID("NewEntry"); @@ -121,29 +121,29 @@ public class DictionaryEditor if (ImGui.SmallButton("-")) { - dictionaryForNewValue = null; - newDictionaryValue = new(); + _dictionaryForNewValue = null; + _newDictionaryValue = new(); } ImGuiExtension.AttachTooltip("Remove the Entry from the dictionary"); if (isEntryOpen) { - object newKey = EntityEditor.ShowFieldEditor(newDictionaryValue.Key, keyType, "Key"); + object newKey = EntityEditor.ShowFieldEditor(_newDictionaryValue.Key, keyType, "Key"); if (newKey != EntityEditor.DidNotChange) { - newEntries.Add(new DictionaryEntry(newKey, newDictionaryValue.Value)); - dictionaryForNewValue = null; - newDictionaryValue = new(); - keyLastCreated = newKey; + newEntries.Add(new DictionaryEntry(newKey, _newDictionaryValue.Value)); + _dictionaryForNewValue = null; + _newDictionaryValue = new(); + _keyLastCreated = newKey; } - object newValue = EntityEditor.ShowFieldEditor(newDictionaryValue.Value, newDictionaryValue.Value?.GetType() ?? valueType, "Value"); + object newValue = EntityEditor.ShowFieldEditor(_newDictionaryValue.Value, _newDictionaryValue.Value?.GetType() ?? valueType, "Value"); if (newValue != EntityEditor.DidNotChange) { - newDictionaryValue.Value = newValue; + _newDictionaryValue.Value = newValue; } ImGui.TreePop(); diff --git a/ScriptCore/Entity.cs b/ScriptCore/Entity.cs index a109fc3..d1c85b9 100644 --- a/ScriptCore/Entity.cs +++ b/ScriptCore/Entity.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using GlitchyEngine.Core; using GlitchyEngine.Extensions; +using GlitchyEngine.Physics; namespace GlitchyEngine; @@ -13,7 +14,7 @@ namespace GlitchyEngine; public class Entity : EngineObject { /// - /// Gets or sets the name of the entity. + /// Gets or sets the name of the . /// public string Name { @@ -22,8 +23,8 @@ public class Entity : EngineObject } /// - /// Only to be called by the engine. Don't call this constructor yourself, it will not result in a valid entity. - /// If you want to create a new entity use or + /// Only to be called by the engine. Don't call this constructor yourself, it will not result in a valid . + /// If you want to create a new use or /// protected Entity() { @@ -35,34 +36,23 @@ public class Entity : EngineObject /// /// Creates a new Entity. /// - /// The name of the new entity. - public Entity(string name = null) + /// The name of the new . + public Entity(string? name = null) { Create(name, null); } /// - /// Creates a new Entity with the specified components attached to it. + /// Creates a new with the specified components attached to it. /// - /// The name of the new entity. - /// The components that the entity shall have. - public Entity(string name, params Type[] components) + /// The name of the new . + /// The components that the will be created with. + public Entity(string? name, params Type[] components) { Create(name, components); } - protected void PrintDecimal(decimal value) - { - ScriptGlue.Print_Decimal(value); - } - - protected decimal GetDecimal(string value) - { - ScriptGlue.Get_Decimal(value, out var dec); - return dec; - } - - private void Create(string name, Type[] components) + private void Create(string? name, Type[]? components) { ScriptGlue.Entity_Create(this, name, components, out _uuid); @@ -73,20 +63,22 @@ public class Entity : EngineObject } /// - /// Creates an instance that represents the Entity with the given id. + /// Creates an instance that represents the with the given id. /// - /// The ID of the entity that belongs to this instance. + /// The ID of the that belongs to this instance. internal Entity(UUID uuid) : base(uuid) { } + + #region Components /// - /// Returns if a component of the given type is attached to this entity. + /// Returns if a component of the given type is attached to this . /// /// The type of the component. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent() => HasComponent(typeof(T)); /// - /// Returns if a component of the given type is attached to this entity. + /// Returns if a component of the given type is attached to this . /// /// The type of the component. [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -96,8 +88,8 @@ public class Entity : EngineObject /// Gets the component with the specified type. /// /// The type of the component to get. - /// The component or null, if the entity doesn't have a component of the specified type. - public T GetComponent() where T : Component, new() + /// The component or null, if the doesn't have a component of the specified type. + public T? GetComponent() where T : Component, new() { if (HasComponent()) { @@ -114,32 +106,28 @@ public class Entity : EngineObject /// Gets the component with the given type. /// /// The type of the component to get. - /// The component or null, if the entity doesn't have a component of the given type. - /// For performance reasons it is recommended to use if possible. - public Component GetComponent(Type componentType) + /// The component; or , if the doesn't have a component of the specified type or if doesn't inherit from . + /// For performance reasons it is recommended to use if possible as it might be slightly faster. + public Component? GetComponent(Type componentType) { - // TODO: probably throw! - if (!componentType.IsSubclassOf(typeof(Component))) return null; + if (!componentType.IsSubclassOf(typeof(Component))) + { + Log.Error($"{nameof(GetComponent)}: Invalid component type \"{componentType}\". Must be a subclass of \"{nameof(Component)}\"."); + return null; + } if (HasComponent(componentType)) { - Component component = Activator.CreateInstance(componentType, true) as Component; - - if (component != null) - component._uuid = _uuid; - - return component; + return ActivatorExtension.CreateComponent(componentType, _uuid); } return null; } - #region Add Components - /// - /// Adds the component with the specified type. + /// Attaches a component of the specified type to the . /// - /// The type of the component to add. + /// The type of the component to attach. /// The component. public T AddComponent() where T : Component, new() { @@ -152,60 +140,63 @@ public class Entity : EngineObject } /// - /// Adds the component with the given type. + /// Attaches a component of the specified type to the . /// - /// The type of the component to add. - /// The component or null, if the given type is not a valid component type. - /// For performance reasons it is recommended to use if possible. - public Component AddComponent(Type componentType) + /// The type of the component to attach. + /// The component that was attached to the ; or if doesn't inherit from . + /// For performance reasons it is recommended to use if possible as it might be slightly faster. + public Component? AddComponent(Type componentType) { if (!componentType.IsSubclassOf(typeof(Component))) { - throw new ArgumentException($"Invalid component type \"{componentType}\". Must be a subclass of \"Component\".", nameof(componentType)); + Log.Error($"{nameof(AddComponent)}: Invalid component type \"{componentType}\". Must be a subclass of \"{nameof(Component)}\"."); + return null; } ScriptGlue.Entity_AddComponent(_uuid, componentType); - - Component component = Activator.CreateInstance(componentType) as Component; - if (component != null) - { - component._uuid = _uuid; - } - - return component; + + return ActivatorExtension.CreateComponent(componentType, _uuid); } /// - /// Adds components with the specified types to the entity. + /// Attaches components with the specified types to the . /// - /// The types of the components to add. - /// An array containing the components. - public Component[] AddComponents(params Type[] componentTypes) + /// The types of the components to attach. + /// An array containing the components that were attached. A component is , if it's type doesn't inherit from . + public Component?[] AddComponents(params Type[] componentTypes) { + // Note: The elements in componentTypes are non-nullable externally, but internally we rely on nullability. + foreach ((Type componentType, int index) in componentTypes.WithIndex()) { if (!componentType.IsSubclassOf(typeof(Component))) { - throw new ArgumentException($"Invalid component type \"{componentType}\" at index {index}. Must be a subclass of \"Component\".", nameof(componentTypes)); + Log.Error($"{nameof(AddComponents)}: Invalid component type \"{componentType}\" at index {index}. Must be a subclass of \"{nameof(Component)}\"."); + + // Setting null here is fine, the engine can handle it! + componentTypes[index] = null!; } } - + ScriptGlue.Entity_AddComponents(_uuid, componentTypes); - Component[] components = new Component[componentTypes.Length]; + Component?[] components = new Component[componentTypes.Length]; foreach ((Type componentType, int index) in componentTypes.WithIndex()) { - components[index] = Activator.CreateInstance(componentType) as Component; - components[index]._uuid = _uuid; + // componentType is null, if it's type is invalid! + if (componentType == null!) + continue; + + components[index] = ActivatorExtension.CreateComponent(componentType, _uuid); } return components; } /// - /// Adds the specified components to the entity. + /// Attaches the specified components to the . /// /// A tuple containing the added components. public (T1, T2) AddComponents() @@ -218,7 +209,7 @@ public class Entity : EngineObject } /// - /// Adds the specified components to the entity. + /// Attaches the specified components to the . /// /// A tuple containing the added components. public (T1, T2, T3) AddComponents() @@ -231,10 +222,8 @@ public class Entity : EngineObject return (new T1 { _uuid = _uuid }, new T2 { _uuid = _uuid }, new T3 { _uuid = _uuid }); } - #endregion Add Components - /// - /// Removes the component with the specified component type. + /// Removes the component with the specified component type from the . /// /// The type of the component to remove. public void RemoveComponent() where T : Component, new() @@ -243,32 +232,35 @@ public class Entity : EngineObject } /// - /// Removes the component with the given component type. + /// Removes the component with the given component type from the . /// /// The type of the component to remove. - /// For performance reasons it is recommended to use if possible. public void RemoveComponent(Type componentType) { if (!componentType.IsSubclassOf(typeof(Component))) { - throw new ArgumentException($"Invalid component type \"{componentType}\". Must be a subclass of \"Component\".", nameof(componentType)); + Log.Error($"{nameof(RemoveComponent)}: Invalid component type \"{componentType}\". Must be a subclass of \"{nameof(Component)}\"."); + return; } ScriptGlue.Entity_RemoveComponent(_uuid, componentType); } + #endregion Components + /// - /// Sets the script of the entity to the specified type. + /// Sets the script of the to the specified type. /// If necessary removes the existing script. /// /// The type of the script. - public T SetScript() where T : Entity + /// A reference to the new script or , if the operation failed. + public T? SetScript() where T : Entity { return ScriptGlue.Entity_SetScript(_uuid, typeof(T)) as T; } /// - /// Removes the script from the entity. + /// Removes the script from the . /// public void RemoveScript() { @@ -278,14 +270,14 @@ public class Entity : EngineObject /// /// Gets the of this . /// - public Transform Transform => GetComponent(); + public Transform Transform => GetComponent()!; // Transform always exists! /// - /// Returns the first entity with the given name. + /// Returns the first with the given name. /// - /// The name of the entity. - /// The first entity with the given name, or null. - public static Entity FindEntityWithName(string name) + /// The name of the . + /// The first with the given name, or if no such entity exists. + public static Entity? FindEntityWithName(string name) { ScriptGlue.Entity_FindEntityWithName(name, out UUID entityId); @@ -296,59 +288,82 @@ public class Entity : EngineObject } /// - /// Returns true if the entity has a script component of the given type; false if the entity either has no script or the script is not of the specified type. + /// Returns whether or not the has a script of the specified type. /// /// The type of the script. + /// if the has a script component of the given type; or if the either has no script or the script is not of the specified type. public bool Is() where T : Entity { - ScriptGlue.Entity_GetScriptInstance(_uuid, out object scriptInstance); + ScriptGlue.Entity_GetScriptInstance(_uuid, out object? scriptInstance); return scriptInstance is T; } /// - /// Returns the script of the given type, or null, if the entity has no script of the given type. + /// Returns whether or not the has a script of the specified type. + /// + /// The type of the script. + /// if the has a script component of the given type; or if the either has no script or the script is not of the specified type. + public bool Is(Type type) + { + if (!type.IsSubclassOf(typeof(Entity))) + { + Log.Warning($"{nameof(Is)}: Invalid script type \"{type}\". Must be a subclass of \"{nameof(Entity)}\"."); + return false; + } + + ScriptGlue.Entity_GetScriptInstance(_uuid, out object? scriptInstance); + + return type.IsInstanceOfType(scriptInstance); + } + + /// + /// Returns the script instance of the given type that is attached to the . /// /// The type of the script. - /// The script instance or null. - public T As() where T : Entity + /// The script instance of the given type; or if the has no script of the given type. + public T? As() where T : Entity { - ScriptGlue.Entity_GetScriptInstance(_uuid, out object scriptInstance); + ScriptGlue.Entity_GetScriptInstance(_uuid, out object? scriptInstance); return scriptInstance as T; } /// - /// Returns the script of the given type, or null, if the entity has no script of the given type. + /// Returns the script instance of the given type that is attached to the . /// /// The type of the script. - /// The script instance or null. - public object As(Type type) + /// The script instance of the given type; or if the has no script of the given type. + public object? As(Type type) { - Debug.Assert(type.IsSubclassOf(typeof(Entity))); + if (!type.IsSubclassOf(typeof(Entity))) + { + Log.Error($"{nameof(As)}: Invalid script type \"{type}\". Must be a subclass of \"{nameof(Entity)}\"."); + return null; + } - ScriptGlue.Entity_GetScriptInstance(_uuid, out object scriptInstance); + ScriptGlue.Entity_GetScriptInstance(_uuid, out object? scriptInstance); return scriptInstance; } /// - /// Returns the script of the given type, or null, if the entity has no script of the given type. + /// Returns the script instance of the given type that is attached to the with the given . /// - /// The id of the entity whose script instance shall be returned. + /// The id of the whose script instance shall be returned. /// The type of the script. - /// The script instance or null. - internal static Entity GetScriptReference(UUID id, Type type) + /// The script instance of the given type; or if the with the given has no script of the given type. + internal static Entity? GetScriptReference(UUID id, Type type) { Debug.Assert(typeof(Entity).IsAssignableFrom(type)); - ScriptGlue.Entity_GetScriptInstance(id, out object scriptInstance); + ScriptGlue.Entity_GetScriptInstance(id, out object? scriptInstance); return scriptInstance as Entity; } /// - /// Destroys the entity and all it's children. + /// Destroys the and all it's children. /// /// /// The destruction will not take place immediately. It will happen at the end of the current frame. @@ -359,10 +374,10 @@ public class Entity : EngineObject } /// - /// Instantiates a copy of the given entity and it's children. + /// Instantiates a copy of the given and all of it's children. /// - /// The entity to copy. - /// The new entity. + /// The to copy. + /// The new . public static Entity CreateInstance(Entity entity) { ScriptGlue.Entity_CreateInstance(entity.UUID, out UUID newEntityId); diff --git a/ScriptCore/Extensions/ActivatorExtension.cs b/ScriptCore/Extensions/ActivatorExtension.cs index 596f85b..9dd2df8 100644 --- a/ScriptCore/Extensions/ActivatorExtension.cs +++ b/ScriptCore/Extensions/ActivatorExtension.cs @@ -2,6 +2,7 @@ using System; using System.Reflection; +using GlitchyEngine.Core; namespace GlitchyEngine.Extensions; @@ -49,4 +50,20 @@ public static class ActivatorExtension return null; } + + /// + /// Creates an instance of the given component type and sets it's entities id. + /// + /// The type of the component. + /// The entities id. + /// An instance of the component type; or if the creation failed. + internal static Component? CreateComponent(Type componentType, UUID entityId) + { + Component? component = (Component?)CreateInstanceSafe(componentType); + + if (component != null) + component._uuid = entityId; + + return component; + } } diff --git a/ScriptCore/ScriptGlue.cs b/ScriptCore/ScriptGlue.cs index 6da163c..996d222 100644 --- a/ScriptCore/ScriptGlue.cs +++ b/ScriptCore/ScriptGlue.cs @@ -14,7 +14,7 @@ internal static class ScriptGlue #region Entity [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void Entity_Create(object scriptInstance, string entityName, Type[] componentTypes, out UUID entityId); + internal static extern void Entity_Create(object scriptInstance, string? entityName, Type[]? componentTypes, out UUID entityId); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void Entity_Destroy(UUID entityId); @@ -38,7 +38,7 @@ internal static class ScriptGlue internal static extern void Entity_FindEntityWithName(string name, out UUID uuid); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void Entity_GetScriptInstance(UUID entityId, out object instance); + internal static extern void Entity_GetScriptInstance(UUID entityId, out object? instance); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object Entity_SetScript(UUID entityId, Type scriptType); @@ -121,12 +121,6 @@ internal static class ScriptGlue [MethodImpl(MethodImplOptions.InternalCall)] internal static extern float4 modf_float4(float4 x, out float4 integerPart); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void Print_Decimal(decimal value); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void Get_Decimal(string str, out decimal value); #endregion @@ -152,7 +146,7 @@ internal static class ScriptGlue #region Serialization [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void Serialization_SerializeField(IntPtr serializationContext, SerializationType type, string name, object value, string fullTypeName = null); + internal static extern void Serialization_SerializeField(IntPtr serializationContext, SerializationType type, string name, object? value, string? fullTypeName = null); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void Serialization_CreateObject(IntPtr currentContext, string fullTypeName, out IntPtr context, out UUID id); diff --git a/ScriptCore/Serialization/DeserializationObject.cs b/ScriptCore/Serialization/DeserializationObject.cs index 9913b43..852c935 100644 --- a/ScriptCore/Serialization/DeserializationObject.cs +++ b/ScriptCore/Serialization/DeserializationObject.cs @@ -32,11 +32,11 @@ public class DeserializationObject private Stack _structScope = new(); - private string _structScopeName; + private string _structScopeName = ""; public Dictionary DeserializedClasses; - private object _instance; + private object? _instance; private Dictionary _fullNameToType = new(); @@ -317,72 +317,49 @@ public class DeserializationObject return false; } - public object DeserializeField(object fieldValue, Type fieldType, string fieldName) + public object? DeserializeField(object? fieldValue, Type fieldType, string fieldName) { + if (TryCustomDeserializer(fieldName, fieldType, out object? deserializedValue)) + { + return deserializedValue; + } + if (fieldType.IsPrimitive) { return DeserializePrimitive(fieldName, fieldType); } - else if (fieldType == typeof(string)) + if (fieldType == typeof(string)) { return GetFieldValue(fieldName, SerializationType.String); } - else if (fieldType.IsEnum) + if (fieldType.IsEnum) { return DeserializeEnum(fieldName, fieldType); } - else if (fieldType.IsArray) + if (fieldType.IsArray) { - return DeserializeList(fieldName, fieldType, fieldType.GetElementType()); - } - else if (fieldType.IsGenericType) - { - if (TryCustomDeserializer(fieldName, fieldType, out object deserializedValue)) - { - return deserializedValue; - } - - if (fieldType.GetGenericTypeDefinition() == typeof(List<>)) - { - return DeserializeList(fieldName, fieldType, fieldType.GetGenericArguments()[0]); - } - //else if (fieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) - //{ - // ImGui.Text($"{fieldName} Dictionary"); - //} - else - { - // TODO: what to do? - Log.Error($"Generic class serialization not yet implemented"); - } - } - else if (fieldType.IsValueType) - { - if (TryCustomDeserializer(fieldName, fieldType, out object deserializedValue)) - { - return deserializedValue; - } + Type? elementType = fieldType.GetElementType(); + if (elementType == null) + return NoValueDeserialized; + + return DeserializeList(fieldName, fieldType, elementType); + } + if (fieldType.IsValueType) + { return DeserializeStruct(fieldName, fieldValue); } - else if (fieldType.IsClass) - { - if (TryCustomDeserializer(fieldName, fieldType, out object deserializedValue)) - { - return deserializedValue; - } - + if (fieldType.IsClass) + { return DeserializeClass(fieldName, fieldType); } - else - { - Log.Error($"Encountered unhandled type \"{fieldType}\" while serializing."); - } + + Log.Error($"Encountered unhandled type \"{fieldType}\" while serializing."); return NoValueDeserialized; } - public object DeserializePrimitive(string fieldName, Type fieldType) + public object? DeserializePrimitive(string fieldName, Type fieldType) { Debug.Assert(fieldType.IsPrimitive, $"{fieldType} is not a primitive type."); @@ -422,9 +399,9 @@ public class DeserializationObject return GetFieldValue(fieldName, expectedType); } - public object DeserializeList(string fieldName, Type fieldType, Type elementType) + public object? DeserializeList(string fieldName, Type fieldType, Type elementType) { - UUID id = (UUID)GetFieldValue(fieldName, SerializationType.ObjectReference); + UUID id = (UUID)GetFieldValue(fieldName, SerializationType.ObjectReference)!; if (id == UUID.Zero) return null; @@ -442,8 +419,8 @@ public class DeserializationObject int count = deserializedObject.GetFieldValue("Count", SerializationType.Int32); - Array array = null; - IList list = null; + Array? array = null; + IList? list = null; if (type.IsArray) { @@ -457,11 +434,11 @@ public class DeserializationObject if (type.GetGenericTypeDefinition() == typeof(List<>)) { - list = (IList)ActivatorExtension.CreateInstanceSafe(type, count); + list = (IList?)ActivatorExtension.CreateInstanceSafe(type, count); } else { - list = (IList)ActivatorExtension.CreateInstanceSafe(type); + list = (IList?)ActivatorExtension.CreateInstanceSafe(type); } if (list == null) @@ -479,17 +456,19 @@ public class DeserializationObject for (int i = 0; i < count; i++) { - object elementValue = ActivatorExtension.CreateInstanceSafe(elementType); + object? elementValue = ActivatorExtension.CreateInstanceSafe(elementType); - object newValue = deserializedObject.DeserializeField(elementValue, elementType, $"{i}"); + object? newValue = deserializedObject.DeserializeField(elementValue, elementType, $"{i}"); if (newValue == NoValueDeserialized) newValue = elementValue; if (array != null) array.SetValue(newValue, i); - else + else if (list != null) list.Add(newValue); + else + Log.Error($"Deserialized element for field {fieldName}, but has no array or list to add it to."); } return deserializedObject._instance; @@ -523,8 +502,13 @@ public class DeserializationObject return changed ? targetInstance : NoValueDeserialized; } - public unsafe object DeserializeClass(string fieldName, Type fieldType) + public unsafe object? DeserializeClass(string fieldName, Type fieldType) { + if (fieldType.GetGenericTypeDefinition() == typeof(List<>)) + { + return DeserializeList(fieldName, fieldType, fieldType.GetGenericArguments()[0]); + } + bool isEntity = typeof(Entity).IsAssignableFrom(fieldType); bool isComponent = fieldType.IsSubclassOf(typeof(Component)); @@ -539,7 +523,7 @@ public class DeserializationObject string fullTypeName = Encoding.UTF8.GetString(data.FullTypeName, (int)data.FullTypeNameLength); - Type type = GetTypeFromName(fullTypeName); + Type? type = GetTypeFromName(fullTypeName); if (type == null) return NoValueDeserialized; @@ -562,21 +546,30 @@ public class DeserializationObject } else { - UUID id = (UUID)GetFieldValue(fieldName, SerializationType.ObjectReference); + UUID id = (UUID)GetFieldValue(fieldName, SerializationType.ObjectReference)!; if (id == UUID.Zero) return null; // Get serialization container for the instance - DeserializationObject deserializedObject = GetDeserializedObject(id); - - Type type = deserializedObject.StoredType; + DeserializationObject? deserializedObject = GetDeserializedObject(id); + + if (deserializedObject == null) + return NoValueDeserialized; + + Type? type = deserializedObject.StoredType; if (type == null) - return null; + return NoValueDeserialized; deserializedObject._instance = ActivatorExtension.CreateInstanceSafe(type); - + + if (deserializedObject._instance == null) + { + Log.Error($"Failed to create instance of type {type} for field {fieldName}"); + return NoValueDeserialized; + } + deserializedObject.DeserializeFields(deserializedObject._instance); return deserializedObject._instance; diff --git a/ScriptCore/Serialization/SerializedObject.cs b/ScriptCore/Serialization/SerializedObject.cs index e97841f..16e0e87 100644 --- a/ScriptCore/Serialization/SerializedObject.cs +++ b/ScriptCore/Serialization/SerializedObject.cs @@ -86,7 +86,7 @@ public class SerializedObject _structScopeName = _structScopeName.Remove(_structScopeName.Length - scopeToRemove.Length - 1); } - public void AddField(string fieldName, SerializationType serializationType, object value, string fullTypeName = null) + public void AddField(string fieldName, SerializationType serializationType, object value, string? fullTypeName = null) { string completeFieldName = $"{_structScopeName}{fieldName}";