From 92ccbc978b529084020643025209d6a9fb49fa3a Mon Sep 17 00:00:00 2001 From: Leonardo Emanuele Date: Tue, 10 Jun 2025 16:58:36 +0200 Subject: [PATCH 1/3] fix(vendor/msgpack-cs): fix foormatter struct serialization --- MsgPack/Formatters/TypeFormatter.cs | 35 +++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/MsgPack/Formatters/TypeFormatter.cs b/MsgPack/Formatters/TypeFormatter.cs index 393b3e0..90f0c46 100644 --- a/MsgPack/Formatters/TypeFormatter.cs +++ b/MsgPack/Formatters/TypeFormatter.cs @@ -227,8 +227,18 @@ private static void BuildSerializeArrayBody(Type type, ILGenerator g, MemberInfo // Value g.Emit(OpCodes.Ldarg_0); - g.Emit(OpCodes.Ldarg_1); - g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); + // if it's a struct.. we handle it differently + // using the address of the argument and not its value. + if (type.IsValueType) + { + g.Emit(OpCodes.Ldarga_S, 1); + g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); + } + else + { + g.Emit(OpCodes.Ldarg_1); + g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); + } g.EmitCall(OpCodes.Call, serializer, null); } break; @@ -290,11 +300,11 @@ private static void BuildSerializeMapBody(Type type, ILGenerator g, DynamicArray case PropertyInfo property: { - var methodFieldSerializer = type == property.PropertyType + var methodPropertySerializer = type == property.PropertyType ? currentSerializer : MsgPackRegistry.GetOrCreateSerializer(property.PropertyType); - if (methodFieldSerializer == null) + if (methodPropertySerializer == null) throw new NotSupportedException($"Requested serializer for {type.Name}.{property.Name} of type {property.PropertyType} could not be found."); // Key @@ -304,9 +314,20 @@ private static void BuildSerializeMapBody(Type type, ILGenerator g, DynamicArray // Value g.Emit(OpCodes.Ldarg_0); - g.Emit(OpCodes.Ldarg_1); - g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); - g.EmitCall(OpCodes.Call, methodFieldSerializer, null); + // Same as above + // if it's a struct.. we handle it differently + // using the address of the argument and not its value. + if (type.IsValueType) + { + g.Emit(OpCodes.Ldarga_S, 1); + g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); + } + else + { + g.Emit(OpCodes.Ldarg_1); + g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); + } + g.EmitCall(OpCodes.Call, methodPropertySerializer, null); } break; From ea31f4f456de97f1492b1ab12e29cb048e3c88c9 Mon Sep 17 00:00:00 2001 From: Leonardo Emanuele Date: Tue, 10 Jun 2025 17:11:55 +0200 Subject: [PATCH 2/3] feat(vendor/msgpack-cs): Remove redundant getter call in property serialization logic --- MsgPack/Formatters/TypeFormatter.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/MsgPack/Formatters/TypeFormatter.cs b/MsgPack/Formatters/TypeFormatter.cs index 90f0c46..8e60080 100644 --- a/MsgPack/Formatters/TypeFormatter.cs +++ b/MsgPack/Formatters/TypeFormatter.cs @@ -230,15 +230,11 @@ private static void BuildSerializeArrayBody(Type type, ILGenerator g, MemberInfo // if it's a struct.. we handle it differently // using the address of the argument and not its value. if (type.IsValueType) - { g.Emit(OpCodes.Ldarga_S, 1); - g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); - } else - { g.Emit(OpCodes.Ldarg_1); - g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); - } + + g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); g.EmitCall(OpCodes.Call, serializer, null); } break; @@ -318,15 +314,11 @@ private static void BuildSerializeMapBody(Type type, ILGenerator g, DynamicArray // if it's a struct.. we handle it differently // using the address of the argument and not its value. if (type.IsValueType) - { g.Emit(OpCodes.Ldarga_S, 1); - g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); - } else - { g.Emit(OpCodes.Ldarg_1); - g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); - } + + g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); g.EmitCall(OpCodes.Call, methodPropertySerializer, null); } break; From d1482ac6f01196548092b43537eaafc4f47fb574 Mon Sep 17 00:00:00 2001 From: Leonardo Emanuele Date: Wed, 18 Jun 2025 16:42:41 +0200 Subject: [PATCH 3/3] feat(vendor/msgpack-cs): Added Enum formatter for all enum types and flags This commit fixes a weird use case where sometimes Enums with or without Flags attribute were serialized using the Type formatter causing a IL crash. Works both clientside and serverside. --- MsgPack/Formatters/EnumFormatter.cs | 83 +++++++++++++++++++++++++++++ MsgPack/MsgPackRegistry.cs | 8 +++ 2 files changed, 91 insertions(+) create mode 100644 MsgPack/Formatters/EnumFormatter.cs diff --git a/MsgPack/Formatters/EnumFormatter.cs b/MsgPack/Formatters/EnumFormatter.cs new file mode 100644 index 0000000..80b5b00 --- /dev/null +++ b/MsgPack/Formatters/EnumFormatter.cs @@ -0,0 +1,83 @@ +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace CitizenFX.MsgPack.Formatters +{ + internal static class EnumFormatter + { + public static Tuple Build(Type enumType, Type unused = null) + { + string name = $"EnumFormatter_{enumType.FullName}"; + Type buildType = MsgPackRegistry.m_moduleBuilder.GetType(name); + + MethodInfo methodSerialize, methodDeserialize, methodObjectSerialize; + + if (buildType == null) + { + TypeBuilder typeBuilder = MsgPackRegistry.m_moduleBuilder.DefineType(name); + + methodSerialize = BuildSerializer(enumType, typeBuilder); + BuildDeserializer(enumType, typeBuilder); + BuildObjectSerializer(enumType, methodSerialize, typeBuilder); + + buildType = typeBuilder.CreateType(); + } + + methodSerialize = buildType.GetMethod("Serialize", new[] { typeof(MsgPackSerializer), enumType }); + methodDeserialize = buildType.GetMethod("Deserialize"); + methodObjectSerialize = buildType.GetMethod("Serialize", new[] { typeof(MsgPackSerializer), typeof(object) }); + + Serializer serializer = new Serializer(methodSerialize, methodObjectSerialize); + + MsgPackRegistry.RegisterSerializer(enumType, serializer); + MsgPackRegistry.RegisterDeserializer(enumType, methodDeserialize); + + return new Tuple(serializer, methodDeserialize); + } + + private static MethodInfo BuildObjectSerializer(Type enumType, MethodInfo methodSerialize, TypeBuilder typeBuilder) + { + MethodBuilder method = typeBuilder.DefineMethod("Serialize", + MethodAttributes.Public | MethodAttributes.Static, + typeof(void), + new[] { typeof(MsgPackSerializer), typeof(object) }); + + var g = method.GetILGenerator(); + g.Emit(OpCodes.Ldarg_0); + g.Emit(OpCodes.Ldarg_1); + g.Emit(OpCodes.Unbox_Any, enumType); + g.EmitCall(OpCodes.Call, methodSerialize, null); + g.Emit(OpCodes.Ret); + + return method; + } + + private static MethodInfo BuildSerializer(Type enumType, TypeBuilder typeBuilder) + { + MethodBuilder method = typeBuilder.DefineMethod("Serialize", + MethodAttributes.Public | MethodAttributes.Static, + typeof(void), + new[] { typeof(MsgPackSerializer), enumType }); + + var g = method.GetILGenerator(); + g.Emit(OpCodes.Ldarg_0); + g.Emit(OpCodes.Ldarg_1); + g.EmitCall(OpCodes.Call, MsgPackRegistry.GetOrCreateSerializer(typeof(uint)), null); + g.Emit(OpCodes.Ret); + return method; + } + + private static MethodInfo BuildDeserializer(Type enumType, TypeBuilder typeBuilder) + { + MethodBuilder methodDeserialize = typeBuilder.DefineMethod("Deserialize", + MethodAttributes.Public | MethodAttributes.Static, + enumType, new[] { typeof(MsgPackDeserializer).MakeByRefType() }); + var g = methodDeserialize.GetILGenerator(); + g.Emit(OpCodes.Ldarg_0); + g.EmitCall(OpCodes.Call, MsgPackRegistry.GetOrCreateDeserializer(typeof(uint)), null); + g.Emit(OpCodes.Ret); + return methodDeserialize; + } + } +} diff --git a/MsgPack/MsgPackRegistry.cs b/MsgPack/MsgPackRegistry.cs index ddd0c25..522b5d7 100644 --- a/MsgPack/MsgPackRegistry.cs +++ b/MsgPack/MsgPackRegistry.cs @@ -146,6 +146,10 @@ internal static void Serialize(MsgPackSerializer serializer, object obj) { serializer.Serialize(e); } + else if (type.IsEnum) + { + serializer.Serialize(Convert.ToUInt64(obj)); + } #endif else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) { @@ -228,6 +232,10 @@ private static Tuple CreateSerializer(Type type) else throw new NotSupportedException($"{type} should've already been registered"); } + else if (type.IsEnum) + { + return EnumFormatter.Build(type); + } else if (type.IsArray) { switch (type.GetArrayRank())