static void LoadWriters(AssemblyDefinition currentAssembly, TypeDefinition klass) { // register all the writers in this class. Skip the ones with wrong signature foreach (MethodDefinition method in klass.Methods) { if (method.Parameters.Count != 2) { continue; } if (method.Parameters[0].ParameterType.FullName != "Mirror.NetworkWriter") { continue; } if (method.ReturnType.FullName != "System.Void") { continue; } if (method.GetCustomAttribute("System.Runtime.CompilerServices.ExtensionAttribute") == null) { continue; } TypeReference dataType = method.Parameters[1].ParameterType; Writers.Register(dataType, currentAssembly.MainModule.ImportReference(method)); } }
/// <summary> /// Creates a method that will store all the readers and writers into /// <see cref="Writer{T}.Write"/> and <see cref="Reader{T}.Read"/> /// /// The method will be marked InitializeOnLoadMethodAttribute so it gets /// executed before mirror runtime code /// </summary> /// <param name="currentAssembly"></param> public static void InitializeReaderAndWriters(ModuleDefinition module) { MethodDefinition rwInitializer = module.GeneratedClass().AddMethod( "InitReadWriters", MethodAttributes.Public | MethodAttributes.Static); System.Reflection.ConstructorInfo attributeconstructor = typeof(RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new [] { typeof(RuntimeInitializeLoadType) }); var customAttributeRef = new CustomAttribute(module.ImportReference(attributeconstructor)); customAttributeRef.ConstructorArguments.Add(new CustomAttributeArgument(module.ImportReference <RuntimeInitializeLoadType>(), RuntimeInitializeLoadType.BeforeSceneLoad)); rwInitializer.CustomAttributes.Add(customAttributeRef); if (IsEditorAssembly(module)) { // editor assembly, add InitializeOnLoadMethod too. Useful for the editor tests System.Reflection.ConstructorInfo initializeOnLoadConstructor = typeof(InitializeOnLoadMethodAttribute).GetConstructor(new Type[0]); var initializeCustomConstructorRef = new CustomAttribute(module.ImportReference(initializeOnLoadConstructor)); rwInitializer.CustomAttributes.Add(initializeCustomConstructorRef); } ILProcessor worker = rwInitializer.Body.GetILProcessor(); Writers.InitializeWriters(worker); Readers.InitializeReaders(worker); RegisterMessages(module, worker); worker.Append(worker.Create(OpCodes.Ret)); }
protected RpcProcessor(ModuleDefinition module, Readers readers, Writers writers, IWeaverLogger logger) { this.module = module; this.readers = readers; this.writers = writers; this.logger = logger; }
// Finds SyncObjects fields in a type // Type should be a NetworkBehaviour public static List <FieldDefinition> FindSyncObjectsFields(Writers writers, Readers readers, Logger Log, TypeDefinition td, ref bool WeavingFailed) { List <FieldDefinition> syncObjects = new List <FieldDefinition>(); foreach (FieldDefinition fd in td.Fields) { if (fd.FieldType.Resolve().ImplementsInterface <SyncObject>()) { if (fd.IsStatic) { Log.Error($"{fd.Name} cannot be static", fd); WeavingFailed = true; continue; } GenerateReadersAndWriters(writers, readers, fd.FieldType, ref WeavingFailed); 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; } return(syncObjects); }
public static bool WriteArguments(ILProcessor worker, MethodDefinition md, bool skipFirst) { // write each argument short argNum = 1; foreach (ParameterDefinition pd in md.Parameters) { if (argNum == 1 && skipFirst) { argNum += 1; continue; } MethodReference writeFunc = Writers.GetWriteFunc(pd.ParameterType); if (writeFunc == null) { Weaver.Error($"{md} has invalid parameter {pd}"); return(false); } // use built-in writer func on writer object worker.Append(worker.Create(OpCodes.Ldloc_0)); // writer object worker.Append(worker.Create(OpCodes.Ldarg, argNum)); // argument worker.Append(worker.Create(OpCodes.Call, writeFunc)); // call writer func on writer object argNum += 1; } return(true); }
// adds Mirror.GeneratedNetworkCode.InitReadWriters() method that // registers all generated writers into Mirror.Writer<T> 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) { MethodDefinition rwInitializer = new MethodDefinition("InitReadWriters", MethodAttributes.Public | MethodAttributes.Static, WeaverTypes.Import(typeof(void))); // add [RuntimeInitializeOnLoad] in any case System.Reflection.ConstructorInfo attributeconstructor = typeof(RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new[] { typeof(RuntimeInitializeLoadType) }); CustomAttribute customAttributeRef = new CustomAttribute(currentAssembly.MainModule.ImportReference(attributeconstructor)); customAttributeRef.ConstructorArguments.Add(new CustomAttributeArgument(WeaverTypes.Import <RuntimeInitializeLoadType>(), RuntimeInitializeLoadType.BeforeSceneLoad)); rwInitializer.CustomAttributes.Add(customAttributeRef); // add [InitializeOnLoad] if UnityEditor is referenced if (IsEditorAssembly(currentAssembly)) { System.Reflection.ConstructorInfo initializeOnLoadConstructor = typeof(InitializeOnLoadMethodAttribute).GetConstructor(new Type[0]); CustomAttribute initializeCustomConstructorRef = new CustomAttribute(currentAssembly.MainModule.ImportReference(initializeOnLoadConstructor)); rwInitializer.CustomAttributes.Add(initializeCustomConstructorRef); } // fill function body with reader/writer initializers ILProcessor worker = rwInitializer.Body.GetILProcessor(); // for debugging: add a log to see if initialized on load //worker.Emit(OpCodes.Ldstr, $"[InitReadWriters] called!"); //worker.Emit(OpCodes.Call, WeaverTypes.logWarningReference); Writers.InitializeWriters(worker); Readers.InitializeReaders(worker); worker.Emit(OpCodes.Ret); Weaver.GeneratedCodeClass.Methods.Add(rwInitializer); }
static void LoadDeclaredWriters(AssemblyDefinition currentAssembly, TypeDefinition klass) { // register all the writers in this class. Skip the ones with wrong signature foreach (MethodDefinition method in klass.Methods) { if (method.Parameters.Count != 2) { continue; } if (!method.Parameters[0].ParameterType.Is <NetworkWriter>()) { continue; } if (!method.ReturnType.Is(typeof(void))) { continue; } if (!method.HasCustomAttribute <System.Runtime.CompilerServices.ExtensionAttribute>()) { continue; } if (method.HasGenericParameters) { continue; } TypeReference dataType = method.Parameters[1].ParameterType; Writers.Register(dataType, currentAssembly.MainModule.ImportReference(method)); } }
public AssemblyDefinition Weave(ICompiledAssembly compiledAssembly) { try { CurrentAssembly = AssemblyDefinitionFor(compiledAssembly); ModuleDefinition module = CurrentAssembly.MainModule; readers = new Readers(module, logger); writers = new Writers(module, logger); var rwstopwatch = System.Diagnostics.Stopwatch.StartNew(); propertySiteProcessor = new PropertySiteProcessor(); var rwProcessor = new ReaderWriterProcessor(module, readers, writers); bool modified = rwProcessor.Process(); rwstopwatch.Stop(); Console.WriteLine($"Find all reader and writers took {rwstopwatch.ElapsedMilliseconds} milliseconds"); Console.WriteLine($"Script Module: {module.Name}"); modified |= WeaveModule(module); if (!modified) return CurrentAssembly; rwProcessor.InitializeReaderAndWriters(); return CurrentAssembly; } catch (Exception e) { logger.Error("Exception :" + e); return null; } }
/// <summary> /// Creates a method that will store all the readers and writers into /// <see cref="Writer{T}.write"/> and <see cref="Reader{T}.read"/> /// /// The method will be marked InitializeOnLoadMethodAttribute so it gets /// executed before mirror runtime code /// </summary> /// <param name="currentAssembly"></param> public static void InitializeReaderAndWriters(AssemblyDefinition currentAssembly) { var rwInitializer = new MethodDefinition("InitReadWriters", MethodAttributes.Public | MethodAttributes.Static, WeaverTypes.Import(typeof(void))); System.Reflection.ConstructorInfo attributeconstructor = typeof(RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new [] { typeof(RuntimeInitializeLoadType) }); var customAttributeRef = new CustomAttribute(currentAssembly.MainModule.ImportReference(attributeconstructor)); customAttributeRef.ConstructorArguments.Add(new CustomAttributeArgument(WeaverTypes.Import <RuntimeInitializeLoadType>(), RuntimeInitializeLoadType.BeforeSceneLoad)); rwInitializer.CustomAttributes.Add(customAttributeRef); if (IsEditorAssembly(currentAssembly)) { // editor assembly, add InitializeOnLoadMethod too. Useful for the editor tests System.Reflection.ConstructorInfo initializeOnLoadConstructor = typeof(InitializeOnLoadMethodAttribute).GetConstructor(new Type[0]); var initializeCustomConstructorRef = new CustomAttribute(currentAssembly.MainModule.ImportReference(initializeOnLoadConstructor)); rwInitializer.CustomAttributes.Add(initializeCustomConstructorRef); } ILProcessor worker = rwInitializer.Body.GetILProcessor(); Writers.InitializeWriters(worker); Readers.InitializeReaders(worker); worker.Append(worker.Create(OpCodes.Ret)); Weaver.WeaveLists.ConfirmGeneratedCodeClass(); TypeDefinition generateClass = Weaver.WeaveLists.generateContainerClass; generateClass.Methods.Add(rwInitializer); }
public static bool Process(ModuleDefinition module, Assembly unityAssembly) { // darn global state causing bugs Readers.Init(); Writers.Init(); messages.Clear(); foreach (Assembly unityAsm in unityAssembly.assemblyReferences) { if (unityAsm.name == "Mirror") { using (var asmResolver = new DefaultAssemblyResolver()) using (var assembly = AssemblyDefinition.ReadAssembly(unityAsm.outputPath, new ReaderParameters { ReadWrite = false, ReadSymbols = false, AssemblyResolver = asmResolver })) { ProcessAssemblyClasses(module, assembly.MainModule); } } } int writeCount = Writers.Count; int readCount = Readers.Count; ProcessAssemblyClasses(module, module); return(Writers.Count != writeCount || Readers.Count != readCount); }
public static MethodDefinition ProcessRpcInvoke(WeaverTypes weaverTypes, Writers writers, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed) { string rpcName = Weaver.GenerateMethodName(Weaver.InvokeRpcPrefix, md); MethodDefinition rpc = new MethodDefinition(rpcName, MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, weaverTypes.Import(typeof(void))); ILProcessor worker = rpc.Body.GetILProcessor(); Instruction label = worker.Create(OpCodes.Nop); NetworkBehaviourProcessor.WriteClientActiveCheck(worker, weaverTypes, md.Name, label, "RPC"); // 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); } // invoke actual command function worker.Emit(OpCodes.Callvirt, rpcCallFunc); worker.Emit(OpCodes.Ret); NetworkBehaviourProcessor.AddInvokeParameters(weaverTypes, rpc.Parameters); td.Methods.Add(rpc); return(rpc); }
// find all readers and writers and register them public static void ProcessReadersAndWriters(AssemblyDefinition CurrentAssembly) { Readers.Init(); Writers.Init(); foreach (Assembly unityAsm in CompilationPipeline.GetAssemblies()) { if (unityAsm.name != CurrentAssembly.Name.Name) { try { using (DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver()) using (AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(unityAsm.outputPath, new ReaderParameters { ReadWrite = false, ReadSymbols = false, AssemblyResolver = asmResolver })) { ProcessAssemblyClasses(CurrentAssembly, assembly); } } catch (FileNotFoundException) { // During first import, this gets called before some assemblies // are built, just skip them } } } ProcessAssemblyClasses(CurrentAssembly, CurrentAssembly); }
public SyncVarProcessor(ModuleDefinition module, Readers readers, Writers writers, PropertySiteProcessor propertySiteProcessor, IWeaverLogger logger) { this.module = module; this.readers = readers; this.writers = writers; this.propertySiteProcessor = propertySiteProcessor; this.logger = logger; }
static void GenerateSerialization(TypeDefinition td) { Weaver.DLog(td, " GenerateSerialization"); foreach (MethodDefinition m in td.Methods) { if (m.Name == "Serialize") { return; } } if (td.Fields.Count == 0) { return; } // check for self-referencing types foreach (FieldDefinition field in td.Fields) { if (field.FieldType.FullName == td.FullName) { Weaver.Error($"{td} has field ${field} that references itself"); return; } } MethodDefinition serializeFunc = new MethodDefinition("Serialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); foreach (FieldDefinition field in td.Fields) { if (field.IsStatic || field.IsPrivate || field.IsSpecialName) { continue; } MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, field)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{field} has unsupported type"); return; } } serWorker.Append(serWorker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); }
public NetworkBehaviourProcessor(TypeDefinition td, Readers readers, Writers writers, PropertySiteProcessor propertySiteProcessor, IWeaverLogger logger) { Weaver.DLog(td, "NetworkBehaviourProcessor"); netBehaviourSubclass = td; this.logger = logger; serverRpcProcessor = new ServerRpcProcessor(netBehaviourSubclass.Module, readers, writers, logger); clientRpcProcessor = new ClientRpcProcessor(netBehaviourSubclass.Module, readers, writers, logger); syncVarProcessor = new SyncVarProcessor(netBehaviourSubclass.Module, readers, writers, propertySiteProcessor, logger); syncObjectProcessor = new SyncObjectProcessor(readers, writers, logger); }
// Finds SyncObjects fields in a type // Type should be a NetworkBehaviour public static List <FieldDefinition> FindSyncObjectsFields(Writers writers, Readers readers, Logger Log, TypeDefinition td, ref bool WeavingFailed) { List <FieldDefinition> syncObjects = new List <FieldDefinition>(); foreach (FieldDefinition fd in td.Fields) { if (fd.FieldType.IsGenericParameter) { // can't call .Resolve on generic ones continue; } if (fd.FieldType.Resolve().IsDerivedFrom <SyncObject>()) { 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); // only log, but keep weaving. no need to break projects. //WeavingFailed = true; } GenerateReadersAndWriters(writers, readers, fd.FieldType, ref WeavingFailed); 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; } return(syncObjects); }
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; }
private static void LoadMessageReadWriter(ModuleDefinition module, TypeDefinition klass) { if (!klass.IsAbstract && !klass.IsInterface && klass.ImplementsInterface <NetworkMessage>()) { Readers.GetReadFunc(module.ImportReference(klass)); Writers.GetWriteFunc(module.ImportReference(klass)); } foreach (TypeDefinition td in klass.NestedTypes) { LoadMessageReadWriter(module, td); } }
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); */ bool skipFirst = callType == RemoteCallType.TargetRpc && TargetRpcProcessor.HasNetworkConnectionParameter(method); // 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; } 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); }
private static MethodDefinition GenerateEnumWriteFunc(TypeReference variable) { MethodDefinition writerFunc = GenerateWriterFunc(variable); ILProcessor worker = writerFunc.Body.GetILProcessor(); MethodReference underlyingWriter = Writers.GetWriteFunc(variable.Resolve().GetEnumUnderlyingType()); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Call, underlyingWriter)); worker.Append(worker.Create(OpCodes.Ret)); return(writerFunc); }
// serialization of individual element private static MethodReference GenerateSerialization(string methodName, TypeDefinition td, TypeReference itemType) { Weaver.DLog(td, " GenerateSerialization"); foreach (var m in td.Methods) { if (m.Name == methodName) { return(m); } } var serializeFunc = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.HideBySig, Weaver.voidType); serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType)); var serWorker = serializeFunc.Body.GetILProcessor(); if (itemType.IsGenericInstance) { Weaver.Error($"{td} cannot have generic elements {itemType}"); return(null); } var writeFunc = Writers.GetWriteFunc(itemType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{td} cannot have item of type {itemType}. Use a type supported by mirror instead"); return(null); } serWorker.Append(serWorker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); return(serializeFunc); }
private static void CallWriter(ILProcessor serWorker, FieldDefinition field) { MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, field)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{field.Name} has unsupported type", field); } }
/* generates code like: * public void TargetTest (NetworkConnection conn, int param) * { * NetworkWriter writer = new NetworkWriter (); * writer.WritePackedUInt32 ((uint)param); * base.SendTargetRPCInternal (conn, typeof(class), "TargetTest", val); * } * public void CallTargetTest (NetworkConnection conn, int param) * { * // whatever the user did before * } * * or if optional: * public void TargetTest (int param) * { * NetworkWriter writer = new NetworkWriter (); * writer.WritePackedUInt32 ((uint)param); * base.SendTargetRPCInternal (null, typeof(class), "TargetTest", val); * } * public void CallTargetTest (int param) * { * // whatever the user did before * } * * Originally HLAPI put the send message code inside the Call function * and then proceeded to replace every call to TargetTest with CallTargetTest * * This method moves all the user's code into the "CallTargetRpc" method * and replaces the body of the original method with the send message code. * This way we do not need to modify the code anywhere else, and this works * correctly in dependent assemblies * */ 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); ILProcessor worker = md.Body.GetILProcessor(); NetworkBehaviourProcessor.WriteSetupLocals(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); } string rpcName = md.Name; // 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); } worker.Emit(OpCodes.Ldtoken, td); // invokerClass worker.Emit(OpCodes.Call, weaverTypes.getTypeFromHandleReference); worker.Emit(OpCodes.Ldstr, rpcName); // 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); worker.Emit(OpCodes.Ret); return(rpc); }
// serialization of individual element static bool GenerateSerialization(string methodName, TypeDefinition td, TypeReference itemType, TypeReference mirrorBaseType) { Weaver.DLog(td, " GenerateSerialization"); bool existing = td.HasMethodInBaseType(methodName, mirrorBaseType); if (existing) { return(true); } // this check needs to happen inside GenerateSerialization because // we need to check if user has made custom function above if (itemType.IsGenericInstance) { Weaver.Error($"Can not create Serialize or Deserialize for generic element in {td.Name}. Override virtual methods with custom Serialize and Deserialize to use {itemType} in SyncList", td); return(false); } var serializeFunc = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.HideBySig, WeaverTypes.Import(typeof(void))); serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, WeaverTypes.Import <NetworkWriter>())); serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType)); ILProcessor worker = serializeFunc.Body.GetILProcessor(); MethodReference writeFunc = Writers.GetWriteFunc(itemType); if (writeFunc != null) { worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Ldarg_2)); worker.Append(worker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{td.Name} has sync object generic type {itemType.Name}. Use a type supported by mirror instead", td); return(false); } worker.Append(worker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); return(true); }
static bool LoadMessageReadWriter(ModuleDefinition module, TypeDefinition klass) { bool modified = false; if (!klass.IsAbstract && !klass.IsInterface && klass.ImplementsInterface <NetworkMessage>()) { Readers.GetReadFunc(module.ImportReference(klass)); Writers.GetWriteFunc(module.ImportReference(klass)); modified = true; } foreach (TypeDefinition td in klass.NestedTypes) { modified |= LoadMessageReadWriter(module, td); } return(modified); }
// serialization of individual element static MethodReference GenerateSerialization(string methodName, TypeDefinition td, TypeReference itemType) { Weaver.DLog(td, " GenerateSerialization"); foreach (MethodDefinition m in td.Methods) { if (m.Name == methodName) { return(m); } } MethodDefinition serializeFunc = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.HideBySig, Weaver.voidType); serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType)); ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); if (itemType.IsGenericInstance) { Weaver.Error("GenerateSerialization for " + Helpers.PrettyPrintType(itemType) + " failed. Can't have generic parameters"); return(null); } MethodReference writeFunc = Writers.GetWriteFunc(itemType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error("GenerateSerialization for " + td.Name + " unknown type [" + itemType + "/" + itemType.FullName + "]. Member variables must be basic types."); return(null); } serWorker.Append(serWorker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); return(serializeFunc); }
/* * generates code like: * * public void RpcTest (int param) * { * NetworkWriter writer = new NetworkWriter (); * writer.WritePackedUInt32((uint)param); * base.SendRPCInternal(typeof(class),"RpcTest", writer, 0); * } * public void CallRpcTest (int param) * { * // whatever the user did before * } * * Originally HLAPI put the send message code inside the Call function * and then proceeded to replace every call to RpcTest with CallRpcTest * * This method moves all the user's code into the "CallRpc" method * and replaces the body of the original method with the send message code. * 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) { MethodDefinition rpc = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); ILProcessor worker = md.Body.GetILProcessor(); 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); 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); } string rpcName = md.Name; int channel = clientRpcAttr.GetField("channel", 0); bool includeOwner = clientRpcAttr.GetField("includeOwner", true); // invoke SendInternal and return // this worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldtoken, td); // invokerClass worker.Emit(OpCodes.Call, weaverTypes.getTypeFromHandleReference); worker.Emit(OpCodes.Ldstr, rpcName); // 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); worker.Emit(OpCodes.Ret); return(rpc); }
public static bool Process(AssemblyDefinition CurrentAssembly) { Readers.Init(); Writers.Init(); foreach (Assembly unityAsm in CompilationPipeline.GetAssemblies()) { if (unityAsm.name == "Mirror") { using (DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver()) using (AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(unityAsm.outputPath, new ReaderParameters { ReadWrite = false, ReadSymbols = false, AssemblyResolver = asmResolver })) { ProcessAssemblyClasses(CurrentAssembly, assembly); } } } return(ProcessAssemblyClasses(CurrentAssembly, CurrentAssembly)); }
// 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); } }
/// <summary> /// Generates serialization methods for synclists /// </summary> /// <param name="td">The synclist class</param> /// <param name="mirrorBaseType">the base SyncObject td inherits from</param> static void GenerateReadersAndWriters(TypeReference tr) { if (tr is GenericInstanceType genericInstance) { foreach (TypeReference argument in genericInstance.GenericArguments) { if (!argument.IsGenericParameter) { Readers.GetReadFunc(argument); Writers.GetWriteFunc(argument); } } } if (tr != null) { GenerateReadersAndWriters(tr.Resolve().BaseType); } }