Skip to content

Commit

Permalink
Implement AriseGamePacketSerializer.
Browse files Browse the repository at this point in the history
Part of #12.
  • Loading branch information
alexrp committed Aug 22, 2023
1 parent 2b1701d commit 3d36492
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 23 deletions.
155 changes: 148 additions & 7 deletions src/shared/core/Net/Serialization/AriseGamePacketSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,165 @@
using Arise.Entities;
using Arise.Net.Packets;
using static DotNext.Metaprogramming.CodeGenerator;

namespace Arise.Net.Serialization;

internal sealed class AriseGamePacketSerializer : GamePacketSerializer<AriseGamePacketCode, AriseGamePacket>
{
public static AriseGamePacketSerializer Instance { get; } = new();
public static AriseGamePacketSerializer Instance { get; }

private static readonly FrozenSet<Type> _extraSimpleTypes = new[]
{
typeof(UInt128),
typeof(Int128),
typeof(Half),
typeof(string),
typeof(Vector3),
typeof(EntityId),
}.ToFrozenSet();

private static readonly FrozenSet<Type> _compactTypes = new[]
{
typeof(ushort),
typeof(short),
typeof(uint),
typeof(int),
typeof(ulong),
typeof(long),
typeof(UInt128),
typeof(Int128),
typeof(char),
typeof(string),
typeof(EntityId),
}.ToFrozenSet();

private static readonly MethodInfo _readEnum =
typeof(GameStreamAccessor).GetMethod("ReadCompactEnum", 1, Type.EmptyTypes)!;

private static readonly MethodInfo _writeEnum =
typeof(GameStreamAccessor).GetMethod("WriteCompactEnum", 1, new[] { Type.MakeGenericMethodParameter(0) })!;

private int _variableCounter;

static AriseGamePacketSerializer()
{
Instance = new();
}

private AriseGamePacketSerializer()
{
}

protected override void GenerateDeserializer(Type type, ParameterExpression packet, ParameterExpression accessor)
private static IEnumerable<PropertyInfo> EnumerateProperties(Type type)
{
var isPacket = type.IsSubclassOf(typeof(GamePacket));

return type
.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(prop => !isPacket || prop.Name != "Code")
.OrderBy(static prop => prop.MetadataToken);
}

private static bool IsSimpleType(Type type)
{
return type!.IsPrimitive || _extraSimpleTypes!.Contains(type);
}

private static bool IsListType(Type type)
{
// TODO
throw new NotImplementedException();
return type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
}

protected override void GenerateSerializer(Type type, ParameterExpression packet, ParameterExpression accessor)
private string GenerateName(string name)
{
// TODO
throw new NotImplementedException();
return name + _variableCounter++;
}

protected override void GenerateDeserializer(Expression packet, Expression accessor)
{
void GenerateFor(Type type, Action<Expression> assign)
{
if (IsSimpleType(type))
assign(accessor.Call((_compactTypes.Contains(type) ? "ReadCompact" : "Read") + type.Name));
else if (type.IsEnum)
assign(accessor.Call(_readEnum.MakeGenericMethod(type)));
else if (type == typeof(byte[]))
{
var array = DeclareVariable<byte[]>(GenerateName("array"));

Assign(array, type.New(accessor.Call("ReadCompactUInt16").Convert<int>()));
Call(accessor, "Read", array.Convert(typeof(Span<byte>)));

assign(array);
}
else if (IsListType(type))
{
var elemType = type.GetGenericArguments()[0];

var list = DeclareVariable(type, GenerateName("list"));
var i = DeclareVariable<int>(GenerateName("i"));

Assign(list, type.New(accessor.Call("ReadCompactUInt16").Convert<int>()));
For(
i.Assign(0.Const()),
i => i.LessThan(list.Property("Capacity")),
static i => PostIncrementAssign(i),
i =>
{
var item = DeclareVariable(elemType, GenerateName("item"));
GenerateFor(elemType, value => Assign(list.Property("Item", i), value));
});

assign(list);
}
else if (type.IsValueType)
{
var obj = DeclareVariable(type, GenerateName("obj"));

foreach (var objProp in EnumerateProperties(type))
GenerateFor(objProp.PropertyType, value => Assign(obj.Property(objProp), value));

assign(obj);
}
else
throw new UnreachableException();
}

foreach (var prop in EnumerateProperties(packet.Type))
GenerateFor(prop.PropertyType, value => Assign(packet.Property(prop), value));
}

protected override void GenerateSerializer(Expression packet, Expression accessor)
{
void GenerateFor(Expression value)
{
var type = value.Type;

if (IsSimpleType(type))
Call(accessor, (_compactTypes.Contains(type) ? "WriteCompact" : "Write") + type.Name, value);
else if (type.IsEnum)
Call(accessor, _writeEnum.MakeGenericMethod(type), value);
else if (type == typeof(byte[]))
{
Call(accessor, "WriteCompactUInt16", value.ArrayLength().Convert<ushort>());
Call(accessor, "Write", value.Convert(typeof(ReadOnlySpan<byte>)));
}
else if (IsListType(type))
{
Call(accessor, "WriteCompactUInt16", value.Property("Count").Convert<ushort>());
ForEach(value, elem => GenerateFor(elem));
}
else if (type.IsValueType)
{
foreach (var objProp in EnumerateProperties(type))
GenerateFor(value.Property(objProp));
}
else
throw new UnreachableException();
}

foreach (var prop in EnumerateProperties(packet.Type))
GenerateFor(packet.Property(prop));
}
}
34 changes: 20 additions & 14 deletions src/shared/core/Net/Serialization/GamePacketSerializer`2.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Arise.Net.Packets;
using static System.Linq.Expressions.Expression;
using static DotNext.Metaprogramming.CodeGenerator;

namespace Arise.Net.Serialization;
Expand All @@ -7,6 +8,9 @@ internal abstract class GamePacketSerializer<TCode, TPacket>
where TCode : unmanaged, Enum
where TPacket : GamePacket<TCode>
{
private static readonly MethodInfo _unsafeAs =
typeof(Unsafe).GetMethod("As", 1, new[] { Type.MakeGenericMethodParameter(0) })!;

private readonly FrozenDictionary<TCode, Func<TPacket>> _creators;

private readonly FrozenDictionary<TCode, Action<object, GameStreamAccessor>> _deserializers;
Expand All @@ -19,6 +23,20 @@ private protected GamePacketSerializer()
var deserializers = new Dictionary<TCode, Action<object, GameStreamAccessor>>();
var serializers = new Dictionary<TCode, Action<object, GameStreamAccessor>>();

static Action<object, GameStreamAccessor> CompileFunction(Type type, Action<Expression, Expression> generator)
{
return Lambda<Action<object, GameStreamAccessor>>(ctx =>
{
var (packet, accessor) = ctx;
var typedPacket = DeclareVariable(type, "typedPacket");
Assign(typedPacket, Call(_unsafeAs.MakeGenericMethod([type]), packet));
generator(typedPacket, accessor);
}).Compile();
}

foreach (var type in typeof(ThisAssembly)
.Assembly
.DefinedTypes
Expand All @@ -36,21 +54,9 @@ private protected GamePacketSerializer()
_serializers = serializers.ToFrozenDictionary();
}

protected static Action<object, GameStreamAccessor> CompileFunction(
Type type,
Action<Type, ParameterExpression, ParameterExpression> generator)
{
return Lambda<Action<object, GameStreamAccessor>>(ctx =>
{
var (packet, accessor) = ctx;
generator(type, packet, accessor);
}).Compile();
}

protected abstract void GenerateDeserializer(Type type, ParameterExpression packet, ParameterExpression accessor);
protected abstract void GenerateDeserializer(Expression packet, Expression accessor);

protected abstract void GenerateSerializer(Type type, ParameterExpression packet, ParameterExpression accessor);
protected abstract void GenerateSerializer(Expression packet, Expression accessor);

public TPacket? CreatePacket(TCode code)
{
Expand Down
4 changes: 2 additions & 2 deletions src/shared/core/Net/Serialization/TeraGamePacketSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ private TeraGamePacketSerializer()
{
}

protected override void GenerateDeserializer(Type type, ParameterExpression packet, ParameterExpression accessor)
protected override void GenerateDeserializer(Expression packet, Expression accessor)
{
// TODO
throw new NotImplementedException();
}

protected override void GenerateSerializer(Type type, ParameterExpression packet, ParameterExpression accessor)
protected override void GenerateSerializer(Expression packet, Expression accessor)
{
// TODO
throw new NotImplementedException();
Expand Down

0 comments on commit 3d36492

Please sign in to comment.