Esempio n. 1
0
        public void Process()
        {
            if (m_td.HasGenericParameters)
            {
                Weaver.fail = true;
                Log.Error("NetworkBehaviour " + m_td.Name + " cannot have generic parameters");
                return;
            }
            Weaver.DLog(m_td, "Process Start");
            MarkAsProcessed(m_td);
            SyncVarProcessor.ProcessSyncVars(m_td, m_SyncVars, m_SyncObjects, m_SyncVarNetIds);
            Weaver.ResetRecursionCount();

            ProcessMethods();

            SyncEventProcessor.ProcessEvents(m_td, m_Events, m_EventInvocationFuncs);
            if (Weaver.fail)
            {
                return;
            }
            GenerateConstants();

            Weaver.ResetRecursionCount();
            GenerateSerialization();
            if (Weaver.fail)
            {
                return;
            }

            GenerateDeSerialization();
            Weaver.DLog(m_td, "Process Done");
        }
        public void Process()
        {
            if (netBehaviourSubclass.HasGenericParameters)
            {
                Weaver.Error($"{netBehaviourSubclass} cannot have generic parameters");
                return;
            }
            Weaver.DLog(netBehaviourSubclass, "Process Start");
            MarkAsProcessed(netBehaviourSubclass);
            SyncVarProcessor.ProcessSyncVars(netBehaviourSubclass, syncVars, syncObjects, syncVarNetIds);

            ProcessMethods();

            SyncEventProcessor.ProcessEvents(netBehaviourSubclass, eventRpcs, eventRpcInvocationFuncs);
            if (Weaver.WeavingFailed)
            {
                return;
            }
            GenerateConstants();

            GenerateSerialization();
            if (Weaver.WeavingFailed)
            {
                return;
            }

            GenerateDeSerialization();
            Weaver.DLog(netBehaviourSubclass, "Process Done");
        }
 public NetworkBehaviourProcessor(TypeDefinition td, Readers readers, Writers writers, PropertySiteProcessor propertySiteProcessor, IWeaverLogger logger)
 {
     Weaver.DLog(td, "NetworkBehaviourProcessor");
     netBehaviourSubclass = td;
     this.logger          = logger;
     serverRpcProcessor   = new ServerRpcProcessor(netBehaviourSubclass.Module, readers, writers, logger);
     clientRpcProcessor   = new ClientRpcProcessor(netBehaviourSubclass.Module, readers, writers, logger);
     syncVarProcessor     = new SyncVarProcessor(netBehaviourSubclass.Module, readers, writers, propertySiteProcessor, logger);
     syncObjectProcessor  = new SyncObjectProcessor(readers, writers, logger);
 }
