Weaver.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using Mono.CecilX;
  6. namespace Mirror.Weaver
  7. {
  8. // This data is flushed each time - if we are run multiple times in the same process/domain
  9. class WeaverLists
  10. {
  11. // setter functions that replace [SyncVar] member variable references. dict<field, replacement>
  12. public Dictionary<FieldDefinition, MethodDefinition> replacementSetterProperties = new Dictionary<FieldDefinition, MethodDefinition>();
  13. // getter functions that replace [SyncVar] member variable references. dict<field, replacement>
  14. public Dictionary<FieldDefinition, MethodDefinition> replacementGetterProperties = new Dictionary<FieldDefinition, MethodDefinition>();
  15. // amount of SyncVars per class. dict<className, amount>
  16. public Dictionary<string, int> numSyncVars = new Dictionary<string, int>();
  17. public int GetSyncVarStart(string className)
  18. {
  19. return numSyncVars.ContainsKey(className)
  20. ? numSyncVars[className]
  21. : 0;
  22. }
  23. public void SetNumSyncVars(string className, int num)
  24. {
  25. numSyncVars[className] = num;
  26. }
  27. }
  28. internal static class Weaver
  29. {
  30. public static string InvokeRpcPrefix => "InvokeUserCode_";
  31. // generated code class
  32. public const string GeneratedCodeNamespace = "Mirror";
  33. public const string GeneratedCodeClassName = "GeneratedNetworkCode";
  34. public static TypeDefinition GeneratedCodeClass;
  35. public static WeaverLists WeaveLists { get; private set; }
  36. public static AssemblyDefinition CurrentAssembly { get; private set; }
  37. public static bool WeavingFailed { get; private set; }
  38. public static bool GenerateLogErrors;
  39. // private properties
  40. static readonly bool DebugLogEnabled = true;
  41. public static void DLog(TypeDefinition td, string fmt, params object[] args)
  42. {
  43. if (!DebugLogEnabled)
  44. return;
  45. Console.WriteLine("[" + td.Name + "] " + string.Format(fmt, args));
  46. }
  47. // display weaver error
  48. // and mark process as failed
  49. public static void Error(string message)
  50. {
  51. Log.Error(message);
  52. WeavingFailed = true;
  53. }
  54. public static void Error(string message, MemberReference mr)
  55. {
  56. Log.Error($"{message} (at {mr})");
  57. WeavingFailed = true;
  58. }
  59. public static void Warning(string message, MemberReference mr)
  60. {
  61. Log.Warning($"{message} (at {mr})");
  62. }
  63. static void CheckMonoBehaviour(TypeDefinition td)
  64. {
  65. if (td.IsDerivedFrom<UnityEngine.MonoBehaviour>())
  66. {
  67. MonoBehaviourProcessor.Process(td);
  68. }
  69. }
  70. static bool WeaveNetworkBehavior(TypeDefinition td)
  71. {
  72. if (!td.IsClass)
  73. return false;
  74. if (!td.IsDerivedFrom<NetworkBehaviour>())
  75. {
  76. CheckMonoBehaviour(td);
  77. return false;
  78. }
  79. // process this and base classes from parent to child order
  80. List<TypeDefinition> behaviourClasses = new List<TypeDefinition>();
  81. TypeDefinition parent = td;
  82. while (parent != null)
  83. {
  84. if (parent.Is<NetworkBehaviour>())
  85. {
  86. break;
  87. }
  88. try
  89. {
  90. behaviourClasses.Insert(0, parent);
  91. parent = parent.BaseType.Resolve();
  92. }
  93. catch (AssemblyResolutionException)
  94. {
  95. // this can happen for plugins.
  96. //Console.WriteLine("AssemblyResolutionException: "+ ex.ToString());
  97. break;
  98. }
  99. }
  100. bool modified = false;
  101. foreach (TypeDefinition behaviour in behaviourClasses)
  102. {
  103. modified |= new NetworkBehaviourProcessor(behaviour).Process();
  104. }
  105. return modified;
  106. }
  107. static bool WeaveModule(ModuleDefinition moduleDefinition)
  108. {
  109. try
  110. {
  111. bool modified = false;
  112. System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
  113. watch.Start();
  114. foreach (TypeDefinition td in moduleDefinition.Types)
  115. {
  116. if (td.IsClass && td.BaseType.CanBeResolved())
  117. {
  118. modified |= WeaveNetworkBehavior(td);
  119. modified |= ServerClientAttributeProcessor.Process(td);
  120. }
  121. }
  122. watch.Stop();
  123. Console.WriteLine("Weave behaviours and messages took " + watch.ElapsedMilliseconds + " milliseconds");
  124. return modified;
  125. }
  126. catch (Exception ex)
  127. {
  128. Error(ex.ToString());
  129. throw new Exception(ex.Message, ex);
  130. }
  131. }
  132. static void CreateGeneratedCodeClass()
  133. {
  134. // create "Mirror.GeneratedNetworkCode" class
  135. GeneratedCodeClass = new TypeDefinition(GeneratedCodeNamespace, GeneratedCodeClassName,
  136. TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass | TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Abstract | TypeAttributes.Sealed,
  137. WeaverTypes.Import<object>());
  138. }
  139. static bool ContainsGeneratedCodeClass(ModuleDefinition module)
  140. {
  141. return module.GetTypes().Any(td => td.Namespace == GeneratedCodeNamespace &&
  142. td.Name == GeneratedCodeClassName);
  143. }
  144. static bool Weave(string assName, IEnumerable<string> dependencies)
  145. {
  146. using (DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver())
  147. using (CurrentAssembly = AssemblyDefinition.ReadAssembly(assName, new ReaderParameters { ReadWrite = true, ReadSymbols = true, AssemblyResolver = asmResolver }))
  148. {
  149. asmResolver.AddSearchDirectory(Path.GetDirectoryName(assName));
  150. asmResolver.AddSearchDirectory(Helpers.UnityEngineDllDirectoryName());
  151. if (dependencies != null)
  152. {
  153. foreach (string path in dependencies)
  154. {
  155. asmResolver.AddSearchDirectory(path);
  156. }
  157. }
  158. // fix "No writer found for ..." error
  159. // https://github.com/vis2k/Mirror/issues/2579
  160. // -> when restarting Unity, weaver would try to weave a DLL
  161. // again
  162. // -> resulting in two GeneratedNetworkCode classes (see ILSpy)
  163. // -> the second one wouldn't have all the writer types setup
  164. if (ContainsGeneratedCodeClass(CurrentAssembly.MainModule))
  165. {
  166. //Log.Warning($"Weaver: skipping {CurrentAssembly.Name} because already weaved");
  167. return true;
  168. }
  169. WeaverTypes.SetupTargetTypes(CurrentAssembly);
  170. CreateGeneratedCodeClass();
  171. // WeaverList depends on WeaverTypes setup because it uses Import
  172. WeaveLists = new WeaverLists();
  173. System.Diagnostics.Stopwatch rwstopwatch = System.Diagnostics.Stopwatch.StartNew();
  174. // Need to track modified from ReaderWriterProcessor too because it could find custom read/write functions or create functions for NetworkMessages
  175. bool modified = ReaderWriterProcessor.Process(CurrentAssembly);
  176. rwstopwatch.Stop();
  177. Console.WriteLine($"Find all reader and writers took {rwstopwatch.ElapsedMilliseconds} milliseconds");
  178. ModuleDefinition moduleDefinition = CurrentAssembly.MainModule;
  179. Console.WriteLine($"Script Module: {moduleDefinition.Name}");
  180. modified |= WeaveModule(moduleDefinition);
  181. if (WeavingFailed)
  182. {
  183. return false;
  184. }
  185. if (modified)
  186. {
  187. PropertySiteProcessor.Process(moduleDefinition);
  188. // add class that holds read/write functions
  189. moduleDefinition.Types.Add(GeneratedCodeClass);
  190. ReaderWriterProcessor.InitializeReaderAndWriters(CurrentAssembly);
  191. // write to outputDir if specified, otherwise perform in-place write
  192. WriterParameters writeParams = new WriterParameters { WriteSymbols = true };
  193. CurrentAssembly.Write(writeParams);
  194. }
  195. }
  196. return true;
  197. }
  198. public static bool WeaveAssembly(string assembly, IEnumerable<string> dependencies)
  199. {
  200. WeavingFailed = false;
  201. try
  202. {
  203. return Weave(assembly, dependencies);
  204. }
  205. catch (Exception e)
  206. {
  207. Log.Error("Exception :" + e);
  208. return false;
  209. }
  210. }
  211. }
  212. }