예제 #1
0
        // adds Mirror.GeneratedNetworkCode.InitReadWriters() method that
        // registers all generated writers into Mirror.Writer<T> static class.
        // -> uses [RuntimeInitializeOnLoad] attribute so it's invoke at runtime
        // -> uses [InitializeOnLoad] if UnityEditor is referenced so it works
        //    in Editor and in tests too
        //
        // use ILSpy to see the result (it's in the DLL's 'Mirror' namespace)
        public static void InitializeReaderAndWriters(AssemblyDefinition currentAssembly, WeaverTypes weaverTypes, Writers writers, Readers readers, TypeDefinition GeneratedCodeClass)
        {
            MethodDefinition initReadWriters = new MethodDefinition("InitReadWriters", MethodAttributes.Public |
                                                                    MethodAttributes.Static,
                                                                    weaverTypes.Import(typeof(void)));

            // add [RuntimeInitializeOnLoad] in any case
            Helpers.AddRuntimeInitializeOnLoadAttribute(currentAssembly, weaverTypes, initReadWriters);

            // add [InitializeOnLoad] if UnityEditor is referenced
            if (Helpers.IsEditorAssembly(currentAssembly))
            {
                Helpers.AddInitializeOnLoadAttribute(currentAssembly, weaverTypes, initReadWriters);
            }

            // fill function body with reader/writer initializers
            ILProcessor worker = initReadWriters.Body.GetILProcessor();

            // for debugging: add a log to see if initialized on load
            //worker.Emit(OpCodes.Ldstr, $"[InitReadWriters] called!");
            //worker.Emit(OpCodes.Call, Weaver.weaverTypes.logWarningReference);
            writers.InitializeWriters(worker);
            readers.InitializeReaders(worker);
            worker.Emit(OpCodes.Ret);

            GeneratedCodeClass.Methods.Add(initReadWriters);
        }
예제 #2
0
        // we need to inject several initializations into NetworkBehaviour cctor
        void InjectIntoStaticConstructor(ref bool WeavingFailed)
        {
            if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0)
            {
                return;
            }

            // find static constructor
            MethodDefinition cctor      = netBehaviourSubclass.GetMethod(".cctor");
            bool             cctorFound = cctor != null;

            if (cctor != null)
            {
                // remove the return opcode from end of function. will add our own later.
                if (!RemoveFinalRetInstruction(cctor))
                {
                    Log.Error($"{netBehaviourSubclass.Name} has invalid class constructor", cctor);
                    WeavingFailed = true;
                    return;
                }
            }
            else
            {
                // make one!
                cctor = new MethodDefinition(".cctor", MethodAttributes.Private |
                                             MethodAttributes.HideBySig |
                                             MethodAttributes.SpecialName |
                                             MethodAttributes.RTSpecialName |
                                             MethodAttributes.Static,
                                             weaverTypes.Import(typeof(void)));
            }

            // Static constructors are lazily called when the class is first "used"
            // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
            // > It is called automatically before the first instance is created or any static members are referenced.
            //
            // This means, in particular circumstances, client and server may diverge on which classes they have "loaded"
            // and thus the rpc index will be desynced
            //
            // One such example would be on-demand-loading of prefabs via custom spawn handlers:
            //   A game might not want to preload all its monster prefabs since there's quite many of them
            //   and it is practically impossible to encounter them all (or even a majority of them) in a single play
            //   session.
            //   So a custom spawn handler is used to load the monster prefabs on demand.
            //   All monsters would have the "Monster" NetworkBehaviour class, which would have a few RPCs on it.
            //   Since the monster prefabs are loaded ONLY when needed via a custom spawn handler and the Monster class
            //   itself isn't referenced or "loaded" by anything else other than the prefab, the monster classes static
            //   constructor will not have been called when a client first joins a game, while it may have been called
            //   on the server/host. This will in turn cause Rpc indexes to not match up between client/server
            //
            // By just forcing the static constructors to run via the [RuntimeInitializeOnLoadMethod] attribute
            // this is not a problem anymore, since all static constructors are always run and all RPCs will
            // always be registered by the time they are used
            if (!cctor.HasCustomAttribute <RuntimeInitializeOnLoadMethodAttribute>())
            {
                Helpers.AddRuntimeInitializeOnLoadAttribute(assembly, weaverTypes, cctor);
            }

            ILProcessor cctorWorker = cctor.Body.GetILProcessor();

            // register all commands in cctor
            for (int i = 0; i < commands.Count; ++i)
            {
                CmdResult cmdResult = commands[i];
                GenerateRegisterCommandDelegate(cctorWorker, weaverTypes.registerCommandReference, commandInvocationFuncs[i], cmdResult);
            }

            // register all client rpcs in cctor
            for (int i = 0; i < clientRpcs.Count; ++i)
            {
                ClientRpcResult clientRpcResult = clientRpcs[i];
                GenerateRegisterRemoteDelegate(cctorWorker, weaverTypes.registerRpcReference, clientRpcInvocationFuncs[i], clientRpcResult.method.FullName);
            }

            // register all target rpcs in cctor
            for (int i = 0; i < targetRpcs.Count; ++i)
            {
                GenerateRegisterRemoteDelegate(cctorWorker, weaverTypes.registerRpcReference, targetRpcInvocationFuncs[i], targetRpcs[i].FullName);
            }

            // add final 'Ret' instruction to cctor
            cctorWorker.Append(cctorWorker.Create(OpCodes.Ret));
            if (!cctorFound)
            {
                netBehaviourSubclass.Methods.Add(cctor);
            }

            // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late
            netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit;
        }