BREAKING CHANGE: moved yaml serialization from Engine.Serialization to Engine.Integration

This commit is contained in:
2025-08-05 19:48:49 +03:00
parent 3d183b21cd
commit 65dcb0c564
29 changed files with 8 additions and 11 deletions

View File

@@ -0,0 +1,13 @@
using Engine.Core;
using Engine.Core.Serialization;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public interface IEngineTypeYamlConverter : IYamlTypeConverter
{
YamlSerializer Serializer { get; set; }
EntityRegistry EntityRegistry { get; set; }
IProgressionTracker ProgressionTracker { get; set; }
}

View File

@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Engine.Core;
using Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class BehaviourControllerConverter : EngineTypeYamlSerializerBase<IBehaviourController>
{
private const string BEHAVIOURS_SCALAR_NAME = "Behaviours";
public override IBehaviourController? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Reading behaviour controller");
string id;
IBehaviourController behaviourController;
IStateEnable stateEnable;
List<IBehaviour> behaviours;
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IBehaviourController.Id)) != 0)
throw new();
id = parser.Consume<Scalar>().Value;
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
behaviourController = (IBehaviourController)instanceSerializedClass.CreateInstance(EntityRegistry);
string value = parser.Consume<Scalar>().Value;
if (value.CompareTo(nameof(IBehaviourController.StateEnable)) != 0)
throw new();
stateEnable = (IStateEnable)rootDeserializer(typeof(IStateEnable))!;
if (parser.Consume<Scalar>().Value.CompareTo(BEHAVIOURS_SCALAR_NAME) != 0)
throw new();
behaviours = (List<IBehaviour>)rootDeserializer(typeof(List<IBehaviour>))!;
parser.Consume<MappingEnd>();
behaviourController.Id = id;
stateEnable.Assign(behaviourController);
behaviourController.Assign(stateEnable);
foreach (IBehaviour behaviour in behaviours)
behaviourController.AddBehaviour(behaviour);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Created {instanceSerializedClass.Type}");
return behaviourController;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
IBehaviourController behaviourController = (IBehaviourController)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing behaviour controller");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IBehaviourController.Id)));
emitter.Emit(new Scalar(behaviourController.Id));
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(behaviourController));
emitter.Emit(new Scalar(nameof(IBehaviourController.StateEnable)));
serializer(behaviourController.StateEnable);
emitter.Emit(new Scalar(BEHAVIOURS_SCALAR_NAME));
serializer(behaviourController.GetBehaviours<IBehaviour>().Where(b => !b.GetType().HasAttribute<IgnoreSerializationAttribute>()));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized behaviour controller");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,81 @@
using System;
using Engine.Core;
using Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class BehaviourConverter : EngineTypeYamlSerializerBase<IBehaviour>
{
public override IBehaviour? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Reading behaviour information");
string id;
int priority;
IBehaviour behaviour;
IStateEnable stateEnable;
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IBehaviour.Id)) != 0)
throw new();
id = parser.Consume<Scalar>().Value;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IBehaviour.Priority)) != 0)
throw new();
priority = int.Parse(parser.Consume<Scalar>().Value);
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
behaviour = (IBehaviour)instanceSerializedClass.CreateInstance(EntityRegistry);
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IBehaviour.StateEnable)) != 0)
throw new();
stateEnable = (IStateEnable)rootDeserializer(typeof(IStateEnable))!;
parser.Consume<MappingEnd>();
behaviour.Id = id;
behaviour.Priority = priority;
stateEnable.Assign(behaviour);
behaviour.Assign(stateEnable);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Created {instanceSerializedClass.Type}");
return behaviour;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
IBehaviour behaviour = (IBehaviour)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing behaviour");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IBehaviour.Id)));
emitter.Emit(new Scalar(behaviour.Id));
emitter.Emit(new Scalar(nameof(IBehaviour.Priority)));
emitter.Emit(new Scalar(behaviour.Priority.ToString()));
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(behaviour));
emitter.Emit(new Scalar(nameof(IBehaviour.StateEnable)));
serializer(behaviour.StateEnable);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized behaviour");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,33 @@
using System;
using Engine.Core;
using Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public abstract class EngineTypeYamlSerializerBase<T> : IEngineTypeYamlConverter
{
protected const string SERIALIZED_SCALAR_NAME = "Properties";
public EntityRegistry EntityRegistry { get; set; } = null!;
public YamlSerializer Serializer { get; set; } = null!;
public IProgressionTracker ProgressionTracker { get; set; } = null!;
public bool Accepts(Type type) => typeof(T).IsAssignableFrom(type);
public abstract T? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer);
public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
T? result = Read(parser, type, rootDeserializer);
if (result is IEntity entity)
EntityRegistry.Add(entity);
return result;
}
public abstract void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer);
}

