diff --git a/Assets/Mirror/Editor/Weaver/Processors/MessageClassProcessor.cs b/Assets/Mirror/Editor/Weaver/Processors/MessageClassProcessor.cs index 405147476..1985b5312 100644 --- a/Assets/Mirror/Editor/Weaver/Processors/MessageClassProcessor.cs +++ b/Assets/Mirror/Editor/Weaver/Processors/MessageClassProcessor.cs @@ -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); + } } } } diff --git a/Assets/Mirror/Editor/Weaver/Weaver.cs b/Assets/Mirror/Editor/Weaver/Weaver.cs index 1e33a00d8..bf4369b93 100644 --- a/Assets/Mirror/Editor/Weaver/Weaver.cs +++ b/Assets/Mirror/Editor/Weaver/Weaver.cs @@ -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 diff --git a/Assets/Mirror/Tests/MessageBaseTests.cs b/Assets/Mirror/Tests/MessageBaseTests.cs index 00ad90ebb..a77740ae6 100644 --- a/Assets/Mirror/Tests/MessageBaseTests.cs +++ b/Assets/Mirror/Tests/MessageBaseTests.cs @@ -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); + } } }