Esempio n. 4
0
        void DeserializeField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize)
        {
            // check for Hook function
            MethodDefinition hookMethod = SyncVarProcessor.GetHookMethod(netBehaviourSubclass, syncVar);

            if (IsNetworkIdentityField(syncVar))
            {
                DeserializeNetworkIdentityField(syncVar, worker, deserialize, hookMethod);
            }
            else
            {
                DeserializeNormalField(syncVar, worker, deserialize, hookMethod);
            }
        }
        // return true if modified
        public bool Process()
        {
            // only process once
            if (WasProcessed(netBehaviourSubclass))
            {
                return(false);
            }
            Weaver.DLog(netBehaviourSubclass, "Found NetworkBehaviour " + netBehaviourSubclass.FullName);

            if (netBehaviourSubclass.HasGenericParameters)
            {
                Weaver.Error($"{netBehaviourSubclass.Name} cannot have generic parameters", netBehaviourSubclass);
                // originally Process returned true in every case, except if already processed.
                // maybe return false here in the future.
                return(true);
            }
            Weaver.DLog(netBehaviourSubclass, "Process Start");
            MarkAsProcessed(netBehaviourSubclass);

            // deconstruct tuple and set fields
            (syncVars, syncVarNetIds) = SyncVarProcessor.ProcessSyncVars(netBehaviourSubclass);

            syncObjects = SyncObjectProcessor.FindSyncObjectsFields(netBehaviourSubclass);

            ProcessMethods();

            SyncEventProcessor.ProcessEvents(netBehaviourSubclass, eventRpcs, eventRpcInvocationFuncs);
            if (Weaver.WeavingFailed)
            {
                // originally Process returned true in every case, except if already processed.
                // maybe return false here in the future.
                return(true);
            }
            GenerateConstants();

            GenerateSerialization();
            if (Weaver.WeavingFailed)
            {
                // originally Process returned true in every case, except if already processed.
                // maybe return false here in the future.
                return(true);
            }

            GenerateDeSerialization();
            Weaver.DLog(netBehaviourSubclass, "Process Done");
            return(true);
        }
        void DeserializeField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize)
        {
            // check for Hook function
            if (!SyncVarProcessor.CheckForHookFunction(netBehaviourSubclass, syncVar, out MethodDefinition foundMethod))
            {
                return;
            }

            // [SyncVar] GameObject/NetworkIdentity?

            /*
             * Generates code like:
             *  uint oldNetId = ___qNetId;
             *  GameObject oldSyncVar = syncvar.getter; // returns GetSyncVarGameObject(___qNetId)
             *  ___qNetId = reader.ReadPackedUInt32();
             *  if (!SyncVarEqual(oldNetId, ref ___goNetId))
             *  {
             *      OnSetQ(oldSyncVar, syncvar.getter); // getter returns GetSyncVarGameObject(___qNetId)
             *  }
             */
            if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName ||
                syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
            {
                // GameObject/NetworkIdentity SyncVar:
                //   OnSerialize sends writer.Write(go);
                //   OnDeserialize reads to __netId manually so we can use
                //     lookups in the getter (so it still works if objects
                //     move in and out of range repeatedly)
                FieldDefinition netIdField = syncVarNetIds[syncVar];

                // uint oldNetId = ___qNetId;
                VariableDefinition oldNetId = new VariableDefinition(Weaver.uint32Type);
                deserialize.Body.Variables.Add(oldNetId);
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                serWorker.Append(serWorker.Create(OpCodes.Ldfld, netIdField));
                serWorker.Append(serWorker.Create(OpCodes.Stloc, oldNetId));

                // GameObject/NetworkIdentity oldSyncVar = syncvar.getter;
                VariableDefinition oldSyncVar = new VariableDefinition(syncVar.FieldType);
                deserialize.Body.Variables.Add(oldSyncVar);
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar));
                serWorker.Append(serWorker.Create(OpCodes.Stloc, oldSyncVar));

                // read id and store in netId field BEFORE calling the hook
                // -> this makes way more sense. by definition, the hook is
                //    supposed to be called after it was changed. not before.
                // -> setting it BEFORE calling the hook fixes the following bug:
                //    https://github.com/vis2k/Mirror/issues/1151 in host mode
                //    where the value during the Hook call would call Cmds on
                //    the host server, and they would all happen and compare
                //    values BEFORE the hook even returned and hence BEFORE the
                //    actual value was even set.
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));                                      // put 'this.' onto stack for 'this.netId' below
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));                                      // reader. for 'reader.Read()' below
                serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(Weaver.uint32Type))); // Read()
                serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField));                            // netId

                if (foundMethod != null)
                {
                    // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32()))
                    // because we send/receive the netID, not the GameObject/NetworkIdentity
                    // but only if SyncVar changed. otherwise a client would
                    // get hook calls for all initial values, even if they
                    // didn't change from the default values on the client.
                    // see also: https://github.com/vis2k/Mirror/issues/1278

                    // IMPORTANT: for GameObjects/NetworkIdentities we usually
                    //            use SyncVarGameObjectEqual to compare equality.
                    //            in this case however, we can just use
                    //            SyncVarEqual with the two uint netIds.
                    //            => this is easier weaver code because we don't
                    //               have to get the GameObject/NetworkIdentity
                    //               from the uint netId
                    //            => this is faster because we void one
                    //               GetComponent call for GameObjects to get
                    //               their NetworkIdentity when comparing.

                    // Generates: if (!SyncVarEqual);
                    Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop);

                    // 'this.' for 'this.SyncVarEqual'
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    // 'oldNetId'
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldNetId));
                    // 'ref this.__netId'
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldflda, netIdField));
                    // call the function
                    GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(Weaver.syncVarEqualReference);
                    syncVarEqualGm.GenericArguments.Add(netIdField.FieldType);
                    serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm));
                    serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel));

                    // call the hook
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));           // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldSyncVar)); // oldSyncVar GO/NI
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));           // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar));    // syncvar.get (finds current GO/NI from netId)
                    serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));

                    // Generates: end if (!SyncVarEqual);
                    serWorker.Append(syncVarEqualLabel);
                }
            }
            // [SyncVar] int/float/struct/etc.?

            /*
             * Generates code like:
             *  int oldValue = a; // for hook
             *  Networka = reader.ReadPackedInt32();
             *  if (!SyncVarEqual(oldValue, ref a))
             *  {
             *      OnSetA(oldValue, Networka);
             *  }
             */
            else
            {
                MethodReference readFunc = Readers.GetReadFunc(syncVar.FieldType);
                if (readFunc == null)
                {
                    Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead");
                    return;
                }

                // T oldValue = value;
                VariableDefinition oldValue = new VariableDefinition(syncVar.FieldType);
                deserialize.Body.Variables.Add(oldValue);
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar));
                serWorker.Append(serWorker.Create(OpCodes.Stloc, oldValue));

                // read value and store in syncvar BEFORE calling the hook
                // -> this makes way more sense. by definition, the hook is
                //    supposed to be called after it was changed. not before.
                // -> setting it BEFORE calling the hook fixes the following bug:
                //    https://github.com/vis2k/Mirror/issues/1151 in host mode
                //    where the value during the Hook call would call Cmds on
                //    the host server, and they would all happen and compare
                //    values BEFORE the hook even returned and hence BEFORE the
                //    actual value was even set.
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));        // put 'this.' onto stack for 'this.syncvar' below
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));        // reader. for 'reader.Read()' below
                serWorker.Append(serWorker.Create(OpCodes.Call, readFunc)); // reader.Read()
                serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar)); // syncvar

                if (foundMethod != null)
                {
                    // call hook
                    // but only if SyncVar changed. otherwise a client would
                    // get hook calls for all initial values, even if they
                    // didn't change from the default values on the client.
                    // see also: https://github.com/vis2k/Mirror/issues/1278

                    // Generates: if (!SyncVarEqual);
                    Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop);

                    // 'this.' for 'this.SyncVarEqual'
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    // 'oldValue'
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue));
                    // 'ref this.syncVar'
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar));
                    // call the function
                    GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(Weaver.syncVarEqualReference);
                    syncVarEqualGm.GenericArguments.Add(syncVar.FieldType);
                    serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm));
                    serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel));

                    // call the hook
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));         // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue)); // oldvalue
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));         // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar));  // syncvar.get
                    serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));

                    // Generates: end if (!SyncVarEqual);
                    serWorker.Append(syncVarEqualLabel);
                }
            }
        }