View File

@@ -0,0 +1,41 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class AABBConverter : EngineTypeYamlSerializerBase<AABB>
{
public override AABB Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(AABB.LowerBoundary)) != 0)
throw new ArgumentException($"{nameof(AABB)} mapping must start with {nameof(AABB.LowerBoundary)}");
Vector2D lowerBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(AABB.UpperBoundary)) != 0)
throw new ArgumentException($"{nameof(AABB)} mapping must end with {nameof(AABB.UpperBoundary)}");
Vector2D upperBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!;
parser.Consume<MappingEnd>();
return new AABB(lowerBoundary, upperBoundary);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
AABB aabb = (AABB)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(AABB.LowerBoundary)));
serializer(aabb.LowerBoundary, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(AABB.UpperBoundary)));
serializer(aabb.UpperBoundary, typeof(Vector2D));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class CircleConverter : EngineTypeYamlSerializerBase<Circle>
{
public override Circle Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Circle.Center)) != 0)
throw new ArgumentException($"{nameof(Circle)} mapping must start with {nameof(Circle.Center)}");
Vector2D lowerBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Circle.Radius)) != 0)
throw new ArgumentException($"{nameof(Circle)} mapping must end with {nameof(Circle.Radius)}");
float radius = (float)rootDeserializer(typeof(float))!;
parser.Consume<MappingEnd>();
return new Circle(lowerBoundary, radius);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Circle circle = (Circle)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Circle.Center)));
serializer(circle.Center, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(Circle.Radius)));
serializer(circle.Radius, typeof(float));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class ColorHSVConverter : EngineTypeYamlSerializerBase<ColorHSV>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(ColorHSV).Length + 1;
public override ColorHSV Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new ColorHSV(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
ColorHSV hsv = (ColorHSV)value!;
emitter.Emit(new Scalar($"{nameof(ColorHSV)}({hsv.Hue}, {hsv.Saturation}, {hsv.Value})"));
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class ColorRGBAConverter : EngineTypeYamlSerializerBase<ColorRGBA>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(ColorRGBA).Length + 1;
public override ColorRGBA Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new ColorRGBA(byte.Parse(values[0]), byte.Parse(values[1]), byte.Parse(values[2]), byte.Parse(values[3]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
ColorRGBA rgb = (ColorRGBA)value!;
emitter.Emit(new Scalar($"{nameof(ColorRGBA)}({rgb.R}, {rgb.G}, {rgb.B}, {rgb.A})"));
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class ColorRGBConverter : EngineTypeYamlSerializerBase<ColorRGB>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(ColorRGB).Length + 1;
public override ColorRGB Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new ColorRGB(byte.Parse(values[0]), byte.Parse(values[1]), byte.Parse(values[2]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
ColorRGB rgb = (ColorRGB)value!;
emitter.Emit(new Scalar($"{nameof(ColorRGB)}({rgb.R}, {rgb.G}, {rgb.B})"));
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class Line2DConverter : EngineTypeYamlSerializerBase<Line2D>
{
public override Line2D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Line2D.From)) != 0)
throw new ArgumentException($"{nameof(Line2D)} mapping must start with {nameof(Line2D.From)}");
Vector2D from = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Line2D.To)) != 0)
throw new ArgumentException($"{nameof(Line2D)} mapping must end with {nameof(Line2D.To)}");
Vector2D to = (Vector2D)rootDeserializer(typeof(Vector2D))!;
parser.Consume<MappingEnd>();
return new Line2D(from, to);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Line2D line2D = (Line2D)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Line2D.From)));
serializer(line2D.From, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(Line2D.To)));
serializer(line2D.To, typeof(Vector2D));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class Line2DEquationConverter : EngineTypeYamlSerializerBase<Line2DEquation>
{
public override Line2DEquation Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Line2DEquation.Slope)) != 0)
throw new ArgumentException($"{nameof(Line2DEquation)} mapping must start with {nameof(Line2DEquation.Slope)}");
float slope = (float)rootDeserializer(typeof(float))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Line2DEquation.OffsetY)) != 0)
throw new ArgumentException($"{nameof(Line2DEquation)} mapping must end with {nameof(Line2DEquation.OffsetY)}");
float offset = (float)rootDeserializer(typeof(float))!;
parser.Consume<MappingEnd>();
return new Line2DEquation(slope, offset);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Line2DEquation line2DEquation = (Line2DEquation)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Line2DEquation.Slope)));
serializer(line2DEquation.Slope, typeof(float));
emitter.Emit(new Scalar(nameof(Line2DEquation.OffsetY)));
serializer(line2DEquation.OffsetY, typeof(float));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class Projection1DConverter : EngineTypeYamlSerializerBase<Projection1D>
{
public override Projection1D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Projection1D.Min)) != 0)
throw new ArgumentException($"{nameof(Projection1D)} mapping must start with {nameof(Projection1D.Min)}");
float min = (float)rootDeserializer(typeof(float))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Projection1D.Max)) != 0)
throw new ArgumentException($"{nameof(Projection1D)} mapping must end with {nameof(Projection1D.Max)}");
float max = (float)rootDeserializer(typeof(float))!;
parser.Consume<MappingEnd>();
return new Projection1D(min, max);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Projection1D projection1D = (Projection1D)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Projection1D.Min)));
serializer(projection1D.Min, typeof(float));
emitter.Emit(new Scalar(nameof(Projection1D.Max)));
serializer(projection1D.Max, typeof(float));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class QuaternionConverter : EngineTypeYamlSerializerBase<Quaternion>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(Quaternion).Length + 1;
public override Quaternion Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new Quaternion(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]), float.Parse(values[3]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Quaternion quaternion = (Quaternion)value!;
emitter.Emit(new Scalar($"{nameof(Quaternion)}({quaternion.X}, {quaternion.Y}, {quaternion.Z}, {quaternion.W})"));
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class Shape2DConverter : EngineTypeYamlSerializerBase<Shape2D>
{
public override Shape2D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Shape2D.Vertices)) != 0)
throw new ArgumentException($"{nameof(Shape2D)} mapping have a {nameof(Shape2D.Vertices)}");
parser.Consume<SequenceStart>();
List<Vector2D> vertices = [];
while (!parser.TryConsume<SequenceEnd>(out _))
{
Vector2D vertex = (Vector2D)rootDeserializer(typeof(Vector2D))!;
vertices.Add(vertex);
}
parser.Consume<MappingEnd>();
return new Shape2D(vertices);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Shape2D shape2D = (Shape2D)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Shape2D.Vertices)));
emitter.Emit(new SequenceStart(anchor: null, tag: null, isImplicit: false, style: SequenceStyle.Block));
foreach (Vector2D vertex in shape2D.Vertices)
serializer(vertex, typeof(Vector2D));
emitter.Emit(new SequenceEnd());
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,47 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class TriangleConverter : EngineTypeYamlSerializerBase<Triangle>
{
public override Triangle Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Triangle.A)) != 0)
throw new ArgumentException($"{nameof(Triangle)} mapping must start with {nameof(Triangle.A)}");
Vector2D a = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Triangle.B)) != 0)
throw new ArgumentException($"{nameof(Triangle)} mapping must have {nameof(Triangle.B)} after {nameof(Triangle.A)}");
Vector2D b = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Triangle.C)) != 0)
throw new ArgumentException($"{nameof(Triangle)} mapping must end with {nameof(Triangle.C)}");
Vector2D c = (Vector2D)rootDeserializer(typeof(Vector2D))!;
parser.Consume<MappingEnd>();
return new Triangle(a, b, c);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Triangle aabb = (Triangle)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Triangle.A)));
serializer(aabb.A, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(Triangle.B)));
serializer(aabb.B, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(Triangle.C)));
serializer(aabb.C, typeof(Vector2D));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class Vector2DConverter : EngineTypeYamlSerializerBase<Vector2D>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(Vector2D).Length + 1;
public override Vector2D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new Vector2D(float.Parse(values[0]), float.Parse(values[1]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Vector2D vector2D = (Vector2D)value!;
emitter.Emit(new Scalar($"{nameof(Vector2D)}({vector2D.X}, {vector2D.Y})"));
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class Vector3DConverter : EngineTypeYamlSerializerBase<Vector3D>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(Vector3D).Length + 1;
public override Vector3D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new Vector3D(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Vector3D vector3D = (Vector3D)value!;
emitter.Emit(new Scalar($"{nameof(Vector3D)}({vector3D.X}, {vector3D.Y}, {vector3D.Z})"));
}
}

View File

@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Engine.Core;
using Engine.Core.Factory;
using Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class SerializedClassConverter : EngineTypeYamlSerializerBase<SerializedClass>
{
public override SerializedClass? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .1f : ProgressionTracker.Progression, $"Reading class");
SerializedClass serializedClass = new();
Dictionary<string, TypeContainer> publicDictionary = [];
Dictionary<string, TypeContainer> privateDictionary = [];
parser.Consume<MappingStart>();
while (!parser.TryConsume<MappingEnd>(out _))
{
string key = parser.Consume<Scalar>().Value;
switch (key)
{
case nameof(SerializedClass.Type): serializedClass.Type = parser.Consume<Scalar>().Value; break;
case nameof(SerializedClass.Public): publicDictionary = (Dictionary<string, TypeContainer>)rootDeserializer(typeof(Dictionary<string, TypeContainer>))!; break;
case nameof(SerializedClass.Private): privateDictionary = (Dictionary<string, TypeContainer>)rootDeserializer(typeof(Dictionary<string, TypeContainer>))!; break;
}
}
foreach ((string key, TypeContainer typeContainer) in publicDictionary)
serializedClass.Public.Add(key, Serializer.InternalDeserialize(typeContainer.Value!.ToString()!, TypeFactory.GetType(typeContainer.Type)));
foreach ((string key, TypeContainer typeContainer) in privateDictionary)
serializedClass.Private.Add(key, Serializer.InternalDeserialize(typeContainer.Value!.ToString()!, TypeFactory.GetType(typeContainer.Type)));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Read {serializedClass.Type}");
return serializedClass;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
SerializedClass serializedClass = (SerializedClass)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing {serializedClass.Type}");
Dictionary<string, TypeContainer> publics = [];
Dictionary<string, TypeContainer> privates = [];
foreach ((string key, object? @object) in serializedClass.Public.Where(v => !v.GetType().HasAttribute<IgnoreSerializationAttribute>()))
publics.Add(key, new TypeContainer(@object));
foreach ((string key, object? @object) in serializedClass.Private.Where(v => !v.GetType().HasAttribute<IgnoreSerializationAttribute>()))
privates.Add(key, new TypeContainer(@object));
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(SerializedClass.Type)));
emitter.Emit(new Scalar(serializedClass.Type));
if (publics.Count > 0)
{
emitter.Emit(new Scalar(nameof(SerializedClass.Public)));
serializer(publics);
}
if (privates.Count > 0)
{
emitter.Emit(new Scalar(nameof(SerializedClass.Private)));
serializer(privates);
}
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized {serializedClass.Type}");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,60 @@
using System;
using Engine.Core;
using Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class StateEnableConverter : EngineTypeYamlSerializerBase<IStateEnable>
{
public override IStateEnable? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Reading state enable");
bool enabled;
IStateEnable stateEnable;
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IStateEnable.Enabled)) != 0)
throw new();
enabled = bool.Parse(parser.Consume<Scalar>().Value);
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
stateEnable = (IStateEnable)instanceSerializedClass.CreateInstance(EntityRegistry);
parser.Consume<MappingEnd>();
stateEnable.Enabled = enabled;
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Read state enable");
return stateEnable;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
IStateEnable stateEnable = (IStateEnable)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing state enable");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IStateEnable.Enabled)));
emitter.Emit(new Scalar(stateEnable.Enabled.ToString()));
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(stateEnable));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized state enable");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,46 @@
using System;
using Engine.Core.Serialization;
using Engine.Core.Factory;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class TypeContainerConverter : EngineTypeYamlSerializerBase<TypeContainer>
{
public override TypeContainer Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(TypeContainer.Type)) != 0)
throw new ArgumentException($"{nameof(TypeContainer)} mapping must start with {nameof(TypeContainer.Type)}");
string typeFullName = parser.Consume<Scalar>().Value;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(TypeContainer.Value)) != 0)
throw new ArgumentException($"{nameof(TypeContainer)} mapping must end with {nameof(TypeContainer.Type)}");
object? value = rootDeserializer(TypeFactory.GetType(typeFullName));
parser.Consume<MappingEnd>();
return new TypeContainer() { Type = typeFullName, Value = value };
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
TypeContainer? typeContainer = (TypeContainer)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(TypeContainer.Type)));
emitter.Emit(new Scalar(typeContainer.Type));
emitter.Emit(new Scalar(nameof(TypeContainer.Value)));
serializer(typeContainer.Value, TypeFactory.GetType(typeContainer.Type));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Engine.Core;
using Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class UniverseConverter : EngineTypeYamlSerializerBase<IUniverse>
{
public override IUniverse? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? 0.1f : ProgressionTracker.Progression, "Reading universe");
string id;
IUniverse universe;
IStateEnable stateEnable;
List<IUniverseObject> universeObjects;
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverse.Id)) != 0)
throw new();
id = parser.Consume<Scalar>().Value;
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .2f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
universe = (IUniverse)instanceSerializedClass.CreateInstance(EntityRegistry);
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverse.StateEnable)) != 0)
throw new();
stateEnable = (IStateEnable)rootDeserializer(typeof(IStateEnable))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Reading universe objects");
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverse.UniverseObjects)) != 0)
throw new();
universeObjects = (List<IUniverseObject>)rootDeserializer(typeof(List<IUniverseObject>))!;
parser.Consume<MappingEnd>();
universe.Id = id;
stateEnable.Assign(universe);
universe.Assign(stateEnable);
ProgressionTracker.Set(isTrackingController ? .9f : ProgressionTracker.Progression, "Registering universe objects");
for (int i = 0; i < universeObjects.Count; i++)
{
IUniverseObject uo = universeObjects[i];
ProgressionTracker.Set(isTrackingController ? .9f + .1f * ((float)i / universeObjects.Count) : ProgressionTracker.Progression, $"Registering {uo.Name}");
universe.Register(uo);
}
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Created {instanceSerializedClass.Type}");
return universe;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
IUniverse universe = (IUniverse)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing universe");
IEnumerable<IUniverseObject> rootUniverseObjects = universe.UniverseObjects.Where(uo => uo.Parent is null);
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IUniverse.Id)));
emitter.Emit(new Scalar(universe.Id));
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(universe));
emitter.Emit(new Scalar(nameof(IUniverse.StateEnable)));
serializer(universe.StateEnable);
emitter.Emit(new Scalar(nameof(IUniverse.UniverseObjects)));
serializer(rootUniverseObjects);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized universe");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Engine.Core;
using Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class UniverseObjectSerializer : EngineTypeYamlSerializerBase<IUniverseObject>
{
public override IUniverseObject? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
string name;
string id;
IUniverseObject universeObject;
IStateEnable stateEnable;
IBehaviourController behaviourController;
List<IUniverseObject> children;
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.Name)) != 0)
throw new();
name = parser.Consume<Scalar>().Value;
ProgressionTracker.Set(isTrackingController ? .1f : ProgressionTracker.Progression, $"Reading {name}");
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.Id)) != 0)
throw new();
id = parser.Consume<Scalar>().Value;
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .3f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
universeObject = (IUniverseObject)instanceSerializedClass.CreateInstance(EntityRegistry);
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.StateEnable)) != 0)
throw new();
stateEnable = (IStateEnable)rootDeserializer(typeof(IStateEnable))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.BehaviourController)) != 0)
throw new();
behaviourController = (IBehaviourController)rootDeserializer(typeof(IBehaviourController))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.Children)) != 0)
throw new();
children = (List<IUniverseObject>)rootDeserializer(typeof(List<IUniverseObject>))!;
parser.Consume<MappingEnd>();
universeObject.Id = id;
universeObject.Name = name;
stateEnable.Assign(universeObject);
universeObject.Assign(stateEnable);
behaviourController.Assign(universeObject);
universeObject.Assign(behaviourController);
foreach (IUniverseObject child in children)
universeObject.AddChild(child);
ProgressionTracker.Set(isTrackingController ? .3f : ProgressionTracker.Progression, $"Created {name}");
return universeObject;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
IUniverseObject universeObject = (IUniverseObject)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing universe object");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IUniverseObject.Name)));
emitter.Emit(new Scalar(universeObject.Name));
emitter.Emit(new Scalar(nameof(IUniverseObject.Id)));
emitter.Emit(new Scalar(universeObject.Id));
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(universeObject));
emitter.Emit(new Scalar(nameof(IUniverseObject.StateEnable)));
serializer(universeObject.StateEnable);
emitter.Emit(new Scalar(nameof(IUniverseObject.BehaviourController)));
serializer(universeObject.BehaviourController);
emitter.Emit(new Scalar(nameof(IUniverseObject.Children)));
serializer(universeObject.Children.Where(c => !c.GetType().HasAttribute<IgnoreSerializationAttribute>()));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serializing universe object");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Engine.Integration.Yaml</RootNamespace>
<AssemblyName>Engine.Integration.Yaml</AssemblyName>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Engine\Engine.csproj" />
<ProjectReference Include="..\YamlDotNet\YamlDotNet\YamlDotNet.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
using System;
namespace Engine.Serializers.Yaml;
[Serializable]
public class SerializerInProgressException() : Exception("There's already a running deserialization in progress.");

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Engine.Core;
using Engine.Core.Serialization;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace Engine.Serializers.Yaml;
public class YamlSerializer : Core.Serialization.ISerializer
{
private readonly YamlDotNet.Serialization.ISerializer serializer = null!;
private readonly YamlDotNet.Serialization.IDeserializer deserializer = null!;
private readonly EntityRegistry entityRegistry = null!;
private readonly IProgressionTracker progressionTracker = null!;
private readonly System.Threading.Lock Lock = new();
public YamlSerializer()
{
entityRegistry = new();
progressionTracker = new ProgressionTracker();
SerializerBuilder serializerBuilder = new SerializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.DisableAliases();
DeserializerBuilder deserializerBuilder = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance);
foreach (IEngineTypeYamlConverter typeConverter in GetEngineYamlTypeConverters())
{
typeConverter.Serializer = this;
typeConverter.EntityRegistry = entityRegistry;
typeConverter.ProgressionTracker = progressionTracker;
deserializerBuilder = deserializerBuilder.WithTypeConverter(typeConverter);
serializerBuilder = serializerBuilder.WithTypeConverter(typeConverter);
}
serializer = serializerBuilder.Build();
deserializer = deserializerBuilder.Build();
}
private static IEnumerable<IEngineTypeYamlConverter> GetEngineYamlTypeConverters()
{
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(IEngineTypeYamlConverter).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract))
yield return (Activator.CreateInstance(type) as IEngineTypeYamlConverter)!;
}
public string Serialize(object instance)
{
lock (Lock)
{
return serializer.Serialize(instance);
}
}
public object Deserialize(string configuration)
{
lock (Lock)
{
entityRegistry.Reset();
object result = deserializer.Deserialize(configuration)!;
entityRegistry.AssignAll();
return result;
}
}
public object Deserialize(string configuration, Type type)
{
lock (Lock)
{
entityRegistry.Reset();
object result = deserializer.Deserialize(configuration, type)!;
entityRegistry.AssignAll();
return result;
}
}
public T Deserialize<T>(string configuration)
{
lock (Lock)
{
entityRegistry.Reset();
T result = deserializer.Deserialize<T>(configuration);
entityRegistry.AssignAll();
return result;
}
}
public ProgressiveTask<object> DeserializeAsync(string configuration)
{
lock (Lock)
{
progressionTracker.Reset();
Task<object> task = Task.Run(() => Deserialize(configuration));
return new ProgressiveTask<object>(progressionTracker, task);
}
}
public ProgressiveTask<object> DeserializeAsync(string configuration, Type type)
{
lock (Lock)
{
progressionTracker.Reset();
Task<object> task = Task.Run(() => Deserialize(configuration, type));
return new ProgressiveTask<object>(progressionTracker, task);
}
}
public ProgressiveTask<T> DeserializeAsync<T>(string configuration)
{
lock (Lock)
{
progressionTracker.Reset();
Task<T> task = Task.Run(() => Deserialize<T>(configuration));
return new ProgressiveTask<T>(progressionTracker, task);
}
}
public ProgressiveTask<string> SerializeAsync(object instance)
{
lock (Lock)
{
progressionTracker.Reset();
Task<string> task = Task.Run(() => Serialize(instance));
return new ProgressiveTask<string>(progressionTracker, task);
}
}
internal object InternalDeserialize(string configuration, Type type)
{
return deserializer.Deserialize(configuration, type)!;
}
}