Weaver.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using Mono.CecilX;
  5. namespace Mirror.Weaver
  6. {
  7. // not static, because ILPostProcessor is multithreaded
  8. internal class Weaver
  9. {
  10. public const string InvokeRpcPrefix = "InvokeUserCode_";
  11. // generated code class
  12. public const string GeneratedCodeNamespace = "Mirror";
  13. public const string GeneratedCodeClassName = "GeneratedNetworkCode";
  14. TypeDefinition GeneratedCodeClass;
  15. // for resolving Mirror.dll in ReaderWriterProcessor, we need to know
  16. // Mirror.dll name
  17. public const string MirrorAssemblyName = "Mirror";
  18. WeaverTypes weaverTypes;
  19. SyncVarAccessLists syncVarAccessLists;
  20. IAssemblyResolver Resolver;
  21. AssemblyDefinition CurrentAssembly;
  22. Writers writers;
  23. Readers readers;
  24. bool WeavingFailed;
  25. // logger functions can be set from the outside.
  26. // for example, Debug.Log or ILPostProcessor Diagnostics log for
  27. // multi threaded logging.
  28. public Logger Log;
  29. public Weaver(Logger Log)
  30. {
  31. this.Log = Log;
  32. }
  33. // returns 'true' if modified (=if we did anything)
  34. bool WeaveNetworkBehavior(TypeDefinition td)
  35. {
  36. if (!td.IsClass)
  37. return false;
  38. if (!td.IsDerivedFrom<NetworkBehaviour>())
  39. {
  40. if (td.IsDerivedFrom<UnityEngine.MonoBehaviour>())
  41. MonoBehaviourProcessor.Process(Log, td, ref WeavingFailed);
  42. return false;
  43. }
  44. // process this and base classes from parent to child order
  45. List<TypeDefinition> behaviourClasses = new List<TypeDefinition>();
  46. TypeDefinition parent = td;
  47. while (parent != null)
  48. {
  49. if (parent.Is<NetworkBehaviour>())
  50. {
  51. break;
  52. }
  53. try
  54. {
  55. behaviourClasses.Insert(0, parent);
  56. parent = parent.BaseType.Resolve();
  57. }
  58. catch (AssemblyResolutionException)
  59. {
  60. // this can happen for plugins.
  61. //Console.WriteLine("AssemblyResolutionException: "+ ex.ToString());
  62. break;
  63. }
  64. }
  65. bool modified = false;
  66. foreach (TypeDefinition behaviour in behaviourClasses)
  67. {
  68. modified |= new NetworkBehaviourProcessor(CurrentAssembly, weaverTypes, syncVarAccessLists, writers, readers, Log, behaviour).Process(ref WeavingFailed);
  69. }
  70. return modified;
  71. }
  72. bool WeaveModule(ModuleDefinition moduleDefinition)
  73. {
  74. bool modified = false;
  75. Stopwatch watch = Stopwatch.StartNew();
  76. watch.Start();
  77. foreach (TypeDefinition td in moduleDefinition.Types)
  78. {
  79. if (td.IsClass && td.BaseType.CanBeResolved())
  80. {
  81. modified |= WeaveNetworkBehavior(td);
  82. modified |= ServerClientAttributeProcessor.Process(weaverTypes, Log, td, ref WeavingFailed);
  83. }
  84. }
  85. watch.Stop();
  86. Console.WriteLine($"Weave behaviours and messages took {watch.ElapsedMilliseconds} milliseconds");
  87. return modified;
  88. }
  89. void CreateGeneratedCodeClass()
  90. {
  91. // create "Mirror.GeneratedNetworkCode" class which holds all
  92. // Readers<T> and Writers<T>
  93. GeneratedCodeClass = new TypeDefinition(GeneratedCodeNamespace, GeneratedCodeClassName,
  94. TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass | TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Abstract | TypeAttributes.Sealed,
  95. weaverTypes.Import<object>());
  96. }
  97. // Weave takes an AssemblyDefinition to be compatible with both old and
  98. // new weavers:
  99. // * old takes a filepath, new takes a in-memory byte[]
  100. // * old uses DefaultAssemblyResolver with added dependencies paths,
  101. // new uses ...?
  102. //
  103. // => assembly: the one we are currently weaving (MyGame.dll)
  104. // => resolver: useful in case we need to resolve any of the assembly's
  105. // assembly.MainModule.AssemblyReferences.
  106. // -> we can resolve ANY of them given that the resolver
  107. // works properly (need custom one for ILPostProcessor)
  108. // -> IMPORTANT: .Resolve() takes an AssemblyNameReference.
  109. // those from assembly.MainModule.AssemblyReferences are
  110. // guaranteed to be resolve-able.
  111. // Parsing from a string for Library/.../Mirror.dll
  112. // would not be guaranteed to be resolve-able because
  113. // for ILPostProcessor we can't assume where Mirror.dll
  114. // is etc.
  115. public bool Weave(AssemblyDefinition assembly, IAssemblyResolver resolver, out bool modified)
  116. {
  117. WeavingFailed = false;
  118. modified = false;
  119. try
  120. {
  121. Resolver = resolver;
  122. CurrentAssembly = assembly;
  123. // fix "No writer found for ..." error
  124. // https://github.com/vis2k/Mirror/issues/2579
  125. // -> when restarting Unity, weaver would try to weave a DLL
  126. // again
  127. // -> resulting in two GeneratedNetworkCode classes (see ILSpy)
  128. // -> the second one wouldn't have all the writer types setup
  129. if (CurrentAssembly.MainModule.ContainsClass(GeneratedCodeNamespace, GeneratedCodeClassName))
  130. {
  131. //Log.Warning($"Weaver: skipping {CurrentAssembly.Name} because already weaved");
  132. return true;
  133. }
  134. weaverTypes = new WeaverTypes(CurrentAssembly, Log, ref WeavingFailed);
  135. // weaverTypes are needed for CreateGeneratedCodeClass
  136. CreateGeneratedCodeClass();
  137. // WeaverList depends on WeaverTypes setup because it uses Import
  138. syncVarAccessLists = new SyncVarAccessLists();
  139. // initialize readers & writers with this assembly.
  140. // we need to do this in every Process() call.
  141. // otherwise we would get
  142. // "System.ArgumentException: Member ... is declared in another module and needs to be imported"
  143. // errors when still using the previous module's reader/writer funcs.
  144. writers = new Writers(CurrentAssembly, weaverTypes, GeneratedCodeClass, Log);
  145. readers = new Readers(CurrentAssembly, weaverTypes, GeneratedCodeClass, Log);
  146. Stopwatch rwstopwatch = Stopwatch.StartNew();
  147. // Need to track modified from ReaderWriterProcessor too because it could find custom read/write functions or create functions for NetworkMessages
  148. modified = ReaderWriterProcessor.Process(CurrentAssembly, resolver, Log, writers, readers, ref WeavingFailed);
  149. rwstopwatch.Stop();
  150. Console.WriteLine($"Find all reader and writers took {rwstopwatch.ElapsedMilliseconds} milliseconds");
  151. ModuleDefinition moduleDefinition = CurrentAssembly.MainModule;
  152. Console.WriteLine($"Script Module: {moduleDefinition.Name}");
  153. modified |= WeaveModule(moduleDefinition);
  154. if (WeavingFailed)
  155. {
  156. return false;
  157. }
  158. if (modified)
  159. {
  160. SyncVarAttributeAccessReplacer.Process(moduleDefinition, syncVarAccessLists);
  161. // add class that holds read/write functions
  162. moduleDefinition.Types.Add(GeneratedCodeClass);
  163. ReaderWriterProcessor.InitializeReaderAndWriters(CurrentAssembly, weaverTypes, writers, readers, GeneratedCodeClass);
  164. // DO NOT WRITE here.
  165. // CompilationFinishedHook writes to the file.
  166. // ILPostProcessor writes to in-memory assembly.
  167. // it depends on the caller.
  168. //CurrentAssembly.Write(new WriterParameters{ WriteSymbols = true });
  169. }
  170. return true;
  171. }
  172. catch (Exception e)
  173. {
  174. Log.Error($"Exception :{e}");
  175. WeavingFailed = true;
  176. return false;
  177. }
  178. }
  179. }
  180. }