From 361a7c53b919bca9235e550315747f758a473492 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 9 Feb 2024 17:50:39 +0300 Subject: [PATCH] wip: Serialization --- .../BehaviourYamlConverter.cs | 62 ++++++++++++++++ Engine.Serialization/DTOs/BehaviourDTO.cs | 7 ++ Engine.Serialization/DTOs/GameObjectDTO.cs | 11 +++ Engine.Serialization/DTOs/StateEnableDto.cs | 6 ++ Engine.Serialization/DTOs/TransformDTO.cs | 10 +++ .../Engine.Serialization.csproj | 17 +++++ .../GameObjectYamlConverter.cs | 70 +++++++++++++++++++ Engine.Serialization/ISerializer.cs | 7 ++ Engine.Serialization/InternalExtensions.cs | 25 +++++++ .../StateEnableYamlConverter.cs | 52 ++++++++++++++ .../TransformYamlConverter.cs | 69 ++++++++++++++++++ Engine.Serialization/Vector2DYamlConverter.cs | 48 +++++++++++++ Engine.Serialization/YamlSerializer.cs | 27 +++++++ 13 files changed, 411 insertions(+) create mode 100644 Engine.Serialization/BehaviourYamlConverter.cs create mode 100644 Engine.Serialization/DTOs/BehaviourDTO.cs create mode 100644 Engine.Serialization/DTOs/GameObjectDTO.cs create mode 100644 Engine.Serialization/DTOs/StateEnableDto.cs create mode 100644 Engine.Serialization/DTOs/TransformDTO.cs create mode 100644 Engine.Serialization/Engine.Serialization.csproj create mode 100644 Engine.Serialization/GameObjectYamlConverter.cs create mode 100644 Engine.Serialization/ISerializer.cs create mode 100644 Engine.Serialization/InternalExtensions.cs create mode 100644 Engine.Serialization/StateEnableYamlConverter.cs create mode 100644 Engine.Serialization/TransformYamlConverter.cs create mode 100644 Engine.Serialization/Vector2DYamlConverter.cs create mode 100644 Engine.Serialization/YamlSerializer.cs diff --git a/Engine.Serialization/BehaviourYamlConverter.cs b/Engine.Serialization/BehaviourYamlConverter.cs new file mode 100644 index 0000000..3fdc7fd --- /dev/null +++ b/Engine.Serialization/BehaviourYamlConverter.cs @@ -0,0 +1,62 @@ +using System; + +using Engine.Serialization.DTOs; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Engine.Serialization; + +internal class BehaviourYamlConverter : IYamlTypeConverter +{ + public bool Accepts(Type type) => type == typeof(BehaviourDTO); + + public object ReadYaml(IParser parser, Type type) + { + if (parser.Current is not MappingStart) + throw new InvalidOperationException("Expected MappingStart"); + + parser.MoveNext(); + var behaviour = new BehaviourDTO(); + while (parser.Current != null && parser.Current is not MappingEnd) + { + var propertyName = ((Scalar)parser.Current).Value; + parser.MoveNext(); + switch (propertyName) + { + case nameof(BehaviourDTO.ClassName): + behaviour.ClassName = ((Scalar)parser.Current).Value; + break; + case nameof(BehaviourDTO.Priority): + behaviour.Priority = int.Parse(((Scalar)parser.Current).Value); + break; + case nameof(BehaviourDTO.StateEnable): + behaviour.StateEnable = (StateEnableDTO)(new StateEnableYamlConverter().ReadYaml(parser, typeof(StateEnableDTO)) ?? new Exception()); + break; + } + parser.MoveNext(); + } + return behaviour; + } + + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var behaviour = (BehaviourDTO)(value ?? throw new Exception()); + + StateEnableYamlConverter stateEnableYamlConverter = new(); + + emitter.Emit(new MappingStart()); + + emitter.Emit(new Scalar(nameof(BehaviourDTO.ClassName))); + emitter.Emit(new Scalar(behaviour.ClassName.ToString())); + + emitter.Emit(new Scalar(nameof(BehaviourDTO.Priority))); + emitter.Emit(new Scalar(behaviour.Priority.ToString())); + + emitter.Emit(new Scalar(nameof(BehaviourDTO.StateEnable))); + stateEnableYamlConverter.WriteYaml(emitter, behaviour.StateEnable, typeof(StateEnableDTO)); + + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/DTOs/BehaviourDTO.cs b/Engine.Serialization/DTOs/BehaviourDTO.cs new file mode 100644 index 0000000..e988cfa --- /dev/null +++ b/Engine.Serialization/DTOs/BehaviourDTO.cs @@ -0,0 +1,7 @@ +namespace Engine.Serialization.DTOs; + +internal record struct BehaviourDTO( + string ClassName, + int Priority, + StateEnableDTO StateEnable +); diff --git a/Engine.Serialization/DTOs/GameObjectDTO.cs b/Engine.Serialization/DTOs/GameObjectDTO.cs new file mode 100644 index 0000000..951d566 --- /dev/null +++ b/Engine.Serialization/DTOs/GameObjectDTO.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Engine.Serialization.DTOs; + +internal record struct GameObjectDTO( + string Id, + string Name, + TransformDTO Transform, + List Behaviours, + StateEnableDTO StateEnable +); diff --git a/Engine.Serialization/DTOs/StateEnableDto.cs b/Engine.Serialization/DTOs/StateEnableDto.cs new file mode 100644 index 0000000..9293d7f --- /dev/null +++ b/Engine.Serialization/DTOs/StateEnableDto.cs @@ -0,0 +1,6 @@ +namespace Engine.Serialization.DTOs; + +internal record struct StateEnableDTO( + string ClassName, + bool Enabled +); diff --git a/Engine.Serialization/DTOs/TransformDTO.cs b/Engine.Serialization/DTOs/TransformDTO.cs new file mode 100644 index 0000000..f0b88bb --- /dev/null +++ b/Engine.Serialization/DTOs/TransformDTO.cs @@ -0,0 +1,10 @@ +using Syntriax.Engine.Core; + +namespace Engine.Serialization.DTOs; + +internal record struct TransformDTO( + string? ParentId, + Vector2D Position, + Vector2D Scale, + float Rotation +); diff --git a/Engine.Serialization/Engine.Serialization.csproj b/Engine.Serialization/Engine.Serialization.csproj new file mode 100644 index 0000000..f7a4776 --- /dev/null +++ b/Engine.Serialization/Engine.Serialization.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + disable + enable + + + + + + + + + + + diff --git a/Engine.Serialization/GameObjectYamlConverter.cs b/Engine.Serialization/GameObjectYamlConverter.cs new file mode 100644 index 0000000..a13eec9 --- /dev/null +++ b/Engine.Serialization/GameObjectYamlConverter.cs @@ -0,0 +1,70 @@ +using System; +using System.Linq; + +using Engine.Serialization.DTOs; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Engine.Serialization; + +internal class GameObjectYamlConverter : IYamlTypeConverter +{ + public bool Accepts(Type type) => type == typeof(GameObjectDTO); + + public object ReadYaml(IParser parser, Type type) + { + if (parser.Current is not MappingStart) + throw new InvalidOperationException("Expected MappingStart"); + + parser.MoveNext(); + var gameObject = new GameObjectDTO(); + while (parser.Current != null && parser.Current is not MappingEnd) + { + var propertyName = ((Scalar)parser.Current).Value; + parser.MoveNext(); + switch (propertyName) + { + case nameof(GameObjectDTO.Id): + gameObject.Id = ((Scalar)parser.Current).Value; + break; + case nameof(GameObjectDTO.Name): + gameObject.Name = ((Scalar)parser.Current).Value; + break; + case nameof(GameObjectDTO.Transform): + gameObject.Transform = (TransformDTO)(new TransformYamlConverter().ReadYaml(parser, typeof(TransformDTO)) ?? new Exception()); + break; + // case nameof(GameObjectDTO.Behaviours): + // gameObject.Rotation = (List)(new BehaviourYamlConverter().ReadYaml(parser, typeof(BehaviourDTO)) ?? new Exception()); + // break; + case nameof(GameObjectDTO.StateEnable): + gameObject.StateEnable = (StateEnableDTO)(new StateEnableYamlConverter().ReadYaml(parser, typeof(StateEnableDTO)) ?? new Exception()); + break; + } + parser.MoveNext(); + } + return gameObject; + } + + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var gameObject = (GameObjectDTO)(value ?? throw new Exception()); + + Vector2DYamlConverter vector2DYamlConverter = new(); + StateEnableYamlConverter stateEnableYamlConverter = new(); + + emitter.Emit(new MappingStart()); + emitter.Emit(new Scalar(nameof(GameObjectDTO.Id))); + emitter.Emit(new Scalar(gameObject.Id.ToString())); + emitter.Emit(new Scalar(nameof(GameObjectDTO.Name))); + emitter.Emit(new Scalar(gameObject.Name.ToString())); + emitter.Emit(new Scalar(nameof(GameObjectDTO.Transform))); + vector2DYamlConverter.WriteYaml(emitter, gameObject.Transform, typeof(TransformDTO)); + // emitter.Emit(new Scalar(nameof(GameObjectDTO.Behaviours))); + // vector2DYamlConverter.WriteYaml(emitter, gameObject.Behaviours, typeof(BehavioursDTO)); + emitter.Emit(new Scalar(nameof(GameObjectDTO.StateEnable))); + stateEnableYamlConverter.WriteYaml(emitter, gameObject.StateEnable, typeof(StateEnableDTO)); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/ISerializer.cs b/Engine.Serialization/ISerializer.cs new file mode 100644 index 0000000..48fed94 --- /dev/null +++ b/Engine.Serialization/ISerializer.cs @@ -0,0 +1,7 @@ +namespace Engine.Serialization; + +public interface ISerializer +{ + public string Serialize(T @object); + public T Deserialize(string serializedString); +} diff --git a/Engine.Serialization/InternalExtensions.cs b/Engine.Serialization/InternalExtensions.cs new file mode 100644 index 0000000..0f5b8b5 --- /dev/null +++ b/Engine.Serialization/InternalExtensions.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Engine.Serialization.DTOs; +using Syntriax.Engine.Core.Abstract; + +namespace Engine.Serialization; + +internal static class InternalExtensions +{ + public static TransformDTO ToDTO(this ITransform transform) + => new(transform.Parent?.GameObject.Id, transform.Position, transform.Scale, transform.Rotation); + + public static GameObjectDTO ToDTO(this IGameObject gameObject) + => new(gameObject.Id, gameObject.Name, gameObject.Transform.ToDTO(), gameObject.BehaviourController.ToDTO(), gameObject.StateEnable.ToDTO()); + + public static StateEnableDTO ToDTO(this IStateEnable stateEnable) + => new(stateEnable.GetType().FullName ?? throw new System.Exception(), stateEnable.Enabled); + + public static List ToDTO(this IBehaviourController behaviourController) + { + List dtos = []; + foreach (var behaviour in behaviourController) + dtos.Add(new(behaviour.GetType().FullName ?? throw new System.Exception(), behaviour.Priority, behaviour.StateEnable.ToDTO())); + return dtos; + } +} diff --git a/Engine.Serialization/StateEnableYamlConverter.cs b/Engine.Serialization/StateEnableYamlConverter.cs new file mode 100644 index 0000000..e281d45 --- /dev/null +++ b/Engine.Serialization/StateEnableYamlConverter.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq; + +using Engine.Serialization.DTOs; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Engine.Serialization; + +internal class StateEnableYamlConverter : IYamlTypeConverter +{ + public bool Accepts(Type type) => type == typeof(StateEnableDTO); + + public object ReadYaml(IParser parser, Type type) + { + if (parser.Current is not MappingStart) + throw new InvalidOperationException("Expected MappingStart"); + + parser.MoveNext(); + var stateEnable = new StateEnableDTO(); + while (parser.Current != null && parser.Current is not MappingEnd) + { + var propertyName = ((Scalar)parser.Current).Value; + parser.MoveNext(); + switch (propertyName) + { + case nameof(StateEnableDTO.ClassName): + stateEnable.ClassName = ((Scalar)parser.Current).Value; + break; + case nameof(StateEnableDTO.Enabled): + stateEnable.Enabled = bool.Parse(((Scalar)parser.Current).Value); + break; + } + parser.MoveNext(); + } + return stateEnable; + } + + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var stateEnable = (StateEnableDTO)(value ?? throw new Exception()); + + emitter.Emit(new MappingStart()); + emitter.Emit(new Scalar(nameof(StateEnableDTO.ClassName))); + emitter.Emit(new Scalar(stateEnable.ClassName)); + emitter.Emit(new Scalar(nameof(StateEnableDTO.Enabled))); + emitter.Emit(new Scalar(stateEnable.Enabled.ToString())); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/TransformYamlConverter.cs b/Engine.Serialization/TransformYamlConverter.cs new file mode 100644 index 0000000..1dccd7c --- /dev/null +++ b/Engine.Serialization/TransformYamlConverter.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; + +using Engine.Serialization.DTOs; + +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Engine.Serialization; + +internal class TransformYamlConverter : IYamlTypeConverter +{ + public bool Accepts(Type type) => type == typeof(TransformDTO); + + public object ReadYaml(IParser parser, Type type) + { + if (parser.Current is not MappingStart) + throw new InvalidOperationException("Expected MappingStart"); + + parser.MoveNext(); + var transform = new TransformDTO(); + while (parser.Current != null && parser.Current is not MappingEnd) + { + var propertyName = ((Scalar)parser.Current).Value; + parser.MoveNext(); + switch (propertyName) + { + case nameof(TransformDTO.ParentId): + transform.ParentId = new Vector2DYamlConverter().ReadYaml(parser, typeof(Vector2D))?.ToString(); + break; + case nameof(TransformDTO.Position): + transform.Position = (Vector2D)(new Vector2DYamlConverter().ReadYaml(parser, typeof(Vector2D)) ?? new Exception()); + break; + case nameof(TransformDTO.Scale): + transform.Scale = (Vector2D)(new Vector2DYamlConverter().ReadYaml(parser, typeof(Vector2D)) ?? new Exception()); + break; + case nameof(TransformDTO.Rotation): + transform.Rotation = float.Parse(((Scalar)parser.Current).Value); + break; + } + parser.MoveNext(); + } + return transform; + } + + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var transform = (TransformDTO)(value ?? throw new Exception()); + + Vector2DYamlConverter vector2DYamlConverter = new(); + + emitter.Emit(new MappingStart()); + if (transform.ParentId is not null) + { + emitter.Emit(new Scalar(nameof(TransformDTO.ParentId))); + emitter.Emit(new Scalar(transform.ParentId)); + } + emitter.Emit(new Scalar(nameof(TransformDTO.Position))); + vector2DYamlConverter.WriteYaml(emitter, transform.Position, typeof(Vector2D)); + emitter.Emit(new Scalar(nameof(TransformDTO.Scale))); + vector2DYamlConverter.WriteYaml(emitter, transform.Scale, typeof(Vector2D)); + emitter.Emit(new Scalar(nameof(TransformDTO.Rotation))); + emitter.Emit(new Scalar(transform.Rotation.ToString())); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/Vector2DYamlConverter.cs b/Engine.Serialization/Vector2DYamlConverter.cs new file mode 100644 index 0000000..a77766a --- /dev/null +++ b/Engine.Serialization/Vector2DYamlConverter.cs @@ -0,0 +1,48 @@ +using System; + +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Engine.Serialization; + +internal class Vector2DYamlConverter : IYamlTypeConverter +{ + public bool Accepts(Type type) => type == typeof(Vector2D); + + public object? ReadYaml(IParser parser, Type type) + { + if (parser.Current is not MappingStart) + throw new InvalidOperationException("Expected MappingStart"); + + parser.MoveNext(); + float x = 0.0f; + float y = 0.0f; + + while (parser.Current != null && parser.Current is not MappingEnd) + { + var propertyName = ((Scalar)parser.Current).Value; + parser.MoveNext(); + switch (propertyName) + { + case nameof(Vector2D.X): x = float.Parse(((Scalar)parser.Current).Value); break; + case nameof(Vector2D.Y): y = float.Parse(((Scalar)parser.Current).Value); break; + } + parser.MoveNext(); + } + return new Vector2D(x, y); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var vector = (Vector2D)(value ?? throw new Exception()); + emitter.Emit(new MappingStart()); + emitter.Emit(new Scalar(nameof(Vector2D.X))); + emitter.Emit(new Scalar(vector.X.ToString())); + emitter.Emit(new Scalar(nameof(Vector2D.Y))); + emitter.Emit(new Scalar(vector.Y.ToString())); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/YamlSerializer.cs b/Engine.Serialization/YamlSerializer.cs new file mode 100644 index 0000000..b6fa71b --- /dev/null +++ b/Engine.Serialization/YamlSerializer.cs @@ -0,0 +1,27 @@ +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Engine.Serialization; + +public class YamlSerializer : ISerializer +{ + private readonly YamlDotNet.Serialization.ISerializer serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .WithTypeConverter(new Vector2DYamlConverter()) + .WithTypeConverter(new TransformYamlConverter()) + .WithTypeConverter(new BehaviourYamlConverter()) + .WithTypeConverter(new GameObjectYamlConverter()) + .WithTypeConverter(new StateEnableYamlConverter()) + .Build(); + private readonly YamlDotNet.Serialization.IDeserializer deserializer = new DeserializerBuilder() + .WithNamingConvention(UnderscoredNamingConvention.Instance) + .WithTypeConverter(new Vector2DYamlConverter()) + .WithTypeConverter(new TransformYamlConverter()) + .WithTypeConverter(new BehaviourYamlConverter()) + .WithTypeConverter(new GameObjectYamlConverter()) + .WithTypeConverter(new StateEnableYamlConverter()) + .Build(); + + public string Serialize(T @object) => serializer.Serialize(@object); + public T Deserialize(string serializedString) => deserializer.Deserialize(serializedString); +}