From 0a5970ebdc8c3b648c514ac0f89b78c7d3da456b Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 31 Jan 2022 14:31:29 +0000 Subject: [PATCH] Revert "cleanup mirrorweaver" This reverts commit 7c5e57db544da18cf7b8eb4b9dca28c6316af333. --- MirrorWeaver/Program.cs | 10 +- MirrorWeaver/QSBReaderWriterProcessor.cs | 20 +- MirrorWeaver/Weaver/Extensions.cs | 450 ++-- MirrorWeaver/Weaver/Helpers.cs | 34 +- MirrorWeaver/Weaver/Logger.cs | 16 +- .../Weaver/Processors/CommandProcessor.cs | 136 +- .../Weaver/Processors/MethodProcessor.cs | 224 +- .../Processors/MonoBehaviourProcessor.cs | 96 +- .../Processors/NetworkBehaviourProcessor.cs | 1959 ++++++++--------- .../Processors/ReaderWriterProcessor.cs | 386 ++-- .../Weaver/Processors/RpcProcessor.cs | 114 +- .../ServerClientAttributeProcessor.cs | 272 ++- .../Processors/SyncObjectInitializer.cs | 60 +- .../Weaver/Processors/SyncObjectProcessor.cs | 136 +- .../SyncVarAttributeAccessReplacer.cs | 290 ++- .../Processors/SyncVarAttributeProcessor.cs | 963 ++++---- .../Weaver/Processors/TargetRpcProcessor.cs | 161 +- MirrorWeaver/Weaver/Readers.cs | 631 +++--- MirrorWeaver/Weaver/Resolvers.cs | 144 +- MirrorWeaver/Weaver/SyncVarAccessLists.cs | 39 +- MirrorWeaver/Weaver/TypeReferenceComparer.cs | 18 +- MirrorWeaver/Weaver/Weaver.cs | 345 +-- MirrorWeaver/Weaver/WeaverExceptions.cs | 31 +- MirrorWeaver/Weaver/WeaverTypes.cs | 238 +- MirrorWeaver/Weaver/Writers.cs | 556 +++-- 25 files changed, 3611 insertions(+), 3718 deletions(-) diff --git a/MirrorWeaver/Program.cs b/MirrorWeaver/Program.cs index d98d5835..cf4bbbc4 100644 --- a/MirrorWeaver/Program.cs +++ b/MirrorWeaver/Program.cs @@ -12,10 +12,7 @@ namespace MirrorWeaver public void Warning(string message, MemberReference mr) { - if (mr != null) - { - message = $"{message} (at {mr})"; - } + if (mr != null) message = $"{message} (at {mr})"; Console.WriteLine(message); } @@ -24,10 +21,7 @@ namespace MirrorWeaver public void Error(string message, MemberReference mr) { - if (mr != null) - { - message = $"{message} (at {mr})"; - } + if (mr != null) message = $"{message} (at {mr})"; Console.Error.WriteLine(message); } diff --git a/MirrorWeaver/QSBReaderWriterProcessor.cs b/MirrorWeaver/QSBReaderWriterProcessor.cs index bf448a11..c3da268b 100644 --- a/MirrorWeaver/QSBReaderWriterProcessor.cs +++ b/MirrorWeaver/QSBReaderWriterProcessor.cs @@ -19,27 +19,17 @@ namespace MirrorWeaver foreach (var type in assembly.MainModule.GetTypes()) { - if (type.HasGenericParameters) - { - continue; - } + if (type.HasGenericParameters) continue; TypeReference currentType = type; while (currentType != null) { foreach (var method in currentType.Resolve().Methods) { - if (!method.HasBody) - { - continue; - } - + if (!method.HasBody) continue; foreach (var instruction in method.Body.Instructions) { - if (instruction.Operand is not GenericInstanceMethod calledMethod) - { - continue; - } + if (instruction.Operand is not GenericInstanceMethod calledMethod) continue; if (calledMethod.DeclaringType.Name == NetworkWriter_Write.DeclaringType.Name && calledMethod.Name == NetworkWriter_Write.Name) @@ -47,9 +37,7 @@ namespace MirrorWeaver var argType = calledMethod.GenericArguments[0]; if (argType is GenericParameter genericParameter && genericParameter.Owner == currentType.Resolve()) - { argType = ((GenericInstanceType)currentType).GenericArguments[genericParameter.Position]; - } writers.GetWriteFunc(argType, ref weavingFailed); } @@ -59,9 +47,7 @@ namespace MirrorWeaver var argType = calledMethod.GenericArguments[0]; if (argType is GenericParameter genericParameter && genericParameter.Owner == currentType.Resolve()) - { argType = ((GenericInstanceType)currentType).GenericArguments[genericParameter.Position]; - } readers.GetReadFunc(argType, ref weavingFailed); } diff --git a/MirrorWeaver/Weaver/Extensions.cs b/MirrorWeaver/Weaver/Extensions.cs index 0fc44522..8b424555 100644 --- a/MirrorWeaver/Weaver/Extensions.cs +++ b/MirrorWeaver/Weaver/Extensions.cs @@ -1,283 +1,263 @@ -using Mono.Cecil; using System; using System.Collections.Generic; using System.Linq; +using Mono.Cecil; namespace Mirror.Weaver { - public static class Extensions - { - public static bool Is(this TypeReference td, Type t) - { - if (t.IsGenericType) - { - return td.GetElementType().FullName == t.FullName; - } - return td.FullName == t.FullName; - } + public static class Extensions + { + public static bool Is(this TypeReference td, Type t) + { + if (t.IsGenericType) + { + return td.GetElementType().FullName == t.FullName; + } + return td.FullName == t.FullName; + } - public static bool Is(this TypeReference td) => Is(td, typeof(T)); + public static bool Is(this TypeReference td) => Is(td, typeof(T)); - public static bool IsDerivedFrom(this TypeReference tr) => IsDerivedFrom(tr, typeof(T)); + public static bool IsDerivedFrom(this TypeReference tr) => IsDerivedFrom(tr, typeof(T)); - public static bool IsDerivedFrom(this TypeReference tr, Type baseClass) - { - if (tr == null) - { - return false; - } + public static bool IsDerivedFrom(this TypeReference tr, Type baseClass) + { + if (tr == null) + return false; + TypeDefinition td = tr.Resolve(); + if (td == null) + return false; + if (!td.IsClass) + return false; - var td = tr.Resolve(); - if (td == null) - { - return false; - } + // are ANY parent classes of baseClass? + TypeReference parent = td.BaseType; - if (!td.IsClass) - { - return false; - } + if (parent == null) + return false; - // are ANY parent classes of baseClass? - var parent = td.BaseType; + if (parent.Is(baseClass)) + return true; - if (parent == null) - { - return false; - } + if (parent.CanBeResolved()) + return IsDerivedFrom(parent.Resolve(), baseClass); - if (parent.Is(baseClass)) - { - return true; - } + return false; + } - if (parent.CanBeResolved()) - { - return IsDerivedFrom(parent.Resolve(), baseClass); - } + public static TypeReference GetEnumUnderlyingType(this TypeDefinition td) + { + foreach (FieldDefinition field in td.Fields) + { + if (!field.IsStatic) + return field.FieldType; + } + throw new ArgumentException($"Invalid enum {td.FullName}"); + } - return false; - } + public static bool ImplementsInterface(this TypeDefinition td) + { + TypeDefinition typedef = td; - public static TypeReference GetEnumUnderlyingType(this TypeDefinition td) - { - foreach (var field in td.Fields) - { - if (!field.IsStatic) - { - return field.FieldType; - } - } - throw new ArgumentException($"Invalid enum {td.FullName}"); - } + while (typedef != null) + { + if (typedef.Interfaces.Any(iface => iface.InterfaceType.Is())) + return true; - public static bool ImplementsInterface(this TypeDefinition td) - { - var typedef = td; + try + { + TypeReference parent = typedef.BaseType; + typedef = parent?.Resolve(); + } + catch (AssemblyResolutionException) + { + // this can happen for plugins. + //Console.WriteLine("AssemblyResolutionException: "+ ex.ToString()); + break; + } + } - while (typedef != null) - { - if (typedef.Interfaces.Any(iface => iface.InterfaceType.Is())) - { - return true; - } + return false; + } - try - { - var parent = typedef.BaseType; - typedef = parent?.Resolve(); - } - catch (AssemblyResolutionException) - { - // this can happen for plugins. - //Console.WriteLine("AssemblyResolutionException: "+ ex.ToString()); - break; - } - } + public static bool IsMultidimensionalArray(this TypeReference tr) => + tr is ArrayType arrayType && arrayType.Rank > 1; - return false; - } + // Does type use netId as backing field + public static bool IsNetworkIdentityField(this TypeReference tr) => + tr.Is() || + tr.Is() || + tr.IsDerivedFrom(); - public static bool IsMultidimensionalArray(this TypeReference tr) => - tr is ArrayType arrayType && arrayType.Rank > 1; + public static bool CanBeResolved(this TypeReference parent) + { + while (parent != null) + { + if (parent.Scope.Name == "Windows") + { + return false; + } - // Does type use netId as backing field - public static bool IsNetworkIdentityField(this TypeReference tr) => - tr.Is() || - tr.Is() || - tr.IsDerivedFrom(); + if (parent.Scope.Name == "mscorlib") + { + TypeDefinition resolved = parent.Resolve(); + return resolved != null; + } - public static bool CanBeResolved(this TypeReference parent) - { - while (parent != null) - { - if (parent.Scope.Name == "Windows") - { - return false; - } + try + { + parent = parent.Resolve().BaseType; + } + catch + { + return false; + } + } + return true; + } - if (parent.Scope.Name == "mscorlib") - { - var resolved = parent.Resolve(); - return resolved != null; - } + // Makes T => Variable and imports function + public static MethodReference MakeGeneric(this MethodReference generic, ModuleDefinition module, TypeReference variableReference) + { + GenericInstanceMethod instance = new GenericInstanceMethod(generic); + instance.GenericArguments.Add(variableReference); - try - { - parent = parent.Resolve().BaseType; - } - catch - { - return false; - } - } - return true; - } + MethodReference readFunc = module.ImportReference(instance); + return readFunc; + } - // Makes T => Variable and imports function - public static MethodReference MakeGeneric(this MethodReference generic, ModuleDefinition module, TypeReference variableReference) - { - var instance = new GenericInstanceMethod(generic); - instance.GenericArguments.Add(variableReference); + // Given a method of a generic class such as ArraySegment`T.get_Count, + // and a generic instance such as ArraySegment`int + // Creates a reference to the specialized method ArraySegment`int`.get_Count + // Note that calling ArraySegment`T.get_Count directly gives an invalid IL error + public static MethodReference MakeHostInstanceGeneric(this MethodReference self, ModuleDefinition module, GenericInstanceType instanceType) + { + MethodReference reference = new MethodReference(self.Name, self.ReturnType, instanceType) + { + CallingConvention = self.CallingConvention, + HasThis = self.HasThis, + ExplicitThis = self.ExplicitThis + }; - var readFunc = module.ImportReference(instance); - return readFunc; - } + foreach (ParameterDefinition parameter in self.Parameters) + reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); - // Given a method of a generic class such as ArraySegment`T.get_Count, - // and a generic instance such as ArraySegment`int - // Creates a reference to the specialized method ArraySegment`int`.get_Count - // Note that calling ArraySegment`T.get_Count directly gives an invalid IL error - public static MethodReference MakeHostInstanceGeneric(this MethodReference self, ModuleDefinition module, GenericInstanceType instanceType) - { - var reference = new MethodReference(self.Name, self.ReturnType, instanceType) - { - CallingConvention = self.CallingConvention, - HasThis = self.HasThis, - ExplicitThis = self.ExplicitThis - }; + foreach (GenericParameter generic_parameter in self.GenericParameters) + reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference)); - foreach (var parameter in self.Parameters) - { - reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); - } + return module.ImportReference(reference); + } - foreach (var generic_parameter in self.GenericParameters) - { - reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference)); - } + // Given a field of a generic class such as Writer.write, + // and a generic instance such as ArraySegment`int + // Creates a reference to the specialized method ArraySegment`int`.get_Count + // Note that calling ArraySegment`T.get_Count directly gives an invalid IL error + public static FieldReference SpecializeField(this FieldReference self, ModuleDefinition module, GenericInstanceType instanceType) + { + FieldReference reference = new FieldReference(self.Name, self.FieldType, instanceType); + return module.ImportReference(reference); + } - return module.ImportReference(reference); - } + public static CustomAttribute GetCustomAttribute(this ICustomAttributeProvider method) + { + return method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Is()); + } - // Given a field of a generic class such as Writer.write, - // and a generic instance such as ArraySegment`int - // Creates a reference to the specialized method ArraySegment`int`.get_Count - // Note that calling ArraySegment`T.get_Count directly gives an invalid IL error - public static FieldReference SpecializeField(this FieldReference self, ModuleDefinition module, GenericInstanceType instanceType) - { - var reference = new FieldReference(self.Name, self.FieldType, instanceType); - return module.ImportReference(reference); - } + public static bool HasCustomAttribute(this ICustomAttributeProvider attributeProvider) + { + return attributeProvider.CustomAttributes.Any(attr => attr.AttributeType.Is()); + } - public static CustomAttribute GetCustomAttribute(this ICustomAttributeProvider method) => method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Is()); + public static T GetField(this CustomAttribute ca, string field, T defaultValue) + { + foreach (CustomAttributeNamedArgument customField in ca.Fields) + if (customField.Name == field) + return (T)customField.Argument.Value; + return defaultValue; + } - public static bool HasCustomAttribute(this ICustomAttributeProvider attributeProvider) => attributeProvider.CustomAttributes.Any(attr => attr.AttributeType.Is()); + public static MethodDefinition GetMethod(this TypeDefinition td, string methodName) + { + return td.Methods.FirstOrDefault(method => method.Name == methodName); + } - public static T GetField(this CustomAttribute ca, string field, T defaultValue) - { - foreach (var customField in ca.Fields) - { - if (customField.Name == field) - { - return (T)customField.Argument.Value; - } - } + public static List GetMethods(this TypeDefinition td, string methodName) + { + return td.Methods.Where(method => method.Name == methodName).ToList(); + } - return defaultValue; - } + public static MethodDefinition GetMethodInBaseType(this TypeDefinition td, string methodName) + { + TypeDefinition typedef = td; + while (typedef != null) + { + foreach (MethodDefinition md in typedef.Methods) + { + if (md.Name == methodName) + return md; + } - public static MethodDefinition GetMethod(this TypeDefinition td, string methodName) => td.Methods.FirstOrDefault(method => method.Name == methodName); + try + { + TypeReference parent = typedef.BaseType; + typedef = parent?.Resolve(); + } + catch (AssemblyResolutionException) + { + // this can happen for plugins. + break; + } + } - public static List GetMethods(this TypeDefinition td, string methodName) => td.Methods.Where(method => method.Name == methodName).ToList(); + return null; + } - public static MethodDefinition GetMethodInBaseType(this TypeDefinition td, string methodName) - { - var typedef = td; - while (typedef != null) - { - foreach (var md in typedef.Methods) - { - if (md.Name == methodName) - { - return md; - } - } + // Finds public fields in type and base type + public static IEnumerable FindAllPublicFields(this TypeReference variable) + { + return FindAllPublicFields(variable.Resolve()); + } - try - { - var parent = typedef.BaseType; - typedef = parent?.Resolve(); - } - catch (AssemblyResolutionException) - { - // this can happen for plugins. - break; - } - } + // Finds public fields in type and base type + public static IEnumerable FindAllPublicFields(this TypeDefinition typeDefinition) + { + while (typeDefinition != null) + { + foreach (FieldDefinition field in typeDefinition.Fields) + { + if (field.IsStatic || field.IsPrivate) + continue; - return null; - } + if (field.IsNotSerialized) + continue; - // Finds public fields in type and base type - public static IEnumerable FindAllPublicFields(this TypeReference variable) => FindAllPublicFields(variable.Resolve()); + yield return field; + } - // Finds public fields in type and base type - public static IEnumerable FindAllPublicFields(this TypeDefinition typeDefinition) - { - while (typeDefinition != null) - { - foreach (var field in typeDefinition.Fields) - { - if (field.IsStatic || field.IsPrivate) - { - continue; - } + try + { + typeDefinition = typeDefinition.BaseType?.Resolve(); + } + catch (AssemblyResolutionException) + { + break; + } + } + } - if (field.IsNotSerialized) - { - continue; - } - - yield return field; - } - - try - { - typeDefinition = typeDefinition.BaseType?.Resolve(); - } - catch (AssemblyResolutionException) - { - break; - } - } - } - - public static bool ContainsClass(this ModuleDefinition module, string nameSpace, string className) => - module.GetTypes().Any(td => td.Namespace == nameSpace && - td.Name == className); + public static bool ContainsClass(this ModuleDefinition module, string nameSpace, string className) => + module.GetTypes().Any(td => td.Namespace == nameSpace && + td.Name == className); - public static AssemblyNameReference FindReference(this ModuleDefinition module, string referenceName) - { - foreach (var reference in module.AssemblyReferences) - { - if (reference.Name == referenceName) - { - return reference; - } - } - return null; - } - } + public static AssemblyNameReference FindReference(this ModuleDefinition module, string referenceName) + { + foreach (AssemblyNameReference reference in module.AssemblyReferences) + { + if (reference.Name == referenceName) + return reference; + } + return null; + } + } } diff --git a/MirrorWeaver/Weaver/Helpers.cs b/MirrorWeaver/Weaver/Helpers.cs index 247dbeb5..dca928b3 100644 --- a/MirrorWeaver/Weaver/Helpers.cs +++ b/MirrorWeaver/Weaver/Helpers.cs @@ -1,24 +1,26 @@ -using Mono.Cecil; using System.IO; using System.Linq; using System.Reflection; +using Mono.Cecil; namespace Mirror.Weaver { - internal static class Helpers - { - // This code is taken from SerializationWeaver - public static string UnityEngineDllDirectoryName() - { - var directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase); - return directoryName?.Replace(@"file:\", ""); - } + static class Helpers + { + // This code is taken from SerializationWeaver + public static string UnityEngineDllDirectoryName() + { + string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase); + return directoryName?.Replace(@"file:\", ""); + } - public static bool IsEditorAssembly(AssemblyDefinition currentAssembly) => - // we want to add the [InitializeOnLoad] attribute if it's available - // -> usually either 'UnityEditor' or 'UnityEditor.CoreModule' - currentAssembly.MainModule.AssemblyReferences.Any(assemblyReference => - assemblyReference.Name.StartsWith(nameof(UnityEditor)) - ); - } + public static bool IsEditorAssembly(AssemblyDefinition currentAssembly) + { + // we want to add the [InitializeOnLoad] attribute if it's available + // -> usually either 'UnityEditor' or 'UnityEditor.CoreModule' + return currentAssembly.MainModule.AssemblyReferences.Any(assemblyReference => + assemblyReference.Name.StartsWith(nameof(UnityEditor)) + ); + } + } } diff --git a/MirrorWeaver/Weaver/Logger.cs b/MirrorWeaver/Weaver/Logger.cs index 472bb3a5..b5951602 100644 --- a/MirrorWeaver/Weaver/Logger.cs +++ b/MirrorWeaver/Weaver/Logger.cs @@ -2,12 +2,12 @@ using Mono.Cecil; namespace Mirror.Weaver { - // not static, because ILPostProcessor is multithreaded - public interface Logger - { - void Warning(string message); - void Warning(string message, MemberReference mr); - void Error(string message); - void Error(string message, MemberReference mr); - } + // not static, because ILPostProcessor is multithreaded + public interface Logger + { + void Warning(string message); + void Warning(string message, MemberReference mr); + void Error(string message); + void Error(string message, MemberReference mr); + } } diff --git a/MirrorWeaver/Weaver/Processors/CommandProcessor.cs b/MirrorWeaver/Weaver/Processors/CommandProcessor.cs index 6e4a04b9..4fef0351 100644 --- a/MirrorWeaver/Weaver/Processors/CommandProcessor.cs +++ b/MirrorWeaver/Weaver/Processors/CommandProcessor.cs @@ -3,10 +3,10 @@ using Mono.Cecil.Cil; namespace Mirror.Weaver { - // Processes [Command] methods in NetworkBehaviour - public static class CommandProcessor - { - /* + // Processes [Command] methods in NetworkBehaviour + public static class CommandProcessor + { + /* // generates code like: public void CmdThrust(float thrusting, int spin) { @@ -29,45 +29,43 @@ namespace Mirror.Weaver This way we do not need to modify the code anywhere else, and this works correctly in dependent assemblies */ - public static MethodDefinition ProcessCommandCall(WeaverTypes weaverTypes, Writers writers, Logger Log, TypeDefinition td, MethodDefinition md, CustomAttribute commandAttr, ref bool WeavingFailed) - { - var cmd = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); + public static MethodDefinition ProcessCommandCall(WeaverTypes weaverTypes, Writers writers, Logger Log, TypeDefinition td, MethodDefinition md, CustomAttribute commandAttr, ref bool WeavingFailed) + { + MethodDefinition cmd = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); - var worker = md.Body.GetILProcessor(); + ILProcessor worker = md.Body.GetILProcessor(); - NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); + NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); - // NetworkWriter writer = new NetworkWriter(); - NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); + // NetworkWriter writer = new NetworkWriter(); + NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); - // write all the arguments that the user passed to the Cmd call - if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.Command, ref WeavingFailed)) - { - return null; - } + // write all the arguments that the user passed to the Cmd call + if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.Command, ref WeavingFailed)) + return null; - var channel = commandAttr.GetField("channel", 0); - var requiresAuthority = commandAttr.GetField("requiresAuthority", true); + int channel = commandAttr.GetField("channel", 0); + bool requiresAuthority = commandAttr.GetField("requiresAuthority", true); - // invoke internal send and return - // load 'base.' to call the SendCommand function with - worker.Emit(OpCodes.Ldarg_0); - // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions - worker.Emit(OpCodes.Ldstr, md.FullName); - // writer - worker.Emit(OpCodes.Ldloc_0); - worker.Emit(OpCodes.Ldc_I4, channel); - // requiresAuthority ? 1 : 0 - worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - worker.Emit(OpCodes.Call, weaverTypes.sendCommandInternal); + // invoke internal send and return + // load 'base.' to call the SendCommand function with + worker.Emit(OpCodes.Ldarg_0); + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + worker.Emit(OpCodes.Ldstr, md.FullName); + // writer + worker.Emit(OpCodes.Ldloc_0); + worker.Emit(OpCodes.Ldc_I4, channel); + // requiresAuthority ? 1 : 0 + worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); + worker.Emit(OpCodes.Call, weaverTypes.sendCommandInternal); - NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); + NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); - worker.Emit(OpCodes.Ret); - return cmd; - } + worker.Emit(OpCodes.Ret); + return cmd; + } - /* + /* // generates code like: protected static void InvokeCmdCmdThrust(NetworkBehaviour obj, NetworkReader reader, NetworkConnection senderConnection) { @@ -78,49 +76,47 @@ namespace Mirror.Weaver ((ShipControl)obj).CmdThrust(reader.ReadSingle(), (int)reader.ReadPackedUInt32()); } */ - public static MethodDefinition ProcessCommandInvoke(WeaverTypes weaverTypes, Readers readers, Logger Log, TypeDefinition td, MethodDefinition method, MethodDefinition cmdCallFunc, ref bool WeavingFailed) - { - var cmd = new MethodDefinition(Weaver.InvokeRpcPrefix + method.Name, - MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, - weaverTypes.Import(typeof(void))); + public static MethodDefinition ProcessCommandInvoke(WeaverTypes weaverTypes, Readers readers, Logger Log, TypeDefinition td, MethodDefinition method, MethodDefinition cmdCallFunc, ref bool WeavingFailed) + { + MethodDefinition cmd = new MethodDefinition(Weaver.InvokeRpcPrefix + method.Name, + MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, + weaverTypes.Import(typeof(void))); - var worker = cmd.Body.GetILProcessor(); - var label = worker.Create(OpCodes.Nop); + ILProcessor worker = cmd.Body.GetILProcessor(); + Instruction label = worker.Create(OpCodes.Nop); - NetworkBehaviourProcessor.WriteServerActiveCheck(worker, weaverTypes, method.Name, label, "Command"); + NetworkBehaviourProcessor.WriteServerActiveCheck(worker, weaverTypes, method.Name, label, "Command"); - // setup for reader - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Castclass, td); + // setup for reader + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Castclass, td); - if (!NetworkBehaviourProcessor.ReadArguments(method, readers, Log, worker, RemoteCallType.Command, ref WeavingFailed)) - { - return null; - } + if (!NetworkBehaviourProcessor.ReadArguments(method, readers, Log, worker, RemoteCallType.Command, ref WeavingFailed)) + return null; - AddSenderConnection(method, worker); + AddSenderConnection(method, worker); - // invoke actual command function - worker.Emit(OpCodes.Callvirt, cmdCallFunc); - worker.Emit(OpCodes.Ret); + // invoke actual command function + worker.Emit(OpCodes.Callvirt, cmdCallFunc); + worker.Emit(OpCodes.Ret); - NetworkBehaviourProcessor.AddInvokeParameters(weaverTypes, cmd.Parameters); + NetworkBehaviourProcessor.AddInvokeParameters(weaverTypes, cmd.Parameters); - td.Methods.Add(cmd); - return cmd; - } + td.Methods.Add(cmd); + return cmd; + } - private static void AddSenderConnection(MethodDefinition method, ILProcessor worker) - { - foreach (var param in method.Parameters) - { - if (NetworkBehaviourProcessor.IsSenderConnection(param, RemoteCallType.Command)) - { - // NetworkConnection is 3nd arg (arg0 is "obj" not "this" because method is static) - // example: static void InvokeCmdCmdSendCommand(NetworkBehaviour obj, NetworkReader reader, NetworkConnection connection) - worker.Emit(OpCodes.Ldarg_2); - } - } - } - } + static void AddSenderConnection(MethodDefinition method, ILProcessor worker) + { + foreach (ParameterDefinition param in method.Parameters) + { + if (NetworkBehaviourProcessor.IsSenderConnection(param, RemoteCallType.Command)) + { + // NetworkConnection is 3nd arg (arg0 is "obj" not "this" because method is static) + // example: static void InvokeCmdCmdSendCommand(NetworkBehaviour obj, NetworkReader reader, NetworkConnection connection) + worker.Emit(OpCodes.Ldarg_2); + } + } + } + } } diff --git a/MirrorWeaver/Weaver/Processors/MethodProcessor.cs b/MirrorWeaver/Weaver/Processors/MethodProcessor.cs index 06508d44..4b81cbd3 100644 --- a/MirrorWeaver/Weaver/Processors/MethodProcessor.cs +++ b/MirrorWeaver/Weaver/Processors/MethodProcessor.cs @@ -3,138 +3,128 @@ using Mono.Cecil.Cil; namespace Mirror.Weaver { - public static class MethodProcessor - { - private const string RpcPrefix = "UserCode_"; + public static class MethodProcessor + { + const string RpcPrefix = "UserCode_"; - // creates a method substitute - // For example, if we have this: - // public void CmdThrust(float thrusting, int spin) - // { - // xxxxx - // } - // - // it will substitute the method and move the code to a new method with a provided name - // for example: - // - // public void CmdTrust(float thrusting, int spin) - // { - // } - // - // public void (float thrusting, int spin) - // { - // xxxxx - // } - // - // Note that all the calls to the method remain untouched - // - // the original method definition loses all code - // this returns the newly created method with all the user provided code - public static MethodDefinition SubstituteMethod(Logger Log, TypeDefinition td, MethodDefinition md, ref bool WeavingFailed) - { - var newName = RpcPrefix + md.Name; - var cmd = new MethodDefinition(newName, md.Attributes, md.ReturnType) - { + // creates a method substitute + // For example, if we have this: + // public void CmdThrust(float thrusting, int spin) + // { + // xxxxx + // } + // + // it will substitute the method and move the code to a new method with a provided name + // for example: + // + // public void CmdTrust(float thrusting, int spin) + // { + // } + // + // public void (float thrusting, int spin) + // { + // xxxxx + // } + // + // Note that all the calls to the method remain untouched + // + // the original method definition loses all code + // this returns the newly created method with all the user provided code + public static MethodDefinition SubstituteMethod(Logger Log, TypeDefinition td, MethodDefinition md, ref bool WeavingFailed) + { + string newName = RpcPrefix + md.Name; + MethodDefinition cmd = new MethodDefinition(newName, md.Attributes, md.ReturnType); - // force the substitute method to be protected. - // -> public would show in the Inspector for UnityEvents as - // User_CmdUsePotion() etc. but the user shouldn't use those. - // -> private would not allow inheriting classes to call it, see - // OverrideVirtualWithBaseCallsBothVirtualAndBase test. - // -> IL has no concept of 'protected', it's called IsFamily there. - IsPublic = false, - IsFamily = true - }; + // force the substitute method to be protected. + // -> public would show in the Inspector for UnityEvents as + // User_CmdUsePotion() etc. but the user shouldn't use those. + // -> private would not allow inheriting classes to call it, see + // OverrideVirtualWithBaseCallsBothVirtualAndBase test. + // -> IL has no concept of 'protected', it's called IsFamily there. + cmd.IsPublic = false; + cmd.IsFamily = true; - // add parameters - foreach (var pd in md.Parameters) - { - cmd.Parameters.Add(new ParameterDefinition(pd.Name, ParameterAttributes.None, pd.ParameterType)); - } + // add parameters + foreach (ParameterDefinition pd in md.Parameters) + { + cmd.Parameters.Add(new ParameterDefinition(pd.Name, ParameterAttributes.None, pd.ParameterType)); + } - // swap bodies - (cmd.Body, md.Body) = (md.Body, cmd.Body); + // swap bodies + (cmd.Body, md.Body) = (md.Body, cmd.Body); - // Move over all the debugging information - foreach (var sequencePoint in md.DebugInformation.SequencePoints) - { - cmd.DebugInformation.SequencePoints.Add(sequencePoint); - } + // Move over all the debugging information + foreach (SequencePoint sequencePoint in md.DebugInformation.SequencePoints) + cmd.DebugInformation.SequencePoints.Add(sequencePoint); + md.DebugInformation.SequencePoints.Clear(); - md.DebugInformation.SequencePoints.Clear(); + foreach (CustomDebugInformation customInfo in md.CustomDebugInformations) + cmd.CustomDebugInformations.Add(customInfo); + md.CustomDebugInformations.Clear(); - foreach (var customInfo in md.CustomDebugInformations) - { - cmd.CustomDebugInformations.Add(customInfo); - } + (md.DebugInformation.Scope, cmd.DebugInformation.Scope) = (cmd.DebugInformation.Scope, md.DebugInformation.Scope); - md.CustomDebugInformations.Clear(); + td.Methods.Add(cmd); - (md.DebugInformation.Scope, cmd.DebugInformation.Scope) = (cmd.DebugInformation.Scope, md.DebugInformation.Scope); + FixRemoteCallToBaseMethod(Log, td, cmd, ref WeavingFailed); + return cmd; + } - td.Methods.Add(cmd); + // Finds and fixes call to base methods within remote calls + //For example, changes `base.CmdDoSomething` to `base.CallCmdDoSomething` within `this.CallCmdDoSomething` + public static void FixRemoteCallToBaseMethod(Logger Log, TypeDefinition type, MethodDefinition method, ref bool WeavingFailed) + { + string callName = method.Name; - FixRemoteCallToBaseMethod(Log, td, cmd, ref WeavingFailed); - return cmd; - } + // Cmd/rpc start with Weaver.RpcPrefix + // e.g. CallCmdDoSomething + if (!callName.StartsWith(RpcPrefix)) + return; - // Finds and fixes call to base methods within remote calls - //For example, changes `base.CmdDoSomething` to `base.CallCmdDoSomething` within `this.CallCmdDoSomething` - public static void FixRemoteCallToBaseMethod(Logger Log, TypeDefinition type, MethodDefinition method, ref bool WeavingFailed) - { - var callName = method.Name; + // e.g. CmdDoSomething + string baseRemoteCallName = method.Name.Substring(RpcPrefix.Length); - // Cmd/rpc start with Weaver.RpcPrefix - // e.g. CallCmdDoSomething - if (!callName.StartsWith(RpcPrefix)) - { - return; - } + foreach (Instruction instruction in method.Body.Instructions) + { + // if call to base.CmdDoSomething within this.CallCmdDoSomething + if (IsCallToMethod(instruction, out MethodDefinition calledMethod) && + calledMethod.Name == baseRemoteCallName) + { + TypeDefinition baseType = type.BaseType.Resolve(); + MethodDefinition baseMethod = baseType.GetMethodInBaseType(callName); - // e.g. CmdDoSomething - var baseRemoteCallName = method.Name.Substring(RpcPrefix.Length); + if (baseMethod == null) + { + Log.Error($"Could not find base method for {callName}", method); + WeavingFailed = true; + return; + } - foreach (var instruction in method.Body.Instructions) - { - // if call to base.CmdDoSomething within this.CallCmdDoSomething - if (IsCallToMethod(instruction, out var calledMethod) && - calledMethod.Name == baseRemoteCallName) - { - var baseType = type.BaseType.Resolve(); - var baseMethod = baseType.GetMethodInBaseType(callName); + if (!baseMethod.IsVirtual) + { + Log.Error($"Could not find base method that was virtual {callName}", method); + WeavingFailed = true; + return; + } - if (baseMethod == null) - { - Log.Error($"Could not find base method for {callName}", method); - WeavingFailed = true; - return; - } + instruction.Operand = baseMethod; + } + } + } - if (!baseMethod.IsVirtual) - { - Log.Error($"Could not find base method that was virtual {callName}", method); - WeavingFailed = true; - return; - } - - instruction.Operand = baseMethod; - } - } - } - - private static bool IsCallToMethod(Instruction instruction, out MethodDefinition calledMethod) - { - if (instruction.OpCode == OpCodes.Call && - instruction.Operand is MethodDefinition method) - { - calledMethod = method; - return true; - } - else - { - calledMethod = null; - return false; - } - } - } + static bool IsCallToMethod(Instruction instruction, out MethodDefinition calledMethod) + { + if (instruction.OpCode == OpCodes.Call && + instruction.Operand is MethodDefinition method) + { + calledMethod = method; + return true; + } + else + { + calledMethod = null; + return false; + } + } + } } diff --git a/MirrorWeaver/Weaver/Processors/MonoBehaviourProcessor.cs b/MirrorWeaver/Weaver/Processors/MonoBehaviourProcessor.cs index 2b915140..9e5d3cb6 100644 --- a/MirrorWeaver/Weaver/Processors/MonoBehaviourProcessor.cs +++ b/MirrorWeaver/Weaver/Processors/MonoBehaviourProcessor.cs @@ -2,55 +2,55 @@ using Mono.Cecil; namespace Mirror.Weaver { - // only shows warnings in case we use SyncVars etc. for MonoBehaviour. - internal static class MonoBehaviourProcessor - { - public static void Process(Logger Log, TypeDefinition td, ref bool WeavingFailed) - { - ProcessSyncVars(Log, td, ref WeavingFailed); - ProcessMethods(Log, td, ref WeavingFailed); - } + // only shows warnings in case we use SyncVars etc. for MonoBehaviour. + static class MonoBehaviourProcessor + { + public static void Process(Logger Log, TypeDefinition td, ref bool WeavingFailed) + { + ProcessSyncVars(Log, td, ref WeavingFailed); + ProcessMethods(Log, td, ref WeavingFailed); + } - private static void ProcessSyncVars(Logger Log, TypeDefinition td, ref bool WeavingFailed) - { - // find syncvars - foreach (var fd in td.Fields) - { - if (fd.HasCustomAttribute()) - { - Log.Error($"SyncVar {fd.Name} must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); - WeavingFailed = true; - } + static void ProcessSyncVars(Logger Log, TypeDefinition td, ref bool WeavingFailed) + { + // find syncvars + foreach (FieldDefinition fd in td.Fields) + { + if (fd.HasCustomAttribute()) + { + Log.Error($"SyncVar {fd.Name} must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); + WeavingFailed = true; + } - if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) - { - Log.Error($"{fd.Name} is a SyncObject and must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); - WeavingFailed = true; - } - } - } + if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) + { + Log.Error($"{fd.Name} is a SyncObject and must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); + WeavingFailed = true; + } + } + } - private static void ProcessMethods(Logger Log, TypeDefinition td, ref bool WeavingFailed) - { - // find command and RPC functions - foreach (var md in td.Methods) - { - if (md.HasCustomAttribute()) - { - Log.Error($"Command {md.Name} must be declared inside a NetworkBehaviour", md); - WeavingFailed = true; - } - if (md.HasCustomAttribute()) - { - Log.Error($"ClientRpc {md.Name} must be declared inside a NetworkBehaviour", md); - WeavingFailed = true; - } - if (md.HasCustomAttribute()) - { - Log.Error($"TargetRpc {md.Name} must be declared inside a NetworkBehaviour", md); - WeavingFailed = true; - } - } - } - } + static void ProcessMethods(Logger Log, TypeDefinition td, ref bool WeavingFailed) + { + // find command and RPC functions + foreach (MethodDefinition md in td.Methods) + { + if (md.HasCustomAttribute()) + { + Log.Error($"Command {md.Name} must be declared inside a NetworkBehaviour", md); + WeavingFailed = true; + } + if (md.HasCustomAttribute()) + { + Log.Error($"ClientRpc {md.Name} must be declared inside a NetworkBehaviour", md); + WeavingFailed = true; + } + if (md.HasCustomAttribute()) + { + Log.Error($"TargetRpc {md.Name} must be declared inside a NetworkBehaviour", md); + WeavingFailed = true; + } + } + } + } } diff --git a/MirrorWeaver/Weaver/Processors/NetworkBehaviourProcessor.cs b/MirrorWeaver/Weaver/Processors/NetworkBehaviourProcessor.cs index bd3ebbc0..03575d11 100644 --- a/MirrorWeaver/Weaver/Processors/NetworkBehaviourProcessor.cs +++ b/MirrorWeaver/Weaver/Processors/NetworkBehaviourProcessor.cs @@ -1,73 +1,74 @@ +using System.Collections.Generic; using Mono.Cecil; using Mono.Cecil.Cil; -using System.Collections.Generic; namespace Mirror.Weaver { - public enum RemoteCallType - { - Command, - ClientRpc, - TargetRpc - } + public enum RemoteCallType + { + Command, + ClientRpc, + TargetRpc + } - // processes SyncVars, Cmds, Rpcs, etc. of NetworkBehaviours - internal class NetworkBehaviourProcessor - { - private readonly AssemblyDefinition assembly; - private readonly WeaverTypes weaverTypes; - private readonly SyncVarAccessLists syncVarAccessLists; - private readonly SyncVarAttributeProcessor syncVarAttributeProcessor; - private readonly Writers writers; - private readonly Readers readers; - private readonly Logger Log; - private List syncVars = new List(); - private List syncObjects = new List(); + // processes SyncVars, Cmds, Rpcs, etc. of NetworkBehaviours + class NetworkBehaviourProcessor + { + AssemblyDefinition assembly; + WeaverTypes weaverTypes; + SyncVarAccessLists syncVarAccessLists; + SyncVarAttributeProcessor syncVarAttributeProcessor; + Writers writers; + Readers readers; + Logger Log; - // - private Dictionary syncVarNetIds = new Dictionary(); - private readonly List commands = new List(); - private readonly List clientRpcs = new List(); - private readonly List targetRpcs = new List(); - private readonly List commandInvocationFuncs = new List(); - private readonly List clientRpcInvocationFuncs = new List(); - private readonly List targetRpcInvocationFuncs = new List(); - private readonly TypeDefinition netBehaviourSubclass; + List syncVars = new List(); + List syncObjects = new List(); + // + Dictionary syncVarNetIds = new Dictionary(); + readonly List commands = new List(); + readonly List clientRpcs = new List(); + readonly List targetRpcs = new List(); + readonly List commandInvocationFuncs = new List(); + readonly List clientRpcInvocationFuncs = new List(); + readonly List targetRpcInvocationFuncs = new List(); - public struct CmdResult - { - public MethodDefinition method; - public bool requiresAuthority; - } + readonly TypeDefinition netBehaviourSubclass; - public struct ClientRpcResult - { - public MethodDefinition method; - public bool includeOwner; - } + public struct CmdResult + { + public MethodDefinition method; + public bool requiresAuthority; + } - public NetworkBehaviourProcessor(AssemblyDefinition assembly, WeaverTypes weaverTypes, SyncVarAccessLists syncVarAccessLists, Writers writers, Readers readers, Logger Log, TypeDefinition td) - { - this.assembly = assembly; - this.weaverTypes = weaverTypes; - this.syncVarAccessLists = syncVarAccessLists; - this.writers = writers; - this.readers = readers; - this.Log = Log; - syncVarAttributeProcessor = new SyncVarAttributeProcessor(assembly, weaverTypes, syncVarAccessLists, Log); - netBehaviourSubclass = td; - } + public struct ClientRpcResult + { + public MethodDefinition method; + public bool includeOwner; + } - // return true if modified - public bool Process(ref bool WeavingFailed) - { - // only process once - if (WasProcessed(netBehaviourSubclass)) - { - return false; - } + public NetworkBehaviourProcessor(AssemblyDefinition assembly, WeaverTypes weaverTypes, SyncVarAccessLists syncVarAccessLists, Writers writers, Readers readers, Logger Log, TypeDefinition td) + { + this.assembly = assembly; + this.weaverTypes = weaverTypes; + this.syncVarAccessLists = syncVarAccessLists; + this.writers = writers; + this.readers = readers; + this.Log = Log; + syncVarAttributeProcessor = new SyncVarAttributeProcessor(assembly, weaverTypes, syncVarAccessLists, Log); + netBehaviourSubclass = td; + } - /* + // return true if modified + public bool Process(ref bool WeavingFailed) + { + // only process once + if (WasProcessed(netBehaviourSubclass)) + { + return false; + } + + /* if (netBehaviourSubclass.HasGenericParameters) { Log.Error($"{netBehaviourSubclass.Name} cannot have generic parameters", netBehaviourSubclass); @@ -77,488 +78,485 @@ namespace Mirror.Weaver return true; } */ - MarkAsProcessed(netBehaviourSubclass); + MarkAsProcessed(netBehaviourSubclass); - // deconstruct tuple and set fields - (syncVars, syncVarNetIds) = syncVarAttributeProcessor.ProcessSyncVars(netBehaviourSubclass, ref WeavingFailed); + // deconstruct tuple and set fields + (syncVars, syncVarNetIds) = syncVarAttributeProcessor.ProcessSyncVars(netBehaviourSubclass, ref WeavingFailed); - syncObjects = SyncObjectProcessor.FindSyncObjectsFields(writers, readers, Log, netBehaviourSubclass, ref WeavingFailed); + syncObjects = SyncObjectProcessor.FindSyncObjectsFields(writers, readers, Log, netBehaviourSubclass, ref WeavingFailed); - ProcessMethods(ref WeavingFailed); - if (WeavingFailed) - { - // originally Process returned true in every case, except if already processed. - // maybe return false here in the future. - return true; - } + ProcessMethods(ref WeavingFailed); + if (WeavingFailed) + { + // originally Process returned true in every case, except if already processed. + // maybe return false here in the future. + return true; + } - // inject initializations into static & instance constructor - InjectIntoStaticConstructor(ref WeavingFailed); - InjectIntoInstanceConstructor(ref WeavingFailed); + // inject initializations into static & instance constructor + InjectIntoStaticConstructor(ref WeavingFailed); + InjectIntoInstanceConstructor(ref WeavingFailed); - GenerateSerialization(ref WeavingFailed); - if (WeavingFailed) - { - // originally Process returned true in every case, except if already processed. - // maybe return false here in the future. - return true; - } + GenerateSerialization(ref WeavingFailed); + if (WeavingFailed) + { + // originally Process returned true in every case, except if already processed. + // maybe return false here in the future. + return true; + } - GenerateDeSerialization(ref WeavingFailed); - return true; - } + GenerateDeSerialization(ref WeavingFailed); + return true; + } - /* + /* generates code like: if (!NetworkClient.active) Debug.LogError((object) "Command function CmdRespawn called on server."); which is used in InvokeCmd, InvokeRpc, etc. */ - public static void WriteClientActiveCheck(ILProcessor worker, WeaverTypes weaverTypes, string mdName, Instruction label, string errString) - { - // client active check - worker.Emit(OpCodes.Call, weaverTypes.NetworkClientGetActive); - worker.Emit(OpCodes.Brtrue, label); + public static void WriteClientActiveCheck(ILProcessor worker, WeaverTypes weaverTypes, string mdName, Instruction label, string errString) + { + // client active check + worker.Emit(OpCodes.Call, weaverTypes.NetworkClientGetActive); + worker.Emit(OpCodes.Brtrue, label); - worker.Emit(OpCodes.Ldstr, $"{errString} {mdName} called on server."); - worker.Emit(OpCodes.Call, weaverTypes.logErrorReference); - worker.Emit(OpCodes.Ret); - worker.Append(label); - } - /* + worker.Emit(OpCodes.Ldstr, $"{errString} {mdName} called on server."); + worker.Emit(OpCodes.Call, weaverTypes.logErrorReference); + worker.Emit(OpCodes.Ret); + worker.Append(label); + } + /* generates code like: if (!NetworkServer.active) Debug.LogError((object) "Command CmdMsgWhisper called on client."); */ - public static void WriteServerActiveCheck(ILProcessor worker, WeaverTypes weaverTypes, string mdName, Instruction label, string errString) - { - // server active check - worker.Emit(OpCodes.Call, weaverTypes.NetworkServerGetActive); - worker.Emit(OpCodes.Brtrue, label); + public static void WriteServerActiveCheck(ILProcessor worker, WeaverTypes weaverTypes, string mdName, Instruction label, string errString) + { + // server active check + worker.Emit(OpCodes.Call, weaverTypes.NetworkServerGetActive); + worker.Emit(OpCodes.Brtrue, label); - worker.Emit(OpCodes.Ldstr, $"{errString} {mdName} called on client."); - worker.Emit(OpCodes.Call, weaverTypes.logErrorReference); - worker.Emit(OpCodes.Ret); - worker.Append(label); - } + worker.Emit(OpCodes.Ldstr, $"{errString} {mdName} called on client."); + worker.Emit(OpCodes.Call, weaverTypes.logErrorReference); + worker.Emit(OpCodes.Ret); + worker.Append(label); + } - public static void WriteSetupLocals(ILProcessor worker, WeaverTypes weaverTypes) - { - worker.Body.InitLocals = true; - worker.Body.Variables.Add(new VariableDefinition(weaverTypes.Import())); - } + public static void WriteSetupLocals(ILProcessor worker, WeaverTypes weaverTypes) + { + worker.Body.InitLocals = true; + worker.Body.Variables.Add(new VariableDefinition(weaverTypes.Import())); + } - public static void WriteCreateWriter(ILProcessor worker, WeaverTypes weaverTypes) - { - // create writer - worker.Emit(OpCodes.Call, weaverTypes.GetPooledWriterReference); - worker.Emit(OpCodes.Stloc_0); - } + public static void WriteCreateWriter(ILProcessor worker, WeaverTypes weaverTypes) + { + // create writer + worker.Emit(OpCodes.Call, weaverTypes.GetPooledWriterReference); + worker.Emit(OpCodes.Stloc_0); + } - public static void WriteRecycleWriter(ILProcessor worker, WeaverTypes weaverTypes) - { - // NetworkWriterPool.Recycle(writer); - worker.Emit(OpCodes.Ldloc_0); - worker.Emit(OpCodes.Call, weaverTypes.RecycleWriterReference); - } + public static void WriteRecycleWriter(ILProcessor worker, WeaverTypes weaverTypes) + { + // NetworkWriterPool.Recycle(writer); + worker.Emit(OpCodes.Ldloc_0); + worker.Emit(OpCodes.Call, weaverTypes.RecycleWriterReference); + } - public static bool WriteArguments(ILProcessor worker, Writers writers, Logger Log, MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed) - { - // write each argument - // example result - /* + public static bool WriteArguments(ILProcessor worker, Writers writers, Logger Log, MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed) + { + // write each argument + // example result + /* writer.WritePackedInt32(someNumber); writer.WriteNetworkIdentity(someTarget); */ - var skipFirst = callType == RemoteCallType.TargetRpc - && TargetRpcProcessor.HasNetworkConnectionParameter(method); + bool skipFirst = callType == RemoteCallType.TargetRpc + && TargetRpcProcessor.HasNetworkConnectionParameter(method); - // arg of calling function, arg 0 is "this" so start counting at 1 - var argNum = 1; - foreach (var param in method.Parameters) - { - // NetworkConnection is not sent via the NetworkWriter so skip it here - // skip first for NetworkConnection in TargetRpc - if (argNum == 1 && skipFirst) - { - argNum += 1; - continue; - } - // skip SenderConnection in Command - if (IsSenderConnection(param, callType)) - { - argNum += 1; - continue; - } + // arg of calling function, arg 0 is "this" so start counting at 1 + int argNum = 1; + foreach (ParameterDefinition param in method.Parameters) + { + // NetworkConnection is not sent via the NetworkWriter so skip it here + // skip first for NetworkConnection in TargetRpc + if (argNum == 1 && skipFirst) + { + argNum += 1; + continue; + } + // skip SenderConnection in Command + if (IsSenderConnection(param, callType)) + { + argNum += 1; + continue; + } - var writeFunc = writers.GetWriteFunc(param.ParameterType, ref WeavingFailed); - if (writeFunc == null) - { - Log.Error($"{method.Name} has invalid parameter {param}", method); - WeavingFailed = true; - return false; - } + MethodReference writeFunc = writers.GetWriteFunc(param.ParameterType, ref WeavingFailed); + if (writeFunc == null) + { + Log.Error($"{method.Name} has invalid parameter {param}", method); + WeavingFailed = true; + return false; + } - // use built-in writer func on writer object - // NetworkWriter object - worker.Emit(OpCodes.Ldloc_0); - // add argument to call - worker.Emit(OpCodes.Ldarg, argNum); - // call writer extension method - worker.Emit(OpCodes.Call, writeFunc); - argNum += 1; - } - return true; - } + // use built-in writer func on writer object + // NetworkWriter object + worker.Emit(OpCodes.Ldloc_0); + // add argument to call + worker.Emit(OpCodes.Ldarg, argNum); + // call writer extension method + worker.Emit(OpCodes.Call, writeFunc); + argNum += 1; + } + return true; + } - #region mark / check type as processed - public const string ProcessedFunctionName = "MirrorProcessed"; + #region mark / check type as processed + public const string ProcessedFunctionName = "MirrorProcessed"; - // by adding an empty MirrorProcessed() function - public static bool WasProcessed(TypeDefinition td) => td.GetMethod(ProcessedFunctionName) != null; + // by adding an empty MirrorProcessed() function + public static bool WasProcessed(TypeDefinition td) + { + return td.GetMethod(ProcessedFunctionName) != null; + } - public void MarkAsProcessed(TypeDefinition td) - { - if (!WasProcessed(td)) - { - var versionMethod = new MethodDefinition(ProcessedFunctionName, MethodAttributes.Private, weaverTypes.Import(typeof(void))); - var worker = versionMethod.Body.GetILProcessor(); - worker.Emit(OpCodes.Ret); - td.Methods.Add(versionMethod); - } - } - #endregion + public void MarkAsProcessed(TypeDefinition td) + { + if (!WasProcessed(td)) + { + MethodDefinition versionMethod = new MethodDefinition(ProcessedFunctionName, MethodAttributes.Private, weaverTypes.Import(typeof(void))); + ILProcessor worker = versionMethod.Body.GetILProcessor(); + worker.Emit(OpCodes.Ret); + td.Methods.Add(versionMethod); + } + } + #endregion - // helper function to remove 'Ret' from the end of the method if 'Ret' - // is the last instruction. - // returns false if there was an issue - private static bool RemoveFinalRetInstruction(MethodDefinition method) - { - // remove the return opcode from end of function. will add our own later. - if (method.Body.Instructions.Count != 0) - { - var retInstr = method.Body.Instructions[method.Body.Instructions.Count - 1]; - if (retInstr.OpCode == OpCodes.Ret) - { - method.Body.Instructions.RemoveAt(method.Body.Instructions.Count - 1); - return true; - } - return false; - } + // helper function to remove 'Ret' from the end of the method if 'Ret' + // is the last instruction. + // returns false if there was an issue + static bool RemoveFinalRetInstruction(MethodDefinition method) + { + // remove the return opcode from end of function. will add our own later. + if (method.Body.Instructions.Count != 0) + { + Instruction retInstr = method.Body.Instructions[method.Body.Instructions.Count - 1]; + if (retInstr.OpCode == OpCodes.Ret) + { + method.Body.Instructions.RemoveAt(method.Body.Instructions.Count - 1); + return true; + } + return false; + } - // we did nothing, but there was no error. - return true; - } + // we did nothing, but there was no error. + return true; + } - // we need to inject several initializations into NetworkBehaviour cctor - private void InjectIntoStaticConstructor(ref bool WeavingFailed) - { - if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0) - { - return; - } + // we need to inject several initializations into NetworkBehaviour cctor + void InjectIntoStaticConstructor(ref bool WeavingFailed) + { + if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0) + return; - // find static constructor - var cctor = netBehaviourSubclass.GetMethod(".cctor"); - var cctorFound = cctor != null; - if (cctor != null) - { - // remove the return opcode from end of function. will add our own later. - if (!RemoveFinalRetInstruction(cctor)) - { - Log.Error($"{netBehaviourSubclass.Name} has invalid class constructor", cctor); - WeavingFailed = true; - return; - } - } - else - { - // make one! - cctor = new MethodDefinition(".cctor", MethodAttributes.Private | - MethodAttributes.HideBySig | - MethodAttributes.SpecialName | - MethodAttributes.RTSpecialName | - MethodAttributes.Static, - weaverTypes.Import(typeof(void))); - } + // find static constructor + MethodDefinition cctor = netBehaviourSubclass.GetMethod(".cctor"); + bool cctorFound = cctor != null; + if (cctor != null) + { + // remove the return opcode from end of function. will add our own later. + if (!RemoveFinalRetInstruction(cctor)) + { + Log.Error($"{netBehaviourSubclass.Name} has invalid class constructor", cctor); + WeavingFailed = true; + return; + } + } + else + { + // make one! + cctor = new MethodDefinition(".cctor", MethodAttributes.Private | + MethodAttributes.HideBySig | + MethodAttributes.SpecialName | + MethodAttributes.RTSpecialName | + MethodAttributes.Static, + weaverTypes.Import(typeof(void))); + } - var cctorWorker = cctor.Body.GetILProcessor(); + ILProcessor cctorWorker = cctor.Body.GetILProcessor(); - // register all commands in cctor - for (var i = 0; i < commands.Count; ++i) - { - var cmdResult = commands[i]; - GenerateRegisterCommandDelegate(cctorWorker, weaverTypes.registerCommandReference, commandInvocationFuncs[i], cmdResult); - } + // register all commands in cctor + for (int i = 0; i < commands.Count; ++i) + { + CmdResult cmdResult = commands[i]; + GenerateRegisterCommandDelegate(cctorWorker, weaverTypes.registerCommandReference, commandInvocationFuncs[i], cmdResult); + } - // register all client rpcs in cctor - for (var i = 0; i < clientRpcs.Count; ++i) - { - var clientRpcResult = clientRpcs[i]; - GenerateRegisterRemoteDelegate(cctorWorker, weaverTypes.registerRpcReference, clientRpcInvocationFuncs[i], clientRpcResult.method.FullName); - } + // register all client rpcs in cctor + for (int i = 0; i < clientRpcs.Count; ++i) + { + ClientRpcResult clientRpcResult = clientRpcs[i]; + GenerateRegisterRemoteDelegate(cctorWorker, weaverTypes.registerRpcReference, clientRpcInvocationFuncs[i], clientRpcResult.method.FullName); + } - // register all target rpcs in cctor - for (var i = 0; i < targetRpcs.Count; ++i) - { - GenerateRegisterRemoteDelegate(cctorWorker, weaverTypes.registerRpcReference, targetRpcInvocationFuncs[i], targetRpcs[i].FullName); - } + // register all target rpcs in cctor + for (int i = 0; i < targetRpcs.Count; ++i) + { + GenerateRegisterRemoteDelegate(cctorWorker, weaverTypes.registerRpcReference, targetRpcInvocationFuncs[i], targetRpcs[i].FullName); + } - // add final 'Ret' instruction to cctor - cctorWorker.Append(cctorWorker.Create(OpCodes.Ret)); - if (!cctorFound) - { - netBehaviourSubclass.Methods.Add(cctor); - } + // add final 'Ret' instruction to cctor + cctorWorker.Append(cctorWorker.Create(OpCodes.Ret)); + if (!cctorFound) + { + netBehaviourSubclass.Methods.Add(cctor); + } - // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late - netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit; - } + // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late + netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit; + } - // we need to inject several initializations into NetworkBehaviour ctor - private void InjectIntoInstanceConstructor(ref bool WeavingFailed) - { - if (syncObjects.Count == 0) - { - return; - } + // we need to inject several initializations into NetworkBehaviour ctor + void InjectIntoInstanceConstructor(ref bool WeavingFailed) + { + if (syncObjects.Count == 0) + return; - // find instance constructor - var ctor = netBehaviourSubclass.GetMethod(".ctor"); - if (ctor == null) - { - Log.Error($"{netBehaviourSubclass.Name} has invalid constructor", netBehaviourSubclass); - WeavingFailed = true; - return; - } + // find instance constructor + MethodDefinition ctor = netBehaviourSubclass.GetMethod(".ctor"); + if (ctor == null) + { + Log.Error($"{netBehaviourSubclass.Name} has invalid constructor", netBehaviourSubclass); + WeavingFailed = true; + return; + } - // remove the return opcode from end of function. will add our own later. - if (!RemoveFinalRetInstruction(ctor)) - { - Log.Error($"{netBehaviourSubclass.Name} has invalid constructor", ctor); - WeavingFailed = true; - return; - } + // remove the return opcode from end of function. will add our own later. + if (!RemoveFinalRetInstruction(ctor)) + { + Log.Error($"{netBehaviourSubclass.Name} has invalid constructor", ctor); + WeavingFailed = true; + return; + } - var ctorWorker = ctor.Body.GetILProcessor(); + ILProcessor ctorWorker = ctor.Body.GetILProcessor(); - // initialize all sync objects in ctor - foreach (var fd in syncObjects) - { - SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, weaverTypes, fd); - } + // initialize all sync objects in ctor + foreach (FieldDefinition fd in syncObjects) + { + SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, weaverTypes, fd); + } - // add final 'Ret' instruction to ctor - ctorWorker.Append(ctorWorker.Create(OpCodes.Ret)); - } + // add final 'Ret' instruction to ctor + ctorWorker.Append(ctorWorker.Create(OpCodes.Ret)); + } - /* + /* // This generates code like: NetworkBehaviour.RegisterCommandDelegate(base.GetType(), "CmdThrust", new NetworkBehaviour.CmdDelegate(ShipControl.InvokeCmdCmdThrust)); */ - // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions - private void GenerateRegisterRemoteDelegate(ILProcessor worker, MethodReference registerMethod, MethodDefinition func, string functionFullName) - { - worker.Emit(OpCodes.Ldtoken, netBehaviourSubclass); - worker.Emit(OpCodes.Call, weaverTypes.getTypeFromHandleReference); - worker.Emit(OpCodes.Ldstr, functionFullName); - worker.Emit(OpCodes.Ldnull); - worker.Emit(OpCodes.Ldftn, func); + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + void GenerateRegisterRemoteDelegate(ILProcessor worker, MethodReference registerMethod, MethodDefinition func, string functionFullName) + { + worker.Emit(OpCodes.Ldtoken, netBehaviourSubclass); + worker.Emit(OpCodes.Call, weaverTypes.getTypeFromHandleReference); + worker.Emit(OpCodes.Ldstr, functionFullName); + worker.Emit(OpCodes.Ldnull); + worker.Emit(OpCodes.Ldftn, func); - worker.Emit(OpCodes.Newobj, weaverTypes.RemoteCallDelegateConstructor); - // - worker.Emit(OpCodes.Call, registerMethod); - } + worker.Emit(OpCodes.Newobj, weaverTypes.RemoteCallDelegateConstructor); + // + worker.Emit(OpCodes.Call, registerMethod); + } - private void GenerateRegisterCommandDelegate(ILProcessor worker, MethodReference registerMethod, MethodDefinition func, CmdResult cmdResult) - { - // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions - var cmdName = cmdResult.method.FullName; - var requiresAuthority = cmdResult.requiresAuthority; + void GenerateRegisterCommandDelegate(ILProcessor worker, MethodReference registerMethod, MethodDefinition func, CmdResult cmdResult) + { + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + string cmdName = cmdResult.method.FullName; + bool requiresAuthority = cmdResult.requiresAuthority; - worker.Emit(OpCodes.Ldtoken, netBehaviourSubclass); - worker.Emit(OpCodes.Call, weaverTypes.getTypeFromHandleReference); - worker.Emit(OpCodes.Ldstr, cmdName); - worker.Emit(OpCodes.Ldnull); - worker.Emit(OpCodes.Ldftn, func); + worker.Emit(OpCodes.Ldtoken, netBehaviourSubclass); + worker.Emit(OpCodes.Call, weaverTypes.getTypeFromHandleReference); + worker.Emit(OpCodes.Ldstr, cmdName); + worker.Emit(OpCodes.Ldnull); + worker.Emit(OpCodes.Ldftn, func); - worker.Emit(OpCodes.Newobj, weaverTypes.RemoteCallDelegateConstructor); + worker.Emit(OpCodes.Newobj, weaverTypes.RemoteCallDelegateConstructor); - // requiresAuthority ? 1 : 0 - worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); + // requiresAuthority ? 1 : 0 + worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - worker.Emit(OpCodes.Call, registerMethod); - } + worker.Emit(OpCodes.Call, registerMethod); + } - private void GenerateSerialization(ref bool WeavingFailed) - { - const string SerializeMethodName = "SerializeSyncVars"; - if (netBehaviourSubclass.GetMethod(SerializeMethodName) != null) - { - return; - } + void GenerateSerialization(ref bool WeavingFailed) + { + const string SerializeMethodName = "SerializeSyncVars"; + if (netBehaviourSubclass.GetMethod(SerializeMethodName) != null) + return; - if (syncVars.Count == 0) - { - // no synvars, no need for custom OnSerialize - return; - } + if (syncVars.Count == 0) + { + // no synvars, no need for custom OnSerialize + return; + } - var serialize = new MethodDefinition(SerializeMethodName, - MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, - weaverTypes.Import()); + MethodDefinition serialize = new MethodDefinition(SerializeMethodName, + MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, + weaverTypes.Import()); - serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import())); - serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, weaverTypes.Import())); - var worker = serialize.Body.GetILProcessor(); + serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import())); + serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, weaverTypes.Import())); + ILProcessor worker = serialize.Body.GetILProcessor(); - serialize.Body.InitLocals = true; + serialize.Body.InitLocals = true; - // loc_0, this local variable is to determine if any variable was dirty - var dirtyLocal = new VariableDefinition(weaverTypes.Import()); - serialize.Body.Variables.Add(dirtyLocal); + // loc_0, this local variable is to determine if any variable was dirty + VariableDefinition dirtyLocal = new VariableDefinition(weaverTypes.Import()); + serialize.Body.Variables.Add(dirtyLocal); - var baseSerialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, SerializeMethodName); - if (baseSerialize != null) - { - // base - worker.Emit(OpCodes.Ldarg_0); - // writer - worker.Emit(OpCodes.Ldarg_1); - // forceAll - worker.Emit(OpCodes.Ldarg_2); - worker.Emit(OpCodes.Call, baseSerialize); - // set dirtyLocal to result of base.OnSerialize() - worker.Emit(OpCodes.Stloc_0); - } + MethodReference baseSerialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, SerializeMethodName); + if (baseSerialize != null) + { + // base + worker.Emit(OpCodes.Ldarg_0); + // writer + worker.Emit(OpCodes.Ldarg_1); + // forceAll + worker.Emit(OpCodes.Ldarg_2); + worker.Emit(OpCodes.Call, baseSerialize); + // set dirtyLocal to result of base.OnSerialize() + worker.Emit(OpCodes.Stloc_0); + } - // Generates: if (forceAll); - var initialStateLabel = worker.Create(OpCodes.Nop); - // forceAll - worker.Emit(OpCodes.Ldarg_2); - worker.Emit(OpCodes.Brfalse, initialStateLabel); + // Generates: if (forceAll); + Instruction initialStateLabel = worker.Create(OpCodes.Nop); + // forceAll + worker.Emit(OpCodes.Ldarg_2); + worker.Emit(OpCodes.Brfalse, initialStateLabel); - foreach (var syncVar in syncVars) - { - // Generates a writer call for each sync variable - // writer - worker.Emit(OpCodes.Ldarg_1); - // this - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, syncVar); - var writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); - if (writeFunc != null) - { - worker.Emit(OpCodes.Call, writeFunc); - } - else - { - Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); - WeavingFailed = true; - return; - } - } + foreach (FieldDefinition syncVar in syncVars) + { + // Generates a writer call for each sync variable + // writer + worker.Emit(OpCodes.Ldarg_1); + // this + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, syncVar); + MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); + if (writeFunc != null) + { + worker.Emit(OpCodes.Call, writeFunc); + } + else + { + Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); + WeavingFailed = true; + return; + } + } - // always return true if forceAll + // always return true if forceAll - // Generates: return true - worker.Emit(OpCodes.Ldc_I4_1); - worker.Emit(OpCodes.Ret); + // Generates: return true + worker.Emit(OpCodes.Ldc_I4_1); + worker.Emit(OpCodes.Ret); - // Generates: end if (forceAll); - worker.Append(initialStateLabel); + // Generates: end if (forceAll); + worker.Append(initialStateLabel); - // write dirty bits before the data fields - // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ()); - // writer - worker.Emit(OpCodes.Ldarg_1); - // base - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Call, weaverTypes.NetworkBehaviourDirtyBitsReference); - var writeUint64Func = writers.GetWriteFunc(weaverTypes.Import(), ref WeavingFailed); - worker.Emit(OpCodes.Call, writeUint64Func); + // write dirty bits before the data fields + // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ()); + // writer + worker.Emit(OpCodes.Ldarg_1); + // base + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Call, weaverTypes.NetworkBehaviourDirtyBitsReference); + MethodReference writeUint64Func = writers.GetWriteFunc(weaverTypes.Import(), ref WeavingFailed); + worker.Emit(OpCodes.Call, writeUint64Func); - // generate a writer call for any dirty variable in this class + // generate a writer call for any dirty variable in this class - // start at number of syncvars in parent - var dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); - foreach (var syncVar in syncVars) - { - var varLabel = worker.Create(OpCodes.Nop); + // start at number of syncvars in parent + int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); + foreach (FieldDefinition syncVar in syncVars) + { + Instruction varLabel = worker.Create(OpCodes.Nop); - // Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL) - // base - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Call, weaverTypes.NetworkBehaviourDirtyBitsReference); - // 8 bytes = long - worker.Emit(OpCodes.Ldc_I8, 1L << dirtyBit); - worker.Emit(OpCodes.And); - worker.Emit(OpCodes.Brfalse, varLabel); + // Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL) + // base + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Call, weaverTypes.NetworkBehaviourDirtyBitsReference); + // 8 bytes = long + worker.Emit(OpCodes.Ldc_I8, 1L << dirtyBit); + worker.Emit(OpCodes.And); + worker.Emit(OpCodes.Brfalse, varLabel); - // Generates a call to the writer for that field - // writer - worker.Emit(OpCodes.Ldarg_1); - // base - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, syncVar); + // Generates a call to the writer for that field + // writer + worker.Emit(OpCodes.Ldarg_1); + // base + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, syncVar); - var writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); - if (writeFunc != null) - { - worker.Emit(OpCodes.Call, writeFunc); - } - else - { - Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); - WeavingFailed = true; - return; - } + MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); + if (writeFunc != null) + { + worker.Emit(OpCodes.Call, writeFunc); + } + else + { + Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); + WeavingFailed = true; + return; + } - // something was dirty - worker.Emit(OpCodes.Ldc_I4_1); - // set dirtyLocal to true - worker.Emit(OpCodes.Stloc_0); + // something was dirty + worker.Emit(OpCodes.Ldc_I4_1); + // set dirtyLocal to true + worker.Emit(OpCodes.Stloc_0); - worker.Append(varLabel); - dirtyBit += 1; - } + worker.Append(varLabel); + dirtyBit += 1; + } - // add a log message if needed for debugging - //worker.Emit(OpCodes.Ldstr, $"Injected Serialize {netBehaviourSubclass.Name}"); - //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); + // add a log message if needed for debugging + //worker.Emit(OpCodes.Ldstr, $"Injected Serialize {netBehaviourSubclass.Name}"); + //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); - // generate: return dirtyLocal - worker.Emit(OpCodes.Ldloc_0); - worker.Emit(OpCodes.Ret); - netBehaviourSubclass.Methods.Add(serialize); - } + // generate: return dirtyLocal + worker.Emit(OpCodes.Ldloc_0); + worker.Emit(OpCodes.Ret); + netBehaviourSubclass.Methods.Add(serialize); + } - private void DeserializeField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize, ref bool WeavingFailed) - { - // check for Hook function - var hookMethod = syncVarAttributeProcessor.GetHookMethod(netBehaviourSubclass, syncVar, ref WeavingFailed); + void DeserializeField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize, ref bool WeavingFailed) + { + // check for Hook function + MethodDefinition hookMethod = syncVarAttributeProcessor.GetHookMethod(netBehaviourSubclass, syncVar, ref WeavingFailed); - if (syncVar.FieldType.IsDerivedFrom()) - { - DeserializeNetworkBehaviourField(syncVar, worker, deserialize, hookMethod, ref WeavingFailed); - } - else if (syncVar.FieldType.IsNetworkIdentityField()) - { - DeserializeNetworkIdentityField(syncVar, worker, deserialize, hookMethod, ref WeavingFailed); - } - else - { - DeserializeNormalField(syncVar, worker, deserialize, hookMethod, ref WeavingFailed); - } - } + if (syncVar.FieldType.IsDerivedFrom()) + { + DeserializeNetworkBehaviourField(syncVar, worker, deserialize, hookMethod, ref WeavingFailed); + } + else if (syncVar.FieldType.IsNetworkIdentityField()) + { + DeserializeNetworkIdentityField(syncVar, worker, deserialize, hookMethod, ref WeavingFailed); + } + else + { + DeserializeNormalField(syncVar, worker, deserialize, hookMethod, ref WeavingFailed); + } + } - /// [SyncVar] GameObject/NetworkIdentity? - private void DeserializeNetworkIdentityField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize, MethodDefinition hookMethod, ref bool WeavingFailed) - { - /* + /// [SyncVar] GameObject/NetworkIdentity? + void DeserializeNetworkIdentityField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize, MethodDefinition hookMethod, ref bool WeavingFailed) + { + /* Generates code like: uint oldNetId = ___qNetId; // returns GetSyncVarGameObject(___qNetId) @@ -571,94 +569,94 @@ namespace Mirror.Weaver } */ - // GameObject/NetworkIdentity SyncVar: - // OnSerialize sends writer.Write(go); - // OnDeserialize reads to __netId manually so we can use - // lookups in the getter (so it still works if objects - // move in and out of range repeatedly) - var netIdField = syncVarNetIds[syncVar]; + // GameObject/NetworkIdentity SyncVar: + // OnSerialize sends writer.Write(go); + // OnDeserialize reads to __netId manually so we can use + // lookups in the getter (so it still works if objects + // move in and out of range repeatedly) + FieldDefinition netIdField = syncVarNetIds[syncVar]; - // uint oldNetId = ___qNetId; - var oldNetId = new VariableDefinition(weaverTypes.Import()); - deserialize.Body.Variables.Add(oldNetId); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netIdField); - worker.Emit(OpCodes.Stloc, oldNetId); + // uint oldNetId = ___qNetId; + VariableDefinition oldNetId = new VariableDefinition(weaverTypes.Import()); + deserialize.Body.Variables.Add(oldNetId); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netIdField); + worker.Emit(OpCodes.Stloc, oldNetId); - // GameObject/NetworkIdentity oldSyncVar = syncvar.getter; - var oldSyncVar = new VariableDefinition(syncVar.FieldType); - deserialize.Body.Variables.Add(oldSyncVar); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, syncVar); - worker.Emit(OpCodes.Stloc, oldSyncVar); + // GameObject/NetworkIdentity oldSyncVar = syncvar.getter; + VariableDefinition oldSyncVar = new VariableDefinition(syncVar.FieldType); + deserialize.Body.Variables.Add(oldSyncVar); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, syncVar); + worker.Emit(OpCodes.Stloc, oldSyncVar); - // read id and store in netId field BEFORE calling the hook - // -> this makes way more sense. by definition, the hook is - // supposed to be called after it was changed. not before. - // -> setting it BEFORE calling the hook fixes the following bug: - // https://github.com/vis2k/Mirror/issues/1151 in host mode - // where the value during the Hook call would call Cmds on - // the host server, and they would all happen and compare - // values BEFORE the hook even returned and hence BEFORE the - // actual value was even set. - // put 'this.' onto stack for 'this.netId' below - worker.Emit(OpCodes.Ldarg_0); - // reader. for 'reader.Read()' below - worker.Emit(OpCodes.Ldarg_1); - // Read() - worker.Emit(OpCodes.Call, readers.GetReadFunc(weaverTypes.Import(), ref WeavingFailed)); - // netId - worker.Emit(OpCodes.Stfld, netIdField); + // read id and store in netId field BEFORE calling the hook + // -> this makes way more sense. by definition, the hook is + // supposed to be called after it was changed. not before. + // -> setting it BEFORE calling the hook fixes the following bug: + // https://github.com/vis2k/Mirror/issues/1151 in host mode + // where the value during the Hook call would call Cmds on + // the host server, and they would all happen and compare + // values BEFORE the hook even returned and hence BEFORE the + // actual value was even set. + // put 'this.' onto stack for 'this.netId' below + worker.Emit(OpCodes.Ldarg_0); + // reader. for 'reader.Read()' below + worker.Emit(OpCodes.Ldarg_1); + // Read() + worker.Emit(OpCodes.Call, readers.GetReadFunc(weaverTypes.Import(), ref WeavingFailed)); + // netId + worker.Emit(OpCodes.Stfld, netIdField); - if (hookMethod != null) - { - // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32())) - // because we send/receive the netID, not the GameObject/NetworkIdentity - // but only if SyncVar changed. otherwise a client would - // get hook calls for all initial values, even if they - // didn't change from the default values on the client. - // see also: https://github.com/vis2k/Mirror/issues/1278 + if (hookMethod != null) + { + // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32())) + // because we send/receive the netID, not the GameObject/NetworkIdentity + // but only if SyncVar changed. otherwise a client would + // get hook calls for all initial values, even if they + // didn't change from the default values on the client. + // see also: https://github.com/vis2k/Mirror/issues/1278 - // IMPORTANT: for GameObjects/NetworkIdentities we usually - // use SyncVarGameObjectEqual to compare equality. - // in this case however, we can just use - // SyncVarEqual with the two uint netIds. - // => this is easier weaver code because we don't - // have to get the GameObject/NetworkIdentity - // from the uint netId - // => this is faster because we void one - // GetComponent call for GameObjects to get - // their NetworkIdentity when comparing. + // IMPORTANT: for GameObjects/NetworkIdentities we usually + // use SyncVarGameObjectEqual to compare equality. + // in this case however, we can just use + // SyncVarEqual with the two uint netIds. + // => this is easier weaver code because we don't + // have to get the GameObject/NetworkIdentity + // from the uint netId + // => this is faster because we void one + // GetComponent call for GameObjects to get + // their NetworkIdentity when comparing. - // Generates: if (!SyncVarEqual); - var syncVarEqualLabel = worker.Create(OpCodes.Nop); + // Generates: if (!SyncVarEqual); + Instruction syncVarEqualLabel = worker.Create(OpCodes.Nop); - // NOTE: static function. don't Emit Ldarg_0 aka 'this'. + // NOTE: static function. don't Emit Ldarg_0 aka 'this'. - // 'oldNetId' - worker.Emit(OpCodes.Ldloc, oldNetId); - // 'ref this.__netId' - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, netIdField); - // call the function - var syncVarEqualGm = new GenericInstanceMethod(weaverTypes.syncVarEqualReference); - syncVarEqualGm.GenericArguments.Add(netIdField.FieldType); - worker.Emit(OpCodes.Call, syncVarEqualGm); - worker.Emit(OpCodes.Brtrue, syncVarEqualLabel); + // 'oldNetId' + worker.Emit(OpCodes.Ldloc, oldNetId); + // 'ref this.__netId' + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, netIdField); + // call the function + GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(weaverTypes.syncVarEqualReference); + syncVarEqualGm.GenericArguments.Add(netIdField.FieldType); + worker.Emit(OpCodes.Call, syncVarEqualGm); + worker.Emit(OpCodes.Brtrue, syncVarEqualLabel); - // call the hook - // Generates: OnValueChanged(oldValue, this.syncVar); - syncVarAttributeProcessor.WriteCallHookMethodUsingField(worker, hookMethod, oldSyncVar, syncVar, ref WeavingFailed); + // call the hook + // Generates: OnValueChanged(oldValue, this.syncVar); + syncVarAttributeProcessor.WriteCallHookMethodUsingField(worker, hookMethod, oldSyncVar, syncVar, ref WeavingFailed); - // Generates: end if (!SyncVarEqual); - worker.Append(syncVarEqualLabel); - } - } + // Generates: end if (!SyncVarEqual); + worker.Append(syncVarEqualLabel); + } + } - // [SyncVar] NetworkBehaviour - private void DeserializeNetworkBehaviourField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize, MethodDefinition hookMethod, ref bool WeavingFailed) - { - /* + // [SyncVar] NetworkBehaviour + void DeserializeNetworkBehaviourField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize, MethodDefinition hookMethod, ref bool WeavingFailed) + { + /* Generates code like: uint oldNetId = ___qNetId.netId; byte oldCompIndex = ___qNetId.componentIndex; @@ -672,94 +670,94 @@ namespace Mirror.Weaver } */ - // GameObject/NetworkIdentity SyncVar: - // OnSerialize sends writer.Write(go); - // OnDeserialize reads to __netId manually so we can use - // lookups in the getter (so it still works if objects - // move in and out of range repeatedly) - var netIdField = syncVarNetIds[syncVar]; + // GameObject/NetworkIdentity SyncVar: + // OnSerialize sends writer.Write(go); + // OnDeserialize reads to __netId manually so we can use + // lookups in the getter (so it still works if objects + // move in and out of range repeatedly) + FieldDefinition netIdField = syncVarNetIds[syncVar]; - // uint oldNetId = ___qNetId; - var oldNetId = new VariableDefinition(weaverTypes.Import()); - deserialize.Body.Variables.Add(oldNetId); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netIdField); - worker.Emit(OpCodes.Stloc, oldNetId); + // uint oldNetId = ___qNetId; + VariableDefinition oldNetId = new VariableDefinition(weaverTypes.Import()); + deserialize.Body.Variables.Add(oldNetId); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netIdField); + worker.Emit(OpCodes.Stloc, oldNetId); - // GameObject/NetworkIdentity oldSyncVar = syncvar.getter; - var oldSyncVar = new VariableDefinition(syncVar.FieldType); - deserialize.Body.Variables.Add(oldSyncVar); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, syncVar); - worker.Emit(OpCodes.Stloc, oldSyncVar); + // GameObject/NetworkIdentity oldSyncVar = syncvar.getter; + VariableDefinition oldSyncVar = new VariableDefinition(syncVar.FieldType); + deserialize.Body.Variables.Add(oldSyncVar); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, syncVar); + worker.Emit(OpCodes.Stloc, oldSyncVar); - // read id and store in netId field BEFORE calling the hook - // -> this makes way more sense. by definition, the hook is - // supposed to be called after it was changed. not before. - // -> setting it BEFORE calling the hook fixes the following bug: - // https://github.com/vis2k/Mirror/issues/1151 in host mode - // where the value during the Hook call would call Cmds on - // the host server, and they would all happen and compare - // values BEFORE the hook even returned and hence BEFORE the - // actual value was even set. - // put 'this.' onto stack for 'this.netId' below - worker.Emit(OpCodes.Ldarg_0); - // reader. for 'reader.Read()' below - worker.Emit(OpCodes.Ldarg_1); - // Read() - worker.Emit(OpCodes.Call, readers.GetReadFunc(weaverTypes.Import(), ref WeavingFailed)); - // netId - worker.Emit(OpCodes.Stfld, netIdField); + // read id and store in netId field BEFORE calling the hook + // -> this makes way more sense. by definition, the hook is + // supposed to be called after it was changed. not before. + // -> setting it BEFORE calling the hook fixes the following bug: + // https://github.com/vis2k/Mirror/issues/1151 in host mode + // where the value during the Hook call would call Cmds on + // the host server, and they would all happen and compare + // values BEFORE the hook even returned and hence BEFORE the + // actual value was even set. + // put 'this.' onto stack for 'this.netId' below + worker.Emit(OpCodes.Ldarg_0); + // reader. for 'reader.Read()' below + worker.Emit(OpCodes.Ldarg_1); + // Read() + worker.Emit(OpCodes.Call, readers.GetReadFunc(weaverTypes.Import(), ref WeavingFailed)); + // netId + worker.Emit(OpCodes.Stfld, netIdField); - if (hookMethod != null) - { - // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32())) - // because we send/receive the netID, not the GameObject/NetworkIdentity - // but only if SyncVar changed. otherwise a client would - // get hook calls for all initial values, even if they - // didn't change from the default values on the client. - // see also: https://github.com/vis2k/Mirror/issues/1278 + if (hookMethod != null) + { + // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32())) + // because we send/receive the netID, not the GameObject/NetworkIdentity + // but only if SyncVar changed. otherwise a client would + // get hook calls for all initial values, even if they + // didn't change from the default values on the client. + // see also: https://github.com/vis2k/Mirror/issues/1278 - // IMPORTANT: for GameObjects/NetworkIdentities we usually - // use SyncVarGameObjectEqual to compare equality. - // in this case however, we can just use - // SyncVarEqual with the two uint netIds. - // => this is easier weaver code because we don't - // have to get the GameObject/NetworkIdentity - // from the uint netId - // => this is faster because we void one - // GetComponent call for GameObjects to get - // their NetworkIdentity when comparing. + // IMPORTANT: for GameObjects/NetworkIdentities we usually + // use SyncVarGameObjectEqual to compare equality. + // in this case however, we can just use + // SyncVarEqual with the two uint netIds. + // => this is easier weaver code because we don't + // have to get the GameObject/NetworkIdentity + // from the uint netId + // => this is faster because we void one + // GetComponent call for GameObjects to get + // their NetworkIdentity when comparing. - // Generates: if (!SyncVarEqual); - var syncVarEqualLabel = worker.Create(OpCodes.Nop); + // Generates: if (!SyncVarEqual); + Instruction syncVarEqualLabel = worker.Create(OpCodes.Nop); - // NOTE: static function. don't Emit Ldarg_0 aka 'this'. + // NOTE: static function. don't Emit Ldarg_0 aka 'this'. - // 'oldNetId' - worker.Emit(OpCodes.Ldloc, oldNetId); - // 'ref this.__netId' - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, netIdField); - // call the function - var syncVarEqualGm = new GenericInstanceMethod(weaverTypes.syncVarEqualReference); - syncVarEqualGm.GenericArguments.Add(netIdField.FieldType); - worker.Emit(OpCodes.Call, syncVarEqualGm); - worker.Emit(OpCodes.Brtrue, syncVarEqualLabel); + // 'oldNetId' + worker.Emit(OpCodes.Ldloc, oldNetId); + // 'ref this.__netId' + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, netIdField); + // call the function + GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(weaverTypes.syncVarEqualReference); + syncVarEqualGm.GenericArguments.Add(netIdField.FieldType); + worker.Emit(OpCodes.Call, syncVarEqualGm); + worker.Emit(OpCodes.Brtrue, syncVarEqualLabel); - // call the hook - // Generates: OnValueChanged(oldValue, this.syncVar); - syncVarAttributeProcessor.WriteCallHookMethodUsingField(worker, hookMethod, oldSyncVar, syncVar, ref WeavingFailed); + // call the hook + // Generates: OnValueChanged(oldValue, this.syncVar); + syncVarAttributeProcessor.WriteCallHookMethodUsingField(worker, hookMethod, oldSyncVar, syncVar, ref WeavingFailed); - // Generates: end if (!SyncVarEqual); - worker.Append(syncVarEqualLabel); - } - } + // Generates: end if (!SyncVarEqual); + worker.Append(syncVarEqualLabel); + } + } - // [SyncVar] int/float/struct/etc.? - private void DeserializeNormalField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize, MethodDefinition hookMethod, ref bool WeavingFailed) - { - /* + // [SyncVar] int/float/struct/etc.? + void DeserializeNormalField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize, MethodDefinition hookMethod, ref bool WeavingFailed) + { + /* Generates code like: // for hook int oldValue = a; @@ -770,448 +768,441 @@ namespace Mirror.Weaver } */ - var readFunc = readers.GetReadFunc(syncVar.FieldType, ref WeavingFailed); - if (readFunc == null) - { - Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); - WeavingFailed = true; - return; - } + MethodReference readFunc = readers.GetReadFunc(syncVar.FieldType, ref WeavingFailed); + if (readFunc == null) + { + Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); + WeavingFailed = true; + return; + } - // T oldValue = value; - var oldValue = new VariableDefinition(syncVar.FieldType); - deserialize.Body.Variables.Add(oldValue); - serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); - serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); - serWorker.Append(serWorker.Create(OpCodes.Stloc, oldValue)); + // T oldValue = value; + VariableDefinition oldValue = new VariableDefinition(syncVar.FieldType); + deserialize.Body.Variables.Add(oldValue); + serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); + serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); + serWorker.Append(serWorker.Create(OpCodes.Stloc, oldValue)); - // read value and store in syncvar BEFORE calling the hook - // -> this makes way more sense. by definition, the hook is - // supposed to be called after it was changed. not before. - // -> setting it BEFORE calling the hook fixes the following bug: - // https://github.com/vis2k/Mirror/issues/1151 in host mode - // where the value during the Hook call would call Cmds on - // the host server, and they would all happen and compare - // values BEFORE the hook even returned and hence BEFORE the - // actual value was even set. - // put 'this.' onto stack for 'this.syncvar' below - serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); - // reader. for 'reader.Read()' below - serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); - // reader.Read() - serWorker.Append(serWorker.Create(OpCodes.Call, readFunc)); - // syncvar - serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar)); + // read value and store in syncvar BEFORE calling the hook + // -> this makes way more sense. by definition, the hook is + // supposed to be called after it was changed. not before. + // -> setting it BEFORE calling the hook fixes the following bug: + // https://github.com/vis2k/Mirror/issues/1151 in host mode + // where the value during the Hook call would call Cmds on + // the host server, and they would all happen and compare + // values BEFORE the hook even returned and hence BEFORE the + // actual value was even set. + // put 'this.' onto stack for 'this.syncvar' below + serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); + // reader. for 'reader.Read()' below + serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); + // reader.Read() + serWorker.Append(serWorker.Create(OpCodes.Call, readFunc)); + // syncvar + serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar)); - if (hookMethod != null) - { - // call hook - // but only if SyncVar changed. otherwise a client would - // get hook calls for all initial values, even if they - // didn't change from the default values on the client. - // see also: https://github.com/vis2k/Mirror/issues/1278 + if (hookMethod != null) + { + // call hook + // but only if SyncVar changed. otherwise a client would + // get hook calls for all initial values, even if they + // didn't change from the default values on the client. + // see also: https://github.com/vis2k/Mirror/issues/1278 - // Generates: if (!SyncVarEqual); - var syncVarEqualLabel = serWorker.Create(OpCodes.Nop); + // Generates: if (!SyncVarEqual); + Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop); - // NOTE: static function. don't Emit Ldarg_0 aka 'this'. + // NOTE: static function. don't Emit Ldarg_0 aka 'this'. - // 'oldValue' - serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue)); - // 'ref this.syncVar' - serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); - serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar)); - // call the function - var syncVarEqualGm = new GenericInstanceMethod(weaverTypes.syncVarEqualReference); - syncVarEqualGm.GenericArguments.Add(syncVar.FieldType); - serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm)); - serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel)); + // 'oldValue' + serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue)); + // 'ref this.syncVar' + serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); + serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar)); + // call the function + GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(weaverTypes.syncVarEqualReference); + syncVarEqualGm.GenericArguments.Add(syncVar.FieldType); + serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm)); + serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel)); - // call the hook - // Generates: OnValueChanged(oldValue, this.syncVar); - syncVarAttributeProcessor.WriteCallHookMethodUsingField(serWorker, hookMethod, oldValue, syncVar, ref WeavingFailed); + // call the hook + // Generates: OnValueChanged(oldValue, this.syncVar); + syncVarAttributeProcessor.WriteCallHookMethodUsingField(serWorker, hookMethod, oldValue, syncVar, ref WeavingFailed); - // Generates: end if (!SyncVarEqual); - serWorker.Append(syncVarEqualLabel); - } - } + // Generates: end if (!SyncVarEqual); + serWorker.Append(syncVarEqualLabel); + } + } - private void GenerateDeSerialization(ref bool WeavingFailed) - { - const string DeserializeMethodName = "DeserializeSyncVars"; - if (netBehaviourSubclass.GetMethod(DeserializeMethodName) != null) - { - return; - } + void GenerateDeSerialization(ref bool WeavingFailed) + { + const string DeserializeMethodName = "DeserializeSyncVars"; + if (netBehaviourSubclass.GetMethod(DeserializeMethodName) != null) + return; - if (syncVars.Count == 0) - { - // no synvars, no need for custom OnDeserialize - return; - } + if (syncVars.Count == 0) + { + // no synvars, no need for custom OnDeserialize + return; + } - var serialize = new MethodDefinition(DeserializeMethodName, - MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, - weaverTypes.Import(typeof(void))); + MethodDefinition serialize = new MethodDefinition(DeserializeMethodName, + MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, + weaverTypes.Import(typeof(void))); - serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); - serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, weaverTypes.Import())); - var serWorker = serialize.Body.GetILProcessor(); - // setup local for dirty bits - serialize.Body.InitLocals = true; - var dirtyBitsLocal = new VariableDefinition(weaverTypes.Import()); - serialize.Body.Variables.Add(dirtyBitsLocal); + serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); + serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, weaverTypes.Import())); + ILProcessor serWorker = serialize.Body.GetILProcessor(); + // setup local for dirty bits + serialize.Body.InitLocals = true; + VariableDefinition dirtyBitsLocal = new VariableDefinition(weaverTypes.Import()); + serialize.Body.Variables.Add(dirtyBitsLocal); - var baseDeserialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, DeserializeMethodName); - if (baseDeserialize != null) - { - // base - serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); - // reader - serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); - // initialState - serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); - serWorker.Append(serWorker.Create(OpCodes.Call, baseDeserialize)); - } + MethodReference baseDeserialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, DeserializeMethodName); + if (baseDeserialize != null) + { + // base + serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); + // reader + serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); + // initialState + serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); + serWorker.Append(serWorker.Create(OpCodes.Call, baseDeserialize)); + } - // Generates: if (initialState); - var initialStateLabel = serWorker.Create(OpCodes.Nop); + // Generates: if (initialState); + Instruction initialStateLabel = serWorker.Create(OpCodes.Nop); - serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); - serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel)); + serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); + serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel)); - foreach (var syncVar in syncVars) - { - DeserializeField(syncVar, serWorker, serialize, ref WeavingFailed); - } + foreach (FieldDefinition syncVar in syncVars) + { + DeserializeField(syncVar, serWorker, serialize, ref WeavingFailed); + } - serWorker.Append(serWorker.Create(OpCodes.Ret)); + serWorker.Append(serWorker.Create(OpCodes.Ret)); - // Generates: end if (initialState); - serWorker.Append(initialStateLabel); + // Generates: end if (initialState); + serWorker.Append(initialStateLabel); - // get dirty bits - serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); - serWorker.Append(serWorker.Create(OpCodes.Call, readers.GetReadFunc(weaverTypes.Import(), ref WeavingFailed))); - serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); + // get dirty bits + serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); + serWorker.Append(serWorker.Create(OpCodes.Call, readers.GetReadFunc(weaverTypes.Import(), ref WeavingFailed))); + serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); - // conditionally read each syncvar - // start at number of syncvars in parent - var dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); - foreach (var syncVar in syncVars) - { - var varLabel = serWorker.Create(OpCodes.Nop); + // conditionally read each syncvar + // start at number of syncvars in parent + int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); + foreach (FieldDefinition syncVar in syncVars) + { + Instruction varLabel = serWorker.Create(OpCodes.Nop); - // check if dirty bit is set - serWorker.Append(serWorker.Create(OpCodes.Ldloc_0)); - serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit)); - serWorker.Append(serWorker.Create(OpCodes.And)); - serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); + // check if dirty bit is set + serWorker.Append(serWorker.Create(OpCodes.Ldloc_0)); + serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit)); + serWorker.Append(serWorker.Create(OpCodes.And)); + serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); - DeserializeField(syncVar, serWorker, serialize, ref WeavingFailed); + DeserializeField(syncVar, serWorker, serialize, ref WeavingFailed); - serWorker.Append(varLabel); - dirtyBit += 1; - } + serWorker.Append(varLabel); + dirtyBit += 1; + } - // add a log message if needed for debugging - //serWorker.Append(serWorker.Create(OpCodes.Ldstr, $"Injected Deserialize {netBehaviourSubclass.Name}")); - //serWorker.Append(serWorker.Create(OpCodes.Call, WeaverTypes.logErrorReference)); + // add a log message if needed for debugging + //serWorker.Append(serWorker.Create(OpCodes.Ldstr, $"Injected Deserialize {netBehaviourSubclass.Name}")); + //serWorker.Append(serWorker.Create(OpCodes.Call, WeaverTypes.logErrorReference)); - serWorker.Append(serWorker.Create(OpCodes.Ret)); - netBehaviourSubclass.Methods.Add(serialize); - } + serWorker.Append(serWorker.Create(OpCodes.Ret)); + netBehaviourSubclass.Methods.Add(serialize); + } - public static bool ReadArguments(MethodDefinition method, Readers readers, Logger Log, ILProcessor worker, RemoteCallType callType, ref bool WeavingFailed) - { - // read each argument - // example result - /* + public static bool ReadArguments(MethodDefinition method, Readers readers, Logger Log, ILProcessor worker, RemoteCallType callType, ref bool WeavingFailed) + { + // read each argument + // example result + /* CallCmdDoSomething(reader.ReadPackedInt32(), reader.ReadNetworkIdentity()); */ - var skipFirst = callType == RemoteCallType.TargetRpc - && TargetRpcProcessor.HasNetworkConnectionParameter(method); + bool skipFirst = callType == RemoteCallType.TargetRpc + && TargetRpcProcessor.HasNetworkConnectionParameter(method); - // arg of calling function, arg 0 is "this" so start counting at 1 - var argNum = 1; - foreach (var param in method.Parameters) - { - // NetworkConnection is not sent via the NetworkWriter so skip it here - // skip first for NetworkConnection in TargetRpc - if (argNum == 1 && skipFirst) - { - argNum += 1; - continue; - } - // skip SenderConnection in Command - if (IsSenderConnection(param, callType)) - { - argNum += 1; - continue; - } + // arg of calling function, arg 0 is "this" so start counting at 1 + int argNum = 1; + foreach (ParameterDefinition param in method.Parameters) + { + // NetworkConnection is not sent via the NetworkWriter so skip it here + // skip first for NetworkConnection in TargetRpc + if (argNum == 1 && skipFirst) + { + argNum += 1; + continue; + } + // skip SenderConnection in Command + if (IsSenderConnection(param, callType)) + { + argNum += 1; + continue; + } - var readFunc = readers.GetReadFunc(param.ParameterType, ref WeavingFailed); + MethodReference readFunc = readers.GetReadFunc(param.ParameterType, ref WeavingFailed); - if (readFunc == null) - { - Log.Error($"{method.Name} has invalid parameter {param}. Unsupported type {param.ParameterType}, use a supported Mirror type instead", method); - WeavingFailed = true; - return false; - } + if (readFunc == null) + { + Log.Error($"{method.Name} has invalid parameter {param}. Unsupported type {param.ParameterType}, use a supported Mirror type instead", method); + WeavingFailed = true; + return false; + } - worker.Emit(OpCodes.Ldarg_1); - worker.Emit(OpCodes.Call, readFunc); + worker.Emit(OpCodes.Ldarg_1); + worker.Emit(OpCodes.Call, readFunc); - // conversion.. is this needed? - if (param.ParameterType.Is()) - { - worker.Emit(OpCodes.Conv_R4); - } - else if (param.ParameterType.Is()) - { - worker.Emit(OpCodes.Conv_R8); - } - } - return true; - } + // conversion.. is this needed? + if (param.ParameterType.Is()) + { + worker.Emit(OpCodes.Conv_R4); + } + else if (param.ParameterType.Is()) + { + worker.Emit(OpCodes.Conv_R8); + } + } + return true; + } - public static void AddInvokeParameters(WeaverTypes weaverTypes, ICollection collection) - { - collection.Add(new ParameterDefinition("obj", ParameterAttributes.None, weaverTypes.Import())); - collection.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); - // senderConnection is only used for commands but NetworkBehaviour.CmdDelegate is used for all remote calls - collection.Add(new ParameterDefinition("senderConnection", ParameterAttributes.None, weaverTypes.Import())); - } + public static void AddInvokeParameters(WeaverTypes weaverTypes, ICollection collection) + { + collection.Add(new ParameterDefinition("obj", ParameterAttributes.None, weaverTypes.Import())); + collection.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); + // senderConnection is only used for commands but NetworkBehaviour.CmdDelegate is used for all remote calls + collection.Add(new ParameterDefinition("senderConnection", ParameterAttributes.None, weaverTypes.Import())); + } - // check if a Command/TargetRpc/Rpc function & parameters are valid for weaving - public bool ValidateRemoteCallAndParameters(MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed) - { - if (method.IsStatic) - { - Log.Error($"{method.Name} must not be static", method); - WeavingFailed = true; - return false; - } + // check if a Command/TargetRpc/Rpc function & parameters are valid for weaving + public bool ValidateRemoteCallAndParameters(MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed) + { + if (method.IsStatic) + { + Log.Error($"{method.Name} must not be static", method); + WeavingFailed = true; + return false; + } - return ValidateFunction(method, ref WeavingFailed) && - ValidateParameters(method, callType, ref WeavingFailed); - } + return ValidateFunction(method, ref WeavingFailed) && + ValidateParameters(method, callType, ref WeavingFailed); + } - // check if a Command/TargetRpc/Rpc function is valid for weaving - private bool ValidateFunction(MethodReference md, ref bool WeavingFailed) - { - if (md.ReturnType.Is()) - { - Log.Error($"{md.Name} cannot be a coroutine", md); - WeavingFailed = true; - return false; - } - if (!md.ReturnType.Is(typeof(void))) - { - Log.Error($"{md.Name} cannot return a value. Make it void instead", md); - WeavingFailed = true; - return false; - } - if (md.HasGenericParameters) - { - Log.Error($"{md.Name} cannot have generic parameters", md); - WeavingFailed = true; - return false; - } - return true; - } + // check if a Command/TargetRpc/Rpc function is valid for weaving + bool ValidateFunction(MethodReference md, ref bool WeavingFailed) + { + if (md.ReturnType.Is()) + { + Log.Error($"{md.Name} cannot be a coroutine", md); + WeavingFailed = true; + return false; + } + if (!md.ReturnType.Is(typeof(void))) + { + Log.Error($"{md.Name} cannot return a value. Make it void instead", md); + WeavingFailed = true; + return false; + } + if (md.HasGenericParameters) + { + Log.Error($"{md.Name} cannot have generic parameters", md); + WeavingFailed = true; + return false; + } + return true; + } - // check if all Command/TargetRpc/Rpc function's parameters are valid for weaving - private bool ValidateParameters(MethodReference method, RemoteCallType callType, ref bool WeavingFailed) - { - for (var i = 0; i < method.Parameters.Count; ++i) - { - var param = method.Parameters[i]; - if (!ValidateParameter(method, param, callType, i == 0, ref WeavingFailed)) - { - return false; - } - } - return true; - } + // check if all Command/TargetRpc/Rpc function's parameters are valid for weaving + bool ValidateParameters(MethodReference method, RemoteCallType callType, ref bool WeavingFailed) + { + for (int i = 0; i < method.Parameters.Count; ++i) + { + ParameterDefinition param = method.Parameters[i]; + if (!ValidateParameter(method, param, callType, i == 0, ref WeavingFailed)) + { + return false; + } + } + return true; + } - // validate parameters for a remote function call like Rpc/Cmd - private bool ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam, ref bool WeavingFailed) - { - var isNetworkConnection = param.ParameterType.Is(); - var isSenderConnection = IsSenderConnection(param, callType); + // validate parameters for a remote function call like Rpc/Cmd + bool ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam, ref bool WeavingFailed) + { + bool isNetworkConnection = param.ParameterType.Is(); + bool isSenderConnection = IsSenderConnection(param, callType); - if (param.IsOut) - { - Log.Error($"{method.Name} cannot have out parameters", method); - WeavingFailed = true; - return false; - } + if (param.IsOut) + { + Log.Error($"{method.Name} cannot have out parameters", method); + WeavingFailed = true; + return false; + } - // if not SenderConnection And not TargetRpc NetworkConnection first param - if (!isSenderConnection && isNetworkConnection && !(callType == RemoteCallType.TargetRpc && firstParam)) - { - if (callType == RemoteCallType.Command) - { - Log.Error($"{method.Name} has invalid parameter {param}, Cannot pass NetworkConnections. Instead use 'NetworkConnectionToClient conn = null' to get the sender's connection on the server", method); - } - else - { - Log.Error($"{method.Name} has invalid parameter {param}. Cannot pass NetworkConnections", method); - } - WeavingFailed = true; - return false; - } + // if not SenderConnection And not TargetRpc NetworkConnection first param + if (!isSenderConnection && isNetworkConnection && !(callType == RemoteCallType.TargetRpc && firstParam)) + { + if (callType == RemoteCallType.Command) + { + Log.Error($"{method.Name} has invalid parameter {param}, Cannot pass NetworkConnections. Instead use 'NetworkConnectionToClient conn = null' to get the sender's connection on the server", method); + } + else + { + Log.Error($"{method.Name} has invalid parameter {param}. Cannot pass NetworkConnections", method); + } + WeavingFailed = true; + return false; + } - // sender connection can be optional - if (param.IsOptional && !isSenderConnection) - { - Log.Error($"{method.Name} cannot have optional parameters", method); - WeavingFailed = true; - return false; - } + // sender connection can be optional + if (param.IsOptional && !isSenderConnection) + { + Log.Error($"{method.Name} cannot have optional parameters", method); + WeavingFailed = true; + return false; + } - return true; - } + return true; + } - public static bool IsSenderConnection(ParameterDefinition param, RemoteCallType callType) - { - if (callType != RemoteCallType.Command) - { - return false; - } + public static bool IsSenderConnection(ParameterDefinition param, RemoteCallType callType) + { + if (callType != RemoteCallType.Command) + { + return false; + } - var type = param.ParameterType; + TypeReference type = param.ParameterType; - return type.Is() - || type.Resolve().IsDerivedFrom(); - } + return type.Is() + || type.Resolve().IsDerivedFrom(); + } - private void ProcessMethods(ref bool WeavingFailed) - { - var names = new HashSet(); + void ProcessMethods(ref bool WeavingFailed) + { + HashSet names = new HashSet(); - // copy the list of methods because we will be adding methods in the loop - var methods = new List(netBehaviourSubclass.Methods); - // find command and RPC functions - foreach (var md in methods) - { - foreach (var ca in md.CustomAttributes) - { - if (ca.AttributeType.Is()) - { - ProcessCommand(names, md, ca, ref WeavingFailed); - break; - } + // copy the list of methods because we will be adding methods in the loop + List methods = new List(netBehaviourSubclass.Methods); + // find command and RPC functions + foreach (MethodDefinition md in methods) + { + foreach (CustomAttribute ca in md.CustomAttributes) + { + if (ca.AttributeType.Is()) + { + ProcessCommand(names, md, ca, ref WeavingFailed); + break; + } - if (ca.AttributeType.Is()) - { - ProcessTargetRpc(names, md, ca, ref WeavingFailed); - break; - } + if (ca.AttributeType.Is()) + { + ProcessTargetRpc(names, md, ca, ref WeavingFailed); + break; + } - if (ca.AttributeType.Is()) - { - ProcessClientRpc(names, md, ca, ref WeavingFailed); - break; - } - } - } - } + if (ca.AttributeType.Is()) + { + ProcessClientRpc(names, md, ca, ref WeavingFailed); + break; + } + } + } + } - private void ProcessClientRpc(HashSet names, MethodDefinition md, CustomAttribute clientRpcAttr, ref bool WeavingFailed) - { - if (md.IsAbstract) - { - Log.Error("Abstract ClientRpc are currently not supported, use virtual method instead", md); - WeavingFailed = true; - return; - } + void ProcessClientRpc(HashSet names, MethodDefinition md, CustomAttribute clientRpcAttr, ref bool WeavingFailed) + { + if (md.IsAbstract) + { + Log.Error("Abstract ClientRpc are currently not supported, use virtual method instead", md); + WeavingFailed = true; + return; + } - if (!ValidateRemoteCallAndParameters(md, RemoteCallType.ClientRpc, ref WeavingFailed)) - { - return; - } + if (!ValidateRemoteCallAndParameters(md, RemoteCallType.ClientRpc, ref WeavingFailed)) + { + return; + } - var includeOwner = clientRpcAttr.GetField("includeOwner", true); + bool includeOwner = clientRpcAttr.GetField("includeOwner", true); - names.Add(md.Name); - clientRpcs.Add(new ClientRpcResult - { - method = md, - includeOwner = includeOwner - }); + names.Add(md.Name); + clientRpcs.Add(new ClientRpcResult + { + method = md, + includeOwner = includeOwner + }); - var rpcCallFunc = RpcProcessor.ProcessRpcCall(weaverTypes, writers, Log, netBehaviourSubclass, md, clientRpcAttr, ref WeavingFailed); - // need null check here because ProcessRpcCall returns null if it can't write all the args - if (rpcCallFunc == null) - { return; } + MethodDefinition rpcCallFunc = RpcProcessor.ProcessRpcCall(weaverTypes, writers, Log, netBehaviourSubclass, md, clientRpcAttr, ref WeavingFailed); + // need null check here because ProcessRpcCall returns null if it can't write all the args + if (rpcCallFunc == null) { return; } - var rpcFunc = RpcProcessor.ProcessRpcInvoke(weaverTypes, writers, readers, Log, netBehaviourSubclass, md, rpcCallFunc, ref WeavingFailed); - if (rpcFunc != null) - { - clientRpcInvocationFuncs.Add(rpcFunc); - } - } + MethodDefinition rpcFunc = RpcProcessor.ProcessRpcInvoke(weaverTypes, writers, readers, Log, netBehaviourSubclass, md, rpcCallFunc, ref WeavingFailed); + if (rpcFunc != null) + { + clientRpcInvocationFuncs.Add(rpcFunc); + } + } - private void ProcessTargetRpc(HashSet names, MethodDefinition md, CustomAttribute targetRpcAttr, ref bool WeavingFailed) - { - if (md.IsAbstract) - { - Log.Error("Abstract TargetRpc are currently not supported, use virtual method instead", md); - WeavingFailed = true; - return; - } + void ProcessTargetRpc(HashSet names, MethodDefinition md, CustomAttribute targetRpcAttr, ref bool WeavingFailed) + { + if (md.IsAbstract) + { + Log.Error("Abstract TargetRpc are currently not supported, use virtual method instead", md); + WeavingFailed = true; + return; + } - if (!ValidateRemoteCallAndParameters(md, RemoteCallType.TargetRpc, ref WeavingFailed)) - { - return; - } + if (!ValidateRemoteCallAndParameters(md, RemoteCallType.TargetRpc, ref WeavingFailed)) + return; - names.Add(md.Name); - targetRpcs.Add(md); + names.Add(md.Name); + targetRpcs.Add(md); - var rpcCallFunc = TargetRpcProcessor.ProcessTargetRpcCall(weaverTypes, writers, Log, netBehaviourSubclass, md, targetRpcAttr, ref WeavingFailed); + MethodDefinition rpcCallFunc = TargetRpcProcessor.ProcessTargetRpcCall(weaverTypes, writers, Log, netBehaviourSubclass, md, targetRpcAttr, ref WeavingFailed); - var rpcFunc = TargetRpcProcessor.ProcessTargetRpcInvoke(weaverTypes, readers, Log, netBehaviourSubclass, md, rpcCallFunc, ref WeavingFailed); - if (rpcFunc != null) - { - targetRpcInvocationFuncs.Add(rpcFunc); - } - } + MethodDefinition rpcFunc = TargetRpcProcessor.ProcessTargetRpcInvoke(weaverTypes, readers, Log, netBehaviourSubclass, md, rpcCallFunc, ref WeavingFailed); + if (rpcFunc != null) + { + targetRpcInvocationFuncs.Add(rpcFunc); + } + } - private void ProcessCommand(HashSet names, MethodDefinition md, CustomAttribute commandAttr, ref bool WeavingFailed) - { - if (md.IsAbstract) - { - Log.Error("Abstract Commands are currently not supported, use virtual method instead", md); - WeavingFailed = true; - return; - } + void ProcessCommand(HashSet names, MethodDefinition md, CustomAttribute commandAttr, ref bool WeavingFailed) + { + if (md.IsAbstract) + { + Log.Error("Abstract Commands are currently not supported, use virtual method instead", md); + WeavingFailed = true; + return; + } - if (!ValidateRemoteCallAndParameters(md, RemoteCallType.Command, ref WeavingFailed)) - { - return; - } + if (!ValidateRemoteCallAndParameters(md, RemoteCallType.Command, ref WeavingFailed)) + return; - var requiresAuthority = commandAttr.GetField("requiresAuthority", true); + bool requiresAuthority = commandAttr.GetField("requiresAuthority", true); - names.Add(md.Name); - commands.Add(new CmdResult - { - method = md, - requiresAuthority = requiresAuthority - }); + names.Add(md.Name); + commands.Add(new CmdResult + { + method = md, + requiresAuthority = requiresAuthority + }); - var cmdCallFunc = CommandProcessor.ProcessCommandCall(weaverTypes, writers, Log, netBehaviourSubclass, md, commandAttr, ref WeavingFailed); + MethodDefinition cmdCallFunc = CommandProcessor.ProcessCommandCall(weaverTypes, writers, Log, netBehaviourSubclass, md, commandAttr, ref WeavingFailed); - var cmdFunc = CommandProcessor.ProcessCommandInvoke(weaverTypes, readers, Log, netBehaviourSubclass, md, cmdCallFunc, ref WeavingFailed); - if (cmdFunc != null) - { - commandInvocationFuncs.Add(cmdFunc); - } - } - } + MethodDefinition cmdFunc = CommandProcessor.ProcessCommandInvoke(weaverTypes, readers, Log, netBehaviourSubclass, md, cmdCallFunc, ref WeavingFailed); + if (cmdFunc != null) + { + commandInvocationFuncs.Add(cmdFunc); + } + } + } } diff --git a/MirrorWeaver/Weaver/Processors/ReaderWriterProcessor.cs b/MirrorWeaver/Weaver/Processors/ReaderWriterProcessor.cs index fc522bb5..b3ebe0e3 100644 --- a/MirrorWeaver/Weaver/Processors/ReaderWriterProcessor.cs +++ b/MirrorWeaver/Weaver/Processors/ReaderWriterProcessor.cs @@ -1,242 +1,216 @@ // finds all readers and writers and register them +using System.Linq; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; -using System.Linq; using UnityEngine; namespace Mirror.Weaver { - public static class ReaderWriterProcessor - { - public static bool Process(AssemblyDefinition CurrentAssembly, IAssemblyResolver resolver, Logger Log, Writers writers, Readers readers, ref bool WeavingFailed) - { - // find NetworkReader/Writer extensions from Mirror.dll first. - // and NetworkMessage custom writer/reader extensions. - // NOTE: do not include this result in our 'modified' return value, - // otherwise Unity crashes when running tests - ProcessMirrorAssemblyClasses(CurrentAssembly, resolver, Log, writers, readers, ref WeavingFailed); + public static class ReaderWriterProcessor + { + public static bool Process(AssemblyDefinition CurrentAssembly, IAssemblyResolver resolver, Logger Log, Writers writers, Readers readers, ref bool WeavingFailed) + { + // find NetworkReader/Writer extensions from Mirror.dll first. + // and NetworkMessage custom writer/reader extensions. + // NOTE: do not include this result in our 'modified' return value, + // otherwise Unity crashes when running tests + ProcessMirrorAssemblyClasses(CurrentAssembly, resolver, Log, writers, readers, ref WeavingFailed); - // find readers/writers in the assembly we are in right now. - return ProcessAssemblyClasses(CurrentAssembly, CurrentAssembly, writers, readers, ref WeavingFailed); - } + // find readers/writers in the assembly we are in right now. + return ProcessAssemblyClasses(CurrentAssembly, CurrentAssembly, writers, readers, ref WeavingFailed); + } - private static void ProcessMirrorAssemblyClasses(AssemblyDefinition CurrentAssembly, IAssemblyResolver resolver, Logger Log, Writers writers, Readers readers, ref bool WeavingFailed) - { - // find Mirror.dll in assembly's references. - // those are guaranteed to be resolvable and correct. - // after all, it references them :) - var mirrorAssemblyReference = CurrentAssembly.MainModule.FindReference(Weaver.MirrorAssemblyName); - if (mirrorAssemblyReference != null) - { - // resolve the assembly to load the AssemblyDefinition. - // we need to search all types in it. - // if we only were to resolve one known type like in WeaverTypes, - // then we wouldn't need it. - var mirrorAssembly = resolver.Resolve(mirrorAssemblyReference); - if (mirrorAssembly != null) - { - ProcessAssemblyClasses(CurrentAssembly, mirrorAssembly, writers, readers, ref WeavingFailed); - } - else - { - Log.Error($"Failed to resolve {mirrorAssemblyReference}"); - } - } - else - { - Log.Error("Failed to find Mirror AssemblyNameReference. Can't register Mirror.dll readers/writers."); - } - } + static void ProcessMirrorAssemblyClasses(AssemblyDefinition CurrentAssembly, IAssemblyResolver resolver, Logger Log, Writers writers, Readers readers, ref bool WeavingFailed) + { + // find Mirror.dll in assembly's references. + // those are guaranteed to be resolvable and correct. + // after all, it references them :) + AssemblyNameReference mirrorAssemblyReference = CurrentAssembly.MainModule.FindReference(Weaver.MirrorAssemblyName); + if (mirrorAssemblyReference != null) + { + // resolve the assembly to load the AssemblyDefinition. + // we need to search all types in it. + // if we only were to resolve one known type like in WeaverTypes, + // then we wouldn't need it. + AssemblyDefinition mirrorAssembly = resolver.Resolve(mirrorAssemblyReference); + if (mirrorAssembly != null) + { + ProcessAssemblyClasses(CurrentAssembly, mirrorAssembly, writers, readers, ref WeavingFailed); + } + else Log.Error($"Failed to resolve {mirrorAssemblyReference}"); + } + else Log.Error("Failed to find Mirror AssemblyNameReference. Can't register Mirror.dll readers/writers."); + } - private static bool ProcessAssemblyClasses(AssemblyDefinition CurrentAssembly, AssemblyDefinition assembly, Writers writers, Readers readers, ref bool WeavingFailed) - { - var modified = false; - foreach (var klass in assembly.MainModule.Types) - { - // extension methods only live in static classes - // static classes are represented as sealed and abstract - if (klass.IsAbstract && klass.IsSealed) - { - // if assembly has any declared writers then it is "modified" - modified |= LoadDeclaredWriters(CurrentAssembly, klass, writers); - modified |= LoadDeclaredReaders(CurrentAssembly, klass, readers); - } - } + static bool ProcessAssemblyClasses(AssemblyDefinition CurrentAssembly, AssemblyDefinition assembly, Writers writers, Readers readers, ref bool WeavingFailed) + { + bool modified = false; + foreach (TypeDefinition klass in assembly.MainModule.Types) + { + // extension methods only live in static classes + // static classes are represented as sealed and abstract + if (klass.IsAbstract && klass.IsSealed) + { + // if assembly has any declared writers then it is "modified" + modified |= LoadDeclaredWriters(CurrentAssembly, klass, writers); + modified |= LoadDeclaredReaders(CurrentAssembly, klass, readers); + } + } - foreach (var klass in assembly.MainModule.Types) - { - // if assembly has any network message then it is modified - modified |= LoadMessageReadWriter(CurrentAssembly.MainModule, writers, readers, klass, ref WeavingFailed); - } - return modified; - } + foreach (TypeDefinition klass in assembly.MainModule.Types) + { + // if assembly has any network message then it is modified + modified |= LoadMessageReadWriter(CurrentAssembly.MainModule, writers, readers, klass, ref WeavingFailed); + } + return modified; + } - private static bool LoadMessageReadWriter(ModuleDefinition module, Writers writers, Readers readers, TypeDefinition klass, ref bool WeavingFailed) - { - var modified = false; - if (!klass.IsAbstract && !klass.IsInterface && klass.ImplementsInterface()) - { - readers.GetReadFunc(module.ImportReference(klass), ref WeavingFailed); - writers.GetWriteFunc(module.ImportReference(klass), ref WeavingFailed); - modified = true; - } + static bool LoadMessageReadWriter(ModuleDefinition module, Writers writers, Readers readers, TypeDefinition klass, ref bool WeavingFailed) + { + bool modified = false; + if (!klass.IsAbstract && !klass.IsInterface && klass.ImplementsInterface()) + { + readers.GetReadFunc(module.ImportReference(klass), ref WeavingFailed); + writers.GetWriteFunc(module.ImportReference(klass), ref WeavingFailed); + modified = true; + } - foreach (var td in klass.NestedTypes) - { - modified |= LoadMessageReadWriter(module, writers, readers, td, ref WeavingFailed); - } - return modified; - } + foreach (TypeDefinition td in klass.NestedTypes) + { + modified |= LoadMessageReadWriter(module, writers, readers, td, ref WeavingFailed); + } + return modified; + } - private static bool LoadDeclaredWriters(AssemblyDefinition currentAssembly, TypeDefinition klass, Writers writers) - { - // register all the writers in this class. Skip the ones with wrong signature - var modified = false; - foreach (var method in klass.Methods) - { - if (method.Parameters.Count != 2) - { - continue; - } + static bool LoadDeclaredWriters(AssemblyDefinition currentAssembly, TypeDefinition klass, Writers writers) + { + // register all the writers in this class. Skip the ones with wrong signature + bool modified = false; + foreach (MethodDefinition method in klass.Methods) + { + if (method.Parameters.Count != 2) + continue; - if (!method.Parameters[0].ParameterType.Is()) - { - continue; - } + if (!method.Parameters[0].ParameterType.Is()) + continue; - if (!method.ReturnType.Is(typeof(void))) - { - continue; - } + if (!method.ReturnType.Is(typeof(void))) + continue; - if (!method.HasCustomAttribute()) - { - continue; - } + if (!method.HasCustomAttribute()) + continue; - if (method.HasGenericParameters) - { - continue; - } + if (method.HasGenericParameters) + continue; - var dataType = method.Parameters[1].ParameterType; - writers.Register(dataType, currentAssembly.MainModule.ImportReference(method)); - modified = true; - } - return modified; - } + TypeReference dataType = method.Parameters[1].ParameterType; + writers.Register(dataType, currentAssembly.MainModule.ImportReference(method)); + modified = true; + } + return modified; + } - private static bool LoadDeclaredReaders(AssemblyDefinition currentAssembly, TypeDefinition klass, Readers readers) - { - // register all the reader in this class. Skip the ones with wrong signature - var modified = false; - foreach (var method in klass.Methods) - { - if (method.Parameters.Count != 1) - { - continue; - } + static bool LoadDeclaredReaders(AssemblyDefinition currentAssembly, TypeDefinition klass, Readers readers) + { + // register all the reader in this class. Skip the ones with wrong signature + bool modified = false; + foreach (MethodDefinition method in klass.Methods) + { + if (method.Parameters.Count != 1) + continue; - if (!method.Parameters[0].ParameterType.Is()) - { - continue; - } + if (!method.Parameters[0].ParameterType.Is()) + continue; - if (method.ReturnType.Is(typeof(void))) - { - continue; - } + if (method.ReturnType.Is(typeof(void))) + continue; - if (!method.HasCustomAttribute()) - { - continue; - } + if (!method.HasCustomAttribute()) + continue; - if (method.HasGenericParameters) - { - continue; - } + if (method.HasGenericParameters) + continue; - readers.Register(method.ReturnType, currentAssembly.MainModule.ImportReference(method)); - modified = true; - } - return modified; - } + readers.Register(method.ReturnType, currentAssembly.MainModule.ImportReference(method)); + modified = true; + } + return modified; + } - // helper function to add [RuntimeInitializeOnLoad] attribute to method - private static void AddRuntimeInitializeOnLoadAttribute(AssemblyDefinition assembly, WeaverTypes weaverTypes, MethodDefinition method) - { - // NOTE: previously we used reflection because according paul, - // 'weaving Mirror.dll caused unity to rebuild all dlls but in wrong - // order, which breaks rewired' - // it's not obvious why importing an attribute via reflection instead - // of cecil would break anything. let's use cecil. + // helper function to add [RuntimeInitializeOnLoad] attribute to method + static void AddRuntimeInitializeOnLoadAttribute(AssemblyDefinition assembly, WeaverTypes weaverTypes, MethodDefinition method) + { + // NOTE: previously we used reflection because according paul, + // 'weaving Mirror.dll caused unity to rebuild all dlls but in wrong + // order, which breaks rewired' + // it's not obvious why importing an attribute via reflection instead + // of cecil would break anything. let's use cecil. - // to add a CustomAttribute, we need the attribute's constructor. - // in this case, there are two: empty, and RuntimeInitializeOnLoadType. - // we want the last one, with the type parameter. - var ctor = weaverTypes.runtimeInitializeOnLoadMethodAttribute.GetConstructors().Last(); - //MethodDefinition ctor = weaverTypes.runtimeInitializeOnLoadMethodAttribute.GetConstructors().First(); - // using ctor directly throws: ArgumentException: Member 'System.Void UnityEditor.InitializeOnLoadMethodAttribute::.ctor()' is declared in another module and needs to be imported - // we need to import it first. - var attribute = new CustomAttribute(assembly.MainModule.ImportReference(ctor)); - // add the RuntimeInitializeLoadType.BeforeSceneLoad argument to ctor - attribute.ConstructorArguments.Add(new CustomAttributeArgument(weaverTypes.Import(), RuntimeInitializeLoadType.BeforeSceneLoad)); - method.CustomAttributes.Add(attribute); - } + // to add a CustomAttribute, we need the attribute's constructor. + // in this case, there are two: empty, and RuntimeInitializeOnLoadType. + // we want the last one, with the type parameter. + MethodDefinition ctor = weaverTypes.runtimeInitializeOnLoadMethodAttribute.GetConstructors().Last(); + //MethodDefinition ctor = weaverTypes.runtimeInitializeOnLoadMethodAttribute.GetConstructors().First(); + // using ctor directly throws: ArgumentException: Member 'System.Void UnityEditor.InitializeOnLoadMethodAttribute::.ctor()' is declared in another module and needs to be imported + // we need to import it first. + CustomAttribute attribute = new CustomAttribute(assembly.MainModule.ImportReference(ctor)); + // add the RuntimeInitializeLoadType.BeforeSceneLoad argument to ctor + attribute.ConstructorArguments.Add(new CustomAttributeArgument(weaverTypes.Import(), RuntimeInitializeLoadType.BeforeSceneLoad)); + method.CustomAttributes.Add(attribute); + } - // helper function to add [InitializeOnLoad] attribute to method - // (only works in Editor assemblies. check IsEditorAssembly first.) - private static void AddInitializeOnLoadAttribute(AssemblyDefinition assembly, WeaverTypes weaverTypes, MethodDefinition method) - { - // NOTE: previously we used reflection because according paul, - // 'weaving Mirror.dll caused unity to rebuild all dlls but in wrong - // order, which breaks rewired' - // it's not obvious why importing an attribute via reflection instead - // of cecil would break anything. let's use cecil. + // helper function to add [InitializeOnLoad] attribute to method + // (only works in Editor assemblies. check IsEditorAssembly first.) + static void AddInitializeOnLoadAttribute(AssemblyDefinition assembly, WeaverTypes weaverTypes, MethodDefinition method) + { + // NOTE: previously we used reflection because according paul, + // 'weaving Mirror.dll caused unity to rebuild all dlls but in wrong + // order, which breaks rewired' + // it's not obvious why importing an attribute via reflection instead + // of cecil would break anything. let's use cecil. - // to add a CustomAttribute, we need the attribute's constructor. - // in this case, there's only one - and it's an empty constructor. - var ctor = weaverTypes.initializeOnLoadMethodAttribute.GetConstructors().First(); - // using ctor directly throws: ArgumentException: Member 'System.Void UnityEditor.InitializeOnLoadMethodAttribute::.ctor()' is declared in another module and needs to be imported - // we need to import it first. - var attribute = new CustomAttribute(assembly.MainModule.ImportReference(ctor)); - method.CustomAttributes.Add(attribute); - } + // to add a CustomAttribute, we need the attribute's constructor. + // in this case, there's only one - and it's an empty constructor. + MethodDefinition ctor = weaverTypes.initializeOnLoadMethodAttribute.GetConstructors().First(); + // using ctor directly throws: ArgumentException: Member 'System.Void UnityEditor.InitializeOnLoadMethodAttribute::.ctor()' is declared in another module and needs to be imported + // we need to import it first. + CustomAttribute attribute = new CustomAttribute(assembly.MainModule.ImportReference(ctor)); + method.CustomAttributes.Add(attribute); + } - // adds Mirror.GeneratedNetworkCode.InitReadWriters() method that - // registers all generated writers into Mirror.Writer static class. - // -> uses [RuntimeInitializeOnLoad] attribute so it's invoke at runtime - // -> uses [InitializeOnLoad] if UnityEditor is referenced so it works - // in Editor and in tests too - // - // use ILSpy to see the result (it's in the DLL's 'Mirror' namespace) - public static void InitializeReaderAndWriters(AssemblyDefinition currentAssembly, WeaverTypes weaverTypes, Writers writers, Readers readers, TypeDefinition GeneratedCodeClass) - { - var initReadWriters = new MethodDefinition("InitReadWriters", MethodAttributes.Public | - MethodAttributes.Static, - weaverTypes.Import(typeof(void))); + // adds Mirror.GeneratedNetworkCode.InitReadWriters() method that + // registers all generated writers into Mirror.Writer static class. + // -> uses [RuntimeInitializeOnLoad] attribute so it's invoke at runtime + // -> uses [InitializeOnLoad] if UnityEditor is referenced so it works + // in Editor and in tests too + // + // use ILSpy to see the result (it's in the DLL's 'Mirror' namespace) + public static void InitializeReaderAndWriters(AssemblyDefinition currentAssembly, WeaverTypes weaverTypes, Writers writers, Readers readers, TypeDefinition GeneratedCodeClass) + { + MethodDefinition initReadWriters = new MethodDefinition("InitReadWriters", MethodAttributes.Public | + MethodAttributes.Static, + weaverTypes.Import(typeof(void))); - // add [RuntimeInitializeOnLoad] in any case - AddRuntimeInitializeOnLoadAttribute(currentAssembly, weaverTypes, initReadWriters); + // add [RuntimeInitializeOnLoad] in any case + AddRuntimeInitializeOnLoadAttribute(currentAssembly, weaverTypes, initReadWriters); - // add [InitializeOnLoad] if UnityEditor is referenced - if (Helpers.IsEditorAssembly(currentAssembly)) - { - AddInitializeOnLoadAttribute(currentAssembly, weaverTypes, initReadWriters); - } + // add [InitializeOnLoad] if UnityEditor is referenced + if (Helpers.IsEditorAssembly(currentAssembly)) + { + AddInitializeOnLoadAttribute(currentAssembly, weaverTypes, initReadWriters); + } - // fill function body with reader/writer initializers - var worker = initReadWriters.Body.GetILProcessor(); - // for debugging: add a log to see if initialized on load - //worker.Emit(OpCodes.Ldstr, $"[InitReadWriters] called!"); - //worker.Emit(OpCodes.Call, Weaver.weaverTypes.logWarningReference); - writers.InitializeWriters(worker); - readers.InitializeReaders(worker); - worker.Emit(OpCodes.Ret); + // fill function body with reader/writer initializers + ILProcessor worker = initReadWriters.Body.GetILProcessor(); + // for debugging: add a log to see if initialized on load + //worker.Emit(OpCodes.Ldstr, $"[InitReadWriters] called!"); + //worker.Emit(OpCodes.Call, Weaver.weaverTypes.logWarningReference); + writers.InitializeWriters(worker); + readers.InitializeReaders(worker); + worker.Emit(OpCodes.Ret); - GeneratedCodeClass.Methods.Add(initReadWriters); - } - } + GeneratedCodeClass.Methods.Add(initReadWriters); + } + } } diff --git a/MirrorWeaver/Weaver/Processors/RpcProcessor.cs b/MirrorWeaver/Weaver/Processors/RpcProcessor.cs index 0d484d05..a1c3ee15 100644 --- a/MirrorWeaver/Weaver/Processors/RpcProcessor.cs +++ b/MirrorWeaver/Weaver/Processors/RpcProcessor.cs @@ -3,40 +3,38 @@ using Mono.Cecil.Cil; namespace Mirror.Weaver { - // Processes [Rpc] methods in NetworkBehaviour - public static class RpcProcessor - { - public static MethodDefinition ProcessRpcInvoke(WeaverTypes weaverTypes, Writers writers, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed) - { - var rpc = new MethodDefinition( - Weaver.InvokeRpcPrefix + md.Name, - MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, - weaverTypes.Import(typeof(void))); + // Processes [Rpc] methods in NetworkBehaviour + public static class RpcProcessor + { + public static MethodDefinition ProcessRpcInvoke(WeaverTypes weaverTypes, Writers writers, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed) + { + MethodDefinition rpc = new MethodDefinition( + Weaver.InvokeRpcPrefix + md.Name, + MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, + weaverTypes.Import(typeof(void))); - var worker = rpc.Body.GetILProcessor(); - var label = worker.Create(OpCodes.Nop); + ILProcessor worker = rpc.Body.GetILProcessor(); + Instruction label = worker.Create(OpCodes.Nop); - NetworkBehaviourProcessor.WriteClientActiveCheck(worker, weaverTypes, md.Name, label, "RPC"); + NetworkBehaviourProcessor.WriteClientActiveCheck(worker, weaverTypes, md.Name, label, "RPC"); - // setup for reader - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Castclass, td); + // setup for reader + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Castclass, td); - if (!NetworkBehaviourProcessor.ReadArguments(md, readers, Log, worker, RemoteCallType.ClientRpc, ref WeavingFailed)) - { - return null; - } + if (!NetworkBehaviourProcessor.ReadArguments(md, readers, Log, worker, RemoteCallType.ClientRpc, ref WeavingFailed)) + return null; - // invoke actual command function - worker.Emit(OpCodes.Callvirt, rpcCallFunc); - worker.Emit(OpCodes.Ret); + // invoke actual command function + worker.Emit(OpCodes.Callvirt, rpcCallFunc); + worker.Emit(OpCodes.Ret); - NetworkBehaviourProcessor.AddInvokeParameters(weaverTypes, rpc.Parameters); - td.Methods.Add(rpc); - return rpc; - } + NetworkBehaviourProcessor.AddInvokeParameters(weaverTypes, rpc.Parameters); + td.Methods.Add(rpc); + return rpc; + } - /* + /* * generates code like: public void RpcTest (int param) @@ -58,46 +56,44 @@ namespace Mirror.Weaver This way we do not need to modify the code anywhere else, and this works correctly in dependent assemblies */ - public static MethodDefinition ProcessRpcCall(WeaverTypes weaverTypes, Writers writers, Logger Log, TypeDefinition td, MethodDefinition md, CustomAttribute clientRpcAttr, ref bool WeavingFailed) - { - var rpc = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); + public static MethodDefinition ProcessRpcCall(WeaverTypes weaverTypes, Writers writers, Logger Log, TypeDefinition td, MethodDefinition md, CustomAttribute clientRpcAttr, ref bool WeavingFailed) + { + MethodDefinition rpc = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); - var worker = md.Body.GetILProcessor(); + ILProcessor worker = md.Body.GetILProcessor(); - NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); + NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); - // add a log message if needed for debugging - //worker.Emit(OpCodes.Ldstr, $"Call ClientRpc function {md.Name}"); - //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); + // add a log message if needed for debugging + //worker.Emit(OpCodes.Ldstr, $"Call ClientRpc function {md.Name}"); + //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); - NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); + NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); - // write all the arguments that the user passed to the Rpc call - if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.ClientRpc, ref WeavingFailed)) - { - return null; - } + // write all the arguments that the user passed to the Rpc call + if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.ClientRpc, ref WeavingFailed)) + return null; - var channel = clientRpcAttr.GetField("channel", 0); - var includeOwner = clientRpcAttr.GetField("includeOwner", true); + int channel = clientRpcAttr.GetField("channel", 0); + bool includeOwner = clientRpcAttr.GetField("includeOwner", true); - // invoke SendInternal and return - // this - worker.Emit(OpCodes.Ldarg_0); - // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions - worker.Emit(OpCodes.Ldstr, md.FullName); - // writer - worker.Emit(OpCodes.Ldloc_0); - worker.Emit(OpCodes.Ldc_I4, channel); - // includeOwner ? 1 : 0 - worker.Emit(includeOwner ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - worker.Emit(OpCodes.Callvirt, weaverTypes.sendRpcInternal); + // invoke SendInternal and return + // this + worker.Emit(OpCodes.Ldarg_0); + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + worker.Emit(OpCodes.Ldstr, md.FullName); + // writer + worker.Emit(OpCodes.Ldloc_0); + worker.Emit(OpCodes.Ldc_I4, channel); + // includeOwner ? 1 : 0 + worker.Emit(includeOwner ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); + worker.Emit(OpCodes.Callvirt, weaverTypes.sendRpcInternal); - NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); + NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); - worker.Emit(OpCodes.Ret); + worker.Emit(OpCodes.Ret); - return rpc; - } - } + return rpc; + } + } } diff --git a/MirrorWeaver/Weaver/Processors/ServerClientAttributeProcessor.cs b/MirrorWeaver/Weaver/Processors/ServerClientAttributeProcessor.cs index f20f71a3..5d528977 100644 --- a/MirrorWeaver/Weaver/Processors/ServerClientAttributeProcessor.cs +++ b/MirrorWeaver/Weaver/Processors/ServerClientAttributeProcessor.cs @@ -4,163 +4,151 @@ using Mono.Cecil.Cil; namespace Mirror.Weaver { - internal static class ServerClientAttributeProcessor - { - public static bool Process(WeaverTypes weaverTypes, Logger Log, TypeDefinition td, ref bool WeavingFailed) - { - var modified = false; - foreach (var md in td.Methods) - { - modified |= ProcessSiteMethod(weaverTypes, Log, md, ref WeavingFailed); - } + static class ServerClientAttributeProcessor + { + public static bool Process(WeaverTypes weaverTypes, Logger Log, TypeDefinition td, ref bool WeavingFailed) + { + bool modified = false; + foreach (MethodDefinition md in td.Methods) + { + modified |= ProcessSiteMethod(weaverTypes, Log, md, ref WeavingFailed); + } - foreach (var nested in td.NestedTypes) - { - modified |= Process(weaverTypes, Log, nested, ref WeavingFailed); - } - return modified; - } + foreach (TypeDefinition nested in td.NestedTypes) + { + modified |= Process(weaverTypes, Log, nested, ref WeavingFailed); + } + return modified; + } - private static bool ProcessSiteMethod(WeaverTypes weaverTypes, Logger Log, MethodDefinition md, ref bool WeavingFailed) - { - if (md.Name == ".cctor" || - md.Name == NetworkBehaviourProcessor.ProcessedFunctionName || - md.Name.StartsWith(Weaver.InvokeRpcPrefix)) - { - return false; - } + static bool ProcessSiteMethod(WeaverTypes weaverTypes, Logger Log, MethodDefinition md, ref bool WeavingFailed) + { + if (md.Name == ".cctor" || + md.Name == NetworkBehaviourProcessor.ProcessedFunctionName || + md.Name.StartsWith(Weaver.InvokeRpcPrefix)) + return false; - if (md.IsAbstract) - { - if (HasServerClientAttribute(md)) - { - Log.Error("Server or Client Attributes can't be added to abstract method. Server and Client Attributes are not inherited so they need to be applied to the override methods instead.", md); - WeavingFailed = true; - } - return false; - } + if (md.IsAbstract) + { + if (HasServerClientAttribute(md)) + { + Log.Error("Server or Client Attributes can't be added to abstract method. Server and Client Attributes are not inherited so they need to be applied to the override methods instead.", md); + WeavingFailed = true; + } + return false; + } - if (md.Body != null && md.Body.Instructions != null) - { - return ProcessMethodAttributes(weaverTypes, md); - } - return false; - } + if (md.Body != null && md.Body.Instructions != null) + { + return ProcessMethodAttributes(weaverTypes, md); + } + return false; + } - public static bool HasServerClientAttribute(MethodDefinition md) - { - foreach (var attr in md.CustomAttributes) - { - switch (attr.Constructor.DeclaringType.ToString()) - { - case "Mirror.ServerAttribute": - case "Mirror.ServerCallbackAttribute": - case "Mirror.ClientAttribute": - case "Mirror.ClientCallbackAttribute": - return true; - default: - break; - } - } - return false; - } + public static bool HasServerClientAttribute(MethodDefinition md) + { + foreach (CustomAttribute attr in md.CustomAttributes) + { + switch (attr.Constructor.DeclaringType.ToString()) + { + case "Mirror.ServerAttribute": + case "Mirror.ServerCallbackAttribute": + case "Mirror.ClientAttribute": + case "Mirror.ClientCallbackAttribute": + return true; + default: + break; + } + } + return false; + } - public static bool ProcessMethodAttributes(WeaverTypes weaverTypes, MethodDefinition md) - { - if (md.HasCustomAttribute()) - { - InjectServerGuard(weaverTypes, md, true); - } - else if (md.HasCustomAttribute()) - { - InjectServerGuard(weaverTypes, md, false); - } - else if (md.HasCustomAttribute()) - { - InjectClientGuard(weaverTypes, md, true); - } - else if (md.HasCustomAttribute()) - { - InjectClientGuard(weaverTypes, md, false); - } - else - { - return false; - } + public static bool ProcessMethodAttributes(WeaverTypes weaverTypes, MethodDefinition md) + { + if (md.HasCustomAttribute()) + InjectServerGuard(weaverTypes, md, true); + else if (md.HasCustomAttribute()) + InjectServerGuard(weaverTypes, md, false); + else if (md.HasCustomAttribute()) + InjectClientGuard(weaverTypes, md, true); + else if (md.HasCustomAttribute()) + InjectClientGuard(weaverTypes, md, false); + else + return false; - return true; - } + return true; + } - private static void InjectServerGuard(WeaverTypes weaverTypes, MethodDefinition md, bool logWarning) - { - var worker = md.Body.GetILProcessor(); - var top = md.Body.Instructions[0]; + static void InjectServerGuard(WeaverTypes weaverTypes, MethodDefinition md, bool logWarning) + { + ILProcessor worker = md.Body.GetILProcessor(); + Instruction top = md.Body.Instructions[0]; - worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.NetworkServerGetActive)); - worker.InsertBefore(top, worker.Create(OpCodes.Brtrue, top)); - if (logWarning) - { - worker.InsertBefore(top, worker.Create(OpCodes.Ldstr, $"[Server] function '{md.FullName}' called when server was not active")); - worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.logWarningReference)); - } - InjectGuardParameters(md, worker, top); - InjectGuardReturnValue(md, worker, top); - worker.InsertBefore(top, worker.Create(OpCodes.Ret)); - } + worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.NetworkServerGetActive)); + worker.InsertBefore(top, worker.Create(OpCodes.Brtrue, top)); + if (logWarning) + { + worker.InsertBefore(top, worker.Create(OpCodes.Ldstr, $"[Server] function '{md.FullName}' called when server was not active")); + worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.logWarningReference)); + } + InjectGuardParameters(md, worker, top); + InjectGuardReturnValue(md, worker, top); + worker.InsertBefore(top, worker.Create(OpCodes.Ret)); + } - private static void InjectClientGuard(WeaverTypes weaverTypes, MethodDefinition md, bool logWarning) - { - var worker = md.Body.GetILProcessor(); - var top = md.Body.Instructions[0]; + static void InjectClientGuard(WeaverTypes weaverTypes, MethodDefinition md, bool logWarning) + { + ILProcessor worker = md.Body.GetILProcessor(); + Instruction top = md.Body.Instructions[0]; - worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.NetworkClientGetActive)); - worker.InsertBefore(top, worker.Create(OpCodes.Brtrue, top)); - if (logWarning) - { - worker.InsertBefore(top, worker.Create(OpCodes.Ldstr, $"[Client] function '{md.FullName}' called when client was not active")); - worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.logWarningReference)); - } + worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.NetworkClientGetActive)); + worker.InsertBefore(top, worker.Create(OpCodes.Brtrue, top)); + if (logWarning) + { + worker.InsertBefore(top, worker.Create(OpCodes.Ldstr, $"[Client] function '{md.FullName}' called when client was not active")); + worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.logWarningReference)); + } - InjectGuardParameters(md, worker, top); - InjectGuardReturnValue(md, worker, top); - worker.InsertBefore(top, worker.Create(OpCodes.Ret)); - } + InjectGuardParameters(md, worker, top); + InjectGuardReturnValue(md, worker, top); + worker.InsertBefore(top, worker.Create(OpCodes.Ret)); + } - // this is required to early-out from a function with "ref" or "out" parameters - private static void InjectGuardParameters(MethodDefinition md, ILProcessor worker, Instruction top) - { - var offset = md.Resolve().IsStatic ? 0 : 1; - for (var index = 0; index < md.Parameters.Count; index++) - { - var param = md.Parameters[index]; - if (param.IsOut) - { - var elementType = param.ParameterType.GetElementType(); + // this is required to early-out from a function with "ref" or "out" parameters + static void InjectGuardParameters(MethodDefinition md, ILProcessor worker, Instruction top) + { + int offset = md.Resolve().IsStatic ? 0 : 1; + for (int index = 0; index < md.Parameters.Count; index++) + { + ParameterDefinition param = md.Parameters[index]; + if (param.IsOut) + { + TypeReference elementType = param.ParameterType.GetElementType(); - md.Body.Variables.Add(new VariableDefinition(elementType)); - md.Body.InitLocals = true; + md.Body.Variables.Add(new VariableDefinition(elementType)); + md.Body.InitLocals = true; - worker.InsertBefore(top, worker.Create(OpCodes.Ldarg, index + offset)); - worker.InsertBefore(top, worker.Create(OpCodes.Ldloca_S, (byte)(md.Body.Variables.Count - 1))); - worker.InsertBefore(top, worker.Create(OpCodes.Initobj, elementType)); - worker.InsertBefore(top, worker.Create(OpCodes.Ldloc, md.Body.Variables.Count - 1)); - worker.InsertBefore(top, worker.Create(OpCodes.Stobj, elementType)); - } - } - } + worker.InsertBefore(top, worker.Create(OpCodes.Ldarg, index + offset)); + worker.InsertBefore(top, worker.Create(OpCodes.Ldloca_S, (byte)(md.Body.Variables.Count - 1))); + worker.InsertBefore(top, worker.Create(OpCodes.Initobj, elementType)); + worker.InsertBefore(top, worker.Create(OpCodes.Ldloc, md.Body.Variables.Count - 1)); + worker.InsertBefore(top, worker.Create(OpCodes.Stobj, elementType)); + } + } + } - // this is required to early-out from a function with a return value. - private static void InjectGuardReturnValue(MethodDefinition md, ILProcessor worker, Instruction top) - { - if (!md.ReturnType.Is(typeof(void))) - { - md.Body.Variables.Add(new VariableDefinition(md.ReturnType)); - md.Body.InitLocals = true; + // this is required to early-out from a function with a return value. + static void InjectGuardReturnValue(MethodDefinition md, ILProcessor worker, Instruction top) + { + if (!md.ReturnType.Is(typeof(void))) + { + md.Body.Variables.Add(new VariableDefinition(md.ReturnType)); + md.Body.InitLocals = true; - worker.InsertBefore(top, worker.Create(OpCodes.Ldloca_S, (byte)(md.Body.Variables.Count - 1))); - worker.InsertBefore(top, worker.Create(OpCodes.Initobj, md.ReturnType)); - worker.InsertBefore(top, worker.Create(OpCodes.Ldloc, md.Body.Variables.Count - 1)); - } - } - } + worker.InsertBefore(top, worker.Create(OpCodes.Ldloca_S, (byte)(md.Body.Variables.Count - 1))); + worker.InsertBefore(top, worker.Create(OpCodes.Initobj, md.ReturnType)); + worker.InsertBefore(top, worker.Create(OpCodes.Ldloc, md.Body.Variables.Count - 1)); + } + } + } } diff --git a/MirrorWeaver/Weaver/Processors/SyncObjectInitializer.cs b/MirrorWeaver/Weaver/Processors/SyncObjectInitializer.cs index 928f672d..e4b4951f 100644 --- a/MirrorWeaver/Weaver/Processors/SyncObjectInitializer.cs +++ b/MirrorWeaver/Weaver/Processors/SyncObjectInitializer.cs @@ -3,37 +3,37 @@ using Mono.Cecil.Cil; namespace Mirror.Weaver { - public static class SyncObjectInitializer - { - // generates code like: - // this.InitSyncObject(m_sizes); - public static void GenerateSyncObjectInitializer(ILProcessor worker, WeaverTypes weaverTypes, FieldDefinition fd) - { - // register syncobject in network behaviour - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, fd); - worker.Emit(OpCodes.Call, weaverTypes.InitSyncObjectReference); - } + public static class SyncObjectInitializer + { + // generates code like: + // this.InitSyncObject(m_sizes); + public static void GenerateSyncObjectInitializer(ILProcessor worker, WeaverTypes weaverTypes, FieldDefinition fd) + { + // register syncobject in network behaviour + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, fd); + worker.Emit(OpCodes.Call, weaverTypes.InitSyncObjectReference); + } - public static bool ImplementsSyncObject(TypeReference typeRef) - { - try - { - // value types cant inherit from SyncObject - if (typeRef.IsValueType) - { - return false; - } + public static bool ImplementsSyncObject(TypeReference typeRef) + { + try + { + // value types cant inherit from SyncObject + if (typeRef.IsValueType) + { + return false; + } - return typeRef.Resolve().IsDerivedFrom(); - } - catch - { - // sometimes this will fail if we reference a weird library that can't be resolved, so we just swallow that exception and return false - } + return typeRef.Resolve().IsDerivedFrom(); + } + catch + { + // sometimes this will fail if we reference a weird library that can't be resolved, so we just swallow that exception and return false + } - return false; - } - } + return false; + } + } } diff --git a/MirrorWeaver/Weaver/Processors/SyncObjectProcessor.cs b/MirrorWeaver/Weaver/Processors/SyncObjectProcessor.cs index 154f6e10..f0898b36 100644 --- a/MirrorWeaver/Weaver/Processors/SyncObjectProcessor.cs +++ b/MirrorWeaver/Weaver/Processors/SyncObjectProcessor.cs @@ -1,84 +1,84 @@ -using Mono.Cecil; using System.Collections.Generic; +using Mono.Cecil; namespace Mirror.Weaver { - public static class SyncObjectProcessor - { - // ulong = 64 bytes - private const int SyncObjectsLimit = 64; + public static class SyncObjectProcessor + { + // ulong = 64 bytes + const int SyncObjectsLimit = 64; - // Finds SyncObjects fields in a type - // Type should be a NetworkBehaviour - public static List FindSyncObjectsFields(Writers writers, Readers readers, Logger Log, TypeDefinition td, ref bool WeavingFailed) - { - var syncObjects = new List(); + // Finds SyncObjects fields in a type + // Type should be a NetworkBehaviour + public static List FindSyncObjectsFields(Writers writers, Readers readers, Logger Log, TypeDefinition td, ref bool WeavingFailed) + { + List syncObjects = new List(); - foreach (var fd in td.Fields) - { - if (fd.FieldType.Resolve().IsDerivedFrom()) - { - if (fd.IsStatic) - { - Log.Error($"{fd.Name} cannot be static", fd); - WeavingFailed = true; - continue; - } + foreach (FieldDefinition fd in td.Fields) + { + if (fd.FieldType.Resolve().IsDerivedFrom()) + { + if (fd.IsStatic) + { + Log.Error($"{fd.Name} cannot be static", fd); + WeavingFailed = true; + continue; + } - // SyncObjects always needs to be readonly to guarantee. - // Weaver calls InitSyncObject on them for dirty bits etc. - // Reassigning at runtime would cause undefined behaviour. - // (C# 'readonly' is called 'initonly' in IL code.) - // - // NOTE: instead of forcing readonly, we could also scan all - // instructions for SyncObject assignments. this would - // make unit tests very difficult though. - if (!fd.IsInitOnly) - { - // just a warning for now. - // many people might still use non-readonly SyncObjects. - Log.Warning($"{fd.Name} should have a 'readonly' keyword in front of the variable because {typeof(SyncObject)}s always need to be initialized by the Weaver.", fd); + // SyncObjects always needs to be readonly to guarantee. + // Weaver calls InitSyncObject on them for dirty bits etc. + // Reassigning at runtime would cause undefined behaviour. + // (C# 'readonly' is called 'initonly' in IL code.) + // + // NOTE: instead of forcing readonly, we could also scan all + // instructions for SyncObject assignments. this would + // make unit tests very difficult though. + if (!fd.IsInitOnly) + { + // just a warning for now. + // many people might still use non-readonly SyncObjects. + Log.Warning($"{fd.Name} should have a 'readonly' keyword in front of the variable because {typeof(SyncObject)}s always need to be initialized by the Weaver.", fd); - // only log, but keep weaving. no need to break projects. - //WeavingFailed = true; - } + // only log, but keep weaving. no need to break projects. + //WeavingFailed = true; + } - GenerateReadersAndWriters(writers, readers, fd.FieldType, ref WeavingFailed); + GenerateReadersAndWriters(writers, readers, fd.FieldType, ref WeavingFailed); - syncObjects.Add(fd); - } - } + syncObjects.Add(fd); + } + } - // SyncObjects dirty mask is 64 bit. can't sync more than 64. - if (syncObjects.Count > 64) - { - Log.Error($"{td.Name} has > {SyncObjectsLimit} SyncObjects (SyncLists etc). Consider refactoring your class into multiple components", td); - WeavingFailed = true; - } + // SyncObjects dirty mask is 64 bit. can't sync more than 64. + if (syncObjects.Count > 64) + { + Log.Error($"{td.Name} has > {SyncObjectsLimit} SyncObjects (SyncLists etc). Consider refactoring your class into multiple components", td); + WeavingFailed = true; + } - return syncObjects; - } + return syncObjects; + } - // Generates serialization methods for synclists - private static void GenerateReadersAndWriters(Writers writers, Readers readers, TypeReference tr, ref bool WeavingFailed) - { - if (tr is GenericInstanceType genericInstance) - { - foreach (var argument in genericInstance.GenericArguments) - { - if (!argument.IsGenericParameter) - { - readers.GetReadFunc(argument, ref WeavingFailed); - writers.GetWriteFunc(argument, ref WeavingFailed); - } - } - } + // Generates serialization methods for synclists + static void GenerateReadersAndWriters(Writers writers, Readers readers, TypeReference tr, ref bool WeavingFailed) + { + if (tr is GenericInstanceType genericInstance) + { + foreach (TypeReference argument in genericInstance.GenericArguments) + { + if (!argument.IsGenericParameter) + { + readers.GetReadFunc(argument, ref WeavingFailed); + writers.GetWriteFunc(argument, ref WeavingFailed); + } + } + } - if (tr != null) - { - GenerateReadersAndWriters(writers, readers, tr.Resolve().BaseType, ref WeavingFailed); - } - } - } + if (tr != null) + { + GenerateReadersAndWriters(writers, readers, tr.Resolve().BaseType, ref WeavingFailed); + } + } + } } diff --git a/MirrorWeaver/Weaver/Processors/SyncVarAttributeAccessReplacer.cs b/MirrorWeaver/Weaver/Processors/SyncVarAttributeAccessReplacer.cs index ae23ac96..85e48fd2 100644 --- a/MirrorWeaver/Weaver/Processors/SyncVarAttributeAccessReplacer.cs +++ b/MirrorWeaver/Weaver/Processors/SyncVarAttributeAccessReplacer.cs @@ -2,181 +2,173 @@ // is replaced with: // public int Networkhealth { get; set; } properties. // this class processes all access to 'health' and replaces it with 'Networkhealth' +using System; using Mono.Cecil; using Mono.Cecil.Cil; -using System; namespace Mirror.Weaver { - public static class SyncVarAttributeAccessReplacer - { - // process the module - public static void Process(ModuleDefinition moduleDef, SyncVarAccessLists syncVarAccessLists) - { - var startTime = DateTime.Now; + public static class SyncVarAttributeAccessReplacer + { + // process the module + public static void Process(ModuleDefinition moduleDef, SyncVarAccessLists syncVarAccessLists) + { + DateTime startTime = DateTime.Now; - // process all classes in this module - foreach (var td in moduleDef.Types) - { - if (td.IsClass) - { - ProcessClass(syncVarAccessLists, td); - } - } + // process all classes in this module + foreach (TypeDefinition td in moduleDef.Types) + { + if (td.IsClass) + { + ProcessClass(syncVarAccessLists, td); + } + } - Console.WriteLine($" ProcessSitesModule {moduleDef.Name} elapsed time:{(DateTime.Now - startTime)}"); - } + Console.WriteLine($" ProcessSitesModule {moduleDef.Name} elapsed time:{(DateTime.Now - startTime)}"); + } - private static void ProcessClass(SyncVarAccessLists syncVarAccessLists, TypeDefinition td) - { - //Console.WriteLine($" ProcessClass {td}"); + static void ProcessClass(SyncVarAccessLists syncVarAccessLists, TypeDefinition td) + { + //Console.WriteLine($" ProcessClass {td}"); - // process all methods in this class - foreach (var md in td.Methods) - { - ProcessMethod(syncVarAccessLists, md); - } + // process all methods in this class + foreach (MethodDefinition md in td.Methods) + { + ProcessMethod(syncVarAccessLists, md); + } - // processes all nested classes in this class recursively - foreach (var nested in td.NestedTypes) - { - ProcessClass(syncVarAccessLists, nested); - } - } + // processes all nested classes in this class recursively + foreach (TypeDefinition nested in td.NestedTypes) + { + ProcessClass(syncVarAccessLists, nested); + } + } - private static void ProcessMethod(SyncVarAccessLists syncVarAccessLists, MethodDefinition md) - { - // process all references to replaced members with properties - //Log.Warning($" ProcessSiteMethod {md}"); + static void ProcessMethod(SyncVarAccessLists syncVarAccessLists, MethodDefinition md) + { + // process all references to replaced members with properties + //Log.Warning($" ProcessSiteMethod {md}"); - // skip static constructor, "MirrorProcessed", "InvokeUserCode_" - if (md.Name == ".cctor" || - md.Name == NetworkBehaviourProcessor.ProcessedFunctionName || - md.Name.StartsWith(Weaver.InvokeRpcPrefix)) - { - return; - } + // skip static constructor, "MirrorProcessed", "InvokeUserCode_" + if (md.Name == ".cctor" || + md.Name == NetworkBehaviourProcessor.ProcessedFunctionName || + md.Name.StartsWith(Weaver.InvokeRpcPrefix)) + return; - // skip abstract - if (md.IsAbstract) - { - return; - } + // skip abstract + if (md.IsAbstract) + { + return; + } - // go through all instructions of this method - if (md.Body != null && md.Body.Instructions != null) - { - for (var i = 0; i < md.Body.Instructions.Count;) - { - var instr = md.Body.Instructions[i]; - i += ProcessInstruction(syncVarAccessLists, md, instr, i); - } - } - } + // go through all instructions of this method + if (md.Body != null && md.Body.Instructions != null) + { + for (int i = 0; i < md.Body.Instructions.Count;) + { + Instruction instr = md.Body.Instructions[i]; + i += ProcessInstruction(syncVarAccessLists, md, instr, i); + } + } + } - private static int ProcessInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction instr, int iCount) - { - // stfld (sets value of a field)? - if (instr.OpCode == OpCodes.Stfld && instr.Operand is FieldDefinition opFieldst) - { - ProcessSetInstruction(syncVarAccessLists, md, instr, opFieldst); - } + static int ProcessInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction instr, int iCount) + { + // stfld (sets value of a field)? + if (instr.OpCode == OpCodes.Stfld && instr.Operand is FieldDefinition opFieldst) + { + ProcessSetInstruction(syncVarAccessLists, md, instr, opFieldst); + } - // ldfld (load value of a field)? - if (instr.OpCode == OpCodes.Ldfld && instr.Operand is FieldDefinition opFieldld) - { - // this instruction gets the value of a field. cache the field reference. - ProcessGetInstruction(syncVarAccessLists, md, instr, opFieldld); - } + // ldfld (load value of a field)? + if (instr.OpCode == OpCodes.Ldfld && instr.Operand is FieldDefinition opFieldld) + { + // this instruction gets the value of a field. cache the field reference. + ProcessGetInstruction(syncVarAccessLists, md, instr, opFieldld); + } - // ldflda (load field address aka reference) - if (instr.OpCode == OpCodes.Ldflda && instr.Operand is FieldDefinition opFieldlda) - { - // watch out for initobj instruction - // see https://github.com/vis2k/Mirror/issues/696 - return ProcessLoadAddressInstruction(syncVarAccessLists, md, instr, opFieldlda, iCount); - } + // ldflda (load field address aka reference) + if (instr.OpCode == OpCodes.Ldflda && instr.Operand is FieldDefinition opFieldlda) + { + // watch out for initobj instruction + // see https://github.com/vis2k/Mirror/issues/696 + return ProcessLoadAddressInstruction(syncVarAccessLists, md, instr, opFieldlda, iCount); + } - // we processed one instruction (instr) - return 1; - } + // we processed one instruction (instr) + return 1; + } - // replaces syncvar write access with the NetworkXYZ.set property calls - private static void ProcessSetInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction i, FieldDefinition opField) - { - // don't replace property call sites in constructors - if (md.Name == ".ctor") - { - return; - } + // replaces syncvar write access with the NetworkXYZ.set property calls + static void ProcessSetInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction i, FieldDefinition opField) + { + // don't replace property call sites in constructors + if (md.Name == ".ctor") + return; - // does it set a field that we replaced? - if (syncVarAccessLists.replacementSetterProperties.TryGetValue(opField, out var replacement)) - { - //replace with property - //Log.Warning($" replacing {md.Name}:{i}", opField); - i.OpCode = OpCodes.Call; - i.Operand = replacement; - //Log.Warning($" replaced {md.Name}:{i}", opField); - } - } + // does it set a field that we replaced? + if (syncVarAccessLists.replacementSetterProperties.TryGetValue(opField, out MethodDefinition replacement)) + { + //replace with property + //Log.Warning($" replacing {md.Name}:{i}", opField); + i.OpCode = OpCodes.Call; + i.Operand = replacement; + //Log.Warning($" replaced {md.Name}:{i}", opField); + } + } - // replaces syncvar read access with the NetworkXYZ.get property calls - private static void ProcessGetInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction i, FieldDefinition opField) - { - // don't replace property call sites in constructors - if (md.Name == ".ctor") - { - return; - } + // replaces syncvar read access with the NetworkXYZ.get property calls + static void ProcessGetInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction i, FieldDefinition opField) + { + // don't replace property call sites in constructors + if (md.Name == ".ctor") + return; - // does it set a field that we replaced? - if (syncVarAccessLists.replacementGetterProperties.TryGetValue(opField, out var replacement)) - { - //replace with property - //Log.Warning($" replacing {md.Name}:{i}"); - i.OpCode = OpCodes.Call; - i.Operand = replacement; - //Log.Warning($" replaced {md.Name}:{i}"); - } - } + // does it set a field that we replaced? + if (syncVarAccessLists.replacementGetterProperties.TryGetValue(opField, out MethodDefinition replacement)) + { + //replace with property + //Log.Warning($" replacing {md.Name}:{i}"); + i.OpCode = OpCodes.Call; + i.Operand = replacement; + //Log.Warning($" replaced {md.Name}:{i}"); + } + } - private static int ProcessLoadAddressInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction instr, FieldDefinition opField, int iCount) - { - // don't replace property call sites in constructors - if (md.Name == ".ctor") - { - return 1; - } + static int ProcessLoadAddressInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction instr, FieldDefinition opField, int iCount) + { + // don't replace property call sites in constructors + if (md.Name == ".ctor") + return 1; - // does it set a field that we replaced? - if (syncVarAccessLists.replacementSetterProperties.TryGetValue(opField, out var replacement)) - { - // we have a replacement for this property - // is the next instruction a initobj? - var nextInstr = md.Body.Instructions[iCount + 1]; + // does it set a field that we replaced? + if (syncVarAccessLists.replacementSetterProperties.TryGetValue(opField, out MethodDefinition replacement)) + { + // we have a replacement for this property + // is the next instruction a initobj? + Instruction nextInstr = md.Body.Instructions[iCount + 1]; - if (nextInstr.OpCode == OpCodes.Initobj) - { - // we need to replace this code with: - // var tmp = new MyStruct(); - // this.set_Networkxxxx(tmp); - var worker = md.Body.GetILProcessor(); - var tmpVariable = new VariableDefinition(opField.FieldType); - md.Body.Variables.Add(tmpVariable); + if (nextInstr.OpCode == OpCodes.Initobj) + { + // we need to replace this code with: + // var tmp = new MyStruct(); + // this.set_Networkxxxx(tmp); + ILProcessor worker = md.Body.GetILProcessor(); + VariableDefinition tmpVariable = new VariableDefinition(opField.FieldType); + md.Body.Variables.Add(tmpVariable); - worker.InsertBefore(instr, worker.Create(OpCodes.Ldloca, tmpVariable)); - worker.InsertBefore(instr, worker.Create(OpCodes.Initobj, opField.FieldType)); - worker.InsertBefore(instr, worker.Create(OpCodes.Ldloc, tmpVariable)); - worker.InsertBefore(instr, worker.Create(OpCodes.Call, replacement)); + worker.InsertBefore(instr, worker.Create(OpCodes.Ldloca, tmpVariable)); + worker.InsertBefore(instr, worker.Create(OpCodes.Initobj, opField.FieldType)); + worker.InsertBefore(instr, worker.Create(OpCodes.Ldloc, tmpVariable)); + worker.InsertBefore(instr, worker.Create(OpCodes.Call, replacement)); - worker.Remove(instr); - worker.Remove(nextInstr); - return 4; - } - } + worker.Remove(instr); + worker.Remove(nextInstr); + return 4; + } + } - return 1; - } - } + return 1; + } + } } diff --git a/MirrorWeaver/Weaver/Processors/SyncVarAttributeProcessor.cs b/MirrorWeaver/Weaver/Processors/SyncVarAttributeProcessor.cs index 7b29605f..ddc9d7d7 100644 --- a/MirrorWeaver/Weaver/Processors/SyncVarAttributeProcessor.cs +++ b/MirrorWeaver/Weaver/Processors/SyncVarAttributeProcessor.cs @@ -1,485 +1,490 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; using System.Collections.Generic; using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; namespace Mirror.Weaver { - // Processes [SyncVar] attribute fields in NetworkBehaviour - // not static, because ILPostProcessor is multithreaded - public class SyncVarAttributeProcessor - { - // ulong = 64 bytes - private const int SyncVarLimit = 64; - private readonly AssemblyDefinition assembly; - private readonly WeaverTypes weaverTypes; - private readonly SyncVarAccessLists syncVarAccessLists; - private readonly Logger Log; - - private string HookParameterMessage(string hookName, TypeReference ValueType) => - $"void {hookName}({ValueType} oldValue, {ValueType} newValue)"; - - public SyncVarAttributeProcessor(AssemblyDefinition assembly, WeaverTypes weaverTypes, SyncVarAccessLists syncVarAccessLists, Logger Log) - { - this.assembly = assembly; - this.weaverTypes = weaverTypes; - this.syncVarAccessLists = syncVarAccessLists; - this.Log = Log; - } - - // Get hook method if any - public MethodDefinition GetHookMethod(TypeDefinition td, FieldDefinition syncVar, ref bool WeavingFailed) - { - var syncVarAttr = syncVar.GetCustomAttribute(); - - if (syncVarAttr == null) - { - return null; - } - - var hookFunctionName = syncVarAttr.GetField("hook", null); - - if (hookFunctionName == null) - { - return null; - } - - return FindHookMethod(td, syncVar, hookFunctionName, ref WeavingFailed); - } - - private MethodDefinition FindHookMethod(TypeDefinition td, FieldDefinition syncVar, string hookFunctionName, ref bool WeavingFailed) - { - var methods = td.GetMethods(hookFunctionName); - - var methodsWith2Param = new List(methods.Where(m => m.Parameters.Count == 2)); - - if (methodsWith2Param.Count == 0) - { - Log.Error($"Could not find hook for '{syncVar.Name}', hook name '{hookFunctionName}'. " + - $"Method signature should be {HookParameterMessage(hookFunctionName, syncVar.FieldType)}", - syncVar); - WeavingFailed = true; - - return null; - } - - foreach (var method in methodsWith2Param) - { - if (MatchesParameters(syncVar, method)) - { - return method; - } - } - - Log.Error($"Wrong type for Parameter in hook for '{syncVar.Name}', hook name '{hookFunctionName}'. " + - $"Method signature should be {HookParameterMessage(hookFunctionName, syncVar.FieldType)}", - syncVar); - WeavingFailed = true; - - return null; - } - - private bool MatchesParameters(FieldDefinition syncVar, MethodDefinition method) => - // matches void onValueChange(T oldValue, T newValue) - method.Parameters[0].ParameterType.FullName == syncVar.FieldType.FullName && - method.Parameters[1].ParameterType.FullName == syncVar.FieldType.FullName; - - public MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string originalName, FieldDefinition netFieldId) - { - //Create the get method - var get = new MethodDefinition( - $"get_Network{originalName}", MethodAttributes.Public | - MethodAttributes.SpecialName | - MethodAttributes.HideBySig, - fd.FieldType); - - var worker = get.Body.GetILProcessor(); - - // [SyncVar] GameObject? - if (fd.FieldType.Is()) - { - // return this.GetSyncVarGameObject(ref field, uint netId); - // this. - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, fd); - worker.Emit(OpCodes.Call, weaverTypes.getSyncVarGameObjectReference); - worker.Emit(OpCodes.Ret); - } - // [SyncVar] NetworkIdentity? - else if (fd.FieldType.Is()) - { - // return this.GetSyncVarNetworkIdentity(ref field, uint netId); - // this. - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, fd); - worker.Emit(OpCodes.Call, weaverTypes.getSyncVarNetworkIdentityReference); - worker.Emit(OpCodes.Ret); - } - else if (fd.FieldType.IsDerivedFrom()) - { - // return this.GetSyncVarNetworkBehaviour(ref field, uint netId); - // this. - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, fd); - var getFunc = weaverTypes.getSyncVarNetworkBehaviourReference.MakeGeneric(assembly.MainModule, fd.FieldType); - worker.Emit(OpCodes.Call, getFunc); - worker.Emit(OpCodes.Ret); - } - // [SyncVar] int, string, etc. - else - { - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, fd); - worker.Emit(OpCodes.Ret); - } - - get.Body.Variables.Add(new VariableDefinition(fd.FieldType)); - get.Body.InitLocals = true; - get.SemanticsAttributes = MethodSemanticsAttributes.Getter; - - return get; - } - - public MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId, ref bool WeavingFailed) - { - //Create the set method - var set = new MethodDefinition($"set_Network{originalName}", MethodAttributes.Public | - MethodAttributes.SpecialName | - MethodAttributes.HideBySig, - weaverTypes.Import(typeof(void))); - - var worker = set.Body.GetILProcessor(); - - // if (!SyncVarEqual(value, ref playerData)) - var endOfMethod = worker.Create(OpCodes.Nop); - - // NOTE: SyncVar...Equal functions are static. - // don't Emit Ldarg_0 aka 'this'. - - // new value to set - worker.Emit(OpCodes.Ldarg_1); - - // reference to field to set - // make generic version of SetSyncVar with field type - if (fd.FieldType.Is()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - - worker.Emit(OpCodes.Call, weaverTypes.syncVarGameObjectEqualReference); - } - else if (fd.FieldType.Is()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - - worker.Emit(OpCodes.Call, weaverTypes.syncVarNetworkIdentityEqualReference); - } - else if (fd.FieldType.IsDerivedFrom()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - - var getFunc = weaverTypes.syncVarNetworkBehaviourEqualReference.MakeGeneric(assembly.MainModule, fd.FieldType); - worker.Emit(OpCodes.Call, getFunc); - } - else - { - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, fd); - - var syncVarEqualGm = new GenericInstanceMethod(weaverTypes.syncVarEqualReference); - syncVarEqualGm.GenericArguments.Add(fd.FieldType); - worker.Emit(OpCodes.Call, syncVarEqualGm); - } - - worker.Emit(OpCodes.Brtrue, endOfMethod); - - // T oldValue = value; - // TODO for GO/NI we need to backup the netId don't we? - var oldValue = new VariableDefinition(fd.FieldType); - set.Body.Variables.Add(oldValue); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, fd); - worker.Emit(OpCodes.Stloc, oldValue); - - // this - worker.Emit(OpCodes.Ldarg_0); - - // new value to set - worker.Emit(OpCodes.Ldarg_1); - - // reference to field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, fd); - - // dirty bit - // 8 byte integer aka long - worker.Emit(OpCodes.Ldc_I8, dirtyBit); - - if (fd.FieldType.Is()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, netFieldId); - - worker.Emit(OpCodes.Call, weaverTypes.setSyncVarGameObjectReference); - } - else if (fd.FieldType.Is()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, netFieldId); - - worker.Emit(OpCodes.Call, weaverTypes.setSyncVarNetworkIdentityReference); - } - else if (fd.FieldType.IsDerivedFrom()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, netFieldId); - - var getFunc = weaverTypes.setSyncVarNetworkBehaviourReference.MakeGeneric(assembly.MainModule, fd.FieldType); - worker.Emit(OpCodes.Call, getFunc); - } - else - { - // make generic version of SetSyncVar with field type - var gm = new GenericInstanceMethod(weaverTypes.setSyncVarReference); - gm.GenericArguments.Add(fd.FieldType); - - // invoke SetSyncVar - worker.Emit(OpCodes.Call, gm); - } - - var hookMethod = GetHookMethod(td, fd, ref WeavingFailed); - - if (hookMethod != null) - { - //if (NetworkServer.localClientActive && !getSyncVarHookGuard(dirtyBit)) - var label = worker.Create(OpCodes.Nop); - worker.Emit(OpCodes.Call, weaverTypes.NetworkServerGetLocalClientActive); - worker.Emit(OpCodes.Brfalse, label); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldc_I8, dirtyBit); - worker.Emit(OpCodes.Call, weaverTypes.getSyncVarHookGuard); - worker.Emit(OpCodes.Brtrue, label); - - // setSyncVarHookGuard(dirtyBit, true); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldc_I8, dirtyBit); - worker.Emit(OpCodes.Ldc_I4_1); - worker.Emit(OpCodes.Call, weaverTypes.setSyncVarHookGuard); - - // call hook (oldValue, newValue) - // Generates: OnValueChanged(oldValue, value); - WriteCallHookMethodUsingArgument(worker, hookMethod, oldValue); - - // setSyncVarHookGuard(dirtyBit, false); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldc_I8, dirtyBit); - worker.Emit(OpCodes.Ldc_I4_0); - worker.Emit(OpCodes.Call, weaverTypes.setSyncVarHookGuard); - - worker.Append(label); - } - - worker.Append(endOfMethod); - - worker.Emit(OpCodes.Ret); - - set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType)); - set.SemanticsAttributes = MethodSemanticsAttributes.Setter; - - return set; - } - - public void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, Dictionary syncVarNetIds, long dirtyBit, ref bool WeavingFailed) - { - var originalName = fd.Name; - - // GameObject/NetworkIdentity SyncVars have a new field for netId - FieldDefinition netIdField = null; - // NetworkBehaviour has different field type than other NetworkIdentityFields - if (fd.FieldType.IsDerivedFrom()) - { - netIdField = new FieldDefinition($"___{fd.Name}NetId", - FieldAttributes.Private, - weaverTypes.Import()); - - syncVarNetIds[fd] = netIdField; - } - else if (fd.FieldType.IsNetworkIdentityField()) - { - netIdField = new FieldDefinition($"___{fd.Name}NetId", - FieldAttributes.Private, - weaverTypes.Import()); - - syncVarNetIds[fd] = netIdField; - } - - var get = GenerateSyncVarGetter(fd, originalName, netIdField); - var set = GenerateSyncVarSetter(td, fd, originalName, dirtyBit, netIdField, ref WeavingFailed); - - //NOTE: is property even needed? Could just use a setter function? - //create the property - var propertyDefinition = new PropertyDefinition($"Network{originalName}", PropertyAttributes.None, fd.FieldType) - { - GetMethod = get, - SetMethod = set - }; - - //add the methods and property to the type. - td.Methods.Add(get); - td.Methods.Add(set); - td.Properties.Add(propertyDefinition); - syncVarAccessLists.replacementSetterProperties[fd] = set; - - // replace getter field if GameObject/NetworkIdentity so it uses - // netId instead - // -> only for GameObjects, otherwise an int syncvar's getter would - // end up in recursion. - if (fd.FieldType.IsNetworkIdentityField()) - { - syncVarAccessLists.replacementGetterProperties[fd] = get; - } - } - - public (List syncVars, Dictionary syncVarNetIds) ProcessSyncVars(TypeDefinition td, ref bool WeavingFailed) - { - var syncVars = new List(); - var syncVarNetIds = new Dictionary(); - - // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties. - // start assigning syncvars at the place the base class stopped, if any - var dirtyBitCounter = syncVarAccessLists.GetSyncVarStart(td.BaseType.FullName); - - // find syncvars - foreach (var fd in td.Fields) - { - if (fd.HasCustomAttribute()) - { - if ((fd.Attributes & FieldAttributes.Static) != 0) - { - Log.Error($"{fd.Name} cannot be static", fd); - WeavingFailed = true; - continue; - } - - if (fd.FieldType.IsArray) - { - Log.Error($"{fd.Name} has invalid type. Use SyncLists instead of arrays", fd); - WeavingFailed = true; - continue; - } - - if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) - { - Log.Warning($"{fd.Name} has [SyncVar] attribute. SyncLists should not be marked with SyncVar", fd); - } - else - { - syncVars.Add(fd); - - ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter, ref WeavingFailed); - dirtyBitCounter += 1; - - if (dirtyBitCounter > SyncVarLimit) - { - Log.Error($"{td.Name} has > {SyncVarLimit} SyncVars. Consider refactoring your class into multiple components", td); - WeavingFailed = true; - continue; - } - } - } - } - - // add all the new SyncVar __netId fields - foreach (var fd in syncVarNetIds.Values) - { - td.Fields.Add(fd); - } - syncVarAccessLists.SetNumSyncVars(td.FullName, syncVars.Count); - - return (syncVars, syncVarNetIds); - } - - public void WriteCallHookMethodUsingArgument(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue) => WriteCallHookMethod(worker, hookMethod, oldValue, null); - - public void WriteCallHookMethodUsingField(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue, FieldDefinition newValue, ref bool WeavingFailed) - { - if (newValue == null) - { - Log.Error("NewValue field was null when writing SyncVar hook"); - WeavingFailed = true; - } - - WriteCallHookMethod(worker, hookMethod, oldValue, newValue); - } - - private void WriteCallHookMethod(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue, FieldDefinition newValue) - { - WriteStartFunctionCall(); - - // write args - WriteOldValue(); - WriteNewValue(); - - WriteEndFunctionCall(); - - - // *** Local functions used to write OpCodes *** - // Local functions have access to function variables, no need to pass in args - - void WriteOldValue() => worker.Emit(OpCodes.Ldloc, oldValue); - - void WriteNewValue() - { - // write arg1 or this.field - if (newValue == null) - { - worker.Emit(OpCodes.Ldarg_1); - } - else - { - // this. - worker.Emit(OpCodes.Ldarg_0); - // syncvar.get - worker.Emit(OpCodes.Ldfld, newValue); - } - } - - // Writes this before method if it is not static - void WriteStartFunctionCall() - { - // don't add this (Ldarg_0) if method is static - if (!hookMethod.IsStatic) - { - // this before method call - // e.g. this.onValueChanged - worker.Emit(OpCodes.Ldarg_0); - } - } - - // Calls method - void WriteEndFunctionCall() - { - // only use Callvirt when not static - var opcode = hookMethod.IsStatic ? OpCodes.Call : OpCodes.Callvirt; - worker.Emit(opcode, hookMethod); - } - } - } + // Processes [SyncVar] attribute fields in NetworkBehaviour + // not static, because ILPostProcessor is multithreaded + public class SyncVarAttributeProcessor + { + // ulong = 64 bytes + const int SyncVarLimit = 64; + + AssemblyDefinition assembly; + WeaverTypes weaverTypes; + SyncVarAccessLists syncVarAccessLists; + Logger Log; + + string HookParameterMessage(string hookName, TypeReference ValueType) => + $"void {hookName}({ValueType} oldValue, {ValueType} newValue)"; + + public SyncVarAttributeProcessor(AssemblyDefinition assembly, WeaverTypes weaverTypes, SyncVarAccessLists syncVarAccessLists, Logger Log) + { + this.assembly = assembly; + this.weaverTypes = weaverTypes; + this.syncVarAccessLists = syncVarAccessLists; + this.Log = Log; + } + + // Get hook method if any + public MethodDefinition GetHookMethod(TypeDefinition td, FieldDefinition syncVar, ref bool WeavingFailed) + { + CustomAttribute syncVarAttr = syncVar.GetCustomAttribute(); + + if (syncVarAttr == null) + return null; + + string hookFunctionName = syncVarAttr.GetField("hook", null); + + if (hookFunctionName == null) + return null; + + return FindHookMethod(td, syncVar, hookFunctionName, ref WeavingFailed); + } + + MethodDefinition FindHookMethod(TypeDefinition td, FieldDefinition syncVar, string hookFunctionName, ref bool WeavingFailed) + { + List methods = td.GetMethods(hookFunctionName); + + List methodsWith2Param = new List(methods.Where(m => m.Parameters.Count == 2)); + + if (methodsWith2Param.Count == 0) + { + Log.Error($"Could not find hook for '{syncVar.Name}', hook name '{hookFunctionName}'. " + + $"Method signature should be {HookParameterMessage(hookFunctionName, syncVar.FieldType)}", + syncVar); + WeavingFailed = true; + + return null; + } + + foreach (MethodDefinition method in methodsWith2Param) + { + if (MatchesParameters(syncVar, method)) + { + return method; + } + } + + Log.Error($"Wrong type for Parameter in hook for '{syncVar.Name}', hook name '{hookFunctionName}'. " + + $"Method signature should be {HookParameterMessage(hookFunctionName, syncVar.FieldType)}", + syncVar); + WeavingFailed = true; + + return null; + } + + bool MatchesParameters(FieldDefinition syncVar, MethodDefinition method) + { + // matches void onValueChange(T oldValue, T newValue) + return method.Parameters[0].ParameterType.FullName == syncVar.FieldType.FullName && + method.Parameters[1].ParameterType.FullName == syncVar.FieldType.FullName; + } + + public MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string originalName, FieldDefinition netFieldId) + { + //Create the get method + MethodDefinition get = new MethodDefinition( + $"get_Network{originalName}", MethodAttributes.Public | + MethodAttributes.SpecialName | + MethodAttributes.HideBySig, + fd.FieldType); + + ILProcessor worker = get.Body.GetILProcessor(); + + // [SyncVar] GameObject? + if (fd.FieldType.Is()) + { + // return this.GetSyncVarGameObject(ref field, uint netId); + // this. + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netFieldId); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, fd); + worker.Emit(OpCodes.Call, weaverTypes.getSyncVarGameObjectReference); + worker.Emit(OpCodes.Ret); + } + // [SyncVar] NetworkIdentity? + else if (fd.FieldType.Is()) + { + // return this.GetSyncVarNetworkIdentity(ref field, uint netId); + // this. + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netFieldId); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, fd); + worker.Emit(OpCodes.Call, weaverTypes.getSyncVarNetworkIdentityReference); + worker.Emit(OpCodes.Ret); + } + else if (fd.FieldType.IsDerivedFrom()) + { + // return this.GetSyncVarNetworkBehaviour(ref field, uint netId); + // this. + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netFieldId); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, fd); + MethodReference getFunc = weaverTypes.getSyncVarNetworkBehaviourReference.MakeGeneric(assembly.MainModule, fd.FieldType); + worker.Emit(OpCodes.Call, getFunc); + worker.Emit(OpCodes.Ret); + } + // [SyncVar] int, string, etc. + else + { + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, fd); + worker.Emit(OpCodes.Ret); + } + + get.Body.Variables.Add(new VariableDefinition(fd.FieldType)); + get.Body.InitLocals = true; + get.SemanticsAttributes = MethodSemanticsAttributes.Getter; + + return get; + } + + public MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId, ref bool WeavingFailed) + { + //Create the set method + MethodDefinition set = new MethodDefinition($"set_Network{originalName}", MethodAttributes.Public | + MethodAttributes.SpecialName | + MethodAttributes.HideBySig, + weaverTypes.Import(typeof(void))); + + ILProcessor worker = set.Body.GetILProcessor(); + + // if (!SyncVarEqual(value, ref playerData)) + Instruction endOfMethod = worker.Create(OpCodes.Nop); + + // NOTE: SyncVar...Equal functions are static. + // don't Emit Ldarg_0 aka 'this'. + + // new value to set + worker.Emit(OpCodes.Ldarg_1); + + // reference to field to set + // make generic version of SetSyncVar with field type + if (fd.FieldType.Is()) + { + // reference to netId Field to set + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netFieldId); + + worker.Emit(OpCodes.Call, weaverTypes.syncVarGameObjectEqualReference); + } + else if (fd.FieldType.Is()) + { + // reference to netId Field to set + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netFieldId); + + worker.Emit(OpCodes.Call, weaverTypes.syncVarNetworkIdentityEqualReference); + } + else if (fd.FieldType.IsDerivedFrom()) + { + // reference to netId Field to set + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netFieldId); + + MethodReference getFunc = weaverTypes.syncVarNetworkBehaviourEqualReference.MakeGeneric(assembly.MainModule, fd.FieldType); + worker.Emit(OpCodes.Call, getFunc); + } + else + { + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, fd); + + GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(weaverTypes.syncVarEqualReference); + syncVarEqualGm.GenericArguments.Add(fd.FieldType); + worker.Emit(OpCodes.Call, syncVarEqualGm); + } + + worker.Emit(OpCodes.Brtrue, endOfMethod); + + // T oldValue = value; + // TODO for GO/NI we need to backup the netId don't we? + VariableDefinition oldValue = new VariableDefinition(fd.FieldType); + set.Body.Variables.Add(oldValue); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, fd); + worker.Emit(OpCodes.Stloc, oldValue); + + // this + worker.Emit(OpCodes.Ldarg_0); + + // new value to set + worker.Emit(OpCodes.Ldarg_1); + + // reference to field to set + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, fd); + + // dirty bit + // 8 byte integer aka long + worker.Emit(OpCodes.Ldc_I8, dirtyBit); + + if (fd.FieldType.Is()) + { + // reference to netId Field to set + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, netFieldId); + + worker.Emit(OpCodes.Call, weaverTypes.setSyncVarGameObjectReference); + } + else if (fd.FieldType.Is()) + { + // reference to netId Field to set + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, netFieldId); + + worker.Emit(OpCodes.Call, weaverTypes.setSyncVarNetworkIdentityReference); + } + else if (fd.FieldType.IsDerivedFrom()) + { + // reference to netId Field to set + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, netFieldId); + + MethodReference getFunc = weaverTypes.setSyncVarNetworkBehaviourReference.MakeGeneric(assembly.MainModule, fd.FieldType); + worker.Emit(OpCodes.Call, getFunc); + } + else + { + // make generic version of SetSyncVar with field type + GenericInstanceMethod gm = new GenericInstanceMethod(weaverTypes.setSyncVarReference); + gm.GenericArguments.Add(fd.FieldType); + + // invoke SetSyncVar + worker.Emit(OpCodes.Call, gm); + } + + MethodDefinition hookMethod = GetHookMethod(td, fd, ref WeavingFailed); + + if (hookMethod != null) + { + //if (NetworkServer.localClientActive && !getSyncVarHookGuard(dirtyBit)) + Instruction label = worker.Create(OpCodes.Nop); + worker.Emit(OpCodes.Call, weaverTypes.NetworkServerGetLocalClientActive); + worker.Emit(OpCodes.Brfalse, label); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldc_I8, dirtyBit); + worker.Emit(OpCodes.Call, weaverTypes.getSyncVarHookGuard); + worker.Emit(OpCodes.Brtrue, label); + + // setSyncVarHookGuard(dirtyBit, true); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldc_I8, dirtyBit); + worker.Emit(OpCodes.Ldc_I4_1); + worker.Emit(OpCodes.Call, weaverTypes.setSyncVarHookGuard); + + // call hook (oldValue, newValue) + // Generates: OnValueChanged(oldValue, value); + WriteCallHookMethodUsingArgument(worker, hookMethod, oldValue); + + // setSyncVarHookGuard(dirtyBit, false); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldc_I8, dirtyBit); + worker.Emit(OpCodes.Ldc_I4_0); + worker.Emit(OpCodes.Call, weaverTypes.setSyncVarHookGuard); + + worker.Append(label); + } + + worker.Append(endOfMethod); + + worker.Emit(OpCodes.Ret); + + set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType)); + set.SemanticsAttributes = MethodSemanticsAttributes.Setter; + + return set; + } + + public void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, Dictionary syncVarNetIds, long dirtyBit, ref bool WeavingFailed) + { + string originalName = fd.Name; + + // GameObject/NetworkIdentity SyncVars have a new field for netId + FieldDefinition netIdField = null; + // NetworkBehaviour has different field type than other NetworkIdentityFields + if (fd.FieldType.IsDerivedFrom()) + { + netIdField = new FieldDefinition($"___{fd.Name}NetId", + FieldAttributes.Private, + weaverTypes.Import()); + + syncVarNetIds[fd] = netIdField; + } + else if (fd.FieldType.IsNetworkIdentityField()) + { + netIdField = new FieldDefinition($"___{fd.Name}NetId", + FieldAttributes.Private, + weaverTypes.Import()); + + syncVarNetIds[fd] = netIdField; + } + + MethodDefinition get = GenerateSyncVarGetter(fd, originalName, netIdField); + MethodDefinition set = GenerateSyncVarSetter(td, fd, originalName, dirtyBit, netIdField, ref WeavingFailed); + + //NOTE: is property even needed? Could just use a setter function? + //create the property + PropertyDefinition propertyDefinition = new PropertyDefinition($"Network{originalName}", PropertyAttributes.None, fd.FieldType) + { + GetMethod = get, + SetMethod = set + }; + + //add the methods and property to the type. + td.Methods.Add(get); + td.Methods.Add(set); + td.Properties.Add(propertyDefinition); + syncVarAccessLists.replacementSetterProperties[fd] = set; + + // replace getter field if GameObject/NetworkIdentity so it uses + // netId instead + // -> only for GameObjects, otherwise an int syncvar's getter would + // end up in recursion. + if (fd.FieldType.IsNetworkIdentityField()) + { + syncVarAccessLists.replacementGetterProperties[fd] = get; + } + } + + public (List syncVars, Dictionary syncVarNetIds) ProcessSyncVars(TypeDefinition td, ref bool WeavingFailed) + { + List syncVars = new List(); + Dictionary syncVarNetIds = new Dictionary(); + + // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties. + // start assigning syncvars at the place the base class stopped, if any + int dirtyBitCounter = syncVarAccessLists.GetSyncVarStart(td.BaseType.FullName); + + // find syncvars + foreach (FieldDefinition fd in td.Fields) + { + if (fd.HasCustomAttribute()) + { + if ((fd.Attributes & FieldAttributes.Static) != 0) + { + Log.Error($"{fd.Name} cannot be static", fd); + WeavingFailed = true; + continue; + } + + if (fd.FieldType.IsArray) + { + Log.Error($"{fd.Name} has invalid type. Use SyncLists instead of arrays", fd); + WeavingFailed = true; + continue; + } + + if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) + { + Log.Warning($"{fd.Name} has [SyncVar] attribute. SyncLists should not be marked with SyncVar", fd); + } + else + { + syncVars.Add(fd); + + ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter, ref WeavingFailed); + dirtyBitCounter += 1; + + if (dirtyBitCounter > SyncVarLimit) + { + Log.Error($"{td.Name} has > {SyncVarLimit} SyncVars. Consider refactoring your class into multiple components", td); + WeavingFailed = true; + continue; + } + } + } + } + + // add all the new SyncVar __netId fields + foreach (FieldDefinition fd in syncVarNetIds.Values) + { + td.Fields.Add(fd); + } + syncVarAccessLists.SetNumSyncVars(td.FullName, syncVars.Count); + + return (syncVars, syncVarNetIds); + } + + public void WriteCallHookMethodUsingArgument(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue) + { + WriteCallHookMethod(worker, hookMethod, oldValue, null); + } + + public void WriteCallHookMethodUsingField(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue, FieldDefinition newValue, ref bool WeavingFailed) + { + if (newValue == null) + { + Log.Error("NewValue field was null when writing SyncVar hook"); + WeavingFailed = true; + } + + WriteCallHookMethod(worker, hookMethod, oldValue, newValue); + } + + void WriteCallHookMethod(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue, FieldDefinition newValue) + { + WriteStartFunctionCall(); + + // write args + WriteOldValue(); + WriteNewValue(); + + WriteEndFunctionCall(); + + + // *** Local functions used to write OpCodes *** + // Local functions have access to function variables, no need to pass in args + + void WriteOldValue() + { + worker.Emit(OpCodes.Ldloc, oldValue); + } + + void WriteNewValue() + { + // write arg1 or this.field + if (newValue == null) + { + worker.Emit(OpCodes.Ldarg_1); + } + else + { + // this. + worker.Emit(OpCodes.Ldarg_0); + // syncvar.get + worker.Emit(OpCodes.Ldfld, newValue); + } + } + + // Writes this before method if it is not static + void WriteStartFunctionCall() + { + // don't add this (Ldarg_0) if method is static + if (!hookMethod.IsStatic) + { + // this before method call + // e.g. this.onValueChanged + worker.Emit(OpCodes.Ldarg_0); + } + } + + // Calls method + void WriteEndFunctionCall() + { + // only use Callvirt when not static + OpCode opcode = hookMethod.IsStatic ? OpCodes.Call : OpCodes.Callvirt; + worker.Emit(opcode, hookMethod); + } + } + } } diff --git a/MirrorWeaver/Weaver/Processors/TargetRpcProcessor.cs b/MirrorWeaver/Weaver/Processors/TargetRpcProcessor.cs index 0cd14640..831d3a5e 100644 --- a/MirrorWeaver/Weaver/Processors/TargetRpcProcessor.cs +++ b/MirrorWeaver/Weaver/Processors/TargetRpcProcessor.cs @@ -3,60 +3,61 @@ using Mono.Cecil.Cil; namespace Mirror.Weaver { - // Processes [TargetRpc] methods in NetworkBehaviour - public static class TargetRpcProcessor - { - // helper functions to check if the method has a NetworkConnection parameter - public static bool HasNetworkConnectionParameter(MethodDefinition md) => md.Parameters.Count > 0 && - md.Parameters[0].ParameterType.Is(); + // Processes [TargetRpc] methods in NetworkBehaviour + public static class TargetRpcProcessor + { + // helper functions to check if the method has a NetworkConnection parameter + public static bool HasNetworkConnectionParameter(MethodDefinition md) + { + return md.Parameters.Count > 0 && + md.Parameters[0].ParameterType.Is(); + } - public static MethodDefinition ProcessTargetRpcInvoke(WeaverTypes weaverTypes, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed) - { - var rpc = new MethodDefinition(Weaver.InvokeRpcPrefix + md.Name, MethodAttributes.Family | - MethodAttributes.Static | - MethodAttributes.HideBySig, - weaverTypes.Import(typeof(void))); + public static MethodDefinition ProcessTargetRpcInvoke(WeaverTypes weaverTypes, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed) + { + MethodDefinition rpc = new MethodDefinition(Weaver.InvokeRpcPrefix + md.Name, MethodAttributes.Family | + MethodAttributes.Static | + MethodAttributes.HideBySig, + weaverTypes.Import(typeof(void))); - var worker = rpc.Body.GetILProcessor(); - var label = worker.Create(OpCodes.Nop); + ILProcessor worker = rpc.Body.GetILProcessor(); + Instruction label = worker.Create(OpCodes.Nop); - NetworkBehaviourProcessor.WriteClientActiveCheck(worker, weaverTypes, md.Name, label, "TargetRPC"); + NetworkBehaviourProcessor.WriteClientActiveCheck(worker, weaverTypes, md.Name, label, "TargetRPC"); - // setup for reader - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Castclass, td); + // setup for reader + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Castclass, td); - // NetworkConnection parameter is optional - if (HasNetworkConnectionParameter(md)) - { - // on server, the NetworkConnection parameter is a connection to client. - // when the rpc is invoked on the client, it still has the same - // function signature. we pass in the connection to server, - // which is cleaner than just passing null) - //NetworkClient.readyconnection - // - // TODO - // a) .connectionToServer = best solution. no doubt. - // b) NetworkClient.connection for now. add TODO to not use static later. - worker.Emit(OpCodes.Call, weaverTypes.NetworkClientConnectionReference); - } + // NetworkConnection parameter is optional + if (HasNetworkConnectionParameter(md)) + { + // on server, the NetworkConnection parameter is a connection to client. + // when the rpc is invoked on the client, it still has the same + // function signature. we pass in the connection to server, + // which is cleaner than just passing null) + //NetworkClient.readyconnection + // + // TODO + // a) .connectionToServer = best solution. no doubt. + // b) NetworkClient.connection for now. add TODO to not use static later. + worker.Emit(OpCodes.Call, weaverTypes.NetworkClientConnectionReference); + } - // process reader parameters and skip first one if first one is NetworkConnection - if (!NetworkBehaviourProcessor.ReadArguments(md, readers, Log, worker, RemoteCallType.TargetRpc, ref WeavingFailed)) - { - return null; - } + // process reader parameters and skip first one if first one is NetworkConnection + if (!NetworkBehaviourProcessor.ReadArguments(md, readers, Log, worker, RemoteCallType.TargetRpc, ref WeavingFailed)) + return null; - // invoke actual command function - worker.Emit(OpCodes.Callvirt, rpcCallFunc); - worker.Emit(OpCodes.Ret); + // invoke actual command function + worker.Emit(OpCodes.Callvirt, rpcCallFunc); + worker.Emit(OpCodes.Ret); - NetworkBehaviourProcessor.AddInvokeParameters(weaverTypes, rpc.Parameters); - td.Methods.Add(rpc); - return rpc; - } + NetworkBehaviourProcessor.AddInvokeParameters(weaverTypes, rpc.Parameters); + td.Methods.Add(rpc); + return rpc; + } - /* generates code like: + /* generates code like: public void TargetTest (NetworkConnection conn, int param) { NetworkWriter writer = new NetworkWriter (); @@ -89,48 +90,46 @@ namespace Mirror.Weaver correctly in dependent assemblies */ - public static MethodDefinition ProcessTargetRpcCall(WeaverTypes weaverTypes, Writers writers, Logger Log, TypeDefinition td, MethodDefinition md, CustomAttribute targetRpcAttr, ref bool WeavingFailed) - { - var rpc = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); + public static MethodDefinition ProcessTargetRpcCall(WeaverTypes weaverTypes, Writers writers, Logger Log, TypeDefinition td, MethodDefinition md, CustomAttribute targetRpcAttr, ref bool WeavingFailed) + { + MethodDefinition rpc = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); - var worker = md.Body.GetILProcessor(); + ILProcessor worker = md.Body.GetILProcessor(); - NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); + NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); - NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); + NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); - // write all the arguments that the user passed to the TargetRpc call - // (skip first one if first one is NetworkConnection) - if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.TargetRpc, ref WeavingFailed)) - { - return null; - } + // write all the arguments that the user passed to the TargetRpc call + // (skip first one if first one is NetworkConnection) + if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.TargetRpc, ref WeavingFailed)) + return null; - // invoke SendInternal and return - // this - worker.Emit(OpCodes.Ldarg_0); - if (HasNetworkConnectionParameter(md)) - { - // connection - worker.Emit(OpCodes.Ldarg_1); - } - else - { - // null - worker.Emit(OpCodes.Ldnull); - } - // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions - worker.Emit(OpCodes.Ldstr, md.FullName); - // writer - worker.Emit(OpCodes.Ldloc_0); - worker.Emit(OpCodes.Ldc_I4, targetRpcAttr.GetField("channel", 0)); - worker.Emit(OpCodes.Callvirt, weaverTypes.sendTargetRpcInternal); + // invoke SendInternal and return + // this + worker.Emit(OpCodes.Ldarg_0); + if (HasNetworkConnectionParameter(md)) + { + // connection + worker.Emit(OpCodes.Ldarg_1); + } + else + { + // null + worker.Emit(OpCodes.Ldnull); + } + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + worker.Emit(OpCodes.Ldstr, md.FullName); + // writer + worker.Emit(OpCodes.Ldloc_0); + worker.Emit(OpCodes.Ldc_I4, targetRpcAttr.GetField("channel", 0)); + worker.Emit(OpCodes.Callvirt, weaverTypes.sendTargetRpcInternal); - NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); + NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); - worker.Emit(OpCodes.Ret); + worker.Emit(OpCodes.Ret); - return rpc; - } - } + return rpc; + } + } } diff --git a/MirrorWeaver/Weaver/Readers.cs b/MirrorWeaver/Weaver/Readers.cs index d1a886d2..789fa3b9 100644 --- a/MirrorWeaver/Weaver/Readers.cs +++ b/MirrorWeaver/Weaver/Readers.cs @@ -1,386 +1,383 @@ +using System; +using System.Collections.Generic; using Mono.Cecil; using Mono.Cecil.Cil; // to use Mono.Cecil.Rocks here, we need to 'override references' in the // Unity.Mirror.CodeGen assembly definition file in the Editor, and add CecilX.Rocks. // otherwise we get an unknown import exception. using Mono.Cecil.Rocks; -using System; -using System.Collections.Generic; namespace Mirror.Weaver { - // not static, because ILPostProcessor is multithreaded - public class Readers - { - // Readers are only for this assembly. - // can't be used from another assembly, otherwise we will get: - // "System.ArgumentException: Member ... is declared in another module and needs to be imported" - private readonly AssemblyDefinition assembly; - private readonly WeaverTypes weaverTypes; - private readonly TypeDefinition GeneratedCodeClass; - private readonly Logger Log; - private readonly Dictionary readFuncs = - new Dictionary(new TypeReferenceComparer()); + // not static, because ILPostProcessor is multithreaded + public class Readers + { + // Readers are only for this assembly. + // can't be used from another assembly, otherwise we will get: + // "System.ArgumentException: Member ... is declared in another module and needs to be imported" + AssemblyDefinition assembly; + WeaverTypes weaverTypes; + TypeDefinition GeneratedCodeClass; + Logger Log; - public Readers(AssemblyDefinition assembly, WeaverTypes weaverTypes, TypeDefinition GeneratedCodeClass, Logger Log) - { - this.assembly = assembly; - this.weaverTypes = weaverTypes; - this.GeneratedCodeClass = GeneratedCodeClass; - this.Log = Log; - } + Dictionary readFuncs = + new Dictionary(new TypeReferenceComparer()); - internal void Register(TypeReference dataType, MethodReference methodReference) - { - if (readFuncs.ContainsKey(dataType)) - { - // TODO enable this again later. - // Reader has some obsolete functions that were renamed. - // Don't want weaver warnings for all of them. - //Log.Warning($"Registering a Read method for {dataType.FullName} when one already exists", methodReference); - } + public Readers(AssemblyDefinition assembly, WeaverTypes weaverTypes, TypeDefinition GeneratedCodeClass, Logger Log) + { + this.assembly = assembly; + this.weaverTypes = weaverTypes; + this.GeneratedCodeClass = GeneratedCodeClass; + this.Log = Log; + } - // we need to import type when we Initialize Readers so import here in case it is used anywhere else - var imported = assembly.MainModule.ImportReference(dataType); - readFuncs[imported] = methodReference; - } + internal void Register(TypeReference dataType, MethodReference methodReference) + { + if (readFuncs.ContainsKey(dataType)) + { + // TODO enable this again later. + // Reader has some obsolete functions that were renamed. + // Don't want weaver warnings for all of them. + //Log.Warning($"Registering a Read method for {dataType.FullName} when one already exists", methodReference); + } - private void RegisterReadFunc(TypeReference typeReference, MethodDefinition newReaderFunc) - { - Register(typeReference, newReaderFunc); - GeneratedCodeClass.Methods.Add(newReaderFunc); - } + // we need to import type when we Initialize Readers so import here in case it is used anywhere else + TypeReference imported = assembly.MainModule.ImportReference(dataType); + readFuncs[imported] = methodReference; + } - // Finds existing reader for type, if non exists trys to create one - public MethodReference GetReadFunc(TypeReference variable, ref bool WeavingFailed) - { - if (readFuncs.TryGetValue(variable, out var foundFunc)) - { - return foundFunc; - } + void RegisterReadFunc(TypeReference typeReference, MethodDefinition newReaderFunc) + { + Register(typeReference, newReaderFunc); + GeneratedCodeClass.Methods.Add(newReaderFunc); + } - var importedVariable = assembly.MainModule.ImportReference(variable); - return GenerateReader(importedVariable, ref WeavingFailed); - } + // Finds existing reader for type, if non exists trys to create one + public MethodReference GetReadFunc(TypeReference variable, ref bool WeavingFailed) + { + if (readFuncs.TryGetValue(variable, out MethodReference foundFunc)) + return foundFunc; - private MethodReference GenerateReader(TypeReference variableReference, ref bool WeavingFailed) - { - // Arrays are special, if we resolve them, we get the element type, - // so the following ifs might choke on it for scriptable objects - // or other objects that require a custom serializer - // thus check if it is an array and skip all the checks. - if (variableReference.IsArray) - { - if (variableReference.IsMultidimensionalArray()) - { - Log.Error($"{variableReference.Name} is an unsupported type. Multidimensional arrays are not supported", variableReference); - WeavingFailed = true; - return null; - } + TypeReference importedVariable = assembly.MainModule.ImportReference(variable); + return GenerateReader(importedVariable, ref WeavingFailed); + } - return GenerateReadCollection(variableReference, variableReference.GetElementType(), nameof(NetworkReaderExtensions.ReadArray), ref WeavingFailed); - } + MethodReference GenerateReader(TypeReference variableReference, ref bool WeavingFailed) + { + // Arrays are special, if we resolve them, we get the element type, + // so the following ifs might choke on it for scriptable objects + // or other objects that require a custom serializer + // thus check if it is an array and skip all the checks. + if (variableReference.IsArray) + { + if (variableReference.IsMultidimensionalArray()) + { + Log.Error($"{variableReference.Name} is an unsupported type. Multidimensional arrays are not supported", variableReference); + WeavingFailed = true; + return null; + } - var variableDefinition = variableReference.Resolve(); + return GenerateReadCollection(variableReference, variableReference.GetElementType(), nameof(NetworkReaderExtensions.ReadArray), ref WeavingFailed); + } - // check if the type is completely invalid - if (variableDefinition == null) - { - Log.Error($"{variableReference.Name} is not a supported type", variableReference); - WeavingFailed = true; - return null; - } - else if (variableReference.IsByReference) - { - // error?? - Log.Error($"Cannot pass type {variableReference.Name} by reference", variableReference); - WeavingFailed = true; - return null; - } + TypeDefinition variableDefinition = variableReference.Resolve(); - // use existing func for known types - if (variableDefinition.IsEnum) - { - return GenerateEnumReadFunc(variableReference, ref WeavingFailed); - } - else if (variableDefinition.Is(typeof(ArraySegment<>))) - { - return GenerateArraySegmentReadFunc(variableReference, ref WeavingFailed); - } - else if (variableDefinition.Is(typeof(List<>))) - { - var genericInstance = (GenericInstanceType)variableReference; - var elementType = genericInstance.GenericArguments[0]; + // check if the type is completely invalid + if (variableDefinition == null) + { + Log.Error($"{variableReference.Name} is not a supported type", variableReference); + WeavingFailed = true; + return null; + } + else if (variableReference.IsByReference) + { + // error?? + Log.Error($"Cannot pass type {variableReference.Name} by reference", variableReference); + WeavingFailed = true; + return null; + } - return GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList), ref WeavingFailed); - } - else if (variableReference.IsDerivedFrom()) - { - return GetNetworkBehaviourReader(variableReference); - } + // use existing func for known types + if (variableDefinition.IsEnum) + { + return GenerateEnumReadFunc(variableReference, ref WeavingFailed); + } + else if (variableDefinition.Is(typeof(ArraySegment<>))) + { + return GenerateArraySegmentReadFunc(variableReference, ref WeavingFailed); + } + else if (variableDefinition.Is(typeof(List<>))) + { + GenericInstanceType genericInstance = (GenericInstanceType)variableReference; + TypeReference elementType = genericInstance.GenericArguments[0]; - // check if reader generation is applicable on this type - if (variableDefinition.IsDerivedFrom()) - { - Log.Error($"Cannot generate reader for component type {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - WeavingFailed = true; - return null; - } - if (variableReference.Is()) - { - Log.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - WeavingFailed = true; - return null; - } - if (variableReference.Is()) - { - Log.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - WeavingFailed = true; - return null; - } - if (variableDefinition.HasGenericParameters) - { - Log.Error($"Cannot generate reader for generic variable {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - WeavingFailed = true; - return null; - } - if (variableDefinition.IsInterface) - { - Log.Error($"Cannot generate reader for interface {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - WeavingFailed = true; - return null; - } - if (variableDefinition.IsAbstract) - { - Log.Error($"Cannot generate reader for abstract class {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - WeavingFailed = true; - return null; - } + return GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList), ref WeavingFailed); + } + else if (variableReference.IsDerivedFrom()) + { + return GetNetworkBehaviourReader(variableReference); + } - return GenerateClassOrStructReadFunction(variableReference, ref WeavingFailed); - } + // check if reader generation is applicable on this type + if (variableDefinition.IsDerivedFrom()) + { + Log.Error($"Cannot generate reader for component type {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + if (variableReference.Is()) + { + Log.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + if (variableReference.Is()) + { + Log.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + if (variableDefinition.HasGenericParameters) + { + Log.Error($"Cannot generate reader for generic variable {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + if (variableDefinition.IsInterface) + { + Log.Error($"Cannot generate reader for interface {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + if (variableDefinition.IsAbstract) + { + Log.Error($"Cannot generate reader for abstract class {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } - private MethodReference GetNetworkBehaviourReader(TypeReference variableReference) - { - // uses generic ReadNetworkBehaviour rather than having weaver create one for each NB - var generic = weaverTypes.readNetworkBehaviourGeneric; + return GenerateClassOrStructReadFunction(variableReference, ref WeavingFailed); + } - var readFunc = generic.MakeGeneric(assembly.MainModule, variableReference); + MethodReference GetNetworkBehaviourReader(TypeReference variableReference) + { + // uses generic ReadNetworkBehaviour rather than having weaver create one for each NB + MethodReference generic = weaverTypes.readNetworkBehaviourGeneric; - // register function so it is added to Reader - // use Register instead of RegisterWriteFunc because this is not a generated function - Register(variableReference, readFunc); + MethodReference readFunc = generic.MakeGeneric(assembly.MainModule, variableReference); - return readFunc; - } + // register function so it is added to Reader + // use Register instead of RegisterWriteFunc because this is not a generated function + Register(variableReference, readFunc); - private MethodDefinition GenerateEnumReadFunc(TypeReference variable, ref bool WeavingFailed) - { - var readerFunc = GenerateReaderFunction(variable); + return readFunc; + } - var worker = readerFunc.Body.GetILProcessor(); + MethodDefinition GenerateEnumReadFunc(TypeReference variable, ref bool WeavingFailed) + { + MethodDefinition readerFunc = GenerateReaderFunction(variable); - worker.Emit(OpCodes.Ldarg_0); + ILProcessor worker = readerFunc.Body.GetILProcessor(); - var underlyingType = variable.Resolve().GetEnumUnderlyingType(); - var underlyingFunc = GetReadFunc(underlyingType, ref WeavingFailed); + worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Call, underlyingFunc); - worker.Emit(OpCodes.Ret); - return readerFunc; - } + TypeReference underlyingType = variable.Resolve().GetEnumUnderlyingType(); + MethodReference underlyingFunc = GetReadFunc(underlyingType, ref WeavingFailed); - private MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable, ref bool WeavingFailed) - { - var genericInstance = (GenericInstanceType)variable; - var elementType = genericInstance.GenericArguments[0]; + worker.Emit(OpCodes.Call, underlyingFunc); + worker.Emit(OpCodes.Ret); + return readerFunc; + } - var readerFunc = GenerateReaderFunction(variable); + MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable, ref bool WeavingFailed) + { + GenericInstanceType genericInstance = (GenericInstanceType)variable; + TypeReference elementType = genericInstance.GenericArguments[0]; - var worker = readerFunc.Body.GetILProcessor(); + MethodDefinition readerFunc = GenerateReaderFunction(variable); - // $array = reader.Read<[T]>() - var arrayType = elementType.MakeArrayType(); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Call, GetReadFunc(arrayType, ref WeavingFailed)); + ILProcessor worker = readerFunc.Body.GetILProcessor(); - // return new ArraySegment($array); - worker.Emit(OpCodes.Newobj, weaverTypes.ArraySegmentConstructorReference.MakeHostInstanceGeneric(assembly.MainModule, genericInstance)); - worker.Emit(OpCodes.Ret); - return readerFunc; - } + // $array = reader.Read<[T]>() + ArrayType arrayType = elementType.MakeArrayType(); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Call, GetReadFunc(arrayType, ref WeavingFailed)); - private MethodDefinition GenerateReaderFunction(TypeReference variable) - { - var functionName = $"_Read_{variable.FullName}"; + // return new ArraySegment($array); + worker.Emit(OpCodes.Newobj, weaverTypes.ArraySegmentConstructorReference.MakeHostInstanceGeneric(assembly.MainModule, genericInstance)); + worker.Emit(OpCodes.Ret); + return readerFunc; + } - // create new reader for this type - var readerFunc = new MethodDefinition(functionName, - MethodAttributes.Public | - MethodAttributes.Static | - MethodAttributes.HideBySig, - variable); + MethodDefinition GenerateReaderFunction(TypeReference variable) + { + string functionName = $"_Read_{variable.FullName}"; - readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); - readerFunc.Body.InitLocals = true; - RegisterReadFunc(variable, readerFunc); + // create new reader for this type + MethodDefinition readerFunc = new MethodDefinition(functionName, + MethodAttributes.Public | + MethodAttributes.Static | + MethodAttributes.HideBySig, + variable); - return readerFunc; - } + readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); + readerFunc.Body.InitLocals = true; + RegisterReadFunc(variable, readerFunc); - private MethodDefinition GenerateReadCollection(TypeReference variable, TypeReference elementType, string readerFunction, ref bool WeavingFailed) - { - var readerFunc = GenerateReaderFunction(variable); - // generate readers for the element - GetReadFunc(elementType, ref WeavingFailed); + return readerFunc; + } - var module = assembly.MainModule; - var readerExtensions = module.ImportReference(typeof(NetworkReaderExtensions)); - var listReader = Resolvers.ResolveMethod(readerExtensions, assembly, Log, readerFunction, ref WeavingFailed); + MethodDefinition GenerateReadCollection(TypeReference variable, TypeReference elementType, string readerFunction, ref bool WeavingFailed) + { + MethodDefinition readerFunc = GenerateReaderFunction(variable); + // generate readers for the element + GetReadFunc(elementType, ref WeavingFailed); - var methodRef = new GenericInstanceMethod(listReader); - methodRef.GenericArguments.Add(elementType); + ModuleDefinition module = assembly.MainModule; + TypeReference readerExtensions = module.ImportReference(typeof(NetworkReaderExtensions)); + MethodReference listReader = Resolvers.ResolveMethod(readerExtensions, assembly, Log, readerFunction, ref WeavingFailed); - // generates - // return reader.ReadList(); + GenericInstanceMethod methodRef = new GenericInstanceMethod(listReader); + methodRef.GenericArguments.Add(elementType); - var worker = readerFunc.Body.GetILProcessor(); - worker.Emit(OpCodes.Ldarg_0); // reader - worker.Emit(OpCodes.Call, methodRef); // Read + // generates + // return reader.ReadList(); - worker.Emit(OpCodes.Ret); + ILProcessor worker = readerFunc.Body.GetILProcessor(); + worker.Emit(OpCodes.Ldarg_0); // reader + worker.Emit(OpCodes.Call, methodRef); // Read - return readerFunc; - } + worker.Emit(OpCodes.Ret); - private MethodDefinition GenerateClassOrStructReadFunction(TypeReference variable, ref bool WeavingFailed) - { - var readerFunc = GenerateReaderFunction(variable); + return readerFunc; + } - // create local for return value - readerFunc.Body.Variables.Add(new VariableDefinition(variable)); + MethodDefinition GenerateClassOrStructReadFunction(TypeReference variable, ref bool WeavingFailed) + { + MethodDefinition readerFunc = GenerateReaderFunction(variable); - var worker = readerFunc.Body.GetILProcessor(); + // create local for return value + readerFunc.Body.Variables.Add(new VariableDefinition(variable)); - var td = variable.Resolve(); + ILProcessor worker = readerFunc.Body.GetILProcessor(); - if (!td.IsValueType) - { - GenerateNullCheck(worker, ref WeavingFailed); - } + TypeDefinition td = variable.Resolve(); - CreateNew(variable, worker, td, ref WeavingFailed); - ReadAllFields(variable, worker, ref WeavingFailed); + if (!td.IsValueType) + GenerateNullCheck(worker, ref WeavingFailed); - worker.Emit(OpCodes.Ldloc_0); - worker.Emit(OpCodes.Ret); - return readerFunc; - } + CreateNew(variable, worker, td, ref WeavingFailed); + ReadAllFields(variable, worker, ref WeavingFailed); - private void GenerateNullCheck(ILProcessor worker, ref bool WeavingFailed) - { - // if (!reader.ReadBoolean()) { - // return null; - // } - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Call, GetReadFunc(weaverTypes.Import(), ref WeavingFailed)); + worker.Emit(OpCodes.Ldloc_0); + worker.Emit(OpCodes.Ret); + return readerFunc; + } - var labelEmptyArray = worker.Create(OpCodes.Nop); - worker.Emit(OpCodes.Brtrue, labelEmptyArray); - // return null - worker.Emit(OpCodes.Ldnull); - worker.Emit(OpCodes.Ret); - worker.Append(labelEmptyArray); - } + void GenerateNullCheck(ILProcessor worker, ref bool WeavingFailed) + { + // if (!reader.ReadBoolean()) { + // return null; + // } + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Call, GetReadFunc(weaverTypes.Import(), ref WeavingFailed)); - // Initialize the local variable with a new instance - private void CreateNew(TypeReference variable, ILProcessor worker, TypeDefinition td, ref bool WeavingFailed) - { - if (variable.IsValueType) - { - // structs are created with Initobj - worker.Emit(OpCodes.Ldloca, 0); - worker.Emit(OpCodes.Initobj, variable); - } - else if (td.IsDerivedFrom()) - { - var genericInstanceMethod = new GenericInstanceMethod(weaverTypes.ScriptableObjectCreateInstanceMethod); - genericInstanceMethod.GenericArguments.Add(variable); - worker.Emit(OpCodes.Call, genericInstanceMethod); - worker.Emit(OpCodes.Stloc_0); - } - else - { - // classes are created with their constructor - var ctor = Resolvers.ResolveDefaultPublicCtor(variable); - if (ctor == null) - { - Log.Error($"{variable.Name} can't be deserialized because it has no default constructor. Don't use {variable.Name} in [SyncVar]s, Rpcs, Cmds, etc.", variable); - WeavingFailed = true; - return; - } + Instruction labelEmptyArray = worker.Create(OpCodes.Nop); + worker.Emit(OpCodes.Brtrue, labelEmptyArray); + // return null + worker.Emit(OpCodes.Ldnull); + worker.Emit(OpCodes.Ret); + worker.Append(labelEmptyArray); + } - var ctorRef = assembly.MainModule.ImportReference(ctor); + // Initialize the local variable with a new instance + void CreateNew(TypeReference variable, ILProcessor worker, TypeDefinition td, ref bool WeavingFailed) + { + if (variable.IsValueType) + { + // structs are created with Initobj + worker.Emit(OpCodes.Ldloca, 0); + worker.Emit(OpCodes.Initobj, variable); + } + else if (td.IsDerivedFrom()) + { + GenericInstanceMethod genericInstanceMethod = new GenericInstanceMethod(weaverTypes.ScriptableObjectCreateInstanceMethod); + genericInstanceMethod.GenericArguments.Add(variable); + worker.Emit(OpCodes.Call, genericInstanceMethod); + worker.Emit(OpCodes.Stloc_0); + } + else + { + // classes are created with their constructor + MethodDefinition ctor = Resolvers.ResolveDefaultPublicCtor(variable); + if (ctor == null) + { + Log.Error($"{variable.Name} can't be deserialized because it has no default constructor. Don't use {variable.Name} in [SyncVar]s, Rpcs, Cmds, etc.", variable); + WeavingFailed = true; + return; + } - worker.Emit(OpCodes.Newobj, ctorRef); - worker.Emit(OpCodes.Stloc_0); - } - } + MethodReference ctorRef = assembly.MainModule.ImportReference(ctor); - private void ReadAllFields(TypeReference variable, ILProcessor worker, ref bool WeavingFailed) - { - foreach (var field in variable.FindAllPublicFields()) - { - // mismatched ldloca/ldloc for struct/class combinations is invalid IL, which causes crash at runtime - var opcode = variable.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc; - worker.Emit(opcode, 0); - var readFunc = GetReadFunc(field.FieldType, ref WeavingFailed); - if (readFunc != null) - { - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Call, readFunc); - } - else - { - Log.Error($"{field.Name} has an unsupported type", field); - WeavingFailed = true; - } - var fieldRef = assembly.MainModule.ImportReference(field); + worker.Emit(OpCodes.Newobj, ctorRef); + worker.Emit(OpCodes.Stloc_0); + } + } - worker.Emit(OpCodes.Stfld, fieldRef); - } - } + void ReadAllFields(TypeReference variable, ILProcessor worker, ref bool WeavingFailed) + { + foreach (FieldDefinition field in variable.FindAllPublicFields()) + { + // mismatched ldloca/ldloc for struct/class combinations is invalid IL, which causes crash at runtime + OpCode opcode = variable.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc; + worker.Emit(opcode, 0); + MethodReference readFunc = GetReadFunc(field.FieldType, ref WeavingFailed); + if (readFunc != null) + { + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Call, readFunc); + } + else + { + Log.Error($"{field.Name} has an unsupported type", field); + WeavingFailed = true; + } + FieldReference fieldRef = assembly.MainModule.ImportReference(field); - // Save a delegate for each one of the readers into Reader.read - internal void InitializeReaders(ILProcessor worker) - { - var module = assembly.MainModule; + worker.Emit(OpCodes.Stfld, fieldRef); + } + } - var genericReaderClassRef = module.ImportReference(typeof(Reader<>)); + // Save a delegate for each one of the readers into Reader.read + internal void InitializeReaders(ILProcessor worker) + { + ModuleDefinition module = assembly.MainModule; - var fieldInfo = typeof(Reader<>).GetField(nameof(Reader.read)); - var fieldRef = module.ImportReference(fieldInfo); - var networkReaderRef = module.ImportReference(typeof(NetworkReader)); - var funcRef = module.ImportReference(typeof(Func<,>)); - var funcConstructorRef = module.ImportReference(typeof(Func<,>).GetConstructors()[0]); + TypeReference genericReaderClassRef = module.ImportReference(typeof(Reader<>)); - foreach (var kvp in readFuncs) - { - var targetType = kvp.Key; - var readFunc = kvp.Value; + System.Reflection.FieldInfo fieldInfo = typeof(Reader<>).GetField(nameof(Reader.read)); + FieldReference fieldRef = module.ImportReference(fieldInfo); + TypeReference networkReaderRef = module.ImportReference(typeof(NetworkReader)); + TypeReference funcRef = module.ImportReference(typeof(Func<,>)); + MethodReference funcConstructorRef = module.ImportReference(typeof(Func<,>).GetConstructors()[0]); - // create a Func delegate - worker.Emit(OpCodes.Ldnull); - worker.Emit(OpCodes.Ldftn, readFunc); - var funcGenericInstance = funcRef.MakeGenericInstanceType(networkReaderRef, targetType); - var funcConstructorInstance = funcConstructorRef.MakeHostInstanceGeneric(assembly.MainModule, funcGenericInstance); - worker.Emit(OpCodes.Newobj, funcConstructorInstance); + foreach (KeyValuePair kvp in readFuncs) + { + TypeReference targetType = kvp.Key; + MethodReference readFunc = kvp.Value; - // save it in Reader.read - var genericInstance = genericReaderClassRef.MakeGenericInstanceType(targetType); - var specializedField = fieldRef.SpecializeField(assembly.MainModule, genericInstance); - worker.Emit(OpCodes.Stsfld, specializedField); - } - } - } + // create a Func delegate + worker.Emit(OpCodes.Ldnull); + worker.Emit(OpCodes.Ldftn, readFunc); + GenericInstanceType funcGenericInstance = funcRef.MakeGenericInstanceType(networkReaderRef, targetType); + MethodReference funcConstructorInstance = funcConstructorRef.MakeHostInstanceGeneric(assembly.MainModule, funcGenericInstance); + worker.Emit(OpCodes.Newobj, funcConstructorInstance); + + // save it in Reader.read + GenericInstanceType genericInstance = genericReaderClassRef.MakeGenericInstanceType(targetType); + FieldReference specializedField = fieldRef.SpecializeField(assembly.MainModule, genericInstance); + worker.Emit(OpCodes.Stsfld, specializedField); + } + } + } } diff --git a/MirrorWeaver/Weaver/Resolvers.cs b/MirrorWeaver/Weaver/Resolvers.cs index 85392cf4..ab3f72ad 100644 --- a/MirrorWeaver/Weaver/Resolvers.cs +++ b/MirrorWeaver/Weaver/Resolvers.cs @@ -8,82 +8,82 @@ using Mono.Cecil; namespace Mirror.Weaver { - public static class Resolvers - { - public static MethodReference ResolveMethod(TypeReference tr, AssemblyDefinition assembly, Logger Log, string name, ref bool WeavingFailed) - { - if (tr == null) - { - Log.Error($"Cannot resolve method {name} without a class"); - WeavingFailed = true; - return null; - } - var method = ResolveMethod(tr, assembly, Log, m => m.Name == name, ref WeavingFailed); - if (method == null) - { - Log.Error($"Method not found with name {name} in type {tr.Name}", tr); - WeavingFailed = true; - } - return method; - } + public static class Resolvers + { + public static MethodReference ResolveMethod(TypeReference tr, AssemblyDefinition assembly, Logger Log, string name, ref bool WeavingFailed) + { + if (tr == null) + { + Log.Error($"Cannot resolve method {name} without a class"); + WeavingFailed = true; + return null; + } + MethodReference method = ResolveMethod(tr, assembly, Log, m => m.Name == name, ref WeavingFailed); + if (method == null) + { + Log.Error($"Method not found with name {name} in type {tr.Name}", tr); + WeavingFailed = true; + } + return method; + } - public static MethodReference ResolveMethod(TypeReference t, AssemblyDefinition assembly, Logger Log, System.Func predicate, ref bool WeavingFailed) - { - foreach (var methodRef in t.Resolve().Methods) - { - if (predicate(methodRef)) - { - return assembly.MainModule.ImportReference(methodRef); - } - } + public static MethodReference ResolveMethod(TypeReference t, AssemblyDefinition assembly, Logger Log, System.Func predicate, ref bool WeavingFailed) + { + foreach (MethodDefinition methodRef in t.Resolve().Methods) + { + if (predicate(methodRef)) + { + return assembly.MainModule.ImportReference(methodRef); + } + } - Log.Error($"Method not found in type {t.Name}", t); - WeavingFailed = true; - return null; - } + Log.Error($"Method not found in type {t.Name}", t); + WeavingFailed = true; + return null; + } - public static MethodReference TryResolveMethodInParents(TypeReference tr, AssemblyDefinition assembly, string name) - { - if (tr == null) - { - return null; - } - foreach (var methodRef in tr.Resolve().Methods) - { - if (methodRef.Name == name) - { - return assembly.MainModule.ImportReference(methodRef); - } - } + public static MethodReference TryResolveMethodInParents(TypeReference tr, AssemblyDefinition assembly, string name) + { + if (tr == null) + { + return null; + } + foreach (MethodDefinition methodRef in tr.Resolve().Methods) + { + if (methodRef.Name == name) + { + return assembly.MainModule.ImportReference(methodRef); + } + } - // Could not find the method in this class, try the parent - return TryResolveMethodInParents(tr.Resolve().BaseType, assembly, name); - } + // Could not find the method in this class, try the parent + return TryResolveMethodInParents(tr.Resolve().BaseType, assembly, name); + } - public static MethodDefinition ResolveDefaultPublicCtor(TypeReference variable) - { - foreach (var methodRef in variable.Resolve().Methods) - { - if (methodRef.Name == ".ctor" && - methodRef.Resolve().IsPublic && - methodRef.Parameters.Count == 0) - { - return methodRef; - } - } - return null; - } + public static MethodDefinition ResolveDefaultPublicCtor(TypeReference variable) + { + foreach (MethodDefinition methodRef in variable.Resolve().Methods) + { + if (methodRef.Name == ".ctor" && + methodRef.Resolve().IsPublic && + methodRef.Parameters.Count == 0) + { + return methodRef; + } + } + return null; + } - public static MethodReference ResolveProperty(TypeReference tr, AssemblyDefinition assembly, string name) - { - foreach (var pd in tr.Resolve().Properties) - { - if (pd.Name == name) - { - return assembly.MainModule.ImportReference(pd.GetMethod); - } - } - return null; - } - } + public static MethodReference ResolveProperty(TypeReference tr, AssemblyDefinition assembly, string name) + { + foreach (PropertyDefinition pd in tr.Resolve().Properties) + { + if (pd.Name == name) + { + return assembly.MainModule.ImportReference(pd.GetMethod); + } + } + return null; + } + } } diff --git a/MirrorWeaver/Weaver/SyncVarAccessLists.cs b/MirrorWeaver/Weaver/SyncVarAccessLists.cs index 01b9f257..f531a5b0 100644 --- a/MirrorWeaver/Weaver/SyncVarAccessLists.cs +++ b/MirrorWeaver/Weaver/SyncVarAccessLists.cs @@ -1,29 +1,32 @@ // tracks SyncVar read/write access when processing NetworkBehaviour, // to later be replaced by SyncVarAccessReplacer. -using Mono.Cecil; using System.Collections.Generic; +using Mono.Cecil; namespace Mirror.Weaver { - // This data is flushed each time - if we are run multiple times in the same process/domain - public class SyncVarAccessLists - { - // setter functions that replace [SyncVar] member variable references. dict - public Dictionary replacementSetterProperties = - new Dictionary(); + // This data is flushed each time - if we are run multiple times in the same process/domain + public class SyncVarAccessLists + { + // setter functions that replace [SyncVar] member variable references. dict + public Dictionary replacementSetterProperties = + new Dictionary(); - // getter functions that replace [SyncVar] member variable references. dict - public Dictionary replacementGetterProperties = - new Dictionary(); + // getter functions that replace [SyncVar] member variable references. dict + public Dictionary replacementGetterProperties = + new Dictionary(); - // amount of SyncVars per class. dict - // necessary for SyncVar dirty bits, where inheriting classes start - // their dirty bits at base class SyncVar amount. - public Dictionary numSyncVars = new Dictionary(); + // amount of SyncVars per class. dict + // necessary for SyncVar dirty bits, where inheriting classes start + // their dirty bits at base class SyncVar amount. + public Dictionary numSyncVars = new Dictionary(); - public int GetSyncVarStart(string className) => - numSyncVars.TryGetValue(className, out var value) ? value : 0; + public int GetSyncVarStart(string className) => + numSyncVars.TryGetValue(className, out int value) ? value : 0; - public void SetNumSyncVars(string className, int num) => numSyncVars[className] = num; - } + public void SetNumSyncVars(string className, int num) + { + numSyncVars[className] = num; + } + } } diff --git a/MirrorWeaver/Weaver/TypeReferenceComparer.cs b/MirrorWeaver/Weaver/TypeReferenceComparer.cs index 10102bd9..e8c224c7 100644 --- a/MirrorWeaver/Weaver/TypeReferenceComparer.cs +++ b/MirrorWeaver/Weaver/TypeReferenceComparer.cs @@ -1,15 +1,15 @@ -using Mono.Cecil; using System.Collections.Generic; +using Mono.Cecil; namespace Mirror.Weaver { - // Compares TypeReference using FullName - public class TypeReferenceComparer : IEqualityComparer - { - public bool Equals(TypeReference x, TypeReference y) => - x.FullName == y.FullName; + // Compares TypeReference using FullName + public class TypeReferenceComparer : IEqualityComparer + { + public bool Equals(TypeReference x, TypeReference y) => + x.FullName == y.FullName; - public int GetHashCode(TypeReference obj) => - obj.FullName.GetHashCode(); - } + public int GetHashCode(TypeReference obj) => + obj.FullName.GetHashCode(); + } } diff --git a/MirrorWeaver/Weaver/Weaver.cs b/MirrorWeaver/Weaver/Weaver.cs index 339e2b3c..0b115205 100644 --- a/MirrorWeaver/Weaver/Weaver.cs +++ b/MirrorWeaver/Weaver/Weaver.cs @@ -1,213 +1,214 @@ using MirrorWeaver; -using Mono.Cecil; using System; using System.Collections.Generic; using System.Diagnostics; +using Mono.Cecil; namespace Mirror.Weaver { - // not static, because ILPostProcessor is multithreaded - internal class Weaver - { - public const string InvokeRpcPrefix = "InvokeUserCode_"; + // not static, because ILPostProcessor is multithreaded + internal class Weaver + { + public const string InvokeRpcPrefix = "InvokeUserCode_"; - // generated code class - public const string GeneratedCodeNamespace = "Mirror"; - public const string GeneratedCodeClassName = "GeneratedNetworkCode"; - private TypeDefinition GeneratedCodeClass; + // generated code class + public const string GeneratedCodeNamespace = "Mirror"; + public const string GeneratedCodeClassName = "GeneratedNetworkCode"; + TypeDefinition GeneratedCodeClass; - // for resolving Mirror.dll in ReaderWriterProcessor, we need to know - // Mirror.dll name - public const string MirrorAssemblyName = "Mirror"; - private WeaverTypes weaverTypes; - private SyncVarAccessLists syncVarAccessLists; - private AssemblyDefinition CurrentAssembly; - private Writers writers; - private Readers readers; - private bool WeavingFailed; + // for resolving Mirror.dll in ReaderWriterProcessor, we need to know + // Mirror.dll name + public const string MirrorAssemblyName = "Mirror"; - // logger functions can be set from the outside. - // for example, Debug.Log or ILPostProcessor Diagnostics log for - // multi threaded logging. - public Logger Log; + WeaverTypes weaverTypes; + SyncVarAccessLists syncVarAccessLists; + AssemblyDefinition CurrentAssembly; + Writers writers; + Readers readers; + bool WeavingFailed; - public Weaver(Logger Log) => this.Log = Log; + // logger functions can be set from the outside. + // for example, Debug.Log or ILPostProcessor Diagnostics log for + // multi threaded logging. + public Logger Log; - // returns 'true' if modified (=if we did anything) - private bool WeaveNetworkBehavior(TypeDefinition td) - { - if (!td.IsClass) - { - return false; - } + public Weaver(Logger Log) + { + this.Log = Log; + } - if (!td.IsDerivedFrom()) - { - if (td.IsDerivedFrom()) - { - MonoBehaviourProcessor.Process(Log, td, ref WeavingFailed); - } + // returns 'true' if modified (=if we did anything) + bool WeaveNetworkBehavior(TypeDefinition td) + { + if (!td.IsClass) + return false; - return false; - } + if (!td.IsDerivedFrom()) + { + if (td.IsDerivedFrom()) + MonoBehaviourProcessor.Process(Log, td, ref WeavingFailed); + return false; + } - // process this and base classes from parent to child order + // process this and base classes from parent to child order - var behaviourClasses = new List(); + List behaviourClasses = new List(); - var parent = td; - while (parent != null) - { - if (parent.Is()) - { - break; - } + TypeDefinition parent = td; + while (parent != null) + { + if (parent.Is()) + { + break; + } - try - { - behaviourClasses.Insert(0, parent); - parent = parent.BaseType.Resolve(); - } - catch (AssemblyResolutionException) - { - // this can happen for plugins. - //Console.WriteLine("AssemblyResolutionException: "+ ex.ToString()); - break; - } - } + try + { + behaviourClasses.Insert(0, parent); + parent = parent.BaseType.Resolve(); + } + catch (AssemblyResolutionException) + { + // this can happen for plugins. + //Console.WriteLine("AssemblyResolutionException: "+ ex.ToString()); + break; + } + } - var modified = false; - foreach (var behaviour in behaviourClasses) - { - modified |= new NetworkBehaviourProcessor(CurrentAssembly, weaverTypes, syncVarAccessLists, writers, readers, Log, behaviour).Process(ref WeavingFailed); - } - return modified; - } + bool modified = false; + foreach (TypeDefinition behaviour in behaviourClasses) + { + modified |= new NetworkBehaviourProcessor(CurrentAssembly, weaverTypes, syncVarAccessLists, writers, readers, Log, behaviour).Process(ref WeavingFailed); + } + return modified; + } - private bool WeaveModule(ModuleDefinition moduleDefinition) - { - var modified = false; + bool WeaveModule(ModuleDefinition moduleDefinition) + { + bool modified = false; - var watch = Stopwatch.StartNew(); - watch.Start(); + Stopwatch watch = Stopwatch.StartNew(); + watch.Start(); - foreach (var td in moduleDefinition.Types) - { - if (td.IsClass && td.BaseType.CanBeResolved()) - { - modified |= WeaveNetworkBehavior(td); - modified |= ServerClientAttributeProcessor.Process(weaverTypes, Log, td, ref WeavingFailed); - } - } + foreach (TypeDefinition td in moduleDefinition.Types) + { + if (td.IsClass && td.BaseType.CanBeResolved()) + { + modified |= WeaveNetworkBehavior(td); + modified |= ServerClientAttributeProcessor.Process(weaverTypes, Log, td, ref WeavingFailed); + } + } - watch.Stop(); - Console.WriteLine($"Weave behaviours and messages took {watch.ElapsedMilliseconds} milliseconds"); + watch.Stop(); + Console.WriteLine($"Weave behaviours and messages took {watch.ElapsedMilliseconds} milliseconds"); - return modified; - } + return modified; + } - private void CreateGeneratedCodeClass() => - // create "Mirror.GeneratedNetworkCode" class which holds all - // Readers and Writers - GeneratedCodeClass = new TypeDefinition(GeneratedCodeNamespace, GeneratedCodeClassName, - TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass | TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Abstract | TypeAttributes.Sealed, - weaverTypes.Import()); + void CreateGeneratedCodeClass() + { + // create "Mirror.GeneratedNetworkCode" class which holds all + // Readers and Writers + GeneratedCodeClass = new TypeDefinition(GeneratedCodeNamespace, GeneratedCodeClassName, + TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass | TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Abstract | TypeAttributes.Sealed, + weaverTypes.Import()); + } - // Weave takes an AssemblyDefinition to be compatible with both old and - // new weavers: - // * old takes a filepath, new takes a in-memory byte[] - // * old uses DefaultAssemblyResolver with added dependencies paths, - // new uses ...? - // - // => assembly: the one we are currently weaving (MyGame.dll) - // => resolver: useful in case we need to resolve any of the assembly's - // assembly.MainModule.AssemblyReferences. - // -> we can resolve ANY of them given that the resolver - // works properly (need custom one for ILPostProcessor) - // -> IMPORTANT: .Resolve() takes an AssemblyNameReference. - // those from assembly.MainModule.AssemblyReferences are - // guaranteed to be resolve-able. - // Parsing from a string for Library/.../Mirror.dll - // would not be guaranteed to be resolve-able because - // for ILPostProcessor we can't assume where Mirror.dll - // is etc. - public bool Weave(AssemblyDefinition assembly, IAssemblyResolver resolver, out bool modified) - { - WeavingFailed = false; - modified = false; - try - { - CurrentAssembly = assembly; + // Weave takes an AssemblyDefinition to be compatible with both old and + // new weavers: + // * old takes a filepath, new takes a in-memory byte[] + // * old uses DefaultAssemblyResolver with added dependencies paths, + // new uses ...? + // + // => assembly: the one we are currently weaving (MyGame.dll) + // => resolver: useful in case we need to resolve any of the assembly's + // assembly.MainModule.AssemblyReferences. + // -> we can resolve ANY of them given that the resolver + // works properly (need custom one for ILPostProcessor) + // -> IMPORTANT: .Resolve() takes an AssemblyNameReference. + // those from assembly.MainModule.AssemblyReferences are + // guaranteed to be resolve-able. + // Parsing from a string for Library/.../Mirror.dll + // would not be guaranteed to be resolve-able because + // for ILPostProcessor we can't assume where Mirror.dll + // is etc. + public bool Weave(AssemblyDefinition assembly, IAssemblyResolver resolver, out bool modified) + { + WeavingFailed = false; + modified = false; + try + { + CurrentAssembly = assembly; - // fix "No writer found for ..." error - // https://github.com/vis2k/Mirror/issues/2579 - // -> when restarting Unity, weaver would try to weave a DLL - // again - // -> resulting in two GeneratedNetworkCode classes (see ILSpy) - // -> the second one wouldn't have all the writer types setup - if (CurrentAssembly.MainModule.ContainsClass(GeneratedCodeNamespace, GeneratedCodeClassName)) - { - //Log.Warning($"Weaver: skipping {CurrentAssembly.Name} because already weaved"); - return true; - } + // fix "No writer found for ..." error + // https://github.com/vis2k/Mirror/issues/2579 + // -> when restarting Unity, weaver would try to weave a DLL + // again + // -> resulting in two GeneratedNetworkCode classes (see ILSpy) + // -> the second one wouldn't have all the writer types setup + if (CurrentAssembly.MainModule.ContainsClass(GeneratedCodeNamespace, GeneratedCodeClassName)) + { + //Log.Warning($"Weaver: skipping {CurrentAssembly.Name} because already weaved"); + return true; + } - weaverTypes = new WeaverTypes(CurrentAssembly, Log, ref WeavingFailed); + weaverTypes = new WeaverTypes(CurrentAssembly, Log, ref WeavingFailed); - // weaverTypes are needed for CreateGeneratedCodeClass - CreateGeneratedCodeClass(); + // weaverTypes are needed for CreateGeneratedCodeClass + CreateGeneratedCodeClass(); - // WeaverList depends on WeaverTypes setup because it uses Import - syncVarAccessLists = new SyncVarAccessLists(); + // WeaverList depends on WeaverTypes setup because it uses Import + syncVarAccessLists = new SyncVarAccessLists(); - // initialize readers & writers with this assembly. - // we need to do this in every Process() call. - // otherwise we would get - // "System.ArgumentException: Member ... is declared in another module and needs to be imported" - // errors when still using the previous module's reader/writer funcs. - writers = new Writers(CurrentAssembly, weaverTypes, GeneratedCodeClass, Log); - readers = new Readers(CurrentAssembly, weaverTypes, GeneratedCodeClass, Log); + // initialize readers & writers with this assembly. + // we need to do this in every Process() call. + // otherwise we would get + // "System.ArgumentException: Member ... is declared in another module and needs to be imported" + // errors when still using the previous module's reader/writer funcs. + writers = new Writers(CurrentAssembly, weaverTypes, GeneratedCodeClass, Log); + readers = new Readers(CurrentAssembly, weaverTypes, GeneratedCodeClass, Log); - var rwstopwatch = Stopwatch.StartNew(); - // Need to track modified from ReaderWriterProcessor too because it could find custom read/write functions or create functions for NetworkMessages - modified = ReaderWriterProcessor.Process(CurrentAssembly, resolver, Log, writers, readers, ref WeavingFailed); - QSBReaderWriterProcessor.Process(CurrentAssembly, writers, readers, ref WeavingFailed); - rwstopwatch.Stop(); - Console.WriteLine($"Find all reader and writers took {rwstopwatch.ElapsedMilliseconds} milliseconds"); + Stopwatch rwstopwatch = Stopwatch.StartNew(); + // Need to track modified from ReaderWriterProcessor too because it could find custom read/write functions or create functions for NetworkMessages + modified = ReaderWriterProcessor.Process(CurrentAssembly, resolver, Log, writers, readers, ref WeavingFailed); + QSBReaderWriterProcessor.Process(CurrentAssembly, writers, readers, ref WeavingFailed); + rwstopwatch.Stop(); + Console.WriteLine($"Find all reader and writers took {rwstopwatch.ElapsedMilliseconds} milliseconds"); - var moduleDefinition = CurrentAssembly.MainModule; - Console.WriteLine($"Script Module: {moduleDefinition.Name}"); + ModuleDefinition moduleDefinition = CurrentAssembly.MainModule; + Console.WriteLine($"Script Module: {moduleDefinition.Name}"); - modified |= WeaveModule(moduleDefinition); + modified |= WeaveModule(moduleDefinition); - if (WeavingFailed) - { - return false; - } + if (WeavingFailed) + { + return false; + } - if (modified) - { - SyncVarAttributeAccessReplacer.Process(moduleDefinition, syncVarAccessLists); + if (modified) + { + SyncVarAttributeAccessReplacer.Process(moduleDefinition, syncVarAccessLists); - // add class that holds read/write functions - moduleDefinition.Types.Add(GeneratedCodeClass); + // add class that holds read/write functions + moduleDefinition.Types.Add(GeneratedCodeClass); - ReaderWriterProcessor.InitializeReaderAndWriters(CurrentAssembly, weaverTypes, writers, readers, GeneratedCodeClass); + ReaderWriterProcessor.InitializeReaderAndWriters(CurrentAssembly, weaverTypes, writers, readers, GeneratedCodeClass); - // DO NOT WRITE here. - // CompilationFinishedHook writes to the file. - // ILPostProcessor writes to in-memory assembly. - // it depends on the caller. - //CurrentAssembly.Write(new WriterParameters{ WriteSymbols = true }); - } + // DO NOT WRITE here. + // CompilationFinishedHook writes to the file. + // ILPostProcessor writes to in-memory assembly. + // it depends on the caller. + //CurrentAssembly.Write(new WriterParameters{ WriteSymbols = true }); + } - return true; - } - catch (Exception e) - { - Log.Error($"Exception :{e}"); - WeavingFailed = true; - return false; - } - } - } + return true; + } + catch (Exception e) + { + Log.Error($"Exception :{e}"); + WeavingFailed = true; + return false; + } + } + } } diff --git a/MirrorWeaver/Weaver/WeaverExceptions.cs b/MirrorWeaver/Weaver/WeaverExceptions.cs index 66e11518..421775a7 100644 --- a/MirrorWeaver/Weaver/WeaverExceptions.cs +++ b/MirrorWeaver/Weaver/WeaverExceptions.cs @@ -1,23 +1,26 @@ -using Mono.Cecil; using System; using System.Runtime.Serialization; +using Mono.Cecil; namespace Mirror.Weaver { - [Serializable] - public abstract class WeaverException : Exception - { - public MemberReference MemberReference { get; } + [Serializable] + public abstract class WeaverException : Exception + { + public MemberReference MemberReference { get; } - protected WeaverException(string message, MemberReference member) : base(message) => MemberReference = member; + protected WeaverException(string message, MemberReference member) : base(message) + { + MemberReference = member; + } - protected WeaverException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } - } + protected WeaverException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) {} + } - [Serializable] - public class GenerateWriterException : WeaverException - { - public GenerateWriterException(string message, MemberReference member) : base(message, member) { } - protected GenerateWriterException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } - } + [Serializable] + public class GenerateWriterException : WeaverException + { + public GenerateWriterException(string message, MemberReference member) : base(message, member) {} + protected GenerateWriterException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) {} + } } diff --git a/MirrorWeaver/Weaver/WeaverTypes.cs b/MirrorWeaver/Weaver/WeaverTypes.cs index 8cd576a9..c166f605 100644 --- a/MirrorWeaver/Weaver/WeaverTypes.cs +++ b/MirrorWeaver/Weaver/WeaverTypes.cs @@ -1,150 +1,152 @@ -using Mono.Cecil; using System; +using Mono.Cecil; +using UnityEditor; using UnityEngine; namespace Mirror.Weaver { - // not static, because ILPostProcessor is multithreaded - public class WeaverTypes - { - public MethodReference ScriptableObjectCreateInstanceMethod; + // not static, because ILPostProcessor is multithreaded + public class WeaverTypes + { + public MethodReference ScriptableObjectCreateInstanceMethod; - public MethodReference NetworkBehaviourDirtyBitsReference; - public MethodReference GetPooledWriterReference; - public MethodReference RecycleWriterReference; + public MethodReference NetworkBehaviourDirtyBitsReference; + public MethodReference GetPooledWriterReference; + public MethodReference RecycleWriterReference; - public MethodReference NetworkClientConnectionReference; + public MethodReference NetworkClientConnectionReference; - public MethodReference RemoteCallDelegateConstructor; + public MethodReference RemoteCallDelegateConstructor; - public MethodReference NetworkServerGetActive; - public MethodReference NetworkServerGetLocalClientActive; - public MethodReference NetworkClientGetActive; + public MethodReference NetworkServerGetActive; + public MethodReference NetworkServerGetLocalClientActive; + public MethodReference NetworkClientGetActive; - // custom attribute types - public MethodReference InitSyncObjectReference; + // custom attribute types + public MethodReference InitSyncObjectReference; - // array segment - public MethodReference ArraySegmentConstructorReference; + // array segment + public MethodReference ArraySegmentConstructorReference; - // syncvar - public MethodReference syncVarEqualReference; - public MethodReference syncVarNetworkIdentityEqualReference; - public MethodReference syncVarGameObjectEqualReference; - public MethodReference setSyncVarReference; - public MethodReference setSyncVarHookGuard; - public MethodReference getSyncVarHookGuard; - public MethodReference setSyncVarGameObjectReference; - public MethodReference getSyncVarGameObjectReference; - public MethodReference setSyncVarNetworkIdentityReference; - public MethodReference getSyncVarNetworkIdentityReference; - public MethodReference syncVarNetworkBehaviourEqualReference; - public MethodReference setSyncVarNetworkBehaviourReference; - public MethodReference getSyncVarNetworkBehaviourReference; - public MethodReference registerCommandReference; - public MethodReference registerRpcReference; - public MethodReference getTypeFromHandleReference; - public MethodReference logErrorReference; - public MethodReference logWarningReference; - public MethodReference sendCommandInternal; - public MethodReference sendRpcInternal; - public MethodReference sendTargetRpcInternal; + // syncvar + public MethodReference syncVarEqualReference; + public MethodReference syncVarNetworkIdentityEqualReference; + public MethodReference syncVarGameObjectEqualReference; + public MethodReference setSyncVarReference; + public MethodReference setSyncVarHookGuard; + public MethodReference getSyncVarHookGuard; + public MethodReference setSyncVarGameObjectReference; + public MethodReference getSyncVarGameObjectReference; + public MethodReference setSyncVarNetworkIdentityReference; + public MethodReference getSyncVarNetworkIdentityReference; + public MethodReference syncVarNetworkBehaviourEqualReference; + public MethodReference setSyncVarNetworkBehaviourReference; + public MethodReference getSyncVarNetworkBehaviourReference; + public MethodReference registerCommandReference; + public MethodReference registerRpcReference; + public MethodReference getTypeFromHandleReference; + public MethodReference logErrorReference; + public MethodReference logWarningReference; + public MethodReference sendCommandInternal; + public MethodReference sendRpcInternal; + public MethodReference sendTargetRpcInternal; - public MethodReference readNetworkBehaviourGeneric; + public MethodReference readNetworkBehaviourGeneric; - // attributes - public TypeDefinition initializeOnLoadMethodAttribute; - public TypeDefinition runtimeInitializeOnLoadMethodAttribute; - private readonly AssemblyDefinition assembly; + // attributes + public TypeDefinition initializeOnLoadMethodAttribute; + public TypeDefinition runtimeInitializeOnLoadMethodAttribute; - public TypeReference Import() => Import(typeof(T)); + AssemblyDefinition assembly; - public TypeReference Import(Type t) => assembly.MainModule.ImportReference(t); + public TypeReference Import() => Import(typeof(T)); - // constructor resolves the types and stores them in fields - public WeaverTypes(AssemblyDefinition assembly, Logger Log, ref bool WeavingFailed) - { - // system types - this.assembly = assembly; + public TypeReference Import(Type t) => assembly.MainModule.ImportReference(t); - var ArraySegmentType = Import(typeof(ArraySegment<>)); - ArraySegmentConstructorReference = Resolvers.ResolveMethod(ArraySegmentType, assembly, Log, ".ctor", ref WeavingFailed); + // constructor resolves the types and stores them in fields + public WeaverTypes(AssemblyDefinition assembly, Logger Log, ref bool WeavingFailed) + { + // system types + this.assembly = assembly; - var NetworkServerType = Import(typeof(NetworkServer)); - NetworkServerGetActive = Resolvers.ResolveMethod(NetworkServerType, assembly, Log, "get_active", ref WeavingFailed); - NetworkServerGetLocalClientActive = Resolvers.ResolveMethod(NetworkServerType, assembly, Log, "get_localClientActive", ref WeavingFailed); - var NetworkClientType = Import(typeof(NetworkClient)); - NetworkClientGetActive = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_active", ref WeavingFailed); + TypeReference ArraySegmentType = Import(typeof(ArraySegment<>)); + ArraySegmentConstructorReference = Resolvers.ResolveMethod(ArraySegmentType, assembly, Log, ".ctor", ref WeavingFailed); - var RemoteCallDelegateType = Import(); - RemoteCallDelegateConstructor = Resolvers.ResolveMethod(RemoteCallDelegateType, assembly, Log, ".ctor", ref WeavingFailed); + TypeReference NetworkServerType = Import(typeof(NetworkServer)); + NetworkServerGetActive = Resolvers.ResolveMethod(NetworkServerType, assembly, Log, "get_active", ref WeavingFailed); + NetworkServerGetLocalClientActive = Resolvers.ResolveMethod(NetworkServerType, assembly, Log, "get_localClientActive", ref WeavingFailed); + TypeReference NetworkClientType = Import(typeof(NetworkClient)); + NetworkClientGetActive = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_active", ref WeavingFailed); - var NetworkBehaviourType = Import(); - var RemoteProcedureCallsType = Import(typeof(RemoteCalls.RemoteProcedureCalls)); + TypeReference RemoteCallDelegateType = Import(); + RemoteCallDelegateConstructor = Resolvers.ResolveMethod(RemoteCallDelegateType, assembly, Log, ".ctor", ref WeavingFailed); - var ScriptableObjectType = Import(); + TypeReference NetworkBehaviourType = Import(); + TypeReference RemoteProcedureCallsType = Import(typeof(RemoteCalls.RemoteProcedureCalls)); - ScriptableObjectCreateInstanceMethod = Resolvers.ResolveMethod( - ScriptableObjectType, assembly, Log, - md => md.Name == "CreateInstance" && md.HasGenericParameters, - ref WeavingFailed); + TypeReference ScriptableObjectType = Import(); - NetworkBehaviourDirtyBitsReference = Resolvers.ResolveProperty(NetworkBehaviourType, assembly, "syncVarDirtyBits"); - var NetworkWriterPoolType = Import(typeof(NetworkWriterPool)); - GetPooledWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "GetWriter", ref WeavingFailed); - RecycleWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "Recycle", ref WeavingFailed); + ScriptableObjectCreateInstanceMethod = Resolvers.ResolveMethod( + ScriptableObjectType, assembly, Log, + md => md.Name == "CreateInstance" && md.HasGenericParameters, + ref WeavingFailed); - NetworkClientConnectionReference = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_connection", ref WeavingFailed); + NetworkBehaviourDirtyBitsReference = Resolvers.ResolveProperty(NetworkBehaviourType, assembly, "syncVarDirtyBits"); + TypeReference NetworkWriterPoolType = Import(typeof(NetworkWriterPool)); + GetPooledWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "GetWriter", ref WeavingFailed); + RecycleWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "Recycle", ref WeavingFailed); - syncVarEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SyncVarEqual", ref WeavingFailed); - syncVarNetworkIdentityEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SyncVarNetworkIdentityEqual", ref WeavingFailed); - syncVarGameObjectEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SyncVarGameObjectEqual", ref WeavingFailed); - setSyncVarReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SetSyncVar", ref WeavingFailed); - setSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SetSyncVarHookGuard", ref WeavingFailed); - getSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarHookGuard", ref WeavingFailed); + NetworkClientConnectionReference = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_connection", ref WeavingFailed); - setSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SetSyncVarGameObject", ref WeavingFailed); - getSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarGameObject", ref WeavingFailed); - setSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SetSyncVarNetworkIdentity", ref WeavingFailed); - getSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkIdentity", ref WeavingFailed); - syncVarNetworkBehaviourEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SyncVarNetworkBehaviourEqual", ref WeavingFailed); - setSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SetSyncVarNetworkBehaviour", ref WeavingFailed); - getSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkBehaviour", ref WeavingFailed); + syncVarEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SyncVarEqual", ref WeavingFailed); + syncVarNetworkIdentityEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SyncVarNetworkIdentityEqual", ref WeavingFailed); + syncVarGameObjectEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SyncVarGameObjectEqual", ref WeavingFailed); + setSyncVarReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SetSyncVar", ref WeavingFailed); + setSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SetSyncVarHookGuard", ref WeavingFailed); + getSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarHookGuard", ref WeavingFailed); - registerCommandReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterCommand", ref WeavingFailed); - registerRpcReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterRpc", ref WeavingFailed); + setSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SetSyncVarGameObject", ref WeavingFailed); + getSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarGameObject", ref WeavingFailed); + setSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SetSyncVarNetworkIdentity", ref WeavingFailed); + getSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkIdentity", ref WeavingFailed); + syncVarNetworkBehaviourEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SyncVarNetworkBehaviourEqual", ref WeavingFailed); + setSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SetSyncVarNetworkBehaviour", ref WeavingFailed); + getSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkBehaviour", ref WeavingFailed); - var unityDebug = Import(typeof(UnityEngine.Debug)); - // these have multiple methods with same name, so need to check parameters too - logErrorReference = Resolvers.ResolveMethod(unityDebug, assembly, Log, md => - md.Name == "LogError" && - md.Parameters.Count == 1 && - md.Parameters[0].ParameterType.FullName == typeof(object).FullName, - ref WeavingFailed); + registerCommandReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterCommand", ref WeavingFailed); + registerRpcReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterRpc", ref WeavingFailed); - logWarningReference = Resolvers.ResolveMethod(unityDebug, assembly, Log, md => - md.Name == "LogWarning" && - md.Parameters.Count == 1 && - md.Parameters[0].ParameterType.FullName == typeof(object).FullName, - ref WeavingFailed); + TypeReference unityDebug = Import(typeof(UnityEngine.Debug)); + // these have multiple methods with same name, so need to check parameters too + logErrorReference = Resolvers.ResolveMethod(unityDebug, assembly, Log, md => + md.Name == "LogError" && + md.Parameters.Count == 1 && + md.Parameters[0].ParameterType.FullName == typeof(object).FullName, + ref WeavingFailed); - var typeType = Import(typeof(Type)); - getTypeFromHandleReference = Resolvers.ResolveMethod(typeType, assembly, Log, "GetTypeFromHandle", ref WeavingFailed); - sendCommandInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendCommandInternal", ref WeavingFailed); - sendRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendRPCInternal", ref WeavingFailed); - sendTargetRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendTargetRPCInternal", ref WeavingFailed); + logWarningReference = Resolvers.ResolveMethod(unityDebug, assembly, Log, md => + md.Name == "LogWarning" && + md.Parameters.Count == 1 && + md.Parameters[0].ParameterType.FullName == typeof(object).FullName, + ref WeavingFailed); - InitSyncObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "InitSyncObject", ref WeavingFailed); + TypeReference typeType = Import(typeof(Type)); + getTypeFromHandleReference = Resolvers.ResolveMethod(typeType, assembly, Log, "GetTypeFromHandle", ref WeavingFailed); + sendCommandInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendCommandInternal", ref WeavingFailed); + sendRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendRPCInternal", ref WeavingFailed); + sendTargetRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendTargetRPCInternal", ref WeavingFailed); - var readerExtensions = Import(typeof(NetworkReaderExtensions)); - readNetworkBehaviourGeneric = Resolvers.ResolveMethod(readerExtensions, assembly, Log, (md => - { - return md.Name == nameof(NetworkReaderExtensions.ReadNetworkBehaviour) && - md.HasGenericParameters; - }), - ref WeavingFailed); + InitSyncObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "InitSyncObject", ref WeavingFailed); - /* + TypeReference readerExtensions = Import(typeof(NetworkReaderExtensions)); + readNetworkBehaviourGeneric = Resolvers.ResolveMethod(readerExtensions, assembly, Log, (md => + { + return md.Name == nameof(NetworkReaderExtensions.ReadNetworkBehaviour) && + md.HasGenericParameters; + }), + ref WeavingFailed); + + /* // [InitializeOnLoadMethod] // 'UnityEditor' is not available in builds. // we can only import this attribute if we are in an Editor assembly. @@ -155,9 +157,9 @@ namespace Mirror.Weaver } */ - // [RuntimeInitializeOnLoadMethod] - var runtimeInitializeOnLoadMethodAttributeRef = Import(typeof(RuntimeInitializeOnLoadMethodAttribute)); - runtimeInitializeOnLoadMethodAttribute = runtimeInitializeOnLoadMethodAttributeRef.Resolve(); - } - } + // [RuntimeInitializeOnLoadMethod] + TypeReference runtimeInitializeOnLoadMethodAttributeRef = Import(typeof(RuntimeInitializeOnLoadMethodAttribute)); + runtimeInitializeOnLoadMethodAttribute = runtimeInitializeOnLoadMethodAttributeRef.Resolve(); + } + } } diff --git a/MirrorWeaver/Weaver/Writers.cs b/MirrorWeaver/Weaver/Writers.cs index 302e9ca5..21254be9 100644 --- a/MirrorWeaver/Weaver/Writers.cs +++ b/MirrorWeaver/Weaver/Writers.cs @@ -1,345 +1,339 @@ +using System; +using System.Collections.Generic; using Mono.Cecil; using Mono.Cecil.Cil; // to use Mono.Cecil.Rocks here, we need to 'override references' in the // Unity.Mirror.CodeGen assembly definition file in the Editor, and add CecilX.Rocks. // otherwise we get an unknown import exception. using Mono.Cecil.Rocks; -using System; -using System.Collections.Generic; namespace Mirror.Weaver { - // not static, because ILPostProcessor is multithreaded - public class Writers - { - // Writers are only for this assembly. - // can't be used from another assembly, otherwise we will get: - // "System.ArgumentException: Member ... is declared in another module and needs to be imported" - private readonly AssemblyDefinition assembly; - private readonly WeaverTypes weaverTypes; - private readonly TypeDefinition GeneratedCodeClass; - private readonly Logger Log; - private readonly Dictionary writeFuncs = - new Dictionary(new TypeReferenceComparer()); + // not static, because ILPostProcessor is multithreaded + public class Writers + { + // Writers are only for this assembly. + // can't be used from another assembly, otherwise we will get: + // "System.ArgumentException: Member ... is declared in another module and needs to be imported" + AssemblyDefinition assembly; + WeaverTypes weaverTypes; + TypeDefinition GeneratedCodeClass; + Logger Log; - public Writers(AssemblyDefinition assembly, WeaverTypes weaverTypes, TypeDefinition GeneratedCodeClass, Logger Log) - { - this.assembly = assembly; - this.weaverTypes = weaverTypes; - this.GeneratedCodeClass = GeneratedCodeClass; - this.Log = Log; - } + Dictionary writeFuncs = + new Dictionary(new TypeReferenceComparer()); - public void Register(TypeReference dataType, MethodReference methodReference) - { - if (writeFuncs.ContainsKey(dataType)) - { - // TODO enable this again later. - // Writer has some obsolete functions that were renamed. - // Don't want weaver warnings for all of them. - //Log.Warning($"Registering a Write method for {dataType.FullName} when one already exists", methodReference); - } + public Writers(AssemblyDefinition assembly, WeaverTypes weaverTypes, TypeDefinition GeneratedCodeClass, Logger Log) + { + this.assembly = assembly; + this.weaverTypes = weaverTypes; + this.GeneratedCodeClass = GeneratedCodeClass; + this.Log = Log; + } - // we need to import type when we Initialize Writers so import here in case it is used anywhere else - var imported = assembly.MainModule.ImportReference(dataType); - writeFuncs[imported] = methodReference; - } + public void Register(TypeReference dataType, MethodReference methodReference) + { + if (writeFuncs.ContainsKey(dataType)) + { + // TODO enable this again later. + // Writer has some obsolete functions that were renamed. + // Don't want weaver warnings for all of them. + //Log.Warning($"Registering a Write method for {dataType.FullName} when one already exists", methodReference); + } - private void RegisterWriteFunc(TypeReference typeReference, MethodDefinition newWriterFunc) - { - Register(typeReference, newWriterFunc); - GeneratedCodeClass.Methods.Add(newWriterFunc); - } + // we need to import type when we Initialize Writers so import here in case it is used anywhere else + TypeReference imported = assembly.MainModule.ImportReference(dataType); + writeFuncs[imported] = methodReference; + } - // Finds existing writer for type, if non exists trys to create one - public MethodReference GetWriteFunc(TypeReference variable, ref bool WeavingFailed) - { - if (writeFuncs.TryGetValue(variable, out var foundFunc)) - { - return foundFunc; - } + void RegisterWriteFunc(TypeReference typeReference, MethodDefinition newWriterFunc) + { + Register(typeReference, newWriterFunc); + GeneratedCodeClass.Methods.Add(newWriterFunc); + } - // this try/catch will be removed in future PR and make `GetWriteFunc` throw instead - try - { - var importedVariable = assembly.MainModule.ImportReference(variable); - return GenerateWriter(importedVariable, ref WeavingFailed); - } - catch (GenerateWriterException e) - { - Log.Error(e.Message, e.MemberReference); - WeavingFailed = true; - return null; - } - } + // Finds existing writer for type, if non exists trys to create one + public MethodReference GetWriteFunc(TypeReference variable, ref bool WeavingFailed) + { + if (writeFuncs.TryGetValue(variable, out MethodReference foundFunc)) + return foundFunc; - //Throws GenerateWriterException when writer could not be generated for type - private MethodReference GenerateWriter(TypeReference variableReference, ref bool WeavingFailed) - { - if (variableReference.IsByReference) - { - throw new GenerateWriterException($"Cannot pass {variableReference.Name} by reference", variableReference); - } + // this try/catch will be removed in future PR and make `GetWriteFunc` throw instead + try + { + TypeReference importedVariable = assembly.MainModule.ImportReference(variable); + return GenerateWriter(importedVariable, ref WeavingFailed); + } + catch (GenerateWriterException e) + { + Log.Error(e.Message, e.MemberReference); + WeavingFailed = true; + return null; + } + } - // Arrays are special, if we resolve them, we get the element type, - // e.g. int[] resolves to int - // therefore process this before checks below - if (variableReference.IsArray) - { - if (variableReference.IsMultidimensionalArray()) - { - throw new GenerateWriterException($"{variableReference.Name} is an unsupported type. Multidimensional arrays are not supported", variableReference); - } - var elementType = variableReference.GetElementType(); - return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteArray), ref WeavingFailed); - } + //Throws GenerateWriterException when writer could not be generated for type + MethodReference GenerateWriter(TypeReference variableReference, ref bool WeavingFailed) + { + if (variableReference.IsByReference) + { + throw new GenerateWriterException($"Cannot pass {variableReference.Name} by reference", variableReference); + } - if (variableReference.Resolve()?.IsEnum ?? false) - { - // serialize enum as their base type - return GenerateEnumWriteFunc(variableReference, ref WeavingFailed); - } + // Arrays are special, if we resolve them, we get the element type, + // e.g. int[] resolves to int + // therefore process this before checks below + if (variableReference.IsArray) + { + if (variableReference.IsMultidimensionalArray()) + { + throw new GenerateWriterException($"{variableReference.Name} is an unsupported type. Multidimensional arrays are not supported", variableReference); + } + TypeReference elementType = variableReference.GetElementType(); + return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteArray), ref WeavingFailed); + } - // check for collections - if (variableReference.Is(typeof(ArraySegment<>))) - { - var genericInstance = (GenericInstanceType)variableReference; - var elementType = genericInstance.GenericArguments[0]; + if (variableReference.Resolve()?.IsEnum ?? false) + { + // serialize enum as their base type + return GenerateEnumWriteFunc(variableReference, ref WeavingFailed); + } - return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteArraySegment), ref WeavingFailed); - } - if (variableReference.Is(typeof(List<>))) - { - var genericInstance = (GenericInstanceType)variableReference; - var elementType = genericInstance.GenericArguments[0]; + // check for collections + if (variableReference.Is(typeof(ArraySegment<>))) + { + GenericInstanceType genericInstance = (GenericInstanceType)variableReference; + TypeReference elementType = genericInstance.GenericArguments[0]; - return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteList), ref WeavingFailed); - } + return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteArraySegment), ref WeavingFailed); + } + if (variableReference.Is(typeof(List<>))) + { + GenericInstanceType genericInstance = (GenericInstanceType)variableReference; + TypeReference elementType = genericInstance.GenericArguments[0]; - if (variableReference.IsDerivedFrom()) - { - return GetNetworkBehaviourWriter(variableReference); - } + return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteList), ref WeavingFailed); + } - // check for invalid types - var variableDefinition = variableReference.Resolve(); - if (variableDefinition == null) - { - throw new GenerateWriterException($"{variableReference.Name} is not a supported type. Use a supported type or provide a custom writer", variableReference); - } - if (variableDefinition.IsDerivedFrom()) - { - throw new GenerateWriterException($"Cannot generate writer for component type {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); - } - if (variableReference.Is()) - { - throw new GenerateWriterException($"Cannot generate writer for {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); - } - if (variableReference.Is()) - { - throw new GenerateWriterException($"Cannot generate writer for {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); - } - if (variableDefinition.HasGenericParameters) - { - throw new GenerateWriterException($"Cannot generate writer for generic type {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); - } - if (variableDefinition.IsInterface) - { - throw new GenerateWriterException($"Cannot generate writer for interface {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); - } - if (variableDefinition.IsAbstract) - { - throw new GenerateWriterException($"Cannot generate writer for abstract class {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); - } + if (variableReference.IsDerivedFrom()) + { + return GetNetworkBehaviourWriter(variableReference); + } - // generate writer for class/struct - return GenerateClassOrStructWriterFunction(variableReference, ref WeavingFailed); - } + // check for invalid types + TypeDefinition variableDefinition = variableReference.Resolve(); + if (variableDefinition == null) + { + throw new GenerateWriterException($"{variableReference.Name} is not a supported type. Use a supported type or provide a custom writer", variableReference); + } + if (variableDefinition.IsDerivedFrom()) + { + throw new GenerateWriterException($"Cannot generate writer for component type {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); + } + if (variableReference.Is()) + { + throw new GenerateWriterException($"Cannot generate writer for {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); + } + if (variableReference.Is()) + { + throw new GenerateWriterException($"Cannot generate writer for {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); + } + if (variableDefinition.HasGenericParameters) + { + throw new GenerateWriterException($"Cannot generate writer for generic type {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); + } + if (variableDefinition.IsInterface) + { + throw new GenerateWriterException($"Cannot generate writer for interface {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); + } + if (variableDefinition.IsAbstract) + { + throw new GenerateWriterException($"Cannot generate writer for abstract class {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); + } - private MethodReference GetNetworkBehaviourWriter(TypeReference variableReference) - { - // all NetworkBehaviours can use the same write function - if (writeFuncs.TryGetValue(weaverTypes.Import(), out var func)) - { - // register function so it is added to writer - // use Register instead of RegisterWriteFunc because this is not a generated function - Register(variableReference, func); + // generate writer for class/struct + return GenerateClassOrStructWriterFunction(variableReference, ref WeavingFailed); + } - return func; - } - else - { - // this exception only happens if mirror is missing the WriteNetworkBehaviour method - throw new MissingMethodException($"Could not find writer for NetworkBehaviour"); - } - } + MethodReference GetNetworkBehaviourWriter(TypeReference variableReference) + { + // all NetworkBehaviours can use the same write function + if (writeFuncs.TryGetValue(weaverTypes.Import(), out MethodReference func)) + { + // register function so it is added to writer + // use Register instead of RegisterWriteFunc because this is not a generated function + Register(variableReference, func); - private MethodDefinition GenerateEnumWriteFunc(TypeReference variable, ref bool WeavingFailed) - { - var writerFunc = GenerateWriterFunc(variable); + return func; + } + else + { + // this exception only happens if mirror is missing the WriteNetworkBehaviour method + throw new MissingMethodException($"Could not find writer for NetworkBehaviour"); + } + } - var worker = writerFunc.Body.GetILProcessor(); + MethodDefinition GenerateEnumWriteFunc(TypeReference variable, ref bool WeavingFailed) + { + MethodDefinition writerFunc = GenerateWriterFunc(variable); - var underlyingWriter = GetWriteFunc(variable.Resolve().GetEnumUnderlyingType(), ref WeavingFailed); + ILProcessor worker = writerFunc.Body.GetILProcessor(); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldarg_1); - worker.Emit(OpCodes.Call, underlyingWriter); + MethodReference underlyingWriter = GetWriteFunc(variable.Resolve().GetEnumUnderlyingType(), ref WeavingFailed); - worker.Emit(OpCodes.Ret); - return writerFunc; - } + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldarg_1); + worker.Emit(OpCodes.Call, underlyingWriter); - private MethodDefinition GenerateWriterFunc(TypeReference variable) - { - var functionName = $"_Write_{variable.FullName}"; - // create new writer for this type - var writerFunc = new MethodDefinition(functionName, - MethodAttributes.Public | - MethodAttributes.Static | - MethodAttributes.HideBySig, - weaverTypes.Import(typeof(void))); + worker.Emit(OpCodes.Ret); + return writerFunc; + } - writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import())); - writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, variable)); - writerFunc.Body.InitLocals = true; + MethodDefinition GenerateWriterFunc(TypeReference variable) + { + string functionName = $"_Write_{variable.FullName}"; + // create new writer for this type + MethodDefinition writerFunc = new MethodDefinition(functionName, + MethodAttributes.Public | + MethodAttributes.Static | + MethodAttributes.HideBySig, + weaverTypes.Import(typeof(void))); - RegisterWriteFunc(variable, writerFunc); - return writerFunc; - } + writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import())); + writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, variable)); + writerFunc.Body.InitLocals = true; - private MethodDefinition GenerateClassOrStructWriterFunction(TypeReference variable, ref bool WeavingFailed) - { - var writerFunc = GenerateWriterFunc(variable); + RegisterWriteFunc(variable, writerFunc); + return writerFunc; + } - var worker = writerFunc.Body.GetILProcessor(); + MethodDefinition GenerateClassOrStructWriterFunction(TypeReference variable, ref bool WeavingFailed) + { + MethodDefinition writerFunc = GenerateWriterFunc(variable); - if (!variable.Resolve().IsValueType) - { - WriteNullCheck(worker, ref WeavingFailed); - } + ILProcessor worker = writerFunc.Body.GetILProcessor(); - if (!WriteAllFields(variable, worker, ref WeavingFailed)) - { - return null; - } + if (!variable.Resolve().IsValueType) + WriteNullCheck(worker, ref WeavingFailed); - worker.Emit(OpCodes.Ret); - return writerFunc; - } + if (!WriteAllFields(variable, worker, ref WeavingFailed)) + return null; - private void WriteNullCheck(ILProcessor worker, ref bool WeavingFailed) - { - // if (value == null) - // { - // writer.WriteBoolean(false); - // return; - // } - // + worker.Emit(OpCodes.Ret); + return writerFunc; + } - var labelNotNull = worker.Create(OpCodes.Nop); - worker.Emit(OpCodes.Ldarg_1); - worker.Emit(OpCodes.Brtrue, labelNotNull); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldc_I4_0); - worker.Emit(OpCodes.Call, GetWriteFunc(weaverTypes.Import(), ref WeavingFailed)); - worker.Emit(OpCodes.Ret); - worker.Append(labelNotNull); + void WriteNullCheck(ILProcessor worker, ref bool WeavingFailed) + { + // if (value == null) + // { + // writer.WriteBoolean(false); + // return; + // } + // - // write.WriteBoolean(true); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldc_I4_1); - worker.Emit(OpCodes.Call, GetWriteFunc(weaverTypes.Import(), ref WeavingFailed)); - } + Instruction labelNotNull = worker.Create(OpCodes.Nop); + worker.Emit(OpCodes.Ldarg_1); + worker.Emit(OpCodes.Brtrue, labelNotNull); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldc_I4_0); + worker.Emit(OpCodes.Call, GetWriteFunc(weaverTypes.Import(), ref WeavingFailed)); + worker.Emit(OpCodes.Ret); + worker.Append(labelNotNull); - // Find all fields in type and write them - private bool WriteAllFields(TypeReference variable, ILProcessor worker, ref bool WeavingFailed) - { - foreach (var field in variable.FindAllPublicFields()) - { - var writeFunc = GetWriteFunc(field.FieldType, ref WeavingFailed); - // need this null check till later PR when GetWriteFunc throws exception instead - if (writeFunc == null) - { return false; } + // write.WriteBoolean(true); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldc_I4_1); + worker.Emit(OpCodes.Call, GetWriteFunc(weaverTypes.Import(), ref WeavingFailed)); + } - var fieldRef = assembly.MainModule.ImportReference(field); + // Find all fields in type and write them + bool WriteAllFields(TypeReference variable, ILProcessor worker, ref bool WeavingFailed) + { + foreach (FieldDefinition field in variable.FindAllPublicFields()) + { + MethodReference writeFunc = GetWriteFunc(field.FieldType, ref WeavingFailed); + // need this null check till later PR when GetWriteFunc throws exception instead + if (writeFunc == null) { return false; } - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldarg_1); - worker.Emit(OpCodes.Ldfld, fieldRef); - worker.Emit(OpCodes.Call, writeFunc); - } + FieldReference fieldRef = assembly.MainModule.ImportReference(field); - return true; - } + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldarg_1); + worker.Emit(OpCodes.Ldfld, fieldRef); + worker.Emit(OpCodes.Call, writeFunc); + } - private MethodDefinition GenerateCollectionWriter(TypeReference variable, TypeReference elementType, string writerFunction, ref bool WeavingFailed) - { + return true; + } - var writerFunc = GenerateWriterFunc(variable); + MethodDefinition GenerateCollectionWriter(TypeReference variable, TypeReference elementType, string writerFunction, ref bool WeavingFailed) + { - var elementWriteFunc = GetWriteFunc(elementType, ref WeavingFailed); - var intWriterFunc = GetWriteFunc(weaverTypes.Import(), ref WeavingFailed); + MethodDefinition writerFunc = GenerateWriterFunc(variable); - // need this null check till later PR when GetWriteFunc throws exception instead - if (elementWriteFunc == null) - { - Log.Error($"Cannot generate writer for {variable}. Use a supported type or provide a custom writer", variable); - WeavingFailed = true; - return writerFunc; - } + MethodReference elementWriteFunc = GetWriteFunc(elementType, ref WeavingFailed); + MethodReference intWriterFunc = GetWriteFunc(weaverTypes.Import(), ref WeavingFailed); - var module = assembly.MainModule; - var readerExtensions = module.ImportReference(typeof(NetworkWriterExtensions)); - var collectionWriter = Resolvers.ResolveMethod(readerExtensions, assembly, Log, writerFunction, ref WeavingFailed); + // need this null check till later PR when GetWriteFunc throws exception instead + if (elementWriteFunc == null) + { + Log.Error($"Cannot generate writer for {variable}. Use a supported type or provide a custom writer", variable); + WeavingFailed = true; + return writerFunc; + } - var methodRef = new GenericInstanceMethod(collectionWriter); - methodRef.GenericArguments.Add(elementType); + ModuleDefinition module = assembly.MainModule; + TypeReference readerExtensions = module.ImportReference(typeof(NetworkWriterExtensions)); + MethodReference collectionWriter = Resolvers.ResolveMethod(readerExtensions, assembly, Log, writerFunction, ref WeavingFailed); - // generates - // reader.WriteArray(array); + GenericInstanceMethod methodRef = new GenericInstanceMethod(collectionWriter); + methodRef.GenericArguments.Add(elementType); - var worker = writerFunc.Body.GetILProcessor(); - worker.Emit(OpCodes.Ldarg_0); // writer - worker.Emit(OpCodes.Ldarg_1); // collection + // generates + // reader.WriteArray(array); - worker.Emit(OpCodes.Call, methodRef); // WriteArray + ILProcessor worker = writerFunc.Body.GetILProcessor(); + worker.Emit(OpCodes.Ldarg_0); // writer + worker.Emit(OpCodes.Ldarg_1); // collection - worker.Emit(OpCodes.Ret); + worker.Emit(OpCodes.Call, methodRef); // WriteArray - return writerFunc; - } + worker.Emit(OpCodes.Ret); - // Save a delegate for each one of the writers into Writer{T}.write - internal void InitializeWriters(ILProcessor worker) - { - var module = assembly.MainModule; + return writerFunc; + } - var genericWriterClassRef = module.ImportReference(typeof(Writer<>)); + // Save a delegate for each one of the writers into Writer{T}.write + internal void InitializeWriters(ILProcessor worker) + { + ModuleDefinition module = assembly.MainModule; - var fieldInfo = typeof(Writer<>).GetField(nameof(Writer.write)); - var fieldRef = module.ImportReference(fieldInfo); - var networkWriterRef = module.ImportReference(typeof(NetworkWriter)); - var actionRef = module.ImportReference(typeof(Action<,>)); - var actionConstructorRef = module.ImportReference(typeof(Action<,>).GetConstructors()[0]); + TypeReference genericWriterClassRef = module.ImportReference(typeof(Writer<>)); - foreach (var kvp in writeFuncs) - { - var targetType = kvp.Key; - var writeFunc = kvp.Value; + System.Reflection.FieldInfo fieldInfo = typeof(Writer<>).GetField(nameof(Writer.write)); + FieldReference fieldRef = module.ImportReference(fieldInfo); + TypeReference networkWriterRef = module.ImportReference(typeof(NetworkWriter)); + TypeReference actionRef = module.ImportReference(typeof(Action<,>)); + MethodReference actionConstructorRef = module.ImportReference(typeof(Action<,>).GetConstructors()[0]); - // create a Action delegate - worker.Emit(OpCodes.Ldnull); - worker.Emit(OpCodes.Ldftn, writeFunc); - var actionGenericInstance = actionRef.MakeGenericInstanceType(networkWriterRef, targetType); - var actionRefInstance = actionConstructorRef.MakeHostInstanceGeneric(assembly.MainModule, actionGenericInstance); - worker.Emit(OpCodes.Newobj, actionRefInstance); + foreach (KeyValuePair kvp in writeFuncs) + { + TypeReference targetType = kvp.Key; + MethodReference writeFunc = kvp.Value; - // save it in Writer.write - var genericInstance = genericWriterClassRef.MakeGenericInstanceType(targetType); - var specializedField = fieldRef.SpecializeField(assembly.MainModule, genericInstance); - worker.Emit(OpCodes.Stsfld, specializedField); - } - } - } + // create a Action delegate + worker.Emit(OpCodes.Ldnull); + worker.Emit(OpCodes.Ldftn, writeFunc); + GenericInstanceType actionGenericInstance = actionRef.MakeGenericInstanceType(networkWriterRef, targetType); + MethodReference actionRefInstance = actionConstructorRef.MakeHostInstanceGeneric(assembly.MainModule, actionGenericInstance); + worker.Emit(OpCodes.Newobj, actionRefInstance); + + // save it in Writer.write + GenericInstanceType genericInstance = genericWriterClassRef.MakeGenericInstanceType(targetType); + FieldReference specializedField = fieldRef.SpecializeField(assembly.MainModule, genericInstance); + worker.Emit(OpCodes.Stsfld, specializedField); + } + } + } }