Пример #1
0
        public static void ProcessSyncVars(TypeDefinition td, List <FieldDefinition> syncVars, List <FieldDefinition> syncObjects, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds)
        {
            int numSyncVars = 0;

            // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties.
            // start assigning syncvars at the place the base class stopped, if any
            int dirtyBitCounter = Weaver.GetSyncVarStart(td.BaseType.FullName);

            syncVarNetIds.Clear();

            // find syncvars
            foreach (FieldDefinition fd in td.Fields)
            {
                if (fd.HasCustomAttribute(WeaverTypes.SyncVarType))
                {
                    if ((fd.Attributes & FieldAttributes.Static) != 0)
                    {
                        Weaver.Error($"{fd.Name} cannot be static", fd);
                        return;
                    }

                    if (fd.FieldType.IsArray)
                    {
                        Weaver.Error($"{fd.Name} has invalid type. Use SyncLists instead of arrays", fd);
                        return;
                    }

                    if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType))
                    {
                        Weaver.Warning($"{fd.Name} has [SyncVar] attribute. SyncLists should not be marked with SyncVar", fd);
                    }
                    else
                    {
                        syncVars.Add(fd);

                        ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter);
                        dirtyBitCounter += 1;
                        numSyncVars     += 1;

                        if (dirtyBitCounter == SyncVarLimit)
                        {
                            Weaver.Error($"{td.Name} has too many SyncVars. Consider refactoring your class into multiple components", td);
                            return;
                        }
                    }
                }

                if (fd.FieldType.Resolve().ImplementsInterface(WeaverTypes.SyncObjectType))
                {
                    if (fd.IsStatic)
                    {
                        Weaver.Error($"{fd.Name} cannot be static", fd);
                        return;
                    }

                    if (fd.FieldType.Resolve().HasGenericParameters)
                    {
                        Weaver.Error($"Cannot use generic SyncObject {fd.Name} directly in NetworkBehaviour. Create a class and inherit from the generic SyncObject instead", fd);
                        return;
                    }

                    syncObjects.Add(fd);
                }
            }

            // add all the new SyncVar __netId fields
            foreach (FieldDefinition fd in syncVarNetIds.Values)
            {
                td.Fields.Add(fd);
            }

            Weaver.SetNumSyncVars(td.FullName, numSyncVars);
        }
        void GenerateConstants()
        {
            if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0 && eventRpcs.Count == 0 && syncObjects.Count == 0)
            {
                return;
            }

            Weaver.DLog(netBehaviourSubclass, "  GenerateConstants ");

            // find static constructor
            MethodDefinition cctor      = null;
            bool             cctorFound = false;

            foreach (MethodDefinition md in netBehaviourSubclass.Methods)
            {
                if (md.Name == ".cctor")
                {
                    cctor      = md;
                    cctorFound = true;
                }
            }
            if (cctor != null)
            {
                // remove the return opcode from end of function. will add our own later.
                if (cctor.Body.Instructions.Count != 0)
                {
                    Instruction ret = cctor.Body.Instructions[cctor.Body.Instructions.Count - 1];
                    if (ret.OpCode == OpCodes.Ret)
                    {
                        cctor.Body.Instructions.RemoveAt(cctor.Body.Instructions.Count - 1);
                    }
                    else
                    {
                        Weaver.Error($"{netBehaviourSubclass} has invalid class constructor");
                        return;
                    }
                }
            }
            else
            {
                // make one!
                cctor = new MethodDefinition(".cctor", MethodAttributes.Private |
                                             MethodAttributes.HideBySig |
                                             MethodAttributes.SpecialName |
                                             MethodAttributes.RTSpecialName |
                                             MethodAttributes.Static,
                                             Weaver.voidType);
            }

            // find instance constructor
            MethodDefinition ctor = null;

            foreach (MethodDefinition md in netBehaviourSubclass.Methods)
            {
                if (md.Name == ".ctor")
                {
                    ctor = md;

                    Instruction ret = ctor.Body.Instructions[ctor.Body.Instructions.Count - 1];
                    if (ret.OpCode == OpCodes.Ret)
                    {
                        ctor.Body.Instructions.RemoveAt(ctor.Body.Instructions.Count - 1);
                    }
                    else
                    {
                        Weaver.Error($"{netBehaviourSubclass} has invalid constructor");
                        return;
                    }

                    break;
                }
            }

            if (ctor == null)
            {
                Weaver.Error($"{netBehaviourSubclass} has invalid constructor");
                return;
            }

            ILProcessor ctorWorker  = ctor.Body.GetILProcessor();
            ILProcessor cctorWorker = cctor.Body.GetILProcessor();

            for (int i = 0; i < commands.Count; ++i)
            {
                GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerCommandDelegateReference, commandInvocationFuncs[i], commands[i].Name);
            }

            for (int i = 0; i < clientRpcs.Count; ++i)
            {
                GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerRpcDelegateReference, clientRpcInvocationFuncs[i], clientRpcs[i].Name);
            }

            for (int i = 0; i < targetRpcs.Count; ++i)
            {
                GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerRpcDelegateReference, targetRpcInvocationFuncs[i], targetRpcs[i].Name);
            }

            for (int i = 0; i < eventRpcs.Count; ++i)
            {
                GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerEventDelegateReference, eventRpcInvocationFuncs[i], eventRpcs[i].Name);
            }

            foreach (FieldDefinition fd in syncObjects)
            {
                SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, fd);
            }

            cctorWorker.Append(cctorWorker.Create(OpCodes.Ret));
            if (!cctorFound)
            {
                netBehaviourSubclass.Methods.Add(cctor);
            }

            // finish ctor
            ctorWorker.Append(ctorWorker.Create(OpCodes.Ret));

            // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late
            netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit;
        }
        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, 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
                    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($"{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
                    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));
            }
        }
Пример #4
0
        static MethodDefinition GenerateListWriteFunc(TypeReference variable, int recursionCount)
        {
            GenericInstanceType genericInstance  = (GenericInstanceType)variable;
            TypeReference       elementType      = genericInstance.GenericArguments[0];
            MethodReference     elementWriteFunc = GetWriteFunc(elementType, recursionCount + 1);
            MethodReference     intWriterFunc    = GetWriteFunc(WeaverTypes.int32Type);

            if (elementWriteFunc == null)
            {
                Weaver.Error($"Cannot generate writer for List because element {elementType.Name} does not have a writer. Use a supported type or provide a custom writer", variable);
                return(null);
            }

            string functionName = "_WriteList_" + elementType.Name + "_";

            if (variable.DeclaringType != null)
            {
                functionName += variable.DeclaringType.Name;
            }
            else
            {
                functionName += "None";
            }

            // create new writer for this type
            MethodDefinition writerFunc = new MethodDefinition(functionName,
                                                               MethodAttributes.Public |
                                                               MethodAttributes.Static |
                                                               MethodAttributes.HideBySig,
                                                               WeaverTypes.voidType);

            writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(WeaverTypes.NetworkWriterType)));
            writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, variable));

            writerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
            writerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
            writerFunc.Body.InitLocals = true;

            ILProcessor worker = writerFunc.Body.GetILProcessor();

            // if (value == null)
            // {
            //     writer.WritePackedInt32(-1);
            //     return;
            // }
            Instruction labelNull = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Ldarg_1));
            worker.Append(worker.Create(OpCodes.Brtrue, labelNull));

            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Ldc_I4_M1));
            worker.Append(worker.Create(OpCodes.Call, intWriterFunc));
            worker.Append(worker.Create(OpCodes.Ret));

            // else not null
            worker.Append(labelNull);

            MethodReference countref = WeaverTypes.ListCountReference.MakeHostInstanceGeneric(genericInstance);

            // int count = value.Count;
            worker.Append(worker.Create(OpCodes.Ldarg_1));
            worker.Append(worker.Create(OpCodes.Call, countref));
            worker.Append(worker.Create(OpCodes.Stloc_0));

            // writer.WritePackedInt32(count);
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Call, intWriterFunc));

            // Loop through the List<T> and call the writer for each element.
            // generates this:
            // for (int i=0; i < count; i++)
            // {
            //    writer.WriteT(value[i]);
            // }
            worker.Append(worker.Create(OpCodes.Ldc_I4_0));
            worker.Append(worker.Create(OpCodes.Stloc_1));
            Instruction labelHead = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Br, labelHead));

            // loop body
            Instruction labelBody = worker.Create(OpCodes.Nop);

            worker.Append(labelBody);

            MethodReference getItem = WeaverTypes.ListGetItemReference.MakeHostInstanceGeneric(genericInstance);

            // writer.Write(value[i]);
            worker.Append(worker.Create(OpCodes.Ldarg_0));                // writer
            worker.Append(worker.Create(OpCodes.Ldarg_1));                // value
            worker.Append(worker.Create(OpCodes.Ldloc_1));                // i
            worker.Append(worker.Create(OpCodes.Call, getItem));          //get_Item
            worker.Append(worker.Create(OpCodes.Call, elementWriteFunc)); // Write


            // end for loop

            // for loop i++
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ldc_I4_1));
            worker.Append(worker.Create(OpCodes.Add));
            worker.Append(worker.Create(OpCodes.Stloc_1));

            worker.Append(labelHead);
            // for loop i < count
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Blt, labelBody));

            // return
            worker.Append(worker.Create(OpCodes.Ret));
            return(writerFunc);
        }
        public static void ProcessSyncVars(TypeDefinition td, List <FieldDefinition> syncVars,
                                           List <FieldDefinition> syncObjects, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds)
        {
            var numSyncVars = 0;

            // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties.
            // start assigning syncvars at the place the base class stopped, if any
            var dirtyBitCounter = Weaver.GetSyncVarStart(td.BaseType.FullName);

            syncVarNetIds.Clear();

            // find syncvars
            foreach (var fd in td.Fields)
            {
                foreach (var ca in fd.CustomAttributes)
                {
                    if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName)
                    {
                        var resolvedField = fd.FieldType.Resolve();

                        if ((fd.Attributes & FieldAttributes.Static) != 0)
                        {
                            Weaver.Error($"{fd} cannot be static");
                            return;
                        }

                        if (fd.FieldType.IsArray)
                        {
                            Weaver.Error($"{fd} has invalid type. Use SyncLists instead of arrays");
                            return;
                        }

                        if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType))
                        {
                            Log.Warning($"{fd} has [SyncVar] attribute. SyncLists should not be marked with SyncVar");
                            break;
                        }

                        syncVars.Add(fd);

                        ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter);
                        dirtyBitCounter += 1;
                        numSyncVars     += 1;

                        if (dirtyBitCounter == SyncVarLimit)
                        {
                            Weaver.Error(
                                $"{td} has too many SyncVars. Consider refactoring your class into multiple components");
                            return;
                        }

                        break;
                    }
                }

                if (fd.FieldType.Resolve().ImplementsInterface(Weaver.SyncObjectType))
                {
                    if (fd.IsStatic)
                    {
                        Weaver.Error($"{fd} cannot be static");
                        return;
                    }

                    syncObjects.Add(fd);
                }
            }

            // add all the new SyncVar __netId fields
            foreach (var fd in syncVarNetIds.Values)
            {
                td.Fields.Add(fd);
            }

            Weaver.SetNumSyncVars(td.FullName, numSyncVars);
        }
