123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using Mono.CecilX;
- namespace Mirror.Weaver
- {
- // 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";
- TypeDefinition GeneratedCodeClass;
- // for resolving Mirror.dll in ReaderWriterProcessor, we need to know
- // Mirror.dll name
- public const string MirrorAssemblyName = "Mirror";
- WeaverTypes weaverTypes;
- SyncVarAccessLists syncVarAccessLists;
- IAssemblyResolver Resolver;
- AssemblyDefinition CurrentAssembly;
- Writers writers;
- Readers readers;
- bool WeavingFailed;
- // logger functions can be set from the outside.
- // for example, Debug.Log or ILPostProcessor Diagnostics log for
- // multi threaded logging.
- public Logger Log;
- public Weaver(Logger Log)
- {
- this.Log = Log;
- }
- // returns 'true' if modified (=if we did anything)
- bool WeaveNetworkBehavior(TypeDefinition td)
- {
- if (!td.IsClass)
- return false;
- if (!td.IsDerivedFrom<NetworkBehaviour>())
- {
- if (td.IsDerivedFrom<UnityEngine.MonoBehaviour>())
- MonoBehaviourProcessor.Process(Log, td, ref WeavingFailed);
- return false;
- }
- // process this and base classes from parent to child order
- List<TypeDefinition> behaviourClasses = new List<TypeDefinition>();
- TypeDefinition parent = td;
- while (parent != null)
- {
- if (parent.Is<NetworkBehaviour>())
- {
- break;
- }
- try
- {
- behaviourClasses.Insert(0, parent);
- parent = parent.BaseType.Resolve();
- }
- catch (AssemblyResolutionException)
- {
- // this can happen for plugins.
- //Console.WriteLine("AssemblyResolutionException: "+ ex.ToString());
- break;
- }
- }
- bool modified = false;
- foreach (TypeDefinition behaviour in behaviourClasses)
- {
- modified |= new NetworkBehaviourProcessor(CurrentAssembly, weaverTypes, syncVarAccessLists, writers, readers, Log, behaviour).Process(ref WeavingFailed);
- }
- return modified;
- }
- bool WeaveModule(ModuleDefinition moduleDefinition)
- {
- bool modified = false;
- Stopwatch watch = Stopwatch.StartNew();
- watch.Start();
- 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");
- return modified;
- }
- void CreateGeneratedCodeClass()
- {
- // create "Mirror.GeneratedNetworkCode" class which holds all
- // Readers<T> and Writers<T>
- GeneratedCodeClass = new TypeDefinition(GeneratedCodeNamespace, GeneratedCodeClassName,
- TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass | TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Abstract | TypeAttributes.Sealed,
- weaverTypes.Import<object>());
- }
- // 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
- {
- Resolver = resolver;
- 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;
- }
- weaverTypes = new WeaverTypes(CurrentAssembly, Log, ref WeavingFailed);
- // weaverTypes are needed for CreateGeneratedCodeClass
- CreateGeneratedCodeClass();
- // 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);
- 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);
- rwstopwatch.Stop();
- Console.WriteLine($"Find all reader and writers took {rwstopwatch.ElapsedMilliseconds} milliseconds");
- ModuleDefinition moduleDefinition = CurrentAssembly.MainModule;
- Console.WriteLine($"Script Module: {moduleDefinition.Name}");
- modified |= WeaveModule(moduleDefinition);
- if (WeavingFailed)
- {
- return false;
- }
- if (modified)
- {
- SyncVarAttributeAccessReplacer.Process(moduleDefinition, syncVarAccessLists);
- // add class that holds read/write functions
- moduleDefinition.Types.Add(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 });
- }
- return true;
- }
- catch (Exception e)
- {
- Log.Error($"Exception :{e}");
- WeavingFailed = true;
- return false;
- }
- }
- }
- }
|