Skip to content

Commit

Permalink
Merge pull request #4 from manups4e/MsgPack_support
Browse files Browse the repository at this point in the history
Added MsgPack support and removed old BinarySerialization
  • Loading branch information
manups4e authored Nov 9, 2022
2 parents 50af2ad + 08cfe39 commit 14fd897
Show file tree
Hide file tree
Showing 13 changed files with 331 additions and 18 deletions.
Binary file added FxEvents/FiveMMsgPack/MsgPack.dll
Binary file not shown.
2 changes: 1 addition & 1 deletion FxEvents/FxEvents.Client/EventSystem/ClientGateway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class ClientGateway : BaseGateway
public ClientGateway()
{
SnowflakeGenerator.Create((short)new Random().Next(1, 199));
Serialization = new BinarySerialization();
Serialization = new MsgPackSerialization();
DelayDelegate = async delay => await BaseScript.Delay(delay);
PrepareDelegate = PrepareAsync;
PushDelegate = Push;
Expand Down
10 changes: 4 additions & 6 deletions FxEvents/FxEvents.Client/FxEvents.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="MsgPack">
<HintPath>..\FiveMMsgPack\MsgPack.dll</HintPath>
</Reference>
<Reference Include="System" />

<Reference Include="System.Core" />
Expand All @@ -40,16 +43,11 @@
<Reference Include="Newtonsoft.Json">
<HintPath>$(PkgNewtonsoft_Json)\lib\portable-net40+sl5+win8+wp8+wpa81\Newtonsoft.Json.dll</HintPath>
</Reference>


<Reference Include="Microsoft.CSharp" />

<Reference Include="System.Data" />

<Reference Include="System.Net.Http" />

<Reference Include="System.Xml" />
<PackageReference Include="CitizenFX.Core.Client" Version="1.0.5854" />
<PackageReference Include="CitizenFX.Core.Client" Version="1.0.5985" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Shared\**\*.cs" />
Expand Down
2 changes: 1 addition & 1 deletion FxEvents/FxEvents.Server/EventSystem/ServerGateway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class ServerGateway : BaseGateway
public ServerGateway()
{
SnowflakeGenerator.Create((short)new Random().Next(200, 399));
Serialization = new BinarySerialization();
Serialization = new MsgPackSerialization();
DelayDelegate = async delay => await BaseScript.Delay(delay);
PushDelegate = Push;
_signatures = new();
Expand Down
9 changes: 6 additions & 3 deletions FxEvents/FxEvents.Server/FxEvents.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
<DefineConstants>SERVER</DefineConstants>
<DebugType>embedded</DebugType>
<DebugType>portable</DebugType>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>SERVER</DefineConstants>
<DebugType>embedded</DebugType>
<DebugType>portable</DebugType>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\Shared\**\*.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="CitizenFX.Core.Server" Version="1.0.5854" />
<PackageReference Include="CitizenFX.Core.Server" Version="1.0.5985" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" ExcludeAssets="Compile" GeneratePathProperty="true" />
<Reference Include="MsgPack">
<HintPath>..\FiveMMsgPack\MsgPack.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>$(PkgNewtonsoft_Json)\lib\portable-net40+sl5+win8+wp8+wpa81\Newtonsoft.Json.dll</HintPath>
</Reference>
Expand Down
7 changes: 4 additions & 3 deletions FxEvents/Shared/BinaryHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ namespace FxEvents.Shared
{
public static class BinaryHelper
{
private static BinarySerialization _serialization = new();
private static BinarySerialization binarySerialization = new();
private static MsgPackSerialization msgpackSerialization = new();

public static byte[] ToBytes<T>(this T obj)
{
using SerializationContext context = new("BinaryHelper", "ToBytes", _serialization);
using SerializationContext context = new("BinaryHelper", "ToBytes", msgpackSerialization);
context.Serialize(typeof(T), obj);
return context.GetData();
}
Expand All @@ -39,7 +40,7 @@ public static string BytesToString(this byte[] ba, bool separator = false, bool

public static T FromBytes<T>(this byte[] data)
{
using SerializationContext context = new(data.ToString(), "FromBytes", _serialization, data);
using SerializationContext context = new(data.ToString(), "FromBytes", msgpackSerialization, data);
return context.Deserialize<T>();
}
}
Expand Down
1 change: 1 addition & 0 deletions FxEvents/Shared/EventSubsystem/Message/EventParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class EventParameter
{
public byte[] Data { get; set; }

public EventParameter() { }
public EventParameter(byte[] data)
{
Data = data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class EventResponseMessage : IMessage
public string? Signature { get; set; }
public byte[]? Data { get; set; }

public EventResponseMessage() { }
public EventResponseMessage(Snowflake id, string endpoint, string? signature, byte[]? data)
{
Id = id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace FxEvents.Shared.Serialization.Implementations

public delegate T DeserializationObjectActivator<out T>(BinaryReader reader);

[Obsolete("This implementations is obsolete now that MsgPack is available")]
public class BinarySerialization : ISerialization
{
public const string PackMethod = "PackSerializedBytes";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using FxEvents.Shared.Diagnostics;
using FxEvents.Shared.Exceptions;
using FxEvents.Shared.TypeExtensions;
using Logger;
using MsgPack.Serialization;

namespace FxEvents.Shared.Serialization.Implementations
{
public class MsgPackSerialization : ISerialization
{
private delegate T ObjectActivator<out T>();
private delegate void VoidMethod();
private Log logger = new();
private MsgPack.Serialization.SerializationContext _context = new(MsgPack.PackerCompatibilityOptions.None) { SerializationMethod = SerializationMethod.Map };
public MsgPackSerialization()
{
}

private bool CanCreateInstanceUsingDefaultConstructor(Type t) => t.IsValueType || !t.IsAbstract && t.GetConstructor(Type.EmptyTypes) != null;
public void Serialize(Type type, object value, SerializationContext context)
{
var typeIdentifier = GetTypeIdentifier(type);
if (typeIdentifier == "System.Collections.Generic.KeyValuePair`2")
{
var generics = type.GetGenericArguments();
var method = GetType().GetMethod("Serialize",
new[] { typeof(Type), typeof(object), typeof(SerializationContext) });
var instanceParam = Expression.Parameter(typeof(MsgPackSerialization), "instance");
var typeParam = Expression.Parameter(typeof(Type), "type");
var contextParam = Expression.Parameter(typeof(SerializationContext), "context");
var pairParam = Expression.Parameter(type, "pair");
var valueParam = Expression.Parameter(typeof(object), "value");
var call = Expression.Call(instanceParam, method!, typeParam, valueParam, contextParam);

void CallSerialization(Type genericType, string property)
{
var action = (Action)Expression.Lambda(typeof(Action), Expression.Block(new[]
{
instanceParam,
typeParam,
contextParam,
pairParam,
valueParam
},
Expression.Assign(instanceParam, Expression.Constant(this, typeof(MsgPackSerialization))),
Expression.Assign(contextParam, Expression.Constant(context, typeof(SerializationContext))),
Expression.Assign(typeParam, Expression.Constant(genericType, typeof(Type))),
Expression.Assign(pairParam, Expression.Constant(value, type)),
Expression.Assign(valueParam,
Expression.Convert(Expression.Property(pairParam, property), typeof(object))),
call
)).Compile();

action.Invoke();
}

CallSerialization(generics[0], "Key");
CallSerialization(generics[1], "Value");
}
else if (typeIdentifier.StartsWith("System.Tuple`"))
{
var generics = type.GetGenericArguments();
var method = GetType().GetMethod("Serialize",
new[] { typeof(Type), typeof(object), typeof(SerializationContext) });
var instanceParam = Expression.Parameter(typeof(MsgPackSerialization), "instance");
var typeParam = Expression.Parameter(typeof(Type), "type");
var valueParam = Expression.Parameter(type, "value");
var contextParam = Expression.Parameter(typeof(SerializationContext), "context");

for (var idx = 0; idx < generics.Length; idx++)
{
var generic = generics[idx];
var call = Expression.Call(instanceParam, method!, typeParam,
Expression.Convert(Expression.Property(valueParam, $"Item{idx + 1}"), typeof(object)),
contextParam);
var action = (Action)Expression.Lambda(typeof(Action), Expression.Block(new[]
{
instanceParam,
typeParam,
contextParam,
valueParam
},
Expression.Assign(instanceParam, Expression.Constant(this, typeof(MsgPackSerialization))),
Expression.Assign(contextParam, Expression.Constant(context, typeof(SerializationContext))),
Expression.Assign(typeParam, Expression.Constant(generic, typeof(Type))),
Expression.Assign(valueParam, Expression.Constant(value, type)),
call
)).Compile();

action.Invoke();
}
}
else
{
var ser = MessagePackSerializer.Get(type, _context);
ser.Pack(context.Writer.BaseStream, value);
}
}

public void Serialize<T>(T value, SerializationContext context)
{
Serialize(typeof(T), value, context);
}

public object Deserialize(Type type, SerializationContext context)
{
var ser = MessagePackSerializer.Get(type, _context);
return ser.Unpack(context.Reader.BaseStream);
}

public T Deserialize<T>(SerializationContext context) => Deserialize<T>(typeof(T), context);

public T Deserialize<T>(Type type, SerializationContext context)
{
var canInstance = CanCreateInstanceUsingDefaultConstructor(type);
var typeIdentifier = GetTypeIdentifier(type);

if (TypeCache<T>.IsSimpleType)
{
var primitive = Deserialize(type, context);
if (primitive != null) return (T)primitive;
}
if (typeIdentifier == "System.Collections.Generic.KeyValuePair`2")
{
var generics = type.GetGenericArguments();
var constructor = type.GetConstructor(generics) ??
throw new SerializationException(context, type,
$"Could not find suitable constructor for type: {type.Name}");

var key = DeserializeAnonymously(generics[0], context);
var value = DeserializeAnonymously(generics[1], context);
var keyParam = Expression.Parameter(generics[0], "key");
var valueParam = Expression.Parameter(generics[1], "value");
var block = Expression.Block(
new[] { keyParam, valueParam },
Expression.Assign(keyParam, Expression.Constant(key, generics[0])),
Expression.Assign(valueParam, Expression.Constant(value, generics[1])),
Expression.New(constructor, keyParam, valueParam)
);

if (typeof(T) == typeof(object))
{
var generic = typeof(ObjectActivator<>).MakeGenericType(type);
var activator = Expression.Lambda(generic, block).Compile();

return (T)activator.DynamicInvoke();
}
else
{
var activator =
(ObjectActivator<T>)Expression.Lambda(typeof(ObjectActivator<T>), block).Compile();

return activator.Invoke();
}
}
else if (typeIdentifier.StartsWith("System.Tuple`"))
{
var generics = type.GetGenericArguments();
var constructor = type.GetConstructor(generics) ??
throw new SerializationException(context, type,
$"Could not find suitable constructor for type: {type.Name}");
var parameters = new List<Expression>();

foreach (var generic in generics)
{
var entry = Deserialize(generic, context);

parameters.Add(Expression.Constant(entry, generic));
}

var expression = Expression.New(constructor, parameters);

if (typeof(T) == typeof(object))
{
var generic = typeof(ObjectActivator<>).MakeGenericType(type);
var activator = Expression.Lambda(generic, expression).Compile();

return (T)activator.DynamicInvoke();
}
else
{
var activator =
(ObjectActivator<T>)Expression.Lambda(typeof(ObjectActivator<T>), expression).Compile();

return activator.Invoke();
}
}
else
{
if (!canInstance)
{
throw new SerializationException(context, type, $"Type {type.Name} is missing its emtpy constructor");
}
var ser = MessagePackSerializer.Get<T>(_context);
return ser.Unpack(context.Reader.BaseStream);
}
}

public object DeserializeAnonymously(Type type, SerializationContext context) =>
Deserialize<object>(type, context);

private static string GetTypeIdentifier(Type type)
{
var builder = new StringBuilder();
var declaring = type;

builder.Append(type.Namespace);
builder.Append(".");

var idx = builder.Length;

while ((declaring = declaring.DeclaringType) != null)
{
builder.Insert(idx, declaring.Name + ".");
}

builder.Append(type.Name);

return builder.ToString();
}
public object? DeserializePrimitive(Type type, SerializationContext context)
{
try
{
if (context.Reader == null) throw new Exception("SerializationContext.Reader is null.");
switch (Type.GetTypeCode(type))
{
case TypeCode.Boolean:
return context.Reader.ReadBoolean();
case TypeCode.Byte:
return context.Reader.ReadByte();
case TypeCode.Char:
return context.Reader.ReadChar();
case TypeCode.Double:
return context.Reader.ReadDouble();
case TypeCode.Int16:
return context.Reader.ReadInt16();
case TypeCode.Int32:
return context.Reader.ReadInt32();
case TypeCode.Int64:
return context.Reader.ReadInt64();
case TypeCode.Single:
return context.Reader.ReadSingle();
case TypeCode.String:
return context.Reader.ReadString();
case TypeCode.SByte:
return context.Reader.ReadSByte();
case TypeCode.UInt16:
return context.Reader.ReadUInt16();
case TypeCode.UInt32:
return context.Reader.ReadUInt32();
case TypeCode.UInt64:
return context.Reader.ReadUInt64();
}

return default;
}
catch (Exception ex)
{
throw new SerializationException(context, type, $"Could not deserialize primitive: {type}", ex);
}
}
}
}
Loading

0 comments on commit 14fd897

Please sign in to comment.