123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- // finds all readers and writers and register them
- using System;
- using System.Linq;
- using Mono.CecilX;
- using Mono.CecilX.Cil;
- using UnityEditor;
- using UnityEditor.Compilation;
- using UnityEngine;
- namespace Mirror.Weaver
- {
- public static class ReaderWriterProcessor
- {
- 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);
- }
- static bool ProcessAssemblyClasses(AssemblyDefinition CurrentAssembly, AssemblyDefinition assembly)
- {
- 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);
- modified |= LoadDeclaredReaders(CurrentAssembly, klass);
- }
- }
- foreach (TypeDefinition klass in assembly.MainModule.Types)
- {
- // if assembly has any network message then it is modified
- modified |= LoadMessageReadWriter(CurrentAssembly.MainModule, klass);
- }
- return modified;
- }
- 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;
- }
- static bool LoadDeclaredWriters(AssemblyDefinition currentAssembly, TypeDefinition klass)
- {
- // 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<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));
- modified = true;
- }
- return modified;
- }
- static bool LoadDeclaredReaders(AssemblyDefinition currentAssembly, TypeDefinition klass)
- {
- // 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<NetworkReader>())
- continue;
- if (method.ReturnType.Is(typeof(void)))
- continue;
- if (!method.HasCustomAttribute<System.Runtime.CompilerServices.ExtensionAttribute>())
- continue;
- if (method.HasGenericParameters)
- continue;
- Readers.Register(method.ReturnType, currentAssembly.MainModule.ImportReference(method));
- modified = true;
- }
- return modified;
- }
- 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))
- );
- }
- // 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);
- }
- }
- }
|