diff --git a/Robust.Benchmarks/Program.cs b/Robust.Benchmarks/Program.cs
new file mode 100644
index 000000000..b1144f1ce
--- /dev/null
+++ b/Robust.Benchmarks/Program.cs
@@ -0,0 +1,12 @@
+using BenchmarkDotNet.Running;
+
+namespace Robust.Benchmarks
+{
+ internal class Program
+ {
+ public static void Main(string[] args)
+ {
+ BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run();
+ }
+ }
+}
diff --git a/Robust.Benchmarks/Robust.Benchmarks.csproj b/Robust.Benchmarks/Robust.Benchmarks.csproj
new file mode 100644
index 000000000..53509a34e
--- /dev/null
+++ b/Robust.Benchmarks/Robust.Benchmarks.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+ false
+ false
+ ../bin/Benchmarks
+ Exe
+
+
+
+
+
+
+
+
+
+
diff --git a/Robust.Benchmarks/Serialization/DataDefinitionWithString.cs b/Robust.Benchmarks/Serialization/DataDefinitionWithString.cs
new file mode 100644
index 000000000..9a0bd0746
--- /dev/null
+++ b/Robust.Benchmarks/Serialization/DataDefinitionWithString.cs
@@ -0,0 +1,11 @@
+using Robust.Shared.Serialization.Manager.Attributes;
+
+namespace Robust.Benchmarks.Serialization
+{
+ [DataDefinition]
+ public class DataDefinitionWithString
+ {
+ [field: DataField("string")]
+ private string StringField { get; } = default!;
+ }
+}
diff --git a/Robust.Benchmarks/Serialization/Read/SerializationReadBenchmark.cs b/Robust.Benchmarks/Serialization/Read/SerializationReadBenchmark.cs
new file mode 100644
index 000000000..5503fd535
--- /dev/null
+++ b/Robust.Benchmarks/Serialization/Read/SerializationReadBenchmark.cs
@@ -0,0 +1,83 @@
+using System;
+using System.IO;
+using BenchmarkDotNet.Attributes;
+using Robust.Server;
+using Robust.Shared.Configuration;
+using Robust.Shared.ContentPack;
+using Robust.Shared.IoC;
+using Robust.Shared.Reflection;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Markdown;
+using YamlDotNet.RepresentationModel;
+
+namespace Robust.Benchmarks.Serialization.Read
+{
+ public class SerializationReadBenchmark
+ {
+ public SerializationReadBenchmark()
+ {
+ IoCManager.InitThread();
+ ServerIoC.RegisterIoC();
+ IoCManager.BuildGraph();
+
+ var assemblies = new[]
+ {
+ AppDomain.CurrentDomain.GetAssemblyByName("Robust.Shared"),
+ AppDomain.CurrentDomain.GetAssemblyByName("Robust.Server"),
+ AppDomain.CurrentDomain.GetAssemblyByName("Robust.Benchmarks")
+ };
+
+ foreach (var assembly in assemblies)
+ {
+ IoCManager.Resolve().LoadCVarsFromAssembly(assembly);
+ }
+
+ IoCManager.Resolve().LoadAssemblies(assemblies);
+
+ SerializationManager = IoCManager.Resolve();
+ SerializationManager.Initialize();
+
+ StringDataDefNode = new MappingDataNode();
+ StringDataDefNode.AddNode(new ValueDataNode("string"), new ValueDataNode("ABC"));
+
+ var yamlStream = new YamlStream();
+ yamlStream.Load(new StringReader(SeedDataDefinition.Prototype));
+
+ SeedNode = yamlStream.Documents[0].RootNode.ToDataNodeCast().Cast(0);
+ }
+
+ private ISerializationManager SerializationManager { get; }
+
+ private ValueDataNode StringNode { get; } = new("ABC");
+
+ private ValueDataNode IntNode { get; } = new("1");
+
+ private MappingDataNode StringDataDefNode { get; }
+
+ private MappingDataNode SeedNode { get; }
+
+ [Benchmark]
+ public string? ReadString()
+ {
+ return SerializationManager.ReadValue(StringNode);
+ }
+
+ [Benchmark]
+ public int? ReadInteger()
+ {
+ return SerializationManager.ReadValue(IntNode);
+ }
+
+ [Benchmark]
+ public DataDefinitionWithString? ReadDataDefinitionWithString()
+ {
+ return SerializationManager.ReadValue(StringDataDefNode);
+ }
+
+ [Benchmark]
+ public SeedDataDefinition? ReadSeedDataDefinition()
+ {
+ return SerializationManager.ReadValue(SeedNode);
+ }
+ }
+}
diff --git a/Robust.Benchmarks/Serialization/SeedDataDefinition.cs b/Robust.Benchmarks/Serialization/SeedDataDefinition.cs
new file mode 100644
index 000000000..00dc8e71a
--- /dev/null
+++ b/Robust.Benchmarks/Serialization/SeedDataDefinition.cs
@@ -0,0 +1,184 @@
+using System.Collections.Generic;
+using Robust.Shared.Maths;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Utility;
+using Robust.Shared.ViewVariables;
+
+namespace Robust.Benchmarks.Serialization
+{
+ [Prototype("seed")]
+ public class SeedDataDefinition : IPrototype
+ {
+ public const string Prototype = @"
+- type: seed
+ id: tobacco
+ name: tobacco
+ seedName: tobacco
+ displayName: tobacco plant
+ productPrototypes:
+ - LeavesTobacco
+ harvestRepeat: Repeat
+ lifespan: 75
+ maturation: 5
+ production: 5
+ yield: 2
+ potency: 20
+ growthStages: 3
+ idealLight: 9
+ idealHeat: 298
+ chemicals:
+ chem.Nicotine:
+ Min: 1
+ Max: 10
+ PotencyDivisor: 10";
+
+ private const string SeedPrototype = "SeedBase";
+
+ [ViewVariables]
+ [field: DataField("id", required: true)]
+ public string ID { get; private init; } = default!;
+
+ ///
+ /// Unique identifier of this seed. Do NOT set this.
+ ///
+ public int Uid { get; internal set; } = -1;
+
+ #region Tracking
+
+ [ViewVariables] [DataField("name")] public string Name { get; set; } = string.Empty;
+ [ViewVariables] [DataField("seedName")] public string SeedName { get; set; } = string.Empty;
+
+ [ViewVariables]
+ [DataField("seedNoun")]
+ public string SeedNoun { get; set; } = "seeds";
+ [ViewVariables] [DataField("displayName")] public string DisplayName { get; set; } = string.Empty;
+
+ [ViewVariables]
+ [DataField("roundStart")]
+ public bool RoundStart { get; private set; } = true;
+ [ViewVariables] [DataField("mysterious")] public bool Mysterious { get; set; }
+ [ViewVariables] [DataField("immutable")] public bool Immutable { get; set; }
+ #endregion
+
+ #region Output
+
+ [ViewVariables]
+ [DataField("productPrototypes")]
+ public List ProductPrototypes { get; set; } = new();
+
+ [ViewVariables]
+ [DataField("chemicals")]
+ public Dictionary Chemicals { get; set; } = new();
+
+ [ViewVariables]
+ [DataField("consumeGasses")]
+ public Dictionary ConsumeGasses { get; set; } = new();
+
+ [ViewVariables]
+ [DataField("exudeGasses")]
+ public Dictionary ExudeGasses { get; set; } = new();
+ #endregion
+
+ #region Tolerances
+
+ [ViewVariables]
+ [DataField("nutrientConsumption")]
+ public float NutrientConsumption { get; set; } = 0.25f;
+
+ [ViewVariables] [DataField("waterConsumption")] public float WaterConsumption { get; set; } = 3f;
+ [ViewVariables] [DataField("idealHeat")] public float IdealHeat { get; set; } = 293f;
+ [ViewVariables] [DataField("heatTolerance")] public float HeatTolerance { get; set; } = 20f;
+ [ViewVariables] [DataField("idealLight")] public float IdealLight { get; set; } = 7f;
+ [ViewVariables] [DataField("lightTolerance")] public float LightTolerance { get; set; } = 5f;
+ [ViewVariables] [DataField("toxinsTolerance")] public float ToxinsTolerance { get; set; } = 4f;
+
+ [ViewVariables]
+ [DataField("lowPressureTolerance")]
+ public float LowPressureTolerance { get; set; } = 25f;
+
+ [ViewVariables]
+ [DataField("highPressureTolerance")]
+ public float HighPressureTolerance { get; set; } = 200f;
+
+ [ViewVariables]
+ [DataField("pestTolerance")]
+ public float PestTolerance { get; set; } = 5f;
+
+ [ViewVariables]
+ [DataField("weedTolerance")]
+ public float WeedTolerance { get; set; } = 5f;
+ #endregion
+
+ #region General traits
+
+ [ViewVariables]
+ [DataField("endurance")]
+ public float Endurance { get; set; } = 100f;
+ [ViewVariables] [DataField("yield")] public int Yield { get; set; }
+ [ViewVariables] [DataField("lifespan")] public float Lifespan { get; set; }
+ [ViewVariables] [DataField("maturation")] public float Maturation { get; set; }
+ [ViewVariables] [DataField("production")] public float Production { get; set; }
+ [ViewVariables] [DataField("growthStages")] public int GrowthStages { get; set; } = 6;
+ [ViewVariables] [DataField("harvestRepeat")] public HarvestType HarvestRepeat { get; set; } = HarvestType.NoRepeat;
+
+ [ViewVariables] [DataField("potency")] public float Potency { get; set; } = 1f;
+ // No, I'm not removing these.
+ //public PlantSpread Spread { get; set; }
+ //public PlantMutation Mutation { get; set; }
+ //public float AlterTemperature { get; set; }
+ //public PlantCarnivorous Carnivorous { get; set; }
+ //public bool Parasite { get; set; }
+ //public bool Hematophage { get; set; }
+ //public bool Thorny { get; set; }
+ //public bool Stinging { get; set; }
+ [DataField("ligneous")]
+ public bool Ligneous { get; set; }
+ // public bool Teleporting { get; set; }
+ // public PlantJuicy Juicy { get; set; }
+ #endregion
+
+ #region Cosmetics
+
+ [ViewVariables]
+ [DataField("plantRsi", required: true)]
+ public ResourcePath PlantRsi { get; set; } = default!;
+
+ [ViewVariables]
+ [DataField("plantIconState")]
+ public string PlantIconState { get; set; } = "produce";
+
+ [ViewVariables]
+ [DataField("bioluminescent")]
+ public bool Bioluminescent { get; set; }
+
+ [ViewVariables]
+ [DataField("bioluminescentColor")]
+ public Color BioluminescentColor { get; set; } = Color.White;
+
+ [ViewVariables]
+ [DataField("splatPrototype")]
+ public string? SplatPrototype { get; set; }
+
+ #endregion
+ }
+
+ public enum HarvestType
+ {
+ NoRepeat,
+ Repeat
+ }
+
+ public enum Gas {}
+
+ [DataDefinition]
+ public struct SeedChemQuantity
+ {
+ [DataField("Min")]
+ public int Min;
+ [DataField("Max")]
+ public int Max;
+ [DataField("PotencyDivisor")]
+ public int PotencyDivisor;
+ }
+}
diff --git a/Robust.Client/Properties/AssemblyInfo.cs b/Robust.Client/Properties/AssemblyInfo.cs
index e1e38e672..bca503790 100644
--- a/Robust.Client/Properties/AssemblyInfo.cs
+++ b/Robust.Client/Properties/AssemblyInfo.cs
@@ -3,6 +3,7 @@
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
[assembly: InternalsVisibleTo("Robust.Lite")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
+[assembly: InternalsVisibleTo("Robust.Benchmarks")]
#if NET5_0
[module: SkipLocalsInit]
diff --git a/Robust.Server/Properties/AssemblyInfo.cs b/Robust.Server/Properties/AssemblyInfo.cs
index 12b3f6cf8..8cb19675d 100644
--- a/Robust.Server/Properties/AssemblyInfo.cs
+++ b/Robust.Server/Properties/AssemblyInfo.cs
@@ -2,6 +2,7 @@
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
+[assembly: InternalsVisibleTo("Robust.Benchmarks")]
#if NET5_0
[module: SkipLocalsInit]
diff --git a/Robust.Shared/Properties/AssemblyInfo.cs b/Robust.Shared/Properties/AssemblyInfo.cs
index 4e964c180..125d836ad 100644
--- a/Robust.Shared/Properties/AssemblyInfo.cs
+++ b/Robust.Shared/Properties/AssemblyInfo.cs
@@ -10,6 +10,7 @@
[assembly: InternalsVisibleTo("OpenToolkit.GraphicsLibraryFramework")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Gives access to Castle(Moq)
[assembly: InternalsVisibleTo("Content.Benchmarks")]
+[assembly: InternalsVisibleTo("Robust.Benchmarks")]
#if NET5_0
[module: SkipLocalsInit]
diff --git a/Robust.Shared/Serialization/Manager/ISerializationManager.cs b/Robust.Shared/Serialization/Manager/ISerializationManager.cs
index 7a239576e..7416220e5 100644
--- a/Robust.Shared/Serialization/Manager/ISerializationManager.cs
+++ b/Robust.Shared/Serialization/Manager/ISerializationManager.cs
@@ -90,7 +90,6 @@ namespace Robust.Shared.Serialization.Manager
#endregion
#region Read
-
///
/// Deserializes a node into an object, populating it.
///
diff --git a/Robust.Shared/Serialization/Manager/Result/DeserializationResult.cs b/Robust.Shared/Serialization/Manager/Result/DeserializationResult.cs
index 4126c811e..05eddd911 100644
--- a/Robust.Shared/Serialization/Manager/Result/DeserializationResult.cs
+++ b/Robust.Shared/Serialization/Manager/Result/DeserializationResult.cs
@@ -18,17 +18,6 @@ namespace Robust.Shared.Serialization.Manager.Result
return (DeserializationResult) Activator.CreateInstance(type, value)!;
}
- public static DeserializationResult Definition(object value, DeserializedFieldEntry[] mappings)
- {
- //if (!IoCManager.Resolve().HasDataDefinition(value.GetType()))
- // throw new ArgumentException("Provided value was not a data definition", nameof(value));
-
- //todo validate mappings array count
- //unless...
- var type = typeof(DeserializedDefinition<>).MakeGenericType(value.GetType());
- return (DeserializationResult) Activator.CreateInstance(type, value, mappings)!;
- }
-
public T Cast() where T : DeserializationResult
{
if (this is T value) return value;
diff --git a/Robust.Shared/Serialization/Manager/Result/DeserializedValue.cs b/Robust.Shared/Serialization/Manager/Result/DeserializedValue.cs
index 0e628dcae..c9e085b4a 100644
--- a/Robust.Shared/Serialization/Manager/Result/DeserializedValue.cs
+++ b/Robust.Shared/Serialization/Manager/Result/DeserializedValue.cs
@@ -1,5 +1,30 @@
namespace Robust.Shared.Serialization.Manager.Result
{
+ public class DeserializedValue : DeserializationResult
+ {
+ public DeserializedValue(object? value)
+ {
+ RawValue = value;
+ }
+
+ public override object? RawValue { get; }
+
+ public override DeserializationResult PushInheritanceFrom(DeserializationResult source)
+ {
+ return source.Copy().Cast();
+ }
+
+ public override DeserializationResult Copy()
+ {
+ return new DeserializedValue(RawValue);
+ }
+
+ public override void CallAfterDeserializationHook()
+ {
+ if (RawValue is ISerializationHooks hooks) hooks.AfterDeserialization();
+ }
+ }
+
public class DeserializedValue : DeserializationResult
{
public DeserializedValue(T value)
@@ -23,7 +48,7 @@
public override void CallAfterDeserializationHook()
{
- if(Value is ISerializationHooks hooks) hooks.AfterDeserialization();
+ if (Value is ISerializationHooks hooks) hooks.AfterDeserialization();
}
}
}
diff --git a/Robust.Shared/Serialization/Manager/SerializationDataDefinition.cs b/Robust.Shared/Serialization/Manager/SerializationDataDefinition.cs
index f9c7115f0..9fa3f4c5a 100644
--- a/Robust.Shared/Serialization/Manager/SerializationDataDefinition.cs
+++ b/Robust.Shared/Serialization/Manager/SerializationDataDefinition.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using System.Linq.Expressions;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Network;
@@ -15,16 +16,33 @@ namespace Robust.Shared.Serialization.Manager
{
public class SerializationDataDefinition
{
- private delegate DeserializedFieldEntry[] DeserializeDelegate(MappingDataNode mappingDataNode,
- ISerializationManager serializationManager, ISerializationContext? context, bool skipHook);
+ private delegate DeserializedFieldEntry[] DeserializeDelegate(
+ MappingDataNode mappingDataNode,
+ ISerializationManager serializationManager,
+ ISerializationContext? context,
+ bool skipHook);
- private delegate DeserializationResult PopulateDelegateSignature(object target, DeserializedFieldEntry[] deserializationResults, object?[] defaultValues);
+ private delegate DeserializationResult PopulateDelegateSignature(
+ object target,
+ DeserializedFieldEntry[] deserializationResults,
+ object?[] defaultValues);
- private delegate MappingDataNode SerializeDelegateSignature(object obj, ISerializationManager serializationManager,
- ISerializationContext? context, bool alwaysWrite, object?[] defaultValues);
+ private delegate MappingDataNode SerializeDelegateSignature(
+ object obj,
+ ISerializationManager serializationManager,
+ ISerializationContext? context,
+ bool alwaysWrite,
+ object?[] defaultValues);
- private delegate object CopyDelegateSignature(object source, object target,
- ISerializationManager serializationManager, ISerializationContext? context);
+ private delegate object CopyDelegateSignature(
+ object source,
+ object target,
+ ISerializationManager serializationManager,
+ ISerializationContext? context);
+
+ private delegate DeserializationResult CreateDefinitionDelegate(
+ object value,
+ DeserializedFieldEntry[] mappings);
public readonly Type Type;
@@ -212,6 +230,19 @@ namespace Robust.Shared.Serialization.Manager
// TODO PAUL SERV3: Turn this back into IL once it is fixed
private PopulateDelegateSignature EmitPopulateDelegate()
{
+ //todo validate mappings array count
+ var constructor =
+ typeof(DeserializedDefinition<>).MakeGenericType(Type).GetConstructor(new[] {Type, typeof(DeserializedFieldEntry[])}) ??
+ throw new NullReferenceException();
+
+ var valueParam = Expression.Parameter(typeof(object), "value");
+ var valueParamCast = Expression.Convert(valueParam, Type);
+
+ var mappingParam = Expression.Parameter(typeof(DeserializedFieldEntry[]), "mapping");
+
+ var newExp = Expression.New(constructor, valueParamCast, mappingParam);
+ var createDefinitionDelegate = Expression.Lambda(newExp, valueParam, mappingParam).Compile();
+
DeserializationResult PopulateDelegate(
object target,
DeserializedFieldEntry[] deserializedFields,
@@ -234,7 +265,7 @@ namespace Robust.Shared.Serialization.Manager
fieldDefinition.FieldInfo.SetValue(target, res.Result?.RawValue);
}
- return DeserializationResult.Definition(target, deserializedFields);
+ return createDefinitionDelegate(target, deserializedFields);
}
return PopulateDelegate;
diff --git a/Robust.Shared/Serialization/Manager/SerializationManager.CustomSerializers.cs b/Robust.Shared/Serialization/Manager/SerializationManager.CustomSerializers.cs
new file mode 100644
index 000000000..b4bb91fb0
--- /dev/null
+++ b/Robust.Shared/Serialization/Manager/SerializationManager.CustomSerializers.cs
@@ -0,0 +1,50 @@
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.Manager
+{
+ public partial class SerializationManager
+ {
+ private DeserializationResult ReadWithCustomTypeSerializer(TNode node, ISerializationContext? context = null, bool skipHook = false)
+ where TSerializer : ITypeReader where T : notnull where TNode : DataNode
+ {
+ var serializer = (ITypeReader)GetTypeSerializer(typeof(TSerializer));
+ return serializer.Read(this, node, DependencyCollection, skipHook, context);
+ }
+
+ private DataNode WriteWithCustomTypeSerializer(T value,
+ ISerializationContext? context = null, bool alwaysWrite = false)
+ where TSerializer : ITypeWriter where T : notnull
+ {
+ var serializer = (ITypeWriter)GetTypeSerializer(typeof(TSerializer));
+ return serializer.Write(this, value, alwaysWrite, context);
+ }
+
+ private TCommon CopyWithCustomTypeSerializer(
+ TSource source,
+ TTarget target,
+ bool skipHook,
+ ISerializationContext? context = null)
+ where TSource : TCommon
+ where TTarget : TCommon
+ where TCommon : notnull
+ where TSerializer : ITypeCopier
+ {
+ var serializer = (ITypeCopier) GetTypeSerializer(typeof(TSerializer));
+ return serializer.Copy(this, source, target, skipHook, context);
+ }
+
+ private ValidationNode ValidateWithCustomTypeSerializer(
+ TNode node,
+ ISerializationContext? context)
+ where T : notnull
+ where TNode : DataNode
+ where TSerializer : ITypeValidator
+ {
+ var serializer = (ITypeValidator) GetTypeSerializer(typeof(TSerializer));
+ return serializer.Validate(this, node, DependencyCollection, context);
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/Manager/SerializationManager_FlagsAndConstants.cs b/Robust.Shared/Serialization/Manager/SerializationManager.FlagsAndConstants.cs
similarity index 96%
rename from Robust.Shared/Serialization/Manager/SerializationManager_FlagsAndConstants.cs
rename to Robust.Shared/Serialization/Manager/SerializationManager.FlagsAndConstants.cs
index a79fb5437..52412b1e9 100644
--- a/Robust.Shared/Serialization/Manager/SerializationManager_FlagsAndConstants.cs
+++ b/Robust.Shared/Serialization/Manager/SerializationManager.FlagsAndConstants.cs
@@ -2,8 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
-using Robust.Shared.Serialization.Markdown;
-using Robust.Shared.Serialization.Markdown.Validation;
namespace Robust.Shared.Serialization.Manager
{
diff --git a/Robust.Shared/Serialization/Manager/SerializationManager.TypeCopiers.cs b/Robust.Shared/Serialization/Manager/SerializationManager.TypeCopiers.cs
new file mode 100644
index 000000000..f21e1bd15
--- /dev/null
+++ b/Robust.Shared/Serialization/Manager/SerializationManager.TypeCopiers.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Reflection;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.Manager
+{
+ public partial class SerializationManager
+ {
+ private bool TryCopyWithTypeCopier(Type type, object source, ref object target, bool skipHook, ISerializationContext? context = null)
+ {
+ //TODO Paul: do this shit w/ delegates
+ var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
+ m.Name == nameof(TryCopyWithTypeCopier) && m.GetParameters().Length == 4).MakeGenericMethod(type, source.GetType(), target.GetType());
+
+ var arr = new[] {source, target, skipHook, context};
+ var res = method.Invoke(this, arr);
+
+ if (res as bool? ?? false)
+ {
+ target = arr[1]!;
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool TryCopyWithTypeCopier(
+ TSource source,
+ ref TTarget target,
+ bool skipHook,
+ ISerializationContext? context = null)
+ where TSource : TCommon
+ where TTarget : TCommon
+ where TCommon : notnull
+ {
+ object? rawTypeCopier;
+
+ if (context != null &&
+ context.TypeCopiers.TryGetValue(typeof(TCommon), out rawTypeCopier) ||
+ _typeCopiers.TryGetValue(typeof(TCommon), out rawTypeCopier))
+ {
+ var ser = (ITypeCopier) rawTypeCopier;
+ target = (TTarget) ser.Copy(this, source, target, skipHook, context);
+ return true;
+ }
+
+ if (TryGetGenericCopier(out ITypeCopier? genericTypeWriter))
+ {
+ target = (TTarget) genericTypeWriter.Copy(this, source, target, skipHook, context);
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool TryGetGenericCopier([NotNullWhen(true)] out ITypeCopier? rawCopier) where T : notnull
+ {
+ rawCopier = null;
+
+ if (typeof(T).IsGenericType)
+ {
+ var typeDef = typeof(T).GetGenericTypeDefinition();
+
+ Type? serializerTypeDef = null;
+
+ foreach (var (key, val) in _genericCopierTypes)
+ {
+ if (typeDef.HasSameMetadataDefinitionAs(key))
+ {
+ serializerTypeDef = val;
+ break;
+ }
+ }
+
+ if (serializerTypeDef == null) return false;
+
+ var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
+ rawCopier = (ITypeCopier) RegisterSerializer(serializerType)!;
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/Manager/SerializationManager.TypeReaders.cs b/Robust.Shared/Serialization/Manager/SerializationManager.TypeReaders.cs
new file mode 100644
index 000000000..3c3512601
--- /dev/null
+++ b/Robust.Shared/Serialization/Manager/SerializationManager.TypeReaders.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq.Expressions;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.Manager
+{
+ public partial class SerializationManager
+ {
+ public delegate bool ReadDelegate(
+ Type type,
+ DataNode node,
+ IDependencyCollection dependencies,
+ [NotNullWhen(true)] out DeserializationResult? obj,
+ bool skipHook,
+ ISerializationContext? context = null);
+
+ private readonly ConcurrentDictionary> _readDelegates = new();
+
+ private ReadDelegate GetOrCreateDelegate(Type type, Type nodeType)
+ {
+ return _readDelegates
+ .GetOrAdd(type, _ => new ConcurrentDictionary())
+ .GetOrAdd(nodeType, (nT, t) =>
+ {
+ var instanceParam = Expression.Constant(this);
+ var typeParam = Expression.Parameter(typeof(Type), "type");
+ var nodeParam = Expression.Parameter(typeof(DataNode), "node");
+ var dependenciesParam = Expression.Parameter(typeof(IDependencyCollection), "dependencies");
+ var objParam = Expression.Parameter(typeof(DeserializationResult).MakeByRefType(), "obj");
+ var skipHookParam = Expression.Parameter(typeof(bool), "skipHook");
+ var contextParam = Expression.Parameter(typeof(ISerializationContext), "context");
+
+ var call = Expression.Call(
+ instanceParam,
+ nameof(TryRead),
+ new[] {t, nT},
+ typeParam,
+ Expression.Convert(nodeParam, nT),
+ dependenciesParam,
+ objParam,
+ skipHookParam,
+ contextParam);
+
+ return Expression.Lambda(
+ call,
+ typeParam,
+ nodeParam,
+ dependenciesParam,
+ objParam,
+ skipHookParam,
+ contextParam).Compile();
+ }, type);
+ }
+
+ private bool TryGetReader(
+ Type type,
+ ISerializationContext? context,
+ [NotNullWhen(true)] out ITypeReader? reader)
+ where T : notnull
+ where TNode : DataNode
+ {
+ var nodeType = typeof(TNode);
+
+ if (context != null &&
+ context.TypeReaders.TryGetValue((type, nodeType), out var rawTypeReader) ||
+ _typeReaders.TryGetValue((type, nodeType), out rawTypeReader))
+ {
+ reader = (ITypeReader) rawTypeReader;
+ return true;
+ }
+
+ return TryGetGenericReader(out reader);
+ }
+
+ private bool TryRead(
+ Type type,
+ TNode node,
+ IDependencyCollection dependencies,
+ [NotNullWhen(true)] out DeserializationResult? obj,
+ bool skipHook,
+ ISerializationContext? context = null)
+ where T : notnull
+ where TNode : DataNode
+ {
+ if (TryGetReader(type, context, out var reader))
+ {
+ obj = reader.Read(this, node, dependencies, skipHook, context);
+ return true;
+ }
+
+ obj = null;
+ return false;
+ }
+
+ private bool TryReadRaw(
+ Type type,
+ DataNode node,
+ IDependencyCollection dependencies,
+ [NotNullWhen(true)] out DeserializationResult? obj,
+ bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return GetOrCreateDelegate(type, node.GetType())(type, node, dependencies, out obj, skipHook, context);
+ }
+
+ private bool TryGetGenericReader(
+ [NotNullWhen(true)] out ITypeReader? reader)
+ where TNode : DataNode
+ where T : notnull
+ {
+ var type = typeof(T);
+ var nodeType = typeof(TNode);
+
+ if (type.IsGenericType)
+ {
+ var typeDef = type.GetGenericTypeDefinition();
+
+ Type? serializerTypeDef = null;
+
+ foreach (var (key, val) in _genericReaderTypes)
+ {
+ if (typeDef.HasSameMetadataDefinitionAs(key.Type) && key.DataNodeType.IsAssignableFrom(nodeType))
+ {
+ serializerTypeDef = val;
+ break;
+ }
+ }
+
+ if (serializerTypeDef == null)
+ {
+ reader = null;
+ return false;
+ }
+
+ var serializerType = serializerTypeDef.MakeGenericType(type.GetGenericArguments());
+
+ reader = (ITypeReader) (RegisterSerializer(serializerType) ??
+ throw new NullReferenceException());
+ return true;
+ }
+
+ reader = null;
+ return false;
+ }
+
+ }
+}
diff --git a/Robust.Shared/Serialization/Manager/SerializationManager.TypeSerializers.cs b/Robust.Shared/Serialization/Manager/SerializationManager.TypeSerializers.cs
new file mode 100644
index 000000000..f23e92229
--- /dev/null
+++ b/Robust.Shared/Serialization/Manager/SerializationManager.TypeSerializers.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Robust.Shared.Log;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.Manager
+{
+ public partial class SerializationManager
+ {
+ private readonly Dictionary<(Type Type, Type DataNodeType), object> _typeReaders = new();
+ private readonly Dictionary _typeWriters = new();
+ private readonly Dictionary _typeCopiers = new();
+ private readonly Dictionary<(Type Type, Type DataNodeType), object> _typeValidators = new();
+
+ private readonly Dictionary<(Type Type, Type DataNodeType), Type> _genericReaderTypes = new();
+ private readonly Dictionary _genericWriterTypes = new();
+ private readonly Dictionary _genericCopierTypes = new();
+ private readonly Dictionary<(Type Type, Type DataNodeType), Type> _genericValidatorTypes = new();
+
+ private readonly Dictionary _customTypeSerializers = new();
+
+ private void InitializeTypeSerializers()
+ {
+ foreach (var type in _reflectionManager.FindTypesWithAttribute())
+ {
+ RegisterSerializer(type);
+ }
+ }
+
+ private object GetTypeSerializer(Type type)
+ {
+ if (type.IsGenericTypeDefinition)
+ throw new ArgumentException("TypeSerializer cannot be TypeDefinition!", nameof(type));
+
+ if (_customTypeSerializers.TryGetValue(type, out var obj)) return obj;
+
+ obj = Activator.CreateInstance(type)!;
+ _customTypeSerializers[type] = obj;
+ return obj;
+ }
+
+ private object? RegisterSerializer(Type type)
+ {
+ var writerInterfaces = type.GetInterfaces()
+ .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeWriter<>)).ToArray();
+ var readerInterfaces = type.GetInterfaces()
+ .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeReader<,>)).ToArray();
+ var copierInterfaces = type.GetInterfaces()
+ .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeCopier<>)).ToArray();
+ var validatorInterfaces = type.GetInterfaces()
+ .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeValidator<,>)).ToArray();
+
+ if (readerInterfaces.Length == 0 &&
+ writerInterfaces.Length == 0 &&
+ copierInterfaces.Length == 0 &&
+ validatorInterfaces.Length == 0)
+ {
+ throw new InvalidOperationException(
+ "Tried to register TypeReader/Writer/Copier that had none of the interfaces inherited.");
+ }
+
+ if (type.IsGenericTypeDefinition)
+ {
+ foreach (var writerInterface in writerInterfaces)
+ {
+ if (!_genericWriterTypes.TryAdd(writerInterface.GetGenericArguments()[0], type))
+ Logger.ErrorS(LogCategory, $"Tried registering generic writer for type {writerInterface.GetGenericArguments()[0]} twice");
+ }
+
+ foreach (var readerInterface in readerInterfaces)
+ {
+ var genericArguments = readerInterface.GetGenericArguments();
+ var readType = genericArguments[0];
+ var nodeType = genericArguments[1];
+
+ if (!_genericReaderTypes.TryAdd((readType, nodeType), type))
+ Logger.ErrorS(LogCategory, $"Tried registering generic reader for type {readType} and node {nodeType} twice");
+ }
+
+ foreach (var copierInterface in copierInterfaces)
+ {
+ if (!_genericCopierTypes.TryAdd(copierInterface.GetGenericArguments()[0], type))
+ Logger.ErrorS(LogCategory, $"Tried registering generic copier for type {copierInterface.GetGenericArguments()[0]} twice");
+ }
+
+ foreach (var validatorInterface in validatorInterfaces)
+ {
+ if (!_genericValidatorTypes.TryAdd((validatorInterface.GetGenericArguments()[0], validatorInterface.GetGenericArguments()[1]), type))
+ Logger.ErrorS(LogCategory, $"Tried registering generic reader for type {validatorInterface.GetGenericArguments()[0]} and node {validatorInterface.GetGenericArguments()[1]} twice");
+ }
+
+ return null;
+ }
+ else
+ {
+ var serializer = GetTypeSerializer(type);
+
+ foreach (var writerInterface in writerInterfaces)
+ {
+ if (!_typeWriters.TryAdd(writerInterface.GetGenericArguments()[0], serializer))
+ Logger.ErrorS(LogCategory, $"Tried registering writer for type {writerInterface.GetGenericArguments()[0]} twice");
+ }
+
+ foreach (var readerInterface in readerInterfaces)
+ {
+ if (!_typeReaders.TryAdd((readerInterface.GetGenericArguments()[0], readerInterface.GetGenericArguments()[1]), serializer))
+ Logger.ErrorS(LogCategory, $"Tried registering reader for type {readerInterface.GetGenericArguments()[0]} and node {readerInterface.GetGenericArguments()[1]} twice");
+ }
+
+ foreach (var copierInterface in copierInterfaces)
+ {
+ if (!_typeCopiers.TryAdd(copierInterface.GetGenericArguments()[0], serializer))
+ Logger.ErrorS(LogCategory, $"Tried registering copier for type {copierInterface.GetGenericArguments()[0]} twice");
+ }
+
+ foreach (var validatorInterface in validatorInterfaces)
+ {
+ if (!_typeValidators.TryAdd((validatorInterface.GetGenericArguments()[0], validatorInterface.GetGenericArguments()[1]), serializer))
+ Logger.ErrorS(LogCategory, $"Tried registering reader for type {validatorInterface.GetGenericArguments()[0]} and node {validatorInterface.GetGenericArguments()[1]} twice");
+ }
+
+ return serializer;
+ }
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/Manager/SerializationManager.TypeValidators.cs b/Robust.Shared/Serialization/Manager/SerializationManager.TypeValidators.cs
new file mode 100644
index 000000000..1aa536f15
--- /dev/null
+++ b/Robust.Shared/Serialization/Manager/SerializationManager.TypeValidators.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Reflection;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.Manager
+{
+ public partial class SerializationManager
+ {
+ private bool TryValidateWithTypeValidator(
+ Type type,
+ DataNode node,
+ IDependencyCollection dependencies,
+ ISerializationContext? context,
+ [NotNullWhen(true)] out ValidationNode? valid)
+ {
+ //TODO Paul: do this shit w/ delegates
+ var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
+ m.Name == nameof(TryValidateWithTypeValidator) && m.GetParameters().Length == 4).MakeGenericMethod(type, node.GetType());
+
+ var arr = new object?[] {node, dependencies, context, null};
+ var res = method.Invoke(this, arr);
+
+ if (res as bool? ?? false)
+ {
+ valid = (ValidationNode) arr[3]!;
+ return true;
+ }
+
+ valid = null;
+ return false;
+ }
+
+ private bool TryValidateWithTypeValidator(
+ TNode node,
+ IDependencyCollection dependencies,
+ ISerializationContext? context,
+ [NotNullWhen(true)] out ValidationNode? valid)
+ where T : notnull
+ where TNode : DataNode
+ {
+ if (TryGetValidator(null, out var reader))
+ {
+ valid = reader.Validate(this, node, dependencies, context);
+ return true;
+ }
+
+ valid = null;
+ return false;
+ }
+
+ private bool TryGetValidator(
+ ISerializationContext? context,
+ [NotNullWhen(true)] out ITypeValidator? reader)
+ where T : notnull
+ where TNode : DataNode
+ {
+ if (context != null && context.TypeValidators.TryGetValue((typeof(T), typeof(TNode)), out var rawTypeValidator) ||
+ _typeValidators.TryGetValue((typeof(T), typeof(TNode)), out rawTypeValidator))
+ {
+ reader = (ITypeReader) rawTypeValidator;
+ return true;
+ }
+
+ return TryGetGenericValidator(out reader);
+ }
+
+ private bool TryGetGenericValidator([NotNullWhen(true)] out ITypeValidator? rawReader)
+ where TNode : DataNode where T : notnull
+ {
+ rawReader = null;
+
+ if (typeof(T).IsGenericType)
+ {
+ var typeDef = typeof(T).GetGenericTypeDefinition();
+
+ Type? serializerTypeDef = null;
+
+ foreach (var (key, val) in _genericValidatorTypes)
+ {
+ if (typeDef.HasSameMetadataDefinitionAs(key.Type) && key.DataNodeType.IsAssignableFrom(typeof(TNode)))
+ {
+ serializerTypeDef = val;
+ break;
+ }
+ }
+
+ if (serializerTypeDef == null) return false;
+
+ var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
+ rawReader = (ITypeValidator) RegisterSerializer(serializerType)!;
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/Manager/SerializationManager.TypeWriters.cs b/Robust.Shared/Serialization/Manager/SerializationManager.TypeWriters.cs
new file mode 100644
index 000000000..be42774a8
--- /dev/null
+++ b/Robust.Shared/Serialization/Manager/SerializationManager.TypeWriters.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Reflection;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.Manager
+{
+ public partial class SerializationManager
+ {
+ private bool TryWriteWithTypeSerializers(
+ Type type,
+ object obj,
+ [NotNullWhen(true)] out DataNode? node,
+ bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ //TODO Paul: do this shit w/ delegates
+ var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
+ m.Name == nameof(TryWriteWithTypeSerializers) && m.GetParameters().Length == 4).MakeGenericMethod(type);
+
+ node = null;
+
+ var arr = new[] {obj, node, alwaysWrite, context};
+ var res = method.Invoke(this, arr);
+
+ if (res as bool? ?? false)
+ {
+ node = (DataNode) arr[1]!;
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool TryGetWriter(ISerializationContext? context, [NotNullWhen(true)] out ITypeWriter? writer) where T : notnull
+ {
+ if (context != null && context.TypeWriters.TryGetValue(typeof(T), out var rawTypeWriter) ||
+ _typeWriters.TryGetValue(typeof(T), out rawTypeWriter))
+ {
+ writer = (ITypeWriter) rawTypeWriter;
+ return true;
+ }
+
+ return TryGetGenericWriter(out writer);
+ }
+
+ private bool TryWriteWithTypeSerializers(
+ T obj,
+ [NotNullWhen(true)] out DataNode? node,
+ bool alwaysWrite = false,
+ ISerializationContext? context = null) where T : notnull
+ {
+ node = default;
+ if (TryGetWriter(context, out var writer))
+ {
+ node = writer.Write(this, obj, alwaysWrite, context);
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool TryGetGenericWriter([NotNullWhen(true)] out ITypeWriter? rawWriter) where T : notnull
+ {
+ rawWriter = null;
+
+ if (typeof(T).IsGenericType)
+ {
+ var typeDef = typeof(T).GetGenericTypeDefinition();
+
+ Type? serializerTypeDef = null;
+
+ foreach (var (key, val) in _genericWriterTypes)
+ {
+ if (typeDef.HasSameMetadataDefinitionAs(key))
+ {
+ serializerTypeDef = val;
+ break;
+ }
+ }
+
+ if (serializerTypeDef == null) return false;
+
+ var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
+ rawWriter = (ITypeWriter) RegisterSerializer(serializerType)!;
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/Manager/SerializationManager.cs b/Robust.Shared/Serialization/Manager/SerializationManager.cs
index b24ad3cef..71efbc6b7 100644
--- a/Robust.Shared/Serialization/Manager/SerializationManager.cs
+++ b/Robust.Shared/Serialization/Manager/SerializationManager.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
@@ -125,9 +124,6 @@ namespace Robust.Shared.Serialization.Manager
{
var underlyingType = type.EnsureNotNullableType();
- if (underlyingType.IsPrimitive || underlyingType == typeof(decimal))
- return node is ValueDataNode valueDataNode ? new ValidatedValueNode(valueDataNode) : new ErrorNode(node, "Invalid nodetype for primitive/decimal.", true);
-
if (underlyingType.IsArray)
{
if (node is not SequenceDataNode sequenceDataNode) return new ErrorNode(node, "Invalid nodetype for array.", true);
@@ -268,14 +264,6 @@ namespace Robust.Shared.Serialization.Manager
{
var underlyingType = type.EnsureNotNullableType();
- // val primitives
- if (underlyingType.IsPrimitive || underlyingType == typeof(decimal))
- {
- if (node is not ValueDataNode valueDataNode) throw new InvalidNodeTypeException();
- var foo = TypeDescriptor.GetConverter(type);
- return DeserializationResult.Value(foo.ConvertFromInvariantString(valueDataNode.Value));
- }
-
// array
if (underlyingType.IsArray)
{
@@ -296,7 +284,7 @@ namespace Robust.Shared.Serialization.Manager
if (underlyingType.IsEnum)
{
- return DeserializationResult.Value(node switch
+ return new DeserializedValue(node switch
{
ValueDataNode valueNode => Enum.Parse(underlyingType, valueNode.Value, true),
SequenceDataNode sequenceNode => Enum.Parse(underlyingType, string.Join(", ", sequenceNode.Sequence), true),
@@ -310,7 +298,7 @@ namespace Robust.Shared.Serialization.Manager
underlyingType = ResolveConcreteType(underlyingType, typeString);
}
- if (TryReadWithTypeSerializers(underlyingType, node, DependencyCollection, out var serializedObj, skipHook, context))
+ if (TryReadRaw(underlyingType, node, DependencyCollection, out var serializedObj, skipHook, context))
{
return serializedObj;
}
@@ -322,7 +310,7 @@ namespace Robust.Shared.Serialization.Manager
var selfSerObj = (ISelfSerialize) Activator.CreateInstance(underlyingType)!;
selfSerObj.Deserialize(valueDataNode.Value);
- return DeserializationResult.Value(selfSerObj);
+ return new DeserializedValue(selfSerObj);
}
//if (node is not MappingDataNode mappingDataNode) throw new InvalidNodeTypeException();
@@ -404,11 +392,9 @@ namespace Robust.Shared.Serialization.Manager
if (value == null) return new MappingDataNode();
- if (underlyingType.IsPrimitive ||
- underlyingType.IsEnum ||
- underlyingType == typeof(decimal))
+ if (underlyingType.IsEnum)
{
- // All primitives and enums implement IConvertible.
+ // Enums implement IConvertible.
// Need it for the culture overload.
var convertible = (IConvertible) value;
return new ValueDataNode(convertible.ToString(CultureInfo.InvariantCulture));
@@ -487,20 +473,6 @@ namespace Robust.Shared.Serialization.Manager
var sourceType = source.GetType();
var targetType = target.GetType();
- if (sourceType.IsPrimitive && targetType.IsPrimitive)
- {
- //todo does this work
- //i think it does
- //todo validate we can assign source
- return source;
- }
-
- if (source.GetType().IsPrimitive != target.GetType().IsPrimitive)
- {
- throw new InvalidOperationException(
- $"Source and target do not match. Source ({sourceType}) is primitive type? {sourceType.IsPrimitive}. Target ({targetType}) is primitive type? {targetType.IsPrimitive}");
- }
-
if (sourceType.IsValueType && targetType.IsValueType)
{
return source;
@@ -611,7 +583,11 @@ namespace Robust.Shared.Serialization.Manager
{
if (source == null) return source;
- if (type.IsPrimitive || type.IsEnum || source is string || _copyByRefRegistrations.Contains(type))
+
+ if (type.IsPrimitive ||
+ type.IsEnum ||
+ source is string ||
+ _copyByRefRegistrations.Contains(type))
{
return source;
}
diff --git a/Robust.Shared/Serialization/Manager/SerializationManager_Typeserializers.cs b/Robust.Shared/Serialization/Manager/SerializationManager_Typeserializers.cs
deleted file mode 100644
index 2789b0ee0..000000000
--- a/Robust.Shared/Serialization/Manager/SerializationManager_Typeserializers.cs
+++ /dev/null
@@ -1,523 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Reflection;
-using Robust.Shared.IoC;
-using Robust.Shared.Log;
-using Robust.Shared.Serialization.Manager.Attributes;
-using Robust.Shared.Serialization.Manager.Result;
-using Robust.Shared.Serialization.Markdown;
-using Robust.Shared.Serialization.Markdown.Validation;
-using Robust.Shared.Serialization.TypeSerializers.Interfaces;
-
-namespace Robust.Shared.Serialization.Manager
-{
- public partial class SerializationManager
- {
- private readonly Dictionary<(Type Type, Type DataNodeType), object> _typeReaders = new();
- private readonly Dictionary _typeWriters = new();
- private readonly Dictionary _typeCopiers = new();
- private readonly Dictionary<(Type Type, Type DataNodeType), object> _typeValidators = new();
-
- private readonly Dictionary<(Type Type, Type DataNodeType), Type> _genericReaderTypes = new();
- private readonly Dictionary _genericWriterTypes = new();
- private readonly Dictionary _genericCopierTypes = new();
- private readonly Dictionary<(Type Type, Type DataNodeType), Type> _genericValidatorTypes = new();
-
- private readonly Dictionary _customTypeSerializers = new();
-
- private void InitializeTypeSerializers()
- {
- foreach (var type in _reflectionManager.FindTypesWithAttribute())
- {
- RegisterSerializer(type);
- }
- }
-
- private object GetTypeSerializer(Type type)
- {
- if (type.IsGenericTypeDefinition)
- throw new ArgumentException("TypeSerializer cannot be TypeDefinition!", nameof(type));
-
- if (_customTypeSerializers.TryGetValue(type, out var obj)) return obj;
-
- obj = Activator.CreateInstance(type)!;
- _customTypeSerializers[type] = obj;
- return obj;
- }
-
- private object? RegisterSerializer(Type type)
- {
- var writerInterfaces = type.GetInterfaces()
- .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeWriter<>)).ToArray();
- var readerInterfaces = type.GetInterfaces()
- .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeReader<,>)).ToArray();
- var copierInterfaces = type.GetInterfaces()
- .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeCopier<>)).ToArray();
- var validatorInterfaces = type.GetInterfaces()
- .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeValidator<,>)).ToArray();
-
- if (readerInterfaces.Length == 0 && writerInterfaces.Length == 0 && copierInterfaces.Length == 0 && validatorInterfaces.Length == 0)
- {
- throw new InvalidOperationException(
- "Tried to register TypeReader/Writer/Copier that had none of the interfaces inherited.");
- }
-
- if (type.IsGenericTypeDefinition)
- {
- foreach (var writerInterface in writerInterfaces)
- {
- if (!_genericWriterTypes.TryAdd(writerInterface.GetGenericArguments()[0], type))
- Logger.ErrorS(LogCategory, $"Tried registering generic writer for type {writerInterface.GetGenericArguments()[0]} twice");
- }
-
- foreach (var readerInterface in readerInterfaces)
- {
- if (!_genericReaderTypes.TryAdd((readerInterface.GetGenericArguments()[0], readerInterface.GetGenericArguments()[1]), type))
- Logger.ErrorS(LogCategory, $"Tried registering generic reader for type {readerInterface.GetGenericArguments()[0]} and datanode {readerInterface.GetGenericArguments()[1]} twice");
- }
-
- foreach (var copierInterface in copierInterfaces)
- {
- if (!_genericCopierTypes.TryAdd(copierInterface.GetGenericArguments()[0], type))
- Logger.ErrorS(LogCategory, $"Tried registering generic copier for type {copierInterface.GetGenericArguments()[0]} twice");
- }
-
- foreach (var validatorInterface in validatorInterfaces)
- {
- if (!_genericValidatorTypes.TryAdd((validatorInterface.GetGenericArguments()[0], validatorInterface.GetGenericArguments()[1]), type))
- Logger.ErrorS(LogCategory, $"Tried registering generic reader for type {validatorInterface.GetGenericArguments()[0]} and datanode {validatorInterface.GetGenericArguments()[1]} twice");
- }
-
- return null;
- }
- else
- {
- var serializer = GetTypeSerializer(type);
-
- foreach (var writerInterface in writerInterfaces)
- {
- if (!_typeWriters.TryAdd(writerInterface.GetGenericArguments()[0], serializer))
- Logger.ErrorS(LogCategory, $"Tried registering writer for type {writerInterface.GetGenericArguments()[0]} twice");
- }
-
- foreach (var readerInterface in readerInterfaces)
- {
- if (!_typeReaders.TryAdd((readerInterface.GetGenericArguments()[0], readerInterface.GetGenericArguments()[1]), serializer))
- Logger.ErrorS(LogCategory, $"Tried registering reader for type {readerInterface.GetGenericArguments()[0]} and datanode {readerInterface.GetGenericArguments()[1]} twice");
- }
-
- foreach (var copierInterface in copierInterfaces)
- {
- if (!_typeCopiers.TryAdd(copierInterface.GetGenericArguments()[0], serializer))
- Logger.ErrorS(LogCategory, $"Tried registering copier for type {copierInterface.GetGenericArguments()[0]} twice");
- }
-
- foreach (var validatorInterface in validatorInterfaces)
- {
- if (!_typeValidators.TryAdd((validatorInterface.GetGenericArguments()[0], validatorInterface.GetGenericArguments()[1]), serializer))
- Logger.ErrorS(LogCategory, $"Tried registering reader for type {validatorInterface.GetGenericArguments()[0]} and datanode {validatorInterface.GetGenericArguments()[1]} twice");
- }
-
- return serializer;
- }
- }
-
- #region TryGetGeneric
- private bool TryGetGenericReader([NotNullWhen(true)] out ITypeReader? rawReader)
- where TNode : DataNode where T : notnull
- {
- rawReader = null;
-
- if (typeof(T).IsGenericType)
- {
- var typeDef = typeof(T).GetGenericTypeDefinition();
-
- Type? serializerTypeDef = null;
-
- foreach (var (key, val) in _genericReaderTypes)
- {
- if (typeDef.HasSameMetadataDefinitionAs(key.Type) && key.DataNodeType.IsAssignableFrom(typeof(TNode)))
- {
- serializerTypeDef = val;
- break;
- }
- }
-
- if (serializerTypeDef == null) return false;
-
- var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
- rawReader = (ITypeReader) RegisterSerializer(serializerType)!;
-
- return true;
- }
-
- return false;
- }
-
- private bool TryGetGenericWriter([NotNullWhen(true)] out ITypeWriter? rawWriter) where T : notnull
- {
- rawWriter = null;
-
- if (typeof(T).IsGenericType)
- {
- var typeDef = typeof(T).GetGenericTypeDefinition();
-
- Type? serializerTypeDef = null;
-
- foreach (var (key, val) in _genericWriterTypes)
- {
- if (typeDef.HasSameMetadataDefinitionAs(key))
- {
- serializerTypeDef = val;
- break;
- }
- }
-
- if (serializerTypeDef == null) return false;
-
- var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
- rawWriter = (ITypeWriter) RegisterSerializer(serializerType)!;
-
- return true;
- }
-
- return false;
- }
-
- private bool TryGetGenericCopier([NotNullWhen(true)] out ITypeCopier? rawCopier) where T : notnull
- {
- rawCopier = null;
-
- if (typeof(T).IsGenericType)
- {
- var typeDef = typeof(T).GetGenericTypeDefinition();
-
- Type? serializerTypeDef = null;
-
- foreach (var (key, val) in _genericCopierTypes)
- {
- if (typeDef.HasSameMetadataDefinitionAs(key))
- {
- serializerTypeDef = val;
- break;
- }
- }
-
- if (serializerTypeDef == null) return false;
-
- var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
- rawCopier = (ITypeCopier) RegisterSerializer(serializerType)!;
-
- return true;
- }
-
- return false;
- }
-
- private bool TryGetGenericValidator([NotNullWhen(true)] out ITypeValidator? rawReader)
- where TNode : DataNode where T : notnull
- {
- rawReader = null;
-
- if (typeof(T).IsGenericType)
- {
- var typeDef = typeof(T).GetGenericTypeDefinition();
-
- Type? serializerTypeDef = null;
-
- foreach (var (key, val) in _genericValidatorTypes)
- {
- if (typeDef.HasSameMetadataDefinitionAs(key.Type) && key.DataNodeType.IsAssignableFrom(typeof(TNode)))
- {
- serializerTypeDef = val;
- break;
- }
- }
-
- if (serializerTypeDef == null) return false;
-
- var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
- rawReader = (ITypeValidator) RegisterSerializer(serializerType)!;
-
- return true;
- }
-
- return false;
- }
- #endregion
-
- #region TryValidate
- private bool TryValidateWithTypeValidator(
- Type type,
- DataNode node,
- IDependencyCollection dependencies,
- ISerializationContext? context,
- [NotNullWhen(true)] out ValidationNode? valid)
- {
- //TODO Paul: do this shit w/ delegates
- var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
- m.Name == nameof(TryValidateWithTypeValidator) && m.GetParameters().Length == 4).MakeGenericMethod(type, node.GetType());
-
- var arr = new object?[] {node, dependencies, context, null};
- var res = method.Invoke(this, arr);
-
- if (res as bool? ?? false)
- {
- valid = (ValidationNode) arr[3]!;
- return true;
- }
-
- valid = null;
- return false;
- }
-
- private bool TryValidateWithTypeValidator(
- TNode node,
- IDependencyCollection dependencies,
- ISerializationContext? context,
- [NotNullWhen(true)] out ValidationNode? valid)
- where T : notnull
- where TNode : DataNode
- {
- if (TryGetValidator(null, out var reader))
- {
- valid = reader.Validate(this, node, dependencies, context);
- return true;
- }
-
- valid = null;
- return false;
- }
-
- private bool TryGetValidator(
- ISerializationContext? context,
- [NotNullWhen(true)] out ITypeValidator? reader)
- where T : notnull
- where TNode : DataNode
- {
- if (context != null && context.TypeValidators.TryGetValue((typeof(T), typeof(TNode)), out var rawTypeValidator) ||
- _typeValidators.TryGetValue((typeof(T), typeof(TNode)), out rawTypeValidator))
- {
- reader = (ITypeReader) rawTypeValidator;
- return true;
- }
-
- return TryGetGenericValidator(out reader);
- }
- #endregion
-
- #region TryRead
- private bool TryGetReader(
- ISerializationContext? context,
- [NotNullWhen(true)] out ITypeReader? reader)
- where T : notnull
- where TNode : DataNode
- {
- if (context != null && context.TypeReaders.TryGetValue((typeof(T), typeof(TNode)), out var rawTypeReader) ||
- _typeReaders.TryGetValue((typeof(T), typeof(TNode)), out rawTypeReader))
- {
- reader = (ITypeReader) rawTypeReader;
- return true;
- }
-
- return TryGetGenericReader(out reader);
- }
-
- private bool TryReadWithTypeSerializers(
- Type type,
- DataNode node,
- IDependencyCollection dependencies,
- [NotNullWhen(true)] out DeserializationResult? obj,
- bool skipHook,
- ISerializationContext? context = null)
- {
- //TODO Paul: do this shit w/ delegates
- var method = typeof(SerializationManager).GetRuntimeMethods()
- .First(m => m.Name == nameof(TryReadWithTypeSerializers) && m.GetParameters().Length == 5)
- .MakeGenericMethod(type, node.GetType());
-
- obj = default;
-
- var arr = new object?[] {node, dependencies, obj, skipHook, context};
- var res = method.Invoke(this, arr);
-
- if (res as bool? ?? false)
- {
- obj = (DeserializationResult) arr[2]!;
- return true;
- }
-
- return false;
- }
-
- private bool TryReadWithTypeSerializers(
- TNode node,
- IDependencyCollection dependencies,
- [NotNullWhen(true)] out DeserializationResult? obj,
- bool skipHook,
- ISerializationContext? context = null)
- where T : notnull
- where TNode : DataNode
- {
- if (TryGetReader(context, out var reader))
- {
- obj = reader.Read(this, node, dependencies, skipHook, context);
- return true;
- }
-
- obj = null;
- return false;
- }
- #endregion
-
- #region TryWrite
- private bool TryWriteWithTypeSerializers(
- Type type,
- object obj,
- [NotNullWhen(true)] out DataNode? node,
- bool alwaysWrite = false,
- ISerializationContext? context = null)
- {
- //TODO Paul: do this shit w/ delegates
- var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
- m.Name == nameof(TryWriteWithTypeSerializers) && m.GetParameters().Length == 4).MakeGenericMethod(type);
-
- node = null;
-
- var arr = new[] {obj, node, alwaysWrite, context};
- var res = method.Invoke(this, arr);
-
- if (res as bool? ?? false)
- {
- node = (DataNode) arr[1]!;
- return true;
- }
-
- return false;
- }
-
- private bool TryGetWriter(ISerializationContext? context, [NotNullWhen(true)] out ITypeWriter? writer) where T : notnull
- {
- if (context != null && context.TypeWriters.TryGetValue(typeof(T), out var rawTypeWriter) ||
- _typeWriters.TryGetValue(typeof(T), out rawTypeWriter))
- {
- writer = (ITypeWriter) rawTypeWriter;
- return true;
- }
-
- return TryGetGenericWriter(out writer);
- }
-
- private bool TryWriteWithTypeSerializers(
- T obj,
- [NotNullWhen(true)] out DataNode? node,
- bool alwaysWrite = false,
- ISerializationContext? context = null) where T : notnull
- {
- node = default;
- if (TryGetWriter(context, out var writer))
- {
- node = writer.Write(this, obj, alwaysWrite, context);
- return true;
- }
-
- return false;
- }
- #endregion
-
- #region TryCopy
- private bool TryCopyWithTypeCopier(Type type, object source, ref object target, bool skipHook, ISerializationContext? context = null)
- {
- //TODO Paul: do this shit w/ delegates
- var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
- m.Name == nameof(TryCopyWithTypeCopier) && m.GetParameters().Length == 4).MakeGenericMethod(type, source.GetType(), target.GetType());
-
- var arr = new[] {source, target, skipHook, context};
- var res = method.Invoke(this, arr);
-
- if (res as bool? ?? false)
- {
- target = arr[1]!;
- return true;
- }
-
- return false;
- }
-
- private bool TryCopyWithTypeCopier(
- TSource source,
- ref TTarget target,
- bool skipHook,
- ISerializationContext? context = null)
- where TSource : TCommon
- where TTarget : TCommon
- where TCommon : notnull
- {
- object? rawTypeCopier;
-
- if (context != null &&
- context.TypeCopiers.TryGetValue(typeof(TCommon), out rawTypeCopier) ||
- _typeCopiers.TryGetValue(typeof(TCommon), out rawTypeCopier))
- {
- var ser = (ITypeCopier) rawTypeCopier;
- target = (TTarget) ser.Copy(this, source, target, skipHook, context);
- return true;
- }
-
- if (TryGetGenericCopier(out ITypeCopier? genericTypeWriter))
- {
- target = (TTarget) genericTypeWriter.Copy(this, source, target, skipHook, context);
- return true;
- }
-
- return false;
- }
- #endregion
-
- #region Custom
-
- private DeserializationResult ReadWithCustomTypeSerializer(TNode node, ISerializationContext? context = null, bool skipHook = false)
- where TSerializer : ITypeReader where T : notnull where TNode : DataNode
- {
- var serializer = (ITypeReader)GetTypeSerializer(typeof(TSerializer));
- return serializer.Read(this, node, DependencyCollection, skipHook, context);
- }
-
- private DataNode WriteWithCustomTypeSerializer(T value,
- ISerializationContext? context = null, bool alwaysWrite = false)
- where TSerializer : ITypeWriter where T : notnull
- {
- var serializer = (ITypeWriter)GetTypeSerializer(typeof(TSerializer));
- return serializer.Write(this, value, alwaysWrite, context);
- }
-
- private TCommon CopyWithCustomTypeSerializer(
- TSource source,
- TTarget target,
- bool skipHook,
- ISerializationContext? context = null)
- where TSource : TCommon
- where TTarget : TCommon
- where TCommon : notnull
- where TSerializer : ITypeCopier
- {
- var serializer = (ITypeCopier) GetTypeSerializer(typeof(TSerializer));
- return serializer.Copy(this, source, target, skipHook, context);
- }
-
- private ValidationNode ValidateWithCustomTypeSerializer(
- TNode node,
- ISerializationContext? context)
- where T : notnull
- where TNode : DataNode
- where TSerializer : ITypeValidator
- {
- var serializer = (ITypeValidator) GetTypeSerializer(typeof(TSerializer));
- return serializer.Validate(this, node, DependencyCollection, context);
- }
-
- #endregion
- }
-}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Custom/ConstantSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Custom/ConstantSerializer.cs
index 7c2c06440..79c69bf0e 100644
--- a/Robust.Shared/Serialization/TypeSerializers/Implementations/Custom/ConstantSerializer.cs
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Custom/ConstantSerializer.cs
@@ -21,7 +21,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
{
var constType = serializationManager.GetConstantTypeFromTag(typeof(TTag));
- return DeserializationResult.Value((int) Enum.Parse(constType, node.Value));
+ return new DeserializedValue((int) Enum.Parse(constType, node.Value));
}
public DataNode Write(ISerializationManager serializationManager, int value, bool alwaysWrite = false,
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Custom/FlagSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Custom/FlagSerializer.cs
index 4b385101b..acf9db661 100644
--- a/Robust.Shared/Serialization/TypeSerializers/Implementations/Custom/FlagSerializer.cs
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Custom/FlagSerializer.cs
@@ -22,7 +22,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
{
var flagType = serializationManager.GetFlagTypeFromTag(typeof(TTag));
- return DeserializationResult.Value((int)Enum.Parse(flagType, node.Value));
+ return new DeserializedValue((int)Enum.Parse(flagType, node.Value));
}
public DataNode Write(ISerializationManager serializationManager, int value, bool alwaysWrite = false,
@@ -89,7 +89,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom
flags |= (int) Enum.Parse(flagType, valueDataNode.Value);
}
- return DeserializationResult.Value(flags);
+ return new DeserializedValue(flags);
}
}
}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/BooleanSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/BooleanSerializer.cs
new file mode 100644
index 000000000..2dd2ad1be
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/BooleanSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class BooleanSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return bool.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing boolean value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(bool.Parse(node.Value));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, bool value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public bool Copy(ISerializationManager serializationManager, bool source, bool target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/ByteSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/ByteSerializer.cs
new file mode 100644
index 000000000..7ff4fe1f1
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/ByteSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class ByteSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return byte.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing byte value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(byte.Parse(node.Value, CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, byte value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public byte Copy(ISerializationManager serializationManager, byte source, byte target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/CharSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/CharSerializer.cs
new file mode 100644
index 000000000..fb0e0c5b5
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/CharSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class CharSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return char.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing char value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(char.Parse(node.Value));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, char value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public char Copy(ISerializationManager serializationManager, char source, char target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/DecimalSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/DecimalSerializer.cs
new file mode 100644
index 000000000..a97bf4437
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/DecimalSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class DecimalSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return decimal.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing decimal value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(decimal.Parse(node.Value, CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, decimal value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public decimal Copy(ISerializationManager serializationManager, decimal source, decimal target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/DoubleSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/DoubleSerializer.cs
new file mode 100644
index 000000000..2d641070b
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/DoubleSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class DoubleSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return bool.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing double value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(double.Parse(node.Value, CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, double value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public double Copy(ISerializationManager serializationManager, double source, double target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/FloatSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/FloatSerializer.cs
new file mode 100644
index 000000000..24a89211e
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/FloatSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class FloatSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return float.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing float value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(float.Parse(node.Value, CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, float value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public float Copy(ISerializationManager serializationManager, float source, float target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/IntSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/IntSerializer.cs
new file mode 100644
index 000000000..82e1f87c1
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/IntSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class IntSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return int.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing int value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(int.Parse(node.Value, CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, int value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public int Copy(ISerializationManager serializationManager, int source, int target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/LongSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/LongSerializer.cs
new file mode 100644
index 000000000..ea68a5904
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/LongSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class LongSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return long.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing long value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(long.Parse(node.Value, CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, long value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public long Copy(ISerializationManager serializationManager, long source, long target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/SByteSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/SByteSerializer.cs
new file mode 100644
index 000000000..56e8ae7f4
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/SByteSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class SByteSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return sbyte.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing signed byte value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(sbyte.Parse(node.Value, CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, sbyte value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public sbyte Copy(ISerializationManager serializationManager, sbyte source, sbyte target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/ShortSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/ShortSerializer.cs
new file mode 100644
index 000000000..e114d91b0
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/ShortSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class ShortSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return short.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing short value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(short.Parse(node.Value, CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, short value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public short Copy(ISerializationManager serializationManager, short source, short target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/UIntSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/UIntSerializer.cs
new file mode 100644
index 000000000..3d92002bb
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/UIntSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class UIntSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return uint.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing unsigned int value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(uint.Parse(node.Value, CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, uint value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public uint Copy(ISerializationManager serializationManager, uint source, uint target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/ULongSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/ULongSerializer.cs
new file mode 100644
index 000000000..599e408ef
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/ULongSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class ULongSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return ulong.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing unsigned long value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(ulong.Parse(node.ToString(), CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, ulong value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public ulong Copy(ISerializationManager serializationManager, ulong source, ulong target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/UShortSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/UShortSerializer.cs
new file mode 100644
index 000000000..38ef2c5ba
--- /dev/null
+++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Primitive/UShortSerializer.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+using Robust.Shared.IoC;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Robust.Shared.Serialization.Manager.Result;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
+{
+ [TypeSerializer]
+ public class UShortSerializer : ITypeSerializer
+ {
+ public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, ISerializationContext? context = null)
+ {
+ return ushort.TryParse(node.Value, out _)
+ ? new ValidatedValueNode(node)
+ : new ErrorNode(node, $"Failed parsing unsigned short value: {node.Value}");
+ }
+
+ public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
+ IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
+ {
+ return new DeserializedValue(ushort.Parse(node.Value, CultureInfo.InvariantCulture));
+ }
+
+ public DataNode Write(ISerializationManager serializationManager, ushort value, bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public ushort Copy(ISerializationManager serializationManager, ushort source, ushort target, bool skipHook,
+ ISerializationContext? context = null)
+ {
+ return source;
+ }
+ }
+}
diff --git a/Robust.Shared/Serialization/TypeSerializers/Interfaces/ITypeReader.cs b/Robust.Shared/Serialization/TypeSerializers/Interfaces/ITypeReader.cs
index 14b5b3bfd..04171c56c 100644
--- a/Robust.Shared/Serialization/TypeSerializers/Interfaces/ITypeReader.cs
+++ b/Robust.Shared/Serialization/TypeSerializers/Interfaces/ITypeReader.cs
@@ -1,15 +1,14 @@
-using JetBrains.Annotations;
-using Robust.Shared.IoC;
+using Robust.Shared.IoC;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Result;
using Robust.Shared.Serialization.Markdown;
-using Robust.Shared.Serialization.Markdown.Validation;
namespace Robust.Shared.Serialization.TypeSerializers.Interfaces
{
- public interface ITypeReader<[UsedImplicitly]TType, TNode> : ITypeValidator where TType : notnull where TNode : DataNode
+ public interface ITypeReader : ITypeValidator where TType : notnull where TNode : DataNode
{
- DeserializationResult Read(ISerializationManager serializationManager,
+ DeserializationResult Read(
+ ISerializationManager serializationManager,
TNode node,
IDependencyCollection dependencies,
bool skipHook,