Пример #6
0
        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;
             *  // returns GetSyncVarGameObject(___qNetId)
             *  GameObject oldSyncVar = syncvar.getter;
             *  ___qNetId = reader.ReadPackedUInt32();
             *  if (!SyncVarEqual(oldNetId, ref ___goNetId))
             *  {
             *      // getter returns GetSyncVarGameObject(___qNetId)
             *      OnSetQ(oldSyncVar, syncvar.getter);
             *  }
             */
            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.
                // put 'this.' onto stack for 'this.netId' below
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                // reader. for 'reader.Read()' below
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                // Read()
                serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(Weaver.uint32Type)));
                // netId
                serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField));

                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
                    // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    // oldSyncVar GO/NI
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldSyncVar));
                    // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    // syncvar.get (finds current GO/NI from netId)
                    serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar));
                    serWorker.Append(serWorker.Create(OpCodes.Callvirt, foundMethod));

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

            /*
             * Generates code like:
             *  // for hook
             *  int oldValue = a;
             *  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.
                // 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 (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
                    // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    // oldvalue
                    serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue));
                    // this.
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    // syncvar.get
                    serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar));
                    serWorker.Append(serWorker.Create(OpCodes.Callvirt, foundMethod));

                    // Generates: end if (!SyncVarEqual);
                    serWorker.Append(syncVarEqualLabel);
                }
            }
        }
Пример #7
0
        static MethodDefinition GenerateArrayWriteFunc(TypeReference variable, MethodReference elementWriteFunc)
        {
            if (!variable.IsArrayType())
            {
                Weaver.Error($"{variable} is an unsupported type. Jagged and multidimensional arrays are not supported");
                return(null);
            }
            string functionName = "_WriteArray" + variable.GetElementType().Name + "_";

            if (variable.DeclaringType != null)
            {
                functionName += variable.DeclaringType.Name;
            }
            else
            {
                functionName += "None";
            }

            // create new writer for this type
            MethodDefinition writerFunc = new MethodDefinition(functionName,
                                                               MethodAttributes.Public |
                                                               MethodAttributes.Static |
                                                               MethodAttributes.HideBySig,
                                                               Weaver.voidType);

            writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
            writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(variable)));

            writerFunc.Body.Variables.Add(new VariableDefinition(Weaver.uint16Type));
            writerFunc.Body.Variables.Add(new VariableDefinition(Weaver.uint16Type));
            writerFunc.Body.InitLocals = true;

            ILProcessor worker = writerFunc.Body.GetILProcessor();

            // null check
            Instruction labelNull = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Ldarg_1));
            worker.Append(worker.Create(OpCodes.Brtrue, labelNull));

            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Ldc_I4_0));
            worker.Append(worker.Create(OpCodes.Call, Weaver.NetworkWriteUInt16));
            worker.Append(worker.Create(OpCodes.Ret));

            // setup array length local variable
            worker.Append(labelNull);
            worker.Append(worker.Create(OpCodes.Ldarg_1));
            worker.Append(worker.Create(OpCodes.Ldlen));
            worker.Append(worker.Create(OpCodes.Conv_I4));
            worker.Append(worker.Create(OpCodes.Conv_U2));
            worker.Append(worker.Create(OpCodes.Stloc_0));

            //write length
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Call, Weaver.NetworkWriteUInt16));

            // start loop
            worker.Append(worker.Create(OpCodes.Ldc_I4_0));
            worker.Append(worker.Create(OpCodes.Stloc_1));
            Instruction labelHead = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Br, labelHead));

            // loop body
            Instruction labelBody = worker.Create(OpCodes.Nop);

            worker.Append(labelBody);
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Ldarg_1));
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ldelema, variable.GetElementType()));
            worker.Append(worker.Create(OpCodes.Ldobj, variable.GetElementType()));
            worker.Append(worker.Create(OpCodes.Call, elementWriteFunc));
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ldc_I4_1));
            worker.Append(worker.Create(OpCodes.Add));
            worker.Append(worker.Create(OpCodes.Conv_U2));
            worker.Append(worker.Create(OpCodes.Stloc_1));

            // loop while check
            worker.Append(labelHead);
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ldarg_1));
            worker.Append(worker.Create(OpCodes.Ldlen));
            worker.Append(worker.Create(OpCodes.Conv_I4));
            worker.Append(worker.Create(OpCodes.Blt, labelBody));

            worker.Append(worker.Create(OpCodes.Ret));
            return(writerFunc);
        }
        static void GenerateSerialization(TypeDefinition td)
        {
            Weaver.DLog(td, "  GenerateSerialization");
            foreach (MethodDefinition m in td.Methods)
            {
                if (m.Name == "Serialize")
                {
                    return;
                }
            }

            if (td.Fields.Count == 0)
            {
                return;
            }

            // check for self-referencing types
            foreach (FieldDefinition field in td.Fields)
            {
                if (field.FieldType.FullName == td.FullName)
                {
                    Weaver.Error($"{td} has field ${field} that references itself");
                    return;
                }
            }

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

            serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
            ILProcessor serWorker = serializeFunc.Body.GetILProcessor();

            foreach (FieldDefinition field in td.Fields)
            {
                if (field.IsStatic || field.IsPrivate || field.IsSpecialName)
                {
                    continue;
                }

                if (field.FieldType.Resolve().HasGenericParameters&& !field.FieldType.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
                {
                    Weaver.Error($"{field} cannot have generic type {field.FieldType}.  Consider creating a class that derives the generic type");
                    return;
                }

                if (field.FieldType.Resolve().IsInterface)
                {
                    Weaver.Error($"{field} has unsupported type. Use a concrete class instead of interface {field.FieldType}");
                    return;
                }

                MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType);
                if (writeFunc != null)
                {
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldfld, field));
                    serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc));
                }
                else
                {
                    Weaver.Error($"{field} has unsupported type");
                    return;
                }
            }
            serWorker.Append(serWorker.Create(OpCodes.Ret));

            td.Methods.Add(serializeFunc);
        }
Пример #9
0
        private static MethodReference GenerateReader(TypeReference variableReference)
        {
            // Arrays are special,  if we resolve them, we get teh element type,
            // so the following ifs might choke on it for scriptable objects
            // or other objects that require a custom serializer
            // thus check if it is an array and skip all the checks.
            if (variableReference.IsArray)
            {
                if (variableReference.IsMultidimensionalArray())
                {
                    Weaver.Error($"{variableReference.Name} is an unsupported type. Multidimensional arrays are not supported", variableReference);
                    return(null);
                }

                return(GenerateReadCollection(variableReference, variableReference.GetElementType(), nameof(NetworkReaderExtensions.ReadArray)));
            }

            TypeDefinition variableDefinition = variableReference.Resolve();

            if (variableDefinition == null)
            {
                Weaver.Error($"{variableReference.Name} is not a supported type", variableReference);
                return(null);
            }
            if (variableDefinition.IsDerivedFrom <UnityEngine.Component>())
            {
                Weaver.Error($"Cannot generate reader for component type {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }
            if (variableReference.Is <UnityEngine.Object>())
            {
                Weaver.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }
            if (variableReference.Is <UnityEngine.ScriptableObject>())
            {
                Weaver.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }
            if (variableReference.IsByReference)
            {
                // error??
                Weaver.Error($"Cannot pass type {variableReference.Name} by reference", variableReference);
                return(null);
            }
            if (variableDefinition.HasGenericParameters && !variableDefinition.Is(typeof(ArraySegment <>)) && !variableDefinition.Is(typeof(List <>)))
            {
                Weaver.Error($"Cannot generate reader for generic variable {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }
            if (variableDefinition.IsInterface)
            {
                Weaver.Error($"Cannot generate reader for interface {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }
            if (variableDefinition.IsAbstract)
            {
                Weaver.Error($"Cannot generate reader for abstract class {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }

            if (variableDefinition.IsEnum)
            {
                return(GenerateEnumReadFunc(variableReference));
            }
            else if (variableDefinition.Is(typeof(ArraySegment <>)))
            {
                return(GenerateArraySegmentReadFunc(variableReference));
            }
            else if (variableDefinition.Is(typeof(List <>)))
            {
                GenericInstanceType genericInstance = (GenericInstanceType)variableReference;
                TypeReference       elementType     = genericInstance.GenericArguments[0];

                return(GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList)));
            }

            return(GenerateClassOrStructReadFunction(variableReference));
        }
Пример #10
0
        void GenerateConstants()
        {
            if (serverRpcs.Count == 0 && clientRpcs.Count == 0 && syncObjects.Count == 0)
            {
                return;
            }

            Weaver.DLog(netBehaviourSubclass, "  GenerateConstants ");

            // 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 (cctor.Body.Instructions.Count != 0)
                {
                    Instruction retInstr = cctor.Body.Instructions[cctor.Body.Instructions.Count - 1];
                    if (retInstr.OpCode == OpCodes.Ret)
                    {
                        cctor.Body.Instructions.RemoveAt(cctor.Body.Instructions.Count - 1);
                    }
                    else
                    {
                        Weaver.Error($"{netBehaviourSubclass.Name} has invalid class constructor", cctor);
                        return;
                    }
                }
            }
            else
            {
                // make one!
                cctor = new MethodDefinition(".cctor", MethodAttributes.Private |
                                             MethodAttributes.HideBySig |
                                             MethodAttributes.SpecialName |
                                             MethodAttributes.RTSpecialName |
                                             MethodAttributes.Static,
                                             WeaverTypes.Import(typeof(void)));
            }

            // find instance constructor
            MethodDefinition ctor = netBehaviourSubclass.GetMethod(".ctor");

            if (ctor == null)
            {
                Weaver.Error($"{netBehaviourSubclass.Name} has invalid constructor", netBehaviourSubclass);
                return;
            }

            Instruction ret = ctor.Body.Instructions[ctor.Body.Instructions.Count - 1];

            if (ret.OpCode == OpCodes.Ret)
            {
                ctor.Body.Instructions.RemoveAt(ctor.Body.Instructions.Count - 1);
            }
            else
            {
                Weaver.Error($"{netBehaviourSubclass.Name} has invalid constructor", ctor);
                return;
            }

            // TODO: find out if the order below matters. If it doesn't split code below into 2 functions
            ILProcessor ctorWorker  = ctor.Body.GetILProcessor();
            ILProcessor cctorWorker = cctor.Body.GetILProcessor();

            for (int i = 0; i < serverRpcs.Count; ++i)
            {
                CmdResult cmdResult = serverRpcs[i];
                GenerateRegisterServerRpcDelegate(cctorWorker, WeaverTypes.registerServerRpcDelegateReference, serverRpcSkeletonFuncs[i], cmdResult);
            }

            for (int i = 0; i < clientRpcs.Count; ++i)
            {
                ClientRpcResult clientRpcResult = clientRpcs[i];
                GenerateRegisterRemoteDelegate(cctorWorker, WeaverTypes.registerRpcDelegateReference, clientRpcSkeletonFuncs[i], clientRpcResult.method.Name);
            }

            foreach (FieldDefinition fd in syncObjects)
            {
                SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, fd);
            }

            cctorWorker.Append(cctorWorker.Create(OpCodes.Ret));
            if (!cctorFound)
            {
                netBehaviourSubclass.Methods.Add(cctor);
            }

            // finish ctor
            ctorWorker.Append(ctorWorker.Create(OpCodes.Ret));

            // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late
            netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit;
        }
Пример #11
0
        static MethodDefinition GenerateStructWriterFunction(TypeReference variable, int recursionCount)
        {
            if (!Weaver.IsValidTypeToGenerate(variable.Resolve()))
            {
                return(null);
            }

            string functionName = "_Write" + variable.Name + "_";

            if (variable.DeclaringType != null)
            {
                functionName += variable.DeclaringType.Name;
            }
            else
            {
                functionName += "None";
            }
            // create new writer for this type
            MethodDefinition writerFunc = new MethodDefinition(functionName,
                                                               MethodAttributes.Public |
                                                               MethodAttributes.Static |
                                                               MethodAttributes.HideBySig,
                                                               Weaver.voidType);

            writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
            writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(variable)));

            ILProcessor worker = writerFunc.Body.GetILProcessor();

            uint fields = 0;

            foreach (FieldDefinition field in variable.Resolve().Fields)
            {
                if (field.IsStatic || field.IsPrivate)
                {
                    continue;
                }

                if (field.FieldType.Resolve().HasGenericParameters)
                {
                    Weaver.Error("WriteReadFunc for " + field.Name + " [" + field.FieldType + "/" + field.FieldType.FullName + "]. Cannot have generic parameters.");
                    return(null);
                }

                if (field.FieldType.Resolve().IsInterface)
                {
                    Weaver.Error("WriteReadFunc for " + field.Name + " [" + field.FieldType + "/" + field.FieldType.FullName + "]. Cannot be an interface.");
                    return(null);
                }

                MethodReference writeFunc = GetWriteFunc(field.FieldType, recursionCount + 1);
                if (writeFunc != null)
                {
                    fields++;
                    worker.Append(worker.Create(OpCodes.Ldarg_0));
                    worker.Append(worker.Create(OpCodes.Ldarg_1));
                    worker.Append(worker.Create(OpCodes.Ldfld, field));
                    worker.Append(worker.Create(OpCodes.Call, writeFunc));
                }
                else
                {
                    Weaver.Error("WriteReadFunc for " + field.Name + " type " + field.FieldType + " no supported");
                    return(null);
                }
            }
            if (fields == 0)
            {
                Log.Warning("The class / struct " + variable.Name + " has no public or non-static fields to serialize");
            }
            worker.Append(worker.Create(OpCodes.Ret));
            return(writerFunc);
        }
Пример #12
0
        static bool Weave(string assName, IEnumerable <string> dependencies, IAssemblyResolver assemblyResolver, string unityEngineDLLPath, string mirrorNetDLLPath, string outputDir)
        {
            ReaderParameters readParams = Helpers.ReaderParameters(assName, dependencies, assemblyResolver, unityEngineDLLPath, mirrorNetDLLPath);

            using (CurrentAssembly = AssemblyDefinition.ReadAssembly(assName, readParams))
            {
                SetupTargetTypes();
                Readers.Init(CurrentAssembly);
                Writers.Init(CurrentAssembly);

                ModuleDefinition moduleDefinition = CurrentAssembly.MainModule;
                Console.WriteLine("Script Module: {0}", moduleDefinition.Name);

                // Process each NetworkBehaviour
                bool didWork = false;

                // We need to do 2 passes, because SyncListStructs might be referenced from other modules, so we must make sure we generate them first.
                for (int pass = 0; pass < 2; pass++)
                {
                    System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
                    foreach (TypeDefinition td in moduleDefinition.Types)
                    {
                        if (td.IsClass && td.BaseType.CanBeResolved())
                        {
                            try
                            {
                                if (pass == 0)
                                {
                                    didWork |= CheckSyncList(td);
                                }
                                else
                                {
                                    didWork |= CheckNetworkBehaviour(td);
                                    didWork |= CheckMessageBase(td);
                                }
                            }
                            catch (Exception ex)
                            {
                                if (CurrentAssembly.MainModule.SymbolReader != null)
                                {
                                    CurrentAssembly.MainModule.SymbolReader.Dispose();
                                }
                                Weaver.Error(ex.Message);
                                throw ex;
                            }
                        }

                        if (WeavingFailed)
                        {
                            if (CurrentAssembly.MainModule.SymbolReader != null)
                            {
                                CurrentAssembly.MainModule.SymbolReader.Dispose();
                            }
                            return(false);
                        }
                    }
                    watch.Stop();
                    Console.WriteLine("Pass: "******" took " + watch.ElapsedMilliseconds + " milliseconds");
                }

                if (didWork)
                {
                    // this must be done for ALL code, not just NetworkBehaviours
                    try
                    {
                        PropertySiteProcessor.ProcessSitesModule(CurrentAssembly.MainModule);
                    }
                    catch (Exception e)
                    {
                        Log.Error("ProcessPropertySites exception: " + e);
                        if (CurrentAssembly.MainModule.SymbolReader != null)
                        {
                            CurrentAssembly.MainModule.SymbolReader.Dispose();
                        }
                        return(false);
                    }

                    if (WeavingFailed)
                    {
                        //Log.Error("Failed phase II.");
                        if (CurrentAssembly.MainModule.SymbolReader != null)
                        {
                            CurrentAssembly.MainModule.SymbolReader.Dispose();
                        }
                        return(false);
                    }

                    string dest = Helpers.DestinationFileFor(outputDir, assName);
                    //Console.WriteLine ("Output:" + dest);

                    WriterParameters writeParams = Helpers.GetWriterParameters(readParams);
                    CurrentAssembly.Write(dest, writeParams);
                }

                if (CurrentAssembly.MainModule.SymbolReader != null)
                {
                    CurrentAssembly.MainModule.SymbolReader.Dispose();
                }
            }

            return(true);
        }
        static void GenerateSerialization(TypeDefinition td)
        {
            Weaver.DLog(td, "  GenerateSerialization");
            foreach (MethodDefinition m in td.Methods)
            {
                if (m.Name == "Serialize")
                {
                    return;
                }
            }

            if (td.Fields.Count == 0)
            {
                return;
            }

            // check for self-referencing types
            foreach (FieldDefinition field in td.Fields)
            {
                if (field.FieldType.FullName == td.FullName)
                {
                    Weaver.Error("GenerateSerialization for " + td.Name + " [" + field.FullName + "]. [MessageBase] member cannot be self referencing.");
                    return;
                }
            }

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

            serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
            ILProcessor serWorker = serializeFunc.Body.GetILProcessor();

            foreach (FieldDefinition field in td.Fields)
            {
                if (field.IsStatic || field.IsPrivate || field.IsSpecialName)
                {
                    continue;
                }

                if (field.FieldType.Resolve().HasGenericParameters)
                {
                    Weaver.Error("GenerateSerialization for " + td.Name + " [" + field.FieldType + "/" + field.FieldType.FullName + "]. [MessageBase] member cannot have generic parameters.");
                    return;
                }

                if (field.FieldType.Resolve().IsInterface)
                {
                    Weaver.Error("GenerateSerialization for " + td.Name + " [" + field.FieldType + "/" + field.FieldType.FullName + "]. [MessageBase] member cannot be an interface.");
                    return;
                }

                MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType);
                if (writeFunc != null)
                {
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldfld, field));
                    serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc));
                }
                else
                {
                    Weaver.Error("GenerateSerialization for " + td.Name + " unknown type [" + field.FieldType + "/" + field.FieldType.FullName + "]. [MessageBase] member variables must be basic types.");
                    return;
                }
            }
            serWorker.Append(serWorker.Create(OpCodes.Ret));

            td.Methods.Add(serializeFunc);
        }
Пример #14
0
        public static MethodReference GetWriteFunc(TypeReference variable, int recursionCount = 0)
        {
            if (writeFuncs.TryGetValue(variable.FullName, out MethodReference foundFunc))
            {
                return(foundFunc);
            }

            if (variable.IsByReference)
            {
                // error??
                Weaver.Error($"Cannot pass {variable} by reference");
                return(null);
            }
            TypeDefinition td = variable.Resolve();

            if (td == null)
            {
                Weaver.Error($"{variable} is not a supported type. Use a supported type or provide a custom writer");
                return(null);
            }
            if (td.IsDerivedFrom(Weaver.ScriptableObjectType))
            {
                Weaver.Error($"Cannot generate writer for scriptable object {variable}. Use a supported type or provide a custom writer");
                return(null);
            }
            if (td.IsDerivedFrom(Weaver.ComponentType))
            {
                Weaver.Error($"Cannot generate writer for component type {variable}. Use a supported type or provide a custom writer");
                return(null);
            }
            if (td.HasGenericParameters && !td.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
            {
                Weaver.Error($"Cannot generate writer for generic type {variable}. Use a concrete type or provide a custom writer");
                return(null);
            }
            if (td.IsInterface)
            {
                Weaver.Error($"Cannot generate writer for interface {variable}. Use a concrete type or provide a custom writer");
                return(null);
            }

            MethodDefinition newWriterFunc;

            if (variable.IsArray)
            {
                newWriterFunc = GenerateArrayWriteFunc(variable, recursionCount);
            }
            else if (variable.Resolve().IsEnum)
            {
                return(GetWriteFunc(variable.Resolve().GetEnumUnderlyingType(), recursionCount));
            }
            else if (variable.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
            {
                newWriterFunc = GenerateArraySegmentWriteFunc(variable, recursionCount);
            }
            else
            {
                newWriterFunc = GenerateStructWriterFunction(variable, recursionCount);
            }

            if (newWriterFunc == null)
            {
                return(null);
            }

            RegisterWriteFunc(variable.FullName, newWriterFunc);
            return(newWriterFunc);
        }
Пример #15
0
        static MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable, int recursionCount)
        {
            GenericInstanceType genericInstance = (GenericInstanceType)variable;
            TypeReference       elementType     = genericInstance.GenericArguments[0];

            MethodReference elementReadFunc = GetReadFunc(elementType, recursionCount + 1);

            if (elementReadFunc == null)
            {
                Weaver.Error($"Cannot generate reader for ArraySegment because element {elementType.Name} does not have a reader. Use a supported type or provide a custom reader", variable);
                return(null);
            }

            string functionName = "_ReadArraySegment_" + elementType.Name + "_";

            if (variable.DeclaringType != null)
            {
                functionName += variable.DeclaringType.Name;
            }
            else
            {
                functionName += "None";
            }

            // create new reader for this type
            MethodDefinition readerFunc = new MethodDefinition(functionName,
                                                               MethodAttributes.Public |
                                                               MethodAttributes.Static |
                                                               MethodAttributes.HideBySig,
                                                               variable);

            readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(WeaverTypes.NetworkReaderType)));

            // int lengh
            readerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
            // T[] array
            readerFunc.Body.Variables.Add(new VariableDefinition(elementType.MakeArrayType()));
            // int i;
            readerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
            readerFunc.Body.InitLocals = true;

            ILProcessor worker = readerFunc.Body.GetILProcessor();

            // int length = reader.ReadPackedInt32();
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Call, GetReadFunc(WeaverTypes.int32Type)));
            worker.Append(worker.Create(OpCodes.Stloc_0));

            // T[] array = new int[length]
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Newarr, elementType));
            worker.Append(worker.Create(OpCodes.Stloc_1));

            // loop through array and deserialize each element
            // generates code like this
            // for (int i=0; i< length ; i++)
            // {
            //     value[i] = reader.ReadXXX();
            // }
            worker.Append(worker.Create(OpCodes.Ldc_I4_0));
            worker.Append(worker.Create(OpCodes.Stloc_2));
            Instruction labelHead = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Br, labelHead));

            // loop body
            Instruction labelBody = worker.Create(OpCodes.Nop);

            worker.Append(labelBody);
            // value[i] = reader.ReadT();
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ldloc_2));
            worker.Append(worker.Create(OpCodes.Ldelema, elementType));
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Call, elementReadFunc));
            worker.Append(worker.Create(OpCodes.Stobj, elementType));

            worker.Append(worker.Create(OpCodes.Ldloc_2));
            worker.Append(worker.Create(OpCodes.Ldc_I4_1));
            worker.Append(worker.Create(OpCodes.Add));
            worker.Append(worker.Create(OpCodes.Stloc_2));

            // loop while check
            worker.Append(labelHead);
            worker.Append(worker.Create(OpCodes.Ldloc_2));
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Blt, labelBody));

            // return new ArraySegment<T>(array);
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Newobj, WeaverTypes.ArraySegmentConstructorReference.MakeHostInstanceGeneric(genericInstance)));
            worker.Append(worker.Create(OpCodes.Ret));
            return(readerFunc);
        }
Пример #16
0
        public static MethodReference GetWriteFunc(TypeReference variable, int recursionCount = 0)
        {
            if (writeFuncs.TryGetValue(variable.FullName, out MethodReference foundFunc))
            {
                return(foundFunc);
            }

            MethodDefinition newWriterFunc;

            // Arrays are special,  if we resolve them, we get the element type,
            // so the following ifs might choke on it for scriptable objects
            // or other objects that require a custom serializer
            // thus check if it is an array and skip all the checks.
            if (variable.IsArray)
            {
                newWriterFunc = GenerateArrayWriteFunc(variable, recursionCount);
                RegisterWriteFunc(variable.FullName, newWriterFunc);
                return(newWriterFunc);
            }

            if (variable.IsByReference)
            {
                // error??
                Weaver.Error($"Cannot pass {variable} by reference");
                return(null);
            }
            TypeDefinition td = variable.Resolve();

            if (td == null)
            {
                Weaver.Error($"{variable} is not a supported type. Use a supported type or provide a custom writer");
                return(null);
            }
            if (td.IsDerivedFrom(Weaver.ComponentType))
            {
                Weaver.Error($"Cannot generate writer for component type {variable}. Use a supported type or provide a custom writer");
                return(null);
            }
            if (td.HasGenericParameters && !td.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
            {
                Weaver.Error($"Cannot generate writer for generic type {variable}. Use a concrete type or provide a custom writer");
                return(null);
            }
            if (td.IsInterface)
            {
                Weaver.Error($"Cannot generate writer for interface {variable}. Use a concrete type or provide a custom writer");
                return(null);
            }

            if (variable.Resolve().IsEnum)
            {
                return(GetWriteFunc(variable.Resolve().GetEnumUnderlyingType(), recursionCount));
            }
            else if (variable.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
            {
                newWriterFunc = GenerateArraySegmentWriteFunc(variable, recursionCount);
            }
            else
            {
                newWriterFunc = GenerateClassOrStructWriterFunction(variable, recursionCount);
            }

            if (newWriterFunc == null)
            {
                return(null);
            }

            RegisterWriteFunc(variable.FullName, newWriterFunc);
            return(newWriterFunc);
        }
Пример #17
0
        static MethodDefinition GenerateListReadFunc(TypeReference variable, int recursionCount)
        {
            GenericInstanceType genericInstance = (GenericInstanceType)variable;
            TypeReference       elementType     = genericInstance.GenericArguments[0];

            MethodReference elementReadFunc = GetReadFunc(elementType, recursionCount + 1);

            if (elementReadFunc == null)
            {
                Weaver.Error($"Cannot generate reader for List because element {elementType.Name} does not have a reader. Use a supported type or provide a custom reader", variable);
                return(null);
            }

            string functionName = "_ReadList_" + elementType.Name + "_";

            if (variable.DeclaringType != null)
            {
                functionName += variable.DeclaringType.Name;
            }
            else
            {
                functionName += "None";
            }

            // create new reader for this type
            MethodDefinition readerFunc = new MethodDefinition(functionName,
                                                               MethodAttributes.Public |
                                                               MethodAttributes.Static |
                                                               MethodAttributes.HideBySig,
                                                               variable);

            readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(WeaverTypes.NetworkReaderType)));

            readerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
            readerFunc.Body.Variables.Add(new VariableDefinition(variable));
            readerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
            readerFunc.Body.InitLocals = true;

            ILProcessor worker = readerFunc.Body.GetILProcessor();

            // int count = reader.ReadPackedInt32();
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Call, GetReadFunc(WeaverTypes.int32Type)));
            worker.Append(worker.Create(OpCodes.Stloc_0));

            // -1 is null list, so if count is less than 0 return null
            // if (count < 0) {
            //    return null
            // }
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Ldc_I4_0));
            Instruction labelEmptyArray = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Bge, labelEmptyArray));
            // return null
            worker.Append(worker.Create(OpCodes.Ldnull));
            worker.Append(worker.Create(OpCodes.Ret));
            worker.Append(labelEmptyArray);

            // List<T> list = new List<T>();
            worker.Append(worker.Create(OpCodes.Newobj, WeaverTypes.ListConstructorReference.MakeHostInstanceGeneric(genericInstance)));
            worker.Append(worker.Create(OpCodes.Stloc_1));

            // loop through array and deserialize each element
            // generates code like this
            // for (int i=0; i< length ; i++)
            // {
            //     list[i] = reader.ReadXXX();
            // }
            worker.Append(worker.Create(OpCodes.Ldc_I4_0));
            worker.Append(worker.Create(OpCodes.Stloc_2));
            Instruction labelHead = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Br, labelHead));

            // loop body
            Instruction labelBody = worker.Create(OpCodes.Nop);

            worker.Append(labelBody);

            MethodReference addItem = WeaverTypes.ListAddReference.MakeHostInstanceGeneric(genericInstance);

            // list.Add(reader.ReadT());
            worker.Append(worker.Create(OpCodes.Ldloc_1));               // list
            worker.Append(worker.Create(OpCodes.Ldarg_0));               // reader
            worker.Append(worker.Create(OpCodes.Call, elementReadFunc)); // Read
            worker.Append(worker.Create(OpCodes.Call, addItem));         // set_Item

            // end for loop

            // for loop i++
            worker.Append(worker.Create(OpCodes.Ldloc_2));
            worker.Append(worker.Create(OpCodes.Ldc_I4_1));
            worker.Append(worker.Create(OpCodes.Add));
            worker.Append(worker.Create(OpCodes.Stloc_2));

            // loop while check
            worker.Append(labelHead);
            // for loop i < count
            worker.Append(worker.Create(OpCodes.Ldloc_2));
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Blt, labelBody));

            // return value;
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ret));
            return(readerFunc);
        }
Пример #18
0
        static void GenerateSerialization(TypeDefinition td)
        {
            Weaver.DLog(td, "  GenerateSerialization");
            MethodDefinition existingMethod = td.Methods.FirstOrDefault(md => md.Name == "Serialize");

            if (existingMethod != null && !existingMethod.Body.IsEmptyDefault())
            {
                return;
            }

            if (td.Fields.Count == 0)
            {
                return;
            }

            // check for self-referencing types
            foreach (FieldDefinition field in td.Fields)
            {
                if (field.FieldType.FullName == td.FullName)
                {
                    Weaver.Error($"{td} has field ${field} that references itself");
                    return;
                }
            }

            MethodDefinition serializeFunc = existingMethod ?? new MethodDefinition("Serialize",
                                                                                    MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig,
                                                                                    Weaver.voidType);

            //only add to new method
            if (existingMethod == null)
            {
                serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
            }
            ILProcessor serWorker = serializeFunc.Body.GetILProcessor();

            if (existingMethod != null)
            {
                //remove default nop&ret from existing empty interface method
                serWorker.Body.Instructions.Clear();
            }

            //if not struct(IMessageBase), likely same as using else {} here in all cases
            if (!td.IsValueType)
            {
                // call base
                MethodReference baseSerialize = Resolvers.ResolveMethodInParents(td.BaseType, Weaver.CurrentAssembly, "Serialize");
                if (baseSerialize != null)
                {
                    // base
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    // writer
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                    serWorker.Append(serWorker.Create(OpCodes.Call, baseSerialize));
                }
            }

            foreach (FieldDefinition field in td.Fields)
            {
                if (field.IsStatic || field.IsPrivate || field.IsSpecialName)
                {
                    continue;
                }

                MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType);
                if (writeFunc != null)
                {
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                    serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                    serWorker.Append(serWorker.Create(OpCodes.Ldfld, field));
                    serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc));
                }
                else
                {
                    Weaver.Error($"{field} has unsupported type");
                    return;
                }
            }
            serWorker.Append(serWorker.Create(OpCodes.Ret));

            //only add if not just replaced body
            if (existingMethod == null)
            {
                td.Methods.Add(serializeFunc);
            }
        }
Пример #19
0
        static MethodDefinition GenerateStructWriterFunction(TypeReference variable, int recursionCount)
        {
            if (recursionCount > MaxRecursionCount)
            {
                Weaver.Error($"{variable} can't be serialized because it references itself");
                return(null);
            }

            if (!Weaver.IsValidTypeToGenerate(variable.Resolve()))
            {
                return(null);
            }

            string functionName = "_Write" + variable.Name + "_";

            if (variable.DeclaringType != null)
            {
                functionName += variable.DeclaringType.Name;
            }
            else
            {
                functionName += "None";
            }
            // create new writer for this type
            MethodDefinition writerFunc = new MethodDefinition(functionName,
                                                               MethodAttributes.Public |
                                                               MethodAttributes.Static |
                                                               MethodAttributes.HideBySig,
                                                               Weaver.voidType);

            writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
            writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(variable)));

            ILProcessor worker = writerFunc.Body.GetILProcessor();

            uint fields = 0;

            foreach (FieldDefinition field in variable.Resolve().Fields)
            {
                if (field.IsStatic || field.IsPrivate)
                {
                    continue;
                }

                if (field.FieldType.Resolve().HasGenericParameters)
                {
                    Weaver.Error($"{field} has unsupported type. Create a derived class instead of using generics");
                    return(null);
                }

                if (field.FieldType.Resolve().IsInterface)
                {
                    Weaver.Error($"{field} has unsupported type. Use a concrete class instead of an interface");
                    return(null);
                }

                MethodReference writeFunc = GetWriteFunc(field.FieldType, recursionCount + 1);
                if (writeFunc != null)
                {
                    fields++;
                    worker.Append(worker.Create(OpCodes.Ldarg_0));
                    worker.Append(worker.Create(OpCodes.Ldarg_1));
                    worker.Append(worker.Create(OpCodes.Ldfld, field));
                    worker.Append(worker.Create(OpCodes.Call, writeFunc));
                }
                else
                {
                    Weaver.Error($"{field} has unsupported type. Use a type supported by Mirror instead");
                    return(null);
                }
            }
            if (fields == 0)
            {
                Log.Warning($" {variable} has no no public or non-static fields to serialize");
            }
            worker.Append(worker.Create(OpCodes.Ret));
            return(writerFunc);
        }
Пример #20
0
        public static void ProcessSyncVars(TypeDefinition td, List <FieldDefinition> syncVars, List <FieldDefinition> syncObjects, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds)
        {
            int numSyncVars = 0;

            // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties.
            // start assigning syncvars at the place the base class stopped, if any
            int dirtyBitCounter = Weaver.GetSyncVarStart(td.BaseType.FullName);

            syncVarNetIds.Clear();

            // find syncvars
            foreach (FieldDefinition fd in td.Fields)
            {
                foreach (CustomAttribute ca in fd.CustomAttributes)
                {
                    if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName)
                    {
                        TypeDefinition resolvedField = fd.FieldType.Resolve();

                        if (resolvedField.IsDerivedFrom(Weaver.NetworkBehaviourType))
                        {
                            Weaver.Error($"{fd} has invalid type. SyncVars cannot be NetworkBehaviours");
                            return;
                        }

                        if (resolvedField.IsDerivedFrom(Weaver.ScriptableObjectType))
                        {
                            Weaver.Error($"{fd} has invalid type. SyncVars cannot be scriptable objects");
                            return;
                        }

                        if ((fd.Attributes & FieldAttributes.Static) != 0)
                        {
                            Weaver.Error($"{fd} cannot be static");
                            return;
                        }

                        if (resolvedField.HasGenericParameters)
                        {
                            Weaver.Error($"{fd} has invalid type. SyncVars cannot have generic parameters");
                            return;
                        }

                        if (resolvedField.IsInterface)
                        {
                            Weaver.Error($"{fd} has invalid type. Use a concrete type instead of interface {fd.FieldType}");
                            return;
                        }

                        string fieldModuleName = resolvedField.Module.Name;
                        if (fieldModuleName != Weaver.CurrentAssembly.MainModule.Name &&
                            fieldModuleName != Weaver.UnityAssembly.MainModule.Name &&
                            fieldModuleName != Weaver.NetAssembly.MainModule.Name &&
                            fieldModuleName != Weaver.CorLibModule.Name &&
                            fieldModuleName != "System.Runtime.dll" && // this is only for Metro, built-in types are not in corlib on metro
                            fieldModuleName != "netstandard.dll"       // handle built-in types when weaving new C#7 compiler assemblies
                            )
                        {
                            Weaver.Error($"{fd} has invalid type. Use a type defined in the same module {fd.Module}");
                            return;
                        }

                        if (fd.FieldType.IsArray)
                        {
                            Weaver.Error($"{fd} has invalid type. Use SyncLists instead of arrays");
                            return;
                        }

                        if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType))
                        {
                            Log.Warning($"{fd} has [SyncVar] attribute. SyncLists should not be marked with SyncVar");
                            break;
                        }

                        syncVars.Add(fd);

                        ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter);
                        dirtyBitCounter += 1;
                        numSyncVars     += 1;

                        if (dirtyBitCounter == SyncVarLimit)
                        {
                            Weaver.Error($"{td} has too many SyncVars. Consider refactoring your class into multiple components");
                            return;
                        }
                        break;
                    }
                }

                if (fd.FieldType.Resolve().ImplementsInterface(Weaver.SyncObjectType))
                {
                    if (fd.IsStatic)
                    {
                        Weaver.Error($"{fd} cannot be static");
                        return;
                    }

                    syncObjects.Add(fd);
                }
            }

            // add all the new SyncVar __netId fields
            foreach (FieldDefinition fd in syncVarNetIds.Values)
            {
                td.Fields.Add(fd);
            }

            Weaver.SetNumSyncVars(td.FullName, numSyncVars);
        }
Пример #21
0
        static MethodDefinition GenerateArrayWriteFunc(TypeReference variable, int recursionCount)
        {
            if (!variable.IsArrayType())
            {
                Weaver.Error($"{variable.Name} is an unsupported type. Jagged and multidimensional arrays are not supported", variable);
                return(null);
            }

            TypeReference   elementType      = variable.GetElementType();
            MethodReference elementWriteFunc = GetWriteFunc(elementType, recursionCount + 1);
            MethodReference intWriterFunc    = GetWriteFunc(WeaverTypes.int32Type);

            if (elementWriteFunc == null)
            {
                Weaver.Error($"Cannot generate writer for Array because element {elementType.Name} does not have a writer. Use a supported type or provide a custom writer", variable);
                return(null);
            }

            string functionName = "_WriteArray" + variable.GetElementType().Name + "_";

            if (variable.DeclaringType != null)
            {
                functionName += variable.DeclaringType.Name;
            }
            else
            {
                functionName += "None";
            }

            // create new writer for this type
            MethodDefinition writerFunc = new MethodDefinition(functionName,
                                                               MethodAttributes.Public |
                                                               MethodAttributes.Static |
                                                               MethodAttributes.HideBySig,
                                                               WeaverTypes.voidType);

            writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(WeaverTypes.NetworkWriterType)));
            writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(variable)));

            writerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
            writerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
            writerFunc.Body.InitLocals = true;

            ILProcessor worker = writerFunc.Body.GetILProcessor();

            // if (value == null)
            // {
            //     writer.WritePackedInt32(-1);
            //     return;
            // }
            Instruction labelNull = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Ldarg_1));
            worker.Append(worker.Create(OpCodes.Brtrue, labelNull));

            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Ldc_I4_M1));
            worker.Append(worker.Create(OpCodes.Call, intWriterFunc));
            worker.Append(worker.Create(OpCodes.Ret));

            // else not null
            worker.Append(labelNull);

            // int length = value.Length;
            worker.Append(worker.Create(OpCodes.Ldarg_1));
            worker.Append(worker.Create(OpCodes.Ldlen));
            worker.Append(worker.Create(OpCodes.Stloc_0));

            // writer.WritePackedInt32(length);
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Call, intWriterFunc));

            // for (int i=0; i< value.length; i++) {
            worker.Append(worker.Create(OpCodes.Ldc_I4_0));
            worker.Append(worker.Create(OpCodes.Stloc_1));
            Instruction labelHead = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Br, labelHead));

            // loop body
            Instruction labelBody = worker.Create(OpCodes.Nop);

            worker.Append(labelBody);
            // writer.Write(value[i]);
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Ldarg_1));
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ldelema, elementType));
            worker.Append(worker.Create(OpCodes.Ldobj, elementType));
            worker.Append(worker.Create(OpCodes.Call, elementWriteFunc));

            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ldc_I4_1));
            worker.Append(worker.Create(OpCodes.Add));
            worker.Append(worker.Create(OpCodes.Stloc_1));

            // end for loop
            worker.Append(labelHead);
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ldarg_1));
            worker.Append(worker.Create(OpCodes.Ldlen));
            worker.Append(worker.Create(OpCodes.Conv_I4));
            worker.Append(worker.Create(OpCodes.Blt, labelBody));

            // return
            worker.Append(worker.Create(OpCodes.Ret));
            return(writerFunc);
        }
Пример #22
0
        static bool Weave(string assName, IEnumerable <string> dependencies, IAssemblyResolver assemblyResolver, string unityEngineDLLPath, string mirrorNetDLLPath, string outputDir)
        {
            using (DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver())
                using (CurrentAssembly = AssemblyDefinition.ReadAssembly(assName, new ReaderParameters {
                    ReadWrite = true, AssemblyResolver = asmResolver
                }))
                {
                    asmResolver.AddSearchDirectory(Path.GetDirectoryName(assName));
                    asmResolver.AddSearchDirectory(Helpers.UnityEngineDLLDirectoryName());
                    asmResolver.AddSearchDirectory(Path.GetDirectoryName(unityEngineDLLPath));
                    asmResolver.AddSearchDirectory(Path.GetDirectoryName(mirrorNetDLLPath));
                    if (dependencies != null)
                    {
                        foreach (string path in dependencies)
                        {
                            asmResolver.AddSearchDirectory(path);
                        }
                    }

                    SetupTargetTypes();
                    Readers.Init(CurrentAssembly);
                    Writers.Init(CurrentAssembly);

                    ModuleDefinition moduleDefinition = CurrentAssembly.MainModule;
                    Console.WriteLine("Script Module: {0}", moduleDefinition.Name);

                    // Process each NetworkBehaviour
                    bool didWork = false;

                    // We need to do 2 passes, because SyncListStructs might be referenced from other modules, so we must make sure we generate them first.
                    for (int pass = 0; pass < 2; pass++)
                    {
                        System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
                        foreach (TypeDefinition td in moduleDefinition.Types)
                        {
                            if (td.IsClass && td.BaseType.CanBeResolved())
                            {
                                try
                                {
                                    if (pass == 0)
                                    {
                                        didWork |= CheckSyncList(td);
                                    }
                                    else
                                    {
                                        didWork |= CheckNetworkBehaviour(td);
                                        didWork |= CheckMessageBase(td);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Weaver.Error(ex.Message);
                                    throw ex;
                                }
                            }

                            if (WeavingFailed)
                            {
                                return(false);
                            }
                        }
                        watch.Stop();
                        Console.WriteLine("Pass: "******" took " + watch.ElapsedMilliseconds + " milliseconds");
                    }

                    if (didWork)
                    {
                        // this must be done for ALL code, not just NetworkBehaviours
                        try
                        {
                            PropertySiteProcessor.ProcessSitesModule(CurrentAssembly.MainModule);
                        }
                        catch (Exception e)
                        {
                            Log.Error("ProcessPropertySites exception: " + e);
                            return(false);
                        }

                        if (WeavingFailed)
                        {
                            //Log.Error("Failed phase II.");
                            return(false);
                        }

                        // write to outputDir if specified, otherwise perform in-place write
                        if (outputDir != null)
                        {
                            CurrentAssembly.Write(Helpers.DestinationFileFor(outputDir, assName));
                        }
                        else
                        {
                            CurrentAssembly.Write();
                        }
                    }
                }

            return(true);
        }
Пример #23
0
        static MethodDefinition GenerateWriter(TypeReference variableReference, int recursionCount = 0)
        {
            // TODO: do we need this check? do we ever receieve types that are "ByReference"s
            if (variableReference.IsByReference)
            {
                // error??
                Weaver.Error($"Cannot pass {variableReference.Name} by reference", variableReference);
                return(null);
            }

            // Arrays are special, if we resolve them, we get the element type,
            // eg int[] resolves to int
            // therefore process this before checks below
            if (variableReference.IsArray)
            {
                return(GenerateArrayWriteFunc(variableReference, recursionCount));
            }

            // check for collections

            if (variableReference.IsArraySegment())
            {
                return(GenerateArraySegmentWriteFunc(variableReference, recursionCount));
            }
            if (variableReference.IsList())
            {
                return(GenerateListWriteFunc(variableReference, recursionCount));
            }

            // check for invalid types

            TypeDefinition variableDefinition = variableReference.Resolve();

            if (variableDefinition == null)
            {
                Weaver.Error($"{variableReference.Name} is not a supported type. Use a supported type or provide a custom writer", variableReference);
                return(null);
            }
            if (variableDefinition.IsDerivedFrom(WeaverTypes.ComponentType))
            {
                Weaver.Error($"Cannot generate writer for component type {variableReference.Name}. Use a supported type or provide a custom writer", variableReference);
                return(null);
            }
            if (variableReference.FullName == WeaverTypes.ObjectType.FullName)
            {
                Weaver.Error($"Cannot generate writer for {variableReference.Name}. Use a supported type or provide a custom writer", variableReference);
                return(null);
            }
            if (variableReference.FullName == WeaverTypes.ScriptableObjectType.FullName)
            {
                Weaver.Error($"Cannot generate writer for {variableReference.Name}. Use a supported type or provide a custom writer", variableReference);
                return(null);
            }
            if (variableDefinition.HasGenericParameters)
            {
                Weaver.Error($"Cannot generate writer for generic type {variableReference.Name}. Use a supported type or provide a custom writer", variableReference);
                return(null);
            }
            if (variableDefinition.IsInterface)
            {
                Weaver.Error($"Cannot generate writer for interface {variableReference.Name}. Use a supported type or provide a custom writer", variableReference);
                return(null);
            }
            if (variableDefinition.IsAbstract)
            {
                Weaver.Error($"Cannot generate writer for abstract class {variableReference.Name}. Use a supported type or provide a custom writer", variableReference);
                return(null);
            }

            // generate writer for class/struct

            return(GenerateClassOrStructWriterFunction(variableReference, recursionCount));
        }
Пример #24
0
        public static void ProcessSyncVars(TypeDefinition td, List <FieldDefinition> syncVars, List <FieldDefinition> syncObjects, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds)
        {
            int numSyncVars = 0;

            // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties.
            // start assigning syncvars at the place the base class stopped, if any
            int dirtyBitCounter = Weaver.GetSyncVarStart(td.BaseType.FullName);

            syncVarNetIds.Clear();

            // find syncvars
            foreach (FieldDefinition fd in td.Fields)
            {
                foreach (var ca in fd.CustomAttributes)
                {
                    if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName)
                    {
                        var resolvedField = fd.FieldType.Resolve();

                        if (resolvedField.IsDerivedFrom(Weaver.NetworkBehaviourType))
                        {
                            Weaver.Error("SyncVar [" + fd.FullName + "] cannot be derived from NetworkBehaviour.");
                            return;
                        }

                        if (resolvedField.IsDerivedFrom(Weaver.ScriptableObjectType))
                        {
                            Weaver.Error("SyncVar [" + fd.FullName + "] cannot be derived from ScriptableObject.");
                            return;
                        }

                        if ((fd.Attributes & FieldAttributes.Static) != 0)
                        {
                            Weaver.Error("SyncVar [" + fd.FullName + "] cannot be static.");
                            return;
                        }

                        if (resolvedField.HasGenericParameters)
                        {
                            Weaver.Error("SyncVar [" + fd.FullName + "] cannot have generic parameters.");
                            return;
                        }

                        if (resolvedField.IsInterface)
                        {
                            Weaver.Error("SyncVar [" + fd.FullName + "] cannot be an interface.");
                            return;
                        }

                        var fieldModuleName = resolvedField.Module.Name;
                        if (fieldModuleName != Weaver.CurrentAssembly.MainModule.Name &&
                            fieldModuleName != Weaver.UnityAssembly.MainModule.Name &&
                            fieldModuleName != Weaver.NetAssembly.MainModule.Name &&
                            fieldModuleName != Weaver.CorLibModule.Name &&
                            fieldModuleName != "System.Runtime.dll" && // this is only for Metro, built-in types are not in corlib on metro
                            fieldModuleName != "netstandard.dll"       // handle built-in types when weaving new C#7 compiler assemblies
                            )
                        {
                            Weaver.Error("SyncVar [" + fd.FullName + "] from " + resolvedField.Module.ToString() + " cannot be a different module.");
                            return;
                        }

                        if (fd.FieldType.IsArray)
                        {
                            Weaver.Error("SyncVar [" + fd.FullName + "] cannot be an array. Use a SyncList instead.");
                            return;
                        }

                        if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType))
                        {
                            Log.Warning(string.Format("Script class [{0}] has [SyncVar] attribute on SyncList field {1}, SyncLists should not be marked with SyncVar.", td.FullName, fd.Name));
                            break;
                        }

                        syncVars.Add(fd);

                        ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter);
                        dirtyBitCounter += 1;
                        numSyncVars     += 1;

                        if (dirtyBitCounter == k_SyncVarLimit)
                        {
                            Weaver.Error("Script class [" + td.FullName + "] has too many SyncVars (" + k_SyncVarLimit + "). (This could include base classes)");
                            return;
                        }
                        break;
                    }
                }

                if (fd.FieldType.FullName.Contains("Mirror.SyncListStruct"))
                {
                    Weaver.Error("SyncListStruct member variable [" + fd.FullName + "] must use a dervied class, like \"class MySyncList : SyncListStruct<MyStruct> {}\".");
                    return;
                }

                if (fd.FieldType.Resolve().ImplementsInterface(Weaver.SyncObjectType))
                {
                    if (fd.IsStatic)
                    {
                        Weaver.Error("SyncList [" + td.FullName + ":" + fd.FullName + "] cannot be a static");
                        return;
                    }

                    syncObjects.Add(fd);
                }
            }

            // add all the new SyncVar __netId fields
            foreach (FieldDefinition fd in syncVarNetIds.Values)
            {
                td.Fields.Add(fd);
            }

            Weaver.SetNumSyncVars(td.FullName, numSyncVars);
        }
Пример #25
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(Weaver.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);
            }
        }
Пример #26
0
        static MethodDefinition GenerateArrayReadFunc(TypeReference variable, int recursionCount)
        {
            if (!variable.IsArrayType())
            {
                Weaver.Error($"{variable.Name} is an unsupported type. Jagged and multidimensional arrays are not supported", variable);
                return(null);
            }

            TypeReference   elementType     = variable.GetElementType();
            MethodReference elementReadFunc = GetReadFunc(elementType, recursionCount + 1);

            if (elementReadFunc == null)
            {
                Weaver.Error($"Cannot generate reader for Array because element {elementType.Name} does not have a reader. Use a supported type or provide a custom reader", variable);
                return(null);
            }

            string functionName = "_ReadArray" + variable.GetElementType().Name + "_";

            if (variable.DeclaringType != null)
            {
                functionName += variable.DeclaringType.Name;
            }
            else
            {
                functionName += "None";
            }

            // create new reader for this type
            MethodDefinition readerFunc = new MethodDefinition(functionName,
                                                               MethodAttributes.Public |
                                                               MethodAttributes.Static |
                                                               MethodAttributes.HideBySig,
                                                               variable);

            readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(WeaverTypes.NetworkReaderType)));

            readerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
            readerFunc.Body.Variables.Add(new VariableDefinition(variable));
            readerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
            readerFunc.Body.InitLocals = true;

            ILProcessor worker = readerFunc.Body.GetILProcessor();

            // int length = reader.ReadPackedInt32();
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Call, GetReadFunc(WeaverTypes.int32Type)));
            worker.Append(worker.Create(OpCodes.Stloc_0));

            // if (length < 0) {
            //    return null
            // }
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Ldc_I4_0));
            Instruction labelEmptyArray = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Bge, labelEmptyArray));
            // return null
            worker.Append(worker.Create(OpCodes.Ldnull));
            worker.Append(worker.Create(OpCodes.Ret));
            worker.Append(labelEmptyArray);

            // T value = new T[length];
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Newarr, variable.GetElementType()));
            worker.Append(worker.Create(OpCodes.Stloc_1));

            // for (int i=0; i< length ; i++) {
            worker.Append(worker.Create(OpCodes.Ldc_I4_0));
            worker.Append(worker.Create(OpCodes.Stloc_2));
            Instruction labelHead = worker.Create(OpCodes.Nop);

            worker.Append(worker.Create(OpCodes.Br, labelHead));

            // loop body
            Instruction labelBody = worker.Create(OpCodes.Nop);

            worker.Append(labelBody);
            // value[i] = reader.ReadT();
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ldloc_2));
            worker.Append(worker.Create(OpCodes.Ldelema, variable.GetElementType()));
            worker.Append(worker.Create(OpCodes.Ldarg_0));
            worker.Append(worker.Create(OpCodes.Call, elementReadFunc));
            worker.Append(worker.Create(OpCodes.Stobj, variable.GetElementType()));

            worker.Append(worker.Create(OpCodes.Ldloc_2));
            worker.Append(worker.Create(OpCodes.Ldc_I4_1));
            worker.Append(worker.Create(OpCodes.Add));
            worker.Append(worker.Create(OpCodes.Stloc_2));

            // loop while check
            worker.Append(labelHead);
            worker.Append(worker.Create(OpCodes.Ldloc_2));
            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Blt, labelBody));

            // return value;
            worker.Append(worker.Create(OpCodes.Ldloc_1));
            worker.Append(worker.Create(OpCodes.Ret));
            return(readerFunc);
        }
        void GenerateSerialization()
        {
            Weaver.DLog(netBehaviourSubclass, "  GenerateSerialization");

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

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

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

            serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
            serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, Weaver.boolType));
            ILProcessor serWorker = serialize.Body.GetILProcessor();

            serialize.Body.InitLocals = true;

            // loc_0,  this local variable is to determine if any variable was dirty
            VariableDefinition dirtyLocal = new VariableDefinition(Weaver.boolType);

            serialize.Body.Variables.Add(dirtyLocal);

            MethodReference baseSerialize = Resolvers.ResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, "OnSerialize");

            if (baseSerialize != null)
            {
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); // forceAll
                serWorker.Append(serWorker.Create(OpCodes.Call, baseSerialize));
                serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); // set dirtyLocal to result of base.OnSerialize()
            }

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

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

            foreach (FieldDefinition syncVar in syncVars)
            {
                // Generates a writer call for each sync variable
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this
                serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar));
                MethodReference writeFunc = Writers.GetWriteFunc(syncVar.FieldType);
                if (writeFunc != null)
                {
                    serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc));
                }
                else
                {
                    Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead");
                    return;
                }
            }

            // always return true if forceAll

            // Generates: return true
            serWorker.Append(serWorker.Create(OpCodes.Ldc_I4_1));
            serWorker.Append(serWorker.Create(OpCodes.Ret));

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

            // write dirty bits before the data fields
            // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ());
            serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer
            serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base
            serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.NetworkBehaviourDirtyBitsReference));
            serWorker.Append(serWorker.Create(OpCodes.Call, Writers.GetWriteFunc(Weaver.uint64Type)));

            // generate a writer call for any dirty variable in this class

            // start at number of syncvars in parent
            int dirtyBit = Weaver.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName);

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

                // Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL)
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));                // base
                serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.NetworkBehaviourDirtyBitsReference));
                serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit)); // 8 bytes = long
                serWorker.Append(serWorker.Create(OpCodes.And));
                serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel));

                // Generates a call to the writer for that field
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base
                serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar));

                MethodReference writeFunc = Writers.GetWriteFunc(syncVar.FieldType);
                if (writeFunc != null)
                {
                    serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc));
                }
                else
                {
                    Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead");
                    return;
                }

                // something was dirty
                serWorker.Append(serWorker.Create(OpCodes.Ldc_I4_1));
                serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); // set dirtyLocal to true

                serWorker.Append(varLabel);
                dirtyBit += 1;
            }

            if (Weaver.GenerateLogErrors)
            {
                serWorker.Append(serWorker.Create(OpCodes.Ldstr, "Injected Serialize " + netBehaviourSubclass.Name));
                serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.logErrorReference));
            }

            // generate: return dirtyLocal
            serWorker.Append(serWorker.Create(OpCodes.Ldloc_0));
            serWorker.Append(serWorker.Create(OpCodes.Ret));
            netBehaviourSubclass.Methods.Add(serialize);
        }
Пример #28
0
        public static MethodReference GetReadFunc(TypeReference variableReference, int recursionCount = 0)
        {
            if (readFuncs.TryGetValue(variableReference.FullName, out MethodReference foundFunc))
            {
                return(foundFunc);
            }

            MethodDefinition newReaderFunc;

            // Arrays are special,  if we resolve them, we get teh element type,
            // so the following ifs might choke on it for scriptable objects
            // or other objects that require a custom serializer
            // thus check if it is an array and skip all the checks.
            if (variableReference.IsArray)
            {
                newReaderFunc = GenerateArrayReadFunc(variableReference, recursionCount);
                if (newReaderFunc != null)
                {
                    RegisterReadFunc(variableReference.FullName, newReaderFunc);
                }
                return(newReaderFunc);
            }

            TypeDefinition variableDefinition = variableReference.Resolve();

            if (variableDefinition == null)
            {
                Weaver.Error($"{variableReference.Name} is not a supported type", variableReference);
                return(null);
            }
            if (variableDefinition.IsDerivedFrom(WeaverTypes.ComponentType))
            {
                Weaver.Error($"Cannot generate reader for component type {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }
            if (variableReference.FullName == WeaverTypes.ObjectType.FullName)
            {
                Weaver.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }
            if (variableReference.FullName == WeaverTypes.ScriptableObjectType.FullName)
            {
                Weaver.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }
            if (variableReference.IsByReference)
            {
                // error??
                Weaver.Error($"Cannot pass type {variableReference.Name} by reference", variableReference);
                return(null);
            }
            if (variableDefinition.HasGenericParameters && !variableDefinition.IsArraySegment() && !variableDefinition.IsList())
            {
                Weaver.Error($"Cannot generate reader for generic variable {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }
            if (variableDefinition.IsInterface)
            {
                Weaver.Error($"Cannot generate reader for interface {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }
            if (variableDefinition.IsAbstract)
            {
                Weaver.Error($"Cannot generate reader for abstract class {variableReference.Name}. Use a supported type or provide a custom reader", variableReference);
                return(null);
            }

            if (variableDefinition.IsEnum)
            {
                return(GetReadFunc(variableDefinition.GetEnumUnderlyingType(), recursionCount));
            }
            else if (variableDefinition.IsArraySegment())
            {
                newReaderFunc = GenerateArraySegmentReadFunc(variableReference, recursionCount);
            }
            else if (variableDefinition.IsList())
            {
                newReaderFunc = GenerateListReadFunc(variableReference, recursionCount);
            }
            else
            {
                newReaderFunc = GenerateClassOrStructReadFunction(variableReference, recursionCount);
            }

            if (newReaderFunc == null)
            {
                Weaver.Error($"{variableReference.Name} is not a supported type", variableReference);
                return(null);
            }
            RegisterReadFunc(variableReference.FullName, newReaderFunc);
            return(newReaderFunc);
        }
Пример #29
0
        static MethodDefinition GenerateStructReadFunction(TypeReference variable, int recursionCount)
        {
            if (recursionCount > MaxRecursionCount)
            {
                Weaver.Error($"{variable} can't be deserialized because it references itself");
                return(null);
            }

            if (!Weaver.IsValidTypeToGenerate(variable.Resolve()))
            {
                return(null);
            }

            string functionName = "_Read" + variable.Name + "_";

            if (variable.DeclaringType != null)
            {
                functionName += variable.DeclaringType.Name;
            }
            else
            {
                functionName += "None";
            }

            // create new reader for this type
            MethodDefinition readerFunc = new MethodDefinition(functionName,
                                                               MethodAttributes.Public |
                                                               MethodAttributes.Static |
                                                               MethodAttributes.HideBySig,
                                                               variable);

            // create local for return value
            readerFunc.Body.Variables.Add(new VariableDefinition(variable));
            readerFunc.Body.InitLocals = true;

            readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType)));

            ILProcessor worker = readerFunc.Body.GetILProcessor();

            if (variable.IsValueType)
            {
                // structs are created with Initobj
                worker.Append(worker.Create(OpCodes.Ldloca, 0));
                worker.Append(worker.Create(OpCodes.Initobj, variable));
            }
            else
            {
                // classes are created with their constructor

                MethodDefinition ctor = Resolvers.ResolveDefaultPublicCtor(variable);
                if (ctor == null)
                {
                    Weaver.Error($"{variable} can't be deserialized bcause i has no default constructor");
                    return(null);
                }

                worker.Append(worker.Create(OpCodes.Newobj, ctor));
                worker.Append(worker.Create(OpCodes.Stloc_0));
            }

            uint fields = 0;

            foreach (FieldDefinition field in variable.Resolve().Fields)
            {
                if (field.IsStatic || field.IsPrivate)
                {
                    continue;
                }

                // mismatched ldloca/ldloc for struct/class combinations is invalid IL, which causes crash at runtime
                OpCode opcode = variable.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc;
                worker.Append(worker.Create(opcode, 0));

                MethodReference readFunc = GetReadFunc(field.FieldType, recursionCount + 1);
                if (readFunc != null)
                {
                    worker.Append(worker.Create(OpCodes.Ldarg_0));
                    worker.Append(worker.Create(OpCodes.Call, readFunc));
                }
                else
                {
                    Weaver.Error($"{field} has an unsupported type");
                    return(null);
                }

                worker.Append(worker.Create(OpCodes.Stfld, field));
                fields++;
            }
            if (fields == 0)
            {
                Log.Warning($"{variable} has no public or non-static fields to deserialize");
            }

            worker.Append(worker.Create(OpCodes.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Ret));
            return(readerFunc);
        }
Пример #30
0
        public static MethodReference GetReadFunc(TypeReference variable, int recursionCount = 0)
        {
            if (recursionCount > MaxRecursionCount)
            {
                Weaver.Error("GetReadFunc recursion depth exceeded for " + variable.Name + ". Check for self-referencing member variables.");
                return(null);
            }

            if (readFuncs.TryGetValue(variable.FullName, out MethodReference foundFunc))
            {
                if (foundFunc.ReturnType.IsArray == variable.IsArray)
                {
                    return(foundFunc);
                }
            }

            TypeDefinition td = variable.Resolve();

            if (td == null)
            {
                Weaver.Error("GetReadFunc unsupported type " + variable.FullName);
                return(null);
            }

            if (variable.IsByReference)
            {
                // error??
                Weaver.Error("GetReadFunc variable.IsByReference error.");
                return(null);
            }

            MethodDefinition newReaderFunc;

            if (variable.IsArray)
            {
                TypeReference   elementType     = variable.GetElementType();
                MethodReference elementReadFunc = GetReadFunc(elementType, recursionCount + 1);
                if (elementReadFunc == null)
                {
                    return(null);
                }
                newReaderFunc = GenerateArrayReadFunc(variable, elementReadFunc);
            }
            else
            {
                if (td.IsEnum)
                {
                    return(Weaver.NetworkReaderReadInt32);
                }

                newReaderFunc = GenerateStructReadFunction(variable, recursionCount);
            }

            if (newReaderFunc == null)
            {
                Weaver.Error("GetReadFunc unable to generate function for:" + variable.FullName);
                return(null);
            }
            RegisterReadFunc(variable.FullName, newReaderFunc);
            return(newReaderFunc);
        }