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,