mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 11:00:32 +00:00
feat: generate serializers for IMessageBase structs (#1353)
* Allow Weaver to add bodies to IMessageBase structs with empty de/serialize methods * Update Assets/Mirror/Editor/Weaver/Processors/MessageClassProcessor.cs Co-Authored-By: Paul Pacheco <paulpach@gmail.com> * applied suggested changes * adjusted empty method check * Update Assets/Mirror/Editor/Weaver/Processors/MessageClassProcessor.cs formatting Co-Authored-By: vis2k <info@noobtuts.com> Co-authored-by: Paul Pacheco <paulpach@gmail.com> Co-authored-by: vis2k <info@noobtuts.com>
This commit is contained in:
parent
8d8cb7eba0
commit
3c0bc28228
@ -1,4 +1,6 @@
|
||||
// this class generates OnSerialize/OnDeserialize when inheriting from MessageBase
|
||||
|
||||
using System.Linq;
|
||||
using Mono.CecilX;
|
||||
using Mono.CecilX.Cil;
|
||||
|
||||
@ -6,6 +8,12 @@ namespace Mirror.Weaver
|
||||
{
|
||||
static class MessageClassProcessor
|
||||
{
|
||||
|
||||
static bool IsEmptyDefault(this MethodBody body)
|
||||
{
|
||||
return body.Instructions.All(instruction => instruction.OpCode == OpCodes.Nop || instruction.OpCode == OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static void Process(TypeDefinition td)
|
||||
{
|
||||
Weaver.DLog(td, "MessageClassProcessor Start");
|
||||
@ -23,10 +31,10 @@ public static void Process(TypeDefinition td)
|
||||
static void GenerateSerialization(TypeDefinition td)
|
||||
{
|
||||
Weaver.DLog(td, " GenerateSerialization");
|
||||
foreach (MethodDefinition m in td.Methods)
|
||||
MethodDefinition existingMethod = td.Methods.FirstOrDefault(md=>md.Name == "Serialize");
|
||||
if (existingMethod != null && !existingMethod.Body.IsEmptyDefault())
|
||||
{
|
||||
if (m.Name == "Serialize")
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
if (td.Fields.Count == 0)
|
||||
@ -44,20 +52,30 @@ static void GenerateSerialization(TypeDefinition td)
|
||||
}
|
||||
}
|
||||
|
||||
MethodDefinition serializeFunc = new MethodDefinition("Serialize",
|
||||
MethodDefinition serializeFunc = existingMethod ?? new MethodDefinition("Serialize",
|
||||
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig,
|
||||
Weaver.voidType);
|
||||
|
||||
serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
|
||||
ILProcessor serWorker = serializeFunc.Body.GetILProcessor();
|
||||
|
||||
// call base
|
||||
MethodReference baseSerialize = Resolvers.ResolveMethodInParents(td.BaseType, Weaver.CurrentAssembly, "Serialize");
|
||||
if (baseSerialize != null)
|
||||
if (existingMethod == null) //only add to new method
|
||||
{
|
||||
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base
|
||||
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer
|
||||
serWorker.Append(serWorker.Create(OpCodes.Call, baseSerialize));
|
||||
serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
|
||||
}
|
||||
ILProcessor serWorker = serializeFunc.Body.GetILProcessor();
|
||||
if (existingMethod != null)
|
||||
{
|
||||
serWorker.Body.Instructions.Clear(); //remove default nop&ret from existing empty interface method
|
||||
}
|
||||
|
||||
if (!td.IsValueType) //if not struct(IMessageBase), likely same as using else {} here in all cases
|
||||
{
|
||||
// call base
|
||||
MethodReference baseSerialize = Resolvers.ResolveMethodInParents(td.BaseType, Weaver.CurrentAssembly, "Serialize");
|
||||
if (baseSerialize != null)
|
||||
{
|
||||
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base
|
||||
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer
|
||||
serWorker.Append(serWorker.Create(OpCodes.Call, baseSerialize));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (FieldDefinition field in td.Fields)
|
||||
@ -81,16 +99,19 @@ static void GenerateSerialization(TypeDefinition td)
|
||||
}
|
||||
serWorker.Append(serWorker.Create(OpCodes.Ret));
|
||||
|
||||
td.Methods.Add(serializeFunc);
|
||||
if (existingMethod == null) //only add if not just replaced body
|
||||
{
|
||||
td.Methods.Add(serializeFunc);
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateDeSerialization(TypeDefinition td)
|
||||
{
|
||||
Weaver.DLog(td, " GenerateDeserialization");
|
||||
foreach (MethodDefinition m in td.Methods)
|
||||
MethodDefinition existingMethod = td.Methods.FirstOrDefault(md=>md.Name == "Deserialize");
|
||||
if (existingMethod != null && !existingMethod.Body.IsEmptyDefault())
|
||||
{
|
||||
if (m.Name == "Deserialize")
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
if (td.Fields.Count == 0)
|
||||
@ -98,20 +119,30 @@ static void GenerateDeSerialization(TypeDefinition td)
|
||||
return;
|
||||
}
|
||||
|
||||
MethodDefinition serializeFunc = new MethodDefinition("Deserialize",
|
||||
MethodDefinition serializeFunc = existingMethod??new MethodDefinition("Deserialize",
|
||||
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig,
|
||||
Weaver.voidType);
|
||||
|
||||
serializeFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType)));
|
||||
ILProcessor serWorker = serializeFunc.Body.GetILProcessor();
|
||||
|
||||
// call base
|
||||
MethodReference baseDeserialize = Resolvers.ResolveMethodInParents(td.BaseType, Weaver.CurrentAssembly, "Deserialize");
|
||||
if (baseDeserialize != null)
|
||||
if (existingMethod == null) //only add to new method
|
||||
{
|
||||
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base
|
||||
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer
|
||||
serWorker.Append(serWorker.Create(OpCodes.Call, baseDeserialize));
|
||||
serializeFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType)));
|
||||
}
|
||||
ILProcessor serWorker = serializeFunc.Body.GetILProcessor();
|
||||
if (existingMethod != null)
|
||||
{
|
||||
serWorker.Body.Instructions.Clear(); //remove default nop&ret from existing empty interface method
|
||||
}
|
||||
|
||||
if (!td.IsValueType) //if not struct(IMessageBase), likely same as using else {} here in all cases
|
||||
{
|
||||
// call base
|
||||
MethodReference baseDeserialize = Resolvers.ResolveMethodInParents(td.BaseType, Weaver.CurrentAssembly, "Deserialize");
|
||||
if (baseDeserialize != null)
|
||||
{
|
||||
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base
|
||||
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer
|
||||
serWorker.Append(serWorker.Create(OpCodes.Call, baseDeserialize));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (FieldDefinition field in td.Fields)
|
||||
@ -135,7 +166,10 @@ static void GenerateDeSerialization(TypeDefinition td)
|
||||
}
|
||||
serWorker.Append(serWorker.Create(OpCodes.Ret));
|
||||
|
||||
td.Methods.Add(serializeFunc);
|
||||
if (existingMethod == null) //only add if not just replaced body
|
||||
{
|
||||
td.Methods.Add(serializeFunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ class Weaver
|
||||
public static TypeReference NetworkConnectionType;
|
||||
|
||||
public static TypeReference MessageBaseType;
|
||||
public static TypeReference IMessageBaseType;
|
||||
public static TypeReference SyncListType;
|
||||
public static TypeReference SyncSetType;
|
||||
public static TypeReference SyncDictionaryType;
|
||||
@ -273,6 +274,7 @@ static void SetupTargetTypes()
|
||||
NetworkConnectionType = CurrentAssembly.MainModule.ImportReference(NetworkConnectionType);
|
||||
|
||||
MessageBaseType = NetAssembly.MainModule.GetType("Mirror.MessageBase");
|
||||
IMessageBaseType = NetAssembly.MainModule.GetType("Mirror.IMessageBase");
|
||||
SyncListType = NetAssembly.MainModule.GetType("Mirror.SyncList`1");
|
||||
SyncSetType = NetAssembly.MainModule.GetType("Mirror.SyncSet`1");
|
||||
SyncDictionaryType = NetAssembly.MainModule.GetType("Mirror.SyncDictionary`2");
|
||||
@ -385,26 +387,10 @@ static bool CheckMessageBase(TypeDefinition td)
|
||||
|
||||
bool didWork = false;
|
||||
|
||||
// are ANY parent classes MessageBase
|
||||
TypeReference parent = td.BaseType;
|
||||
while (parent != null)
|
||||
if (td.ImplementsInterface(IMessageBaseType))
|
||||
{
|
||||
if (parent.FullName == MessageBaseType.FullName)
|
||||
{
|
||||
MessageClassProcessor.Process(td);
|
||||
didWork = true;
|
||||
break;
|
||||
}
|
||||
try
|
||||
{
|
||||
parent = parent.Resolve().BaseType;
|
||||
}
|
||||
catch (AssemblyResolutionException)
|
||||
{
|
||||
// this can happen for plugins.
|
||||
//Console.WriteLine("AssemblyResolutionException: "+ ex.ToString());
|
||||
break;
|
||||
}
|
||||
MessageClassProcessor.Process(td);
|
||||
didWork = true;
|
||||
}
|
||||
|
||||
// check for embedded types
|
||||
|
@ -30,6 +30,16 @@ public void Serialize(NetworkWriter writer)
|
||||
}
|
||||
}
|
||||
|
||||
struct WovenTestMessage: IMessageBase
|
||||
{
|
||||
public int IntValue;
|
||||
public string StringValue;
|
||||
public double DoubleValue;
|
||||
|
||||
public void Deserialize(NetworkReader reader) {}
|
||||
public void Serialize(NetworkWriter writer) {}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
public class MessageBaseTests
|
||||
{
|
||||
@ -47,5 +57,22 @@ public void Roundtrip()
|
||||
|
||||
Assert.AreEqual(1, t.IntValue);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WovenSerializationBodyRoundtrip()
|
||||
{
|
||||
NetworkWriter w = new NetworkWriter();
|
||||
w.Write(new WovenTestMessage{IntValue = 1, StringValue = "2", DoubleValue = 3.3});
|
||||
|
||||
byte[] arr = w.ToArray();
|
||||
|
||||
NetworkReader r = new NetworkReader(arr);
|
||||
WovenTestMessage t = new WovenTestMessage();
|
||||
t.Deserialize(r);
|
||||
|
||||
Assert.AreEqual(1, t.IntValue);
|
||||
Assert.AreEqual("2", t.StringValue);
|
||||
Assert.AreEqual(3.3, t.DoubleValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user