Esempio n. 7
0
        /// <summary>
        /// [SyncVar] int/float/struct/etc.?
        /// </summary>
        /// <param name="syncVar"></param>
        /// <param name="serWorker"></param>
        /// <param name="deserialize"></param>
        /// <param name="initialState"></param>
        /// <param name="hookResult"></param>
        void DeserializeNormalField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize, MethodDefinition hookMethod)
        {
            /*
             * Generates code like:
             *  // for hook
             *  int oldValue = a;
             *  Networka = reader.ReadPackedInt32();
             *  if (!SyncVarEqual(oldValue, ref a))
             *  {
             *      OnSetA(oldValue, Networka);
             *  }
             */

            MethodReference readFunc = Readers.GetReadFunc(syncVar.FieldType);

            if (readFunc == null)
            {
                Weaver.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar);
                return;
            }

            // T oldValue = value;
            VariableDefinition oldValue = new VariableDefinition(syncVar.FieldType);

            deserialize.Body.Variables.Add(oldValue);
            serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
            serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar));
            serWorker.Append(serWorker.Create(OpCodes.Stloc, oldValue));

            // read value and store in syncvar BEFORE calling the hook
            // -> this makes way more sense. by definition, the hook is
            //    supposed to be called after it was changed. not before.
            // -> setting it BEFORE calling the hook fixes the following bug:
            //    https://github.com/vis2k/Mirror/issues/1151 in host mode
            //    where the value during the Hook call would call Cmds on
            //    the host server, and they would all happen and compare
            //    values BEFORE the hook even returned and hence BEFORE the
            //    actual value was even set.
            // put 'this.' onto stack for 'this.syncvar' below
            serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
            // reader. for 'reader.Read()' below
            serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
            // reader.Read()
            serWorker.Append(serWorker.Create(OpCodes.Call, readFunc));
            // syncvar
            serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar));

            if (hookMethod != null)
            {
                // call hook
                // but only if SyncVar changed. otherwise a client would
                // get hook calls for all initial values, even if they
                // didn't change from the default values on the client.
                // see also: https://github.com/vis2k/Mirror/issues/1278

                // Generates: if (!SyncVarEqual);
                Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop);

                // 'this.' for 'this.SyncVarEqual'
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                // 'oldValue'
                serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue));
                // 'ref this.syncVar'
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar));
                // call the function
                GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(WeaverTypes.syncVarEqualReference);
                syncVarEqualGm.GenericArguments.Add(syncVar.FieldType);
                serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm));
                serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel));

                // call the hook
                // Generates: OnValueChanged(oldValue, this.syncVar);
                SyncVarProcessor.WriteCallHookMethodUsingField(serWorker, hookMethod, oldValue, syncVar);

                // Generates: end if (!SyncVarEqual);
                serWorker.Append(syncVarEqualLabel);
            }
        }
