mirror of
				https://github.com/DerTyp7/defrain-shooter-unity.git
				synced 2025-11-03 22:58:59 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			215 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
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;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |