123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using Mono.CecilX;
- namespace Mirror.Weaver
- {
- // This data is flushed each time - if we are run multiple times in the same process/domain
- class WeaverLists
- {
- // setter functions that replace [SyncVar] member variable references. dict<field, replacement>
- public Dictionary<FieldDefinition, MethodDefinition> replacementSetterProperties = new Dictionary<FieldDefinition, MethodDefinition>();
- // getter functions that replace [SyncVar] member variable references. dict<field, replacement>
- public Dictionary<FieldDefinition, MethodDefinition> replacementGetterProperties = new Dictionary<FieldDefinition, MethodDefinition>();
- // amount of SyncVars per class. dict<className, amount>
- public Dictionary<string, int> numSyncVars = new Dictionary<string, int>();
- public int GetSyncVarStart(string className)
- {
- return numSyncVars.ContainsKey(className)
- ? numSyncVars[className]
- : 0;
- }
- public void SetNumSyncVars(string className, int num)
- {
- numSyncVars[className] = num;
- }
- }
- internal static class Weaver
- {
- public static string InvokeRpcPrefix => "InvokeUserCode_";
- // generated code class
- public const string GeneratedCodeNamespace = "Mirror";
- public const string GeneratedCodeClassName = "GeneratedNetworkCode";
- public static TypeDefinition GeneratedCodeClass;
- public static WeaverLists WeaveLists { get; private set; }
- public static AssemblyDefinition CurrentAssembly { get; private set; }
- public static bool WeavingFailed { get; private set; }
- public static bool GenerateLogErrors;
- // private properties
- static readonly bool DebugLogEnabled = true;
- public static void DLog(TypeDefinition td, string fmt, params object[] args)
- {
- if (!DebugLogEnabled)
- return;
- Console.WriteLine("[" + td.Name + "] " + string.Format(fmt, args));
- }
- // display weaver error
- // and mark process as failed
- public static void Error(string message)
- {
- Log.Error(message);
- WeavingFailed = true;
- }
- public static void Error(string message, MemberReference mr)
- {
- Log.Error($"{message} (at {mr})");
- WeavingFailed = true;
- }
- public static void Warning(string message, MemberReference mr)
- {
- Log.Warning($"{message} (at {mr})");
- }
- static void CheckMonoBehaviour(TypeDefinition td)
- {
- if (td.IsDerivedFrom<UnityEngine.MonoBehaviour>())
- {
- MonoBehaviourProcessor.Process(td);
- }
- }
- static bool WeaveNetworkBehavior(TypeDefinition td)
- {
- if (!td.IsClass)
- return false;
- if (!td.IsDerivedFrom<NetworkBehaviour>())
- {
- CheckMonoBehaviour(td);
- 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(behaviour).Process();
- }
- return modified;
- }
- static bool WeaveModule(ModuleDefinition moduleDefinition)
- {
- try
- {
- bool modified = false;
- System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
- watch.Start();
- foreach (TypeDefinition td in moduleDefinition.Types)
- {
- if (td.IsClass && td.BaseType.CanBeResolved())
- {
- modified |= WeaveNetworkBehavior(td);
- modified |= ServerClientAttributeProcessor.Process(td);
- }
- }
- watch.Stop();
- Console.WriteLine("Weave behaviours and messages took " + watch.ElapsedMilliseconds + " milliseconds");
- return modified;
- }
- catch (Exception ex)
- {
- Error(ex.ToString());
- throw new Exception(ex.Message, ex);
- }
- }
- static void CreateGeneratedCodeClass()
- {
- // create "Mirror.GeneratedNetworkCode" class
- GeneratedCodeClass = new TypeDefinition(GeneratedCodeNamespace, GeneratedCodeClassName,
- TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass | TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Abstract | TypeAttributes.Sealed,
- WeaverTypes.Import<object>());
- }
- static bool ContainsGeneratedCodeClass(ModuleDefinition module)
- {
- return module.GetTypes().Any(td => td.Namespace == GeneratedCodeNamespace &&
- td.Name == GeneratedCodeClassName);
- }
- static bool Weave(string assName, IEnumerable<string> dependencies)
- {
- using (DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver())
- using (CurrentAssembly = AssemblyDefinition.ReadAssembly(assName, new ReaderParameters { ReadWrite = true, ReadSymbols = true, AssemblyResolver = asmResolver }))
- {
- asmResolver.AddSearchDirectory(Path.GetDirectoryName(assName));
- asmResolver.AddSearchDirectory(Helpers.UnityEngineDllDirectoryName());
- if (dependencies != null)
- {
- foreach (string path in dependencies)
- {
- asmResolver.AddSearchDirectory(path);
- }
- }
- // 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 (ContainsGeneratedCodeClass(CurrentAssembly.MainModule))
- {
- //Log.Warning($"Weaver: skipping {CurrentAssembly.Name} because already weaved");
- return true;
- }
- WeaverTypes.SetupTargetTypes(CurrentAssembly);
- CreateGeneratedCodeClass();
- // WeaverList depends on WeaverTypes setup because it uses Import
- WeaveLists = new WeaverLists();
- System.Diagnostics.Stopwatch rwstopwatch = System.Diagnostics.Stopwatch.StartNew();
- // Need to track modified from ReaderWriterProcessor too because it could find custom read/write functions or create functions for NetworkMessages
- bool modified = ReaderWriterProcessor.Process(CurrentAssembly);
- 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)
- {
- PropertySiteProcessor.Process(moduleDefinition);
- // add class that holds read/write functions
- moduleDefinition.Types.Add(GeneratedCodeClass);
- ReaderWriterProcessor.InitializeReaderAndWriters(CurrentAssembly);
- // write to outputDir if specified, otherwise perform in-place write
- WriterParameters writeParams = new WriterParameters { WriteSymbols = true };
- CurrentAssembly.Write(writeParams);
- }
- }
- return true;
- }
- public static bool WeaveAssembly(string assembly, IEnumerable<string> dependencies)
- {
- WeavingFailed = false;
- try
- {
- return Weave(assembly, dependencies);
- }
- catch (Exception e)
- {
- Log.Error("Exception :" + e);
- return false;
- }
- }
- }
- }
|