Esempio n. 8
0
        /// <summary>
        /// [SyncVar] GameObject/NetworkIdentity?
        /// </summary>
        /// <param name="syncVar"></param>
        /// <param name="worker"></param>
        /// <param name="deserialize"></param>
        /// <param name="initialState"></param>
        /// <param name="hookResult"></param>
        void DeserializeNetworkIdentityField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize, MethodDefinition hookMethod)
        {
            /*
             * Generates code like:
             * uint oldNetId = ___qNetId;
             * // returns GetSyncVarGameObject(___qNetId)
             * GameObject oldSyncVar = syncvar.getter;
             * ___qNetId = reader.ReadPackedUInt32();
             * if (!SyncVarEqual(oldNetId, ref ___goNetId))
             * {
             *     // getter returns GetSyncVarGameObject(___qNetId)
             *     OnSetQ(oldSyncVar, syncvar.getter);
             * }
             */

            // GameObject/NetworkIdentity SyncVar:
            //   OnSerialize sends writer.Write(go);
            //   OnDeserialize reads to __netId manually so we can use
            //     lookups in the getter (so it still works if objects
            //     move in and out of range repeatedly)
            FieldDefinition netIdField = syncVarNetIds[syncVar];

            // uint oldNetId = ___qNetId;
            VariableDefinition oldNetId = new VariableDefinition(WeaverTypes.Import <uint>());

            deserialize.Body.Variables.Add(oldNetId);
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Ldfld, netIdField));
            worker.Append(worker.Create(OpCodes.Stloc, oldNetId));

            // GameObject/NetworkIdentity oldSyncVar = syncvar.getter;
            VariableDefinition oldSyncVar = new VariableDefinition(syncVar.FieldType);

            deserialize.Body.Variables.Add(oldSyncVar);
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Ldfld, syncVar));
            worker.Append(worker.Create(OpCodes.Stloc, oldSyncVar));

            // read id and store in netId field BEFORE calling the hook
            // -> this makes way more sense. by definition, the hook is
            //    supposed to be called after it was changed. not before.
            // -> setting it BEFORE calling the hook fixes the following bug:
            //    https://github.com/vis2k/Mirror/issues/1151 in host mode
            //    where the value during the Hook call would call Cmds on
            //    the host server, and they would all happen and compare
            //    values BEFORE the hook even returned and hence BEFORE the
            //    actual value was even set.
            // put 'this.' onto stack for 'this.netId' below
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            // reader. for 'reader.Read()' below
            worker.Append(worker.Create(OpCodes.Ldarg_1));
            // Read()
            worker.Append(worker.Create(OpCodes.Call, Readers.GetReadFunc(WeaverTypes.Import <uint>())));
            // netId
            worker.Append(worker.Create(OpCodes.Stfld, netIdField));

            if (hookMethod != null)
            {
                // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32()))
                // because we send/receive the netID, not the GameObject/NetworkIdentity
                // but only if SyncVar changed. otherwise a client would
                // get hook calls for all initial values, even if they
                // didn't change from the default values on the client.
                // see also: https://github.com/vis2k/Mirror/issues/1278

                // IMPORTANT: for GameObjects/NetworkIdentities we usually
                //            use SyncVarGameObjectEqual to compare equality.
                //            in this case however, we can just use
                //            SyncVarEqual with the two uint netIds.
                //            => this is easier weaver code because we don't
                //               have to get the GameObject/NetworkIdentity
                //               from the uint netId
                //            => this is faster because we void one
                //               GetComponent call for GameObjects to get
                //               their NetworkIdentity when comparing.

                // Generates: if (!SyncVarEqual);
                Instruction syncVarEqualLabel = worker.Create(OpCodes.Nop);

                // 'this.' for 'this.SyncVarEqual'
                worker.Append(worker.Create(OpCodes.Ldarg_0));
                // 'oldNetId'
                worker.Append(worker.Create(OpCodes.Ldloc, oldNetId));
                // 'ref this.__netId'
                worker.Append(worker.Create(OpCodes.Ldarg_0));
                worker.Append(worker.Create(OpCodes.Ldflda, netIdField));
                // call the function
                GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(WeaverTypes.syncVarEqualReference);
                syncVarEqualGm.GenericArguments.Add(netIdField.FieldType);
                worker.Append(worker.Create(OpCodes.Call, syncVarEqualGm));
                worker.Append(worker.Create(OpCodes.Brtrue, syncVarEqualLabel));

                // call the hook
                // Generates: OnValueChanged(oldValue, this.syncVar);
                SyncVarProcessor.WriteCallHookMethodUsingField(worker, hookMethod, oldSyncVar, syncVar);

                // Generates: end if (!SyncVarEqual);
                worker.Append(syncVarEqualLabel);
            }
        }
        void DeserializeField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize)
        {
            // check for Hook function
            if (!SyncVarProcessor.CheckForHookFunction(netBehaviourSubclass, syncVar, out MethodDefinition foundMethod))
            {
                return;
            }

            if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName ||
                syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
            {
                // GameObject/NetworkIdentity SyncVar:
                //   OnSerialize sends writer.Write(go);
                //   OnDeserialize reads to __netId manually so we can use
                //     lookups in the getter (so it still works if objects
                //     move in and out of range repeatedly)
                FieldDefinition netIdField = syncVarNetIds[syncVar];

                VariableDefinition tmpValue = new VariableDefinition(Weaver.uint32Type);
                deserialize.Body.Variables.Add(tmpValue);

                // read id and store in a local variable
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.NetworkReaderReadPackedUInt32));
                serWorker.Append(serWorker.Create(OpCodes.Stloc, tmpValue));

                if (foundMethod != null)
                {
                    // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32()))
                    // because we send/receive the netID, not the GameObject/NetworkIdentity
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, tmpValue));
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar));
                    if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName)
                    {
                        serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.getSyncVarGameObjectReference));
                    }
                    else if (syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
                    {
                        serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.getSyncVarNetworkIdentityReference));
                    }
                    serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));
                }
                // set the netid field
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                serWorker.Append(serWorker.Create(OpCodes.Ldloc, tmpValue));
                serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField));
            }
            else
            {
                MethodReference readFunc = Readers.GetReadFunc(syncVar.FieldType);
                if (readFunc == null)
                {
                    Weaver.Error("GenerateDeSerialization for " + netBehaviourSubclass.Name + " unknown type [" + syncVar.FieldType + "]. Mirror [SyncVar] member variables must be basic types.");
                    return;
                }
                VariableDefinition tmpValue = new VariableDefinition(syncVar.FieldType);
                deserialize.Body.Variables.Add(tmpValue);

                // read value and put it in a local variable
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                serWorker.Append(serWorker.Create(OpCodes.Call, readFunc));
                serWorker.Append(serWorker.Create(OpCodes.Stloc, tmpValue));

                if (foundMethod != null)
                {
                    // call hook
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, tmpValue));
                    serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));
                }
                // set the property
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                serWorker.Append(serWorker.Create(OpCodes.Ldloc, tmpValue));
                serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar));
            }
        }
