Esempio n. 1
0
        public static (List <FieldDefinition> syncVars, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds) ProcessSyncVars(TypeDefinition td)
        {
            List <FieldDefinition> syncVars = new List <FieldDefinition>();
            Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds = new Dictionary <FieldDefinition, FieldDefinition>();

            // 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);

            // 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);
                        continue;
                    }

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

                    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;

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

            // add all the new SyncVar __netId fields
            foreach (FieldDefinition fd in syncVarNetIds.Values)
            {
                td.Fields.Add(fd);
            }
            Weaver.SetNumSyncVars(td.FullName, syncVars.Count);

            return(syncVars, syncVarNetIds);
        }
        void GenerateDeSerialization()
        {
            Weaver.DLog(netBehaviourSubclass, "  GenerateDeSerialization");

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

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

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

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

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

            serialize.Body.Variables.Add(dirtyBitsLocal);

            MethodReference baseDeserialize = Resolvers.ResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, "OnDeserialize");

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

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

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

            foreach (FieldDefinition syncVar in syncVars)
            {
                DeserializeField(syncVar, serWorker, serialize);
            }

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

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

            // get dirty bits
            serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
            serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(Weaver.uint64Type)));
            serWorker.Append(serWorker.Create(OpCodes.Stloc_0));

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

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

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

                DeserializeField(syncVar, serWorker, serialize);

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

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

            serWorker.Append(serWorker.Create(OpCodes.Ret));
            netBehaviourSubclass.Methods.Add(serialize);
        }
        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);
        }
Esempio n. 4
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))
                        {
                            Log.Error("SyncVar [" + fd.FullName + "] cannot be derived from NetworkBehaviour.");
                            Weaver.fail = true;
                            return;
                        }

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

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

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

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

                        var fieldModuleName = resolvedField.Module.Name;
                        if (fieldModuleName != Weaver.scriptDef.MainModule.Name &&
                            fieldModuleName != Weaver.m_UnityAssemblyDefinition.MainModule.Name &&
                            fieldModuleName != Weaver.m_UNetAssemblyDefinition.MainModule.Name &&
                            fieldModuleName != Weaver.corLib.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
                            )
                        {
                            Log.Error("SyncVar [" + fd.FullName + "] from " + resolvedField.Module.ToString() + " cannot be a different module.");
                            Weaver.fail = true;
                            return;
                        }

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

                        if (SyncObjectProcessor.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)
                        {
                            Log.Error("Script class [" + td.FullName + "] has too many SyncVars (" + k_SyncVarLimit + "). (This could include base classes)");
                            Weaver.fail = true;
                            return;
                        }
                        break;
                    }
                }

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

                if (fd.FieldType.Resolve().ImplementsInterface(Weaver.SyncObjectType))
                {
                    if (fd.IsStatic)
                    {
                        Log.Error("SyncList [" + td.FullName + ":" + fd.FullName + "] cannot be a static");
                        Weaver.fail = true;
                        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);
        }
Esempio n. 5
0
        void GenerateDeSerialization()
        {
            Weaver.DLog(m_td, "  GenerateDeSerialization");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            serialize.Body.Variables.Add(dirtyBitsLocal);

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

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

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

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

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

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

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

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

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

            serWorker.Append(serWorker.Create(OpCodes.Ret));
            m_td.Methods.Add(serialize);
        }
Esempio n. 6
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 ((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 (FieldDefinition fd in syncVarNetIds.Values)
            {
                td.Fields.Add(fd);
            }

            Weaver.SetNumSyncVars(td.FullName, numSyncVars);
        }
Esempio n. 7
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);
        }
        void GenerateDeSerialization()
        {
            Weaver.DLog(netBehaviourSubclass, "  GenerateDeSerialization");

            const string DeserializeMethodName = "DeserializeSyncVars";

            if (netBehaviourSubclass.GetMethod(DeserializeMethodName) != null)
            {
                return;
            }

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

            var serialize = new MethodDefinition(DeserializeMethodName,
                                                 MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig,
                                                 WeaverTypes.Import(typeof(void)));

            serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, WeaverTypes.Import <NetworkReader>()));
            serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, WeaverTypes.Import <bool>()));
            ILProcessor serWorker = serialize.Body.GetILProcessor();

            // setup local for dirty bits
            serialize.Body.InitLocals = true;
            var dirtyBitsLocal = new VariableDefinition(WeaverTypes.Import <long>());

            serialize.Body.Variables.Add(dirtyBitsLocal);

            MethodReference baseDeserialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, DeserializeMethodName);

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

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

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

            foreach (FieldDefinition syncVar in syncVars)
            {
                DeserializeField(syncVar, serWorker, serialize);
            }

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

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

            // get dirty bits
            serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
            serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(WeaverTypes.Import <ulong>())));
            serWorker.Append(serWorker.Create(OpCodes.Stloc_0));

            // conditionally read each syncvar
            // 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);

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

                DeserializeField(syncVar, serWorker, serialize);

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

            serWorker.Append(serWorker.Create(OpCodes.Ret));
            netBehaviourSubclass.Methods.Add(serialize);
        }