Esempio n. 10
0
        void GenerateDeSerialization()
        {
            Weaver.DLog(m_td, "  GenerateDeSerialization");

            foreach (MethodDefinition m in m_td.Methods)
            {
                if (m.Name == "OnDeserialize")
                {
                    return;
                }
            }

            if (m_SyncVars.Count == 0)
            {
                // no synvars,  no need for custom OnDeserialize
                return;
            }

            MethodDefinition serialize = new MethodDefinition("OnDeserialize",
                                                              MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig,
                                                              Weaver.voidType);

            serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.scriptDef.MainModule.ImportReference(Weaver.NetworkReaderType)));
            serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, Weaver.boolType));
            ILProcessor serWorker = serialize.Body.GetILProcessor();

            MethodReference baseDeserialize = Resolvers.ResolveMethodInParents(m_td.BaseType, Weaver.scriptDef, "OnDeserialize");

            if (baseDeserialize != null)
            {
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // reader
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); // initialState
                serWorker.Append(serWorker.Create(OpCodes.Call, baseDeserialize));
            }

            // Generates: if (initialState);
            Instruction initialStateLabel = serWorker.Create(OpCodes.Nop);

            serWorker.Append(serWorker.Create(OpCodes.Ldarg_2));
            serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel));

            foreach (FieldDefinition syncVar in m_SyncVars)
            {
                // assign value
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));

                if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName ||
                    syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
                {
                    // GameObject/NetworkIdentity SyncVar:
                    //   OnSerialize sends writer.Write(go);
                    //   OnDeserialize reads to __netId manually so we can use
                    //     lookups in the getter (so it still works if objects
                    //     move in and out of range repeatedly)
                    FieldDefinition netIdField = m_SyncVarNetIds[syncVar];

                    serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.NetworkReaderReadPacked32));
                    serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField));
                }
                else
                {
                    MethodReference readFunc = Weaver.GetReadFunc(syncVar.FieldType);
                    if (readFunc != null)
                    {
                        serWorker.Append(serWorker.Create(OpCodes.Call, readFunc));
                    }
                    else
                    {
                        Log.Error("GenerateDeSerialization for " + m_td.Name + " unknown type [" + syncVar.FieldType + "]. UNet [SyncVar] member variables must be basic types.");
                        Weaver.fail = true;
                        return;
                    }
                    serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar));
                }
            }

            serWorker.Append(serWorker.Create(OpCodes.Ret));

            // Generates: end if (initialState);
            serWorker.Append(initialStateLabel);

            // setup local for dirty bits
            serialize.Body.InitLocals = true;
            VariableDefinition dirtyBitsLocal = new VariableDefinition(Weaver.int64Type);

            serialize.Body.Variables.Add(dirtyBitsLocal);

            // get dirty bits
            serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
            serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.NetworkReaderReadPacked64));
            serWorker.Append(serWorker.Create(OpCodes.Stloc_0));

            // conditionally read each syncvar
            int dirtyBit = Weaver.GetSyncVarStart(m_td.BaseType.FullName); // start at number of syncvars in parent

            foreach (FieldDefinition syncVar in m_SyncVars)
            {
                Instruction varLabel = serWorker.Create(OpCodes.Nop);

                // check if dirty bit is set
                serWorker.Append(serWorker.Create(OpCodes.Ldloc_0));
                serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit));
                serWorker.Append(serWorker.Create(OpCodes.And));
                serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel));

                // check for Hook function
                MethodDefinition foundMethod;
                if (!SyncVarProcessor.CheckForHookFunction(m_td, syncVar, out foundMethod))
                {
                    return;
                }

                if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName ||
                    syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
                {
                    // GameObject/NetworkIdentity SyncVar:
                    //   OnSerialize sends writer.Write(go);
                    //   OnDeserialize reads to __netId manually so we can use
                    //     lookups in the getter (so it still works if objects
                    //     move in and out of range repeatedly)
                    FieldDefinition netIdField = m_SyncVarNetIds[syncVar];

                    if (foundMethod == null)
                    {
                        serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                        serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                        serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.NetworkReaderReadPacked32));
                        serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField));
                    }
                    else
                    {
                        // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32()))
                        // because we send/receive the netID, not the GameObject/NetworkIdentity
                        serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this.
                        serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                        serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                        serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.NetworkReaderReadPacked32));
                        serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                        serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar));
                        if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName)
                        {
                            serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.getSyncVarGameObjectReference));
                        }
                        else if (syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
                        {
                            serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.getSyncVarNetworkIdentityReference));
                        }
                        serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));
                    }
                }
                else
                {
                    MethodReference readFunc = Weaver.GetReadFunc(syncVar.FieldType);
                    if (readFunc == null)
                    {
                        Log.Error("GenerateDeSerialization for " + m_td.Name + " unknown type [" + syncVar.FieldType + "]. UNet [SyncVar] member variables must be basic types.");
                        Weaver.fail = true;
                        return;
                    }

                    if (foundMethod == null)
                    {
                        // just assign value
                        serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                        serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                        serWorker.Append(serWorker.Create(OpCodes.Call, readFunc));
                        serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar));
                    }
                    else
                    {
                        // call hook instead
                        serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                        serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                        serWorker.Append(serWorker.Create(OpCodes.Call, readFunc));
                        serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));
                    }
                }
                serWorker.Append(varLabel);
                dirtyBit += 1;
            }

            if (Weaver.generateLogErrors)
            {
                serWorker.Append(serWorker.Create(OpCodes.Ldstr, "Injected Deserialize " + m_td.Name));
                serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.logErrorReference));
            }

            serWorker.Append(serWorker.Create(OpCodes.Ret));
            m_td.Methods.Add(serialize);
        }
Esempio n. 11
0
        void DeserializeField(FieldDefinition syncVar, ILProcessor serWorker)
        {
            // check for Hook function
            MethodDefinition foundMethod;

            if (!SyncVarProcessor.CheckForHookFunction(m_td, syncVar, out foundMethod))
            {
                return;
            }

            if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName ||
                syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
            {
                // GameObject/NetworkIdentity SyncVar:
                //   OnSerialize sends writer.Write(go);
                //   OnDeserialize reads to __netId manually so we can use
                //     lookups in the getter (so it still works if objects
                //     move in and out of range repeatedly)
                FieldDefinition netIdField = m_SyncVarNetIds[syncVar];

                if (foundMethod == null)
                {
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                    serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.NetworkReaderReadPacked32));
                    serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField));
                }
                else
                {
                    // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32()))
                    // because we send/receive the netID, not the GameObject/NetworkIdentity
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                    serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.NetworkReaderReadPacked32));
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar));
                    if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName)
                    {
                        serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.getSyncVarGameObjectReference));
                    }
                    else if (syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
                    {
                        serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.getSyncVarNetworkIdentityReference));
                    }
                    serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));
                }
            }
            else
            {
                MethodReference readFunc = Weaver.GetReadFunc(syncVar.FieldType);
                if (readFunc == null)
                {
                    Weaver.Error("GenerateDeSerialization for " + m_td.Name + " unknown type [" + syncVar.FieldType + "]. Mirror [SyncVar] member variables must be basic types.");
                    return;
                }

                if (foundMethod == null)
                {
                    // just assign value
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                    serWorker.Append(serWorker.Create(OpCodes.Call, readFunc));
                    serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar));
                }
                else
                {
                    // call hook instead
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                    serWorker.Append(serWorker.Create(OpCodes.Call, readFunc));
                    serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));
                }
            }
        }
        void DeserializeField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize)
        {
            // check for Hook function
            if (!SyncVarProcessor.CheckForHookFunction(netBehaviourSubclass, syncVar, out MethodDefinition foundMethod))
            {
                return;
            }

            // [SyncVar] GameObject/NetworkIdentity?

            /*
             * Generates code like:
             *  uint num = reader.ReadPackedUInt32();
             *  if (!SyncVarEqual(num, ref q))
             *  {
             *      OnSetQ(GetSyncVarGameObject(num, ref q));
             *  }
             *  ___qNetId = num;
             */
            if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName ||
                syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
            {
                // GameObject/NetworkIdentity SyncVar:
                //   OnSerialize sends writer.Write(go);
                //   OnDeserialize reads to __netId manually so we can use
                //     lookups in the getter (so it still works if objects
                //     move in and out of range repeatedly)
                FieldDefinition netIdField = syncVarNetIds[syncVar];

                VariableDefinition tmpValue = new VariableDefinition(Weaver.uint32Type);
                deserialize.Body.Variables.Add(tmpValue);

                // read id and store in a local variable
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(Weaver.uint32Type)));
                serWorker.Append(serWorker.Create(OpCodes.Stloc, tmpValue));

                if (foundMethod != null)
                {
                    // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32()))
                    // because we send/receive the netID, not the GameObject/NetworkIdentity
                    // but only if SyncVar changed. otherwise a client would
                    // get hook calls for all initial values, even if they
                    // didn't change from the default values on the client.
                    // see also: https://github.com/vis2k/Mirror/issues/1278

                    // IMPORTANT: for GameObjects/NetworkIdentities we usually
                    //            use SyncVarGameObjectEqual to compare equality.
                    //            in this case however, we can just use
                    //            SyncVarEqual with the two uint netIds.
                    //            => this is easier weaver code because we don't
                    //               have to get the GameObject/NetworkIdentity
                    //               from the uint netId
                    //            => this is faster because we void one
                    //               GetComponent call for GameObjects to get
                    //               their NetworkIdentity when comparing.

                    // Generates: if (!SyncVarEqual);
                    Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop);

                    // 'this.' for 'this.SyncVarEqual'
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    // 'tmpValue'
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, tmpValue));
                    // 'ref this.__netId'
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldflda, netIdField));
                    // call the function
                    GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(Weaver.syncVarEqualReference);
                    syncVarEqualGm.GenericArguments.Add(netIdField.FieldType);
                    serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm));
                    serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel));

                    // call the hook
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, tmpValue));
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar));
                    if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName)
                    {
                        serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.getSyncVarGameObjectReference));
                    }
                    else if (syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
                    {
                        serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.getSyncVarNetworkIdentityReference));
                    }
                    serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));

                    // Generates: end if (!SyncVarEqual);
                    serWorker.Append(syncVarEqualLabel);
                }
                // set the netid field
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                serWorker.Append(serWorker.Create(OpCodes.Ldloc, tmpValue));
                serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField));
            }
            // [SyncVar] int/float/struct/etc.?

            /*
             * Generates code like:
             *  int num = reader.ReadPackedInt32();
             *  if (!SyncVarEqual(num, ref a))
             *  {
             *      OnSetA(num);
             *  }
             *  Networka = num;
             */
            else
            {
                MethodReference readFunc = Readers.GetReadFunc(syncVar.FieldType);
                if (readFunc == null)
                {
                    Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead");
                    return;
                }
                VariableDefinition tmpValue = new VariableDefinition(syncVar.FieldType);
                deserialize.Body.Variables.Add(tmpValue);

                // read value and put it in a local variable
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                serWorker.Append(serWorker.Create(OpCodes.Call, readFunc));
                serWorker.Append(serWorker.Create(OpCodes.Stloc, tmpValue));

                if (foundMethod != null)
                {
                    // call hook
                    // but only if SyncVar changed. otherwise a client would
                    // get hook calls for all initial values, even if they
                    // didn't change from the default values on the client.
                    // see also: https://github.com/vis2k/Mirror/issues/1278

                    // Generates: if (!SyncVarEqual);
                    Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop);

                    // 'this.' for 'this.SyncVarEqual'
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    // 'tmpValue'
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, tmpValue));
                    // 'ref this.syncVar'
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar));
                    // call the function
                    GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(Weaver.syncVarEqualReference);
                    syncVarEqualGm.GenericArguments.Add(syncVar.FieldType);
                    serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm));
                    serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel));

                    // call the hook
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, tmpValue));
                    serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));

                    // Generates: end if (!SyncVarEqual);
                    serWorker.Append(syncVarEqualLabel);
                }
                // set the property
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                serWorker.Append(serWorker.Create(OpCodes.Ldloc, tmpValue));
                serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar));
            }
        }