Example #1
0
 public static Instruction AppendStloc(this Instruction instruction, ILProcessor processor, int index)
 {
     return(instruction.Append(processor.Create(OpCodes.Stloc, index), processor));
 }
Example #2
0
        public static Instruction Prepend(this Instruction instruction, Instruction instructionBefore, ILProcessor processor)
        {
            processor.InsertBefore(instruction, instructionBefore);

            return(instructionBefore);
        }
Example #3
0
 public static Instruction AppendDup(this Instruction instruction, ILProcessor processor)
 {
     return(instruction.Append(processor.Create(OpCodes.Dup), processor));
 }
        public static MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId)
        {
            //Create the set method
            MethodDefinition set = new MethodDefinition("set_Network" + originalName, MethodAttributes.Public |
                                                        MethodAttributes.SpecialName |
                                                        MethodAttributes.HideBySig,
                                                        WeaverTypes.Import(typeof(void)));

            ILProcessor worker = set.Body.GetILProcessor();

            // if (!SyncVarEqual(value, ref playerData))
            Instruction endOfMethod = worker.Create(OpCodes.Nop);

            // this
            worker.Emit(OpCodes.Ldarg_0);
            // new value to set
            worker.Emit(OpCodes.Ldarg_1);
            // reference to field to set
            // make generic version of SetSyncVar with field type
            if (fd.FieldType.Is <UnityEngine.GameObject>())
            {
                // reference to netId Field to set
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldfld, netFieldId);

                worker.Emit(OpCodes.Call, WeaverTypes.syncVarGameObjectEqualReference);
            }
            else if (fd.FieldType.Is <NetworkIdentity>())
            {
                // reference to netId Field to set
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldfld, netFieldId);

                worker.Emit(OpCodes.Call, WeaverTypes.syncVarNetworkIdentityEqualReference);
            }
            else if (fd.FieldType.IsDerivedFrom <NetworkBehaviour>())
            {
                // reference to netId Field to set
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldfld, netFieldId);

                MethodReference getFunc = WeaverTypes.syncVarNetworkBehaviourEqualReference.MakeGeneric(fd.FieldType);
                worker.Emit(OpCodes.Call, getFunc);
            }
            else
            {
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldflda, fd);

                GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(WeaverTypes.syncVarEqualReference);
                syncVarEqualGm.GenericArguments.Add(fd.FieldType);
                worker.Emit(OpCodes.Call, syncVarEqualGm);
            }

            worker.Emit(OpCodes.Brtrue, endOfMethod);

            // T oldValue = value;
            // TODO for GO/NI we need to backup the netId don't we?
            VariableDefinition oldValue = new VariableDefinition(fd.FieldType);

            set.Body.Variables.Add(oldValue);
            worker.Emit(OpCodes.Ldarg_0);
            worker.Emit(OpCodes.Ldfld, fd);
            worker.Emit(OpCodes.Stloc, oldValue);

            // this
            worker.Emit(OpCodes.Ldarg_0);

            // new value to set
            worker.Emit(OpCodes.Ldarg_1);

            // reference to field to set
            worker.Emit(OpCodes.Ldarg_0);
            worker.Emit(OpCodes.Ldflda, fd);

            // dirty bit
            // 8 byte integer aka long
            worker.Emit(OpCodes.Ldc_I8, dirtyBit);

            if (fd.FieldType.Is <UnityEngine.GameObject>())
            {
                // reference to netId Field to set
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldflda, netFieldId);

                worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarGameObjectReference);
            }
            else if (fd.FieldType.Is <NetworkIdentity>())
            {
                // reference to netId Field to set
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldflda, netFieldId);

                worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarNetworkIdentityReference);
            }
            else if (fd.FieldType.IsDerivedFrom <NetworkBehaviour>())
            {
                // reference to netId Field to set
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldflda, netFieldId);

                MethodReference getFunc = WeaverTypes.setSyncVarNetworkBehaviourReference.MakeGeneric(fd.FieldType);
                worker.Emit(OpCodes.Call, getFunc);
            }
            else
            {
                // make generic version of SetSyncVar with field type
                GenericInstanceMethod gm = new GenericInstanceMethod(WeaverTypes.setSyncVarReference);
                gm.GenericArguments.Add(fd.FieldType);

                // invoke SetSyncVar
                worker.Emit(OpCodes.Call, gm);
            }

            MethodDefinition hookMethod = GetHookMethod(td, fd);

            if (hookMethod != null)
            {
                //if (NetworkServer.localClientActive && !getSyncVarHookGuard(dirtyBit))
                Instruction label = worker.Create(OpCodes.Nop);
                worker.Emit(OpCodes.Call, WeaverTypes.NetworkServerGetLocalClientActive);
                worker.Emit(OpCodes.Brfalse, label);
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldc_I8, dirtyBit);
                worker.Emit(OpCodes.Call, WeaverTypes.getSyncVarHookGuard);
                worker.Emit(OpCodes.Brtrue, label);

                // setSyncVarHookGuard(dirtyBit, true);
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldc_I8, dirtyBit);
                worker.Emit(OpCodes.Ldc_I4_1);
                worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarHookGuard);

                // call hook (oldValue, newValue)
                // Generates: OnValueChanged(oldValue, value);
                WriteCallHookMethodUsingArgument(worker, hookMethod, oldValue);

                // setSyncVarHookGuard(dirtyBit, false);
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldc_I8, dirtyBit);
                worker.Emit(OpCodes.Ldc_I4_0);
                worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarHookGuard);

                worker.Append(label);
            }

            worker.Append(endOfMethod);

            worker.Emit(OpCodes.Ret);

            set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType));
            set.SemanticsAttributes = MethodSemanticsAttributes.Setter;

            return(set);
        }
        public static MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string originalName, FieldDefinition netFieldId)
        {
            //Create the get method
            MethodDefinition get = new MethodDefinition(
                "get_Network" + originalName, MethodAttributes.Public |
                MethodAttributes.SpecialName |
                MethodAttributes.HideBySig,
                fd.FieldType);

            ILProcessor worker = get.Body.GetILProcessor();

            // [SyncVar] GameObject?
            if (fd.FieldType.Is <UnityEngine.GameObject>())
            {
                // return this.GetSyncVarGameObject(ref field, uint netId);
                // this.
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldfld, netFieldId);
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldflda, fd);
                worker.Emit(OpCodes.Call, WeaverTypes.getSyncVarGameObjectReference);
                worker.Emit(OpCodes.Ret);
            }
            // [SyncVar] NetworkIdentity?
            else if (fd.FieldType.Is <NetworkIdentity>())
            {
                // return this.GetSyncVarNetworkIdentity(ref field, uint netId);
                // this.
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldfld, netFieldId);
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldflda, fd);
                worker.Emit(OpCodes.Call, WeaverTypes.getSyncVarNetworkIdentityReference);
                worker.Emit(OpCodes.Ret);
            }
            else if (fd.FieldType.IsDerivedFrom <NetworkBehaviour>())
            {
                // return this.GetSyncVarNetworkBehaviour<T>(ref field, uint netId);
                // this.
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldfld, netFieldId);
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldflda, fd);
                MethodReference getFunc = WeaverTypes.getSyncVarNetworkBehaviourReference.MakeGeneric(fd.FieldType);
                worker.Emit(OpCodes.Call, getFunc);
                worker.Emit(OpCodes.Ret);
            }
            // [SyncVar] int, string, etc.
            else
            {
                worker.Emit(OpCodes.Ldarg_0);
                worker.Emit(OpCodes.Ldfld, fd);
                worker.Emit(OpCodes.Ret);
            }

            get.Body.Variables.Add(new VariableDefinition(fd.FieldType));
            get.Body.InitLocals     = true;
            get.SemanticsAttributes = MethodSemanticsAttributes.Getter;

            return(get);
        }
Example #6
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);
        }
Example #7
0
        static MethodDefinition GenerateArraySegmentWriteFunc(TypeReference variable, int recursionCount)
        {
            GenericInstanceType genericInstance  = (GenericInstanceType)variable;
            TypeReference       elementType      = genericInstance.GenericArguments[0];
            MethodReference     elementWriteFunc = GetWriteFunc(elementType, recursionCount + 1);

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

            string functionName = "_WriteArraySegment_" + 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,
                                                               Weaver.voidType);

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

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

            ILProcessor worker = writerFunc.Body.GetILProcessor();

            MethodReference countref = Weaver.ArraySegmentCountReference.MakeHostInstanceGeneric(genericInstance);

            // int length = value.Count;
            worker.Append(worker.Create(OpCodes.Ldarga_S, (byte)1));
            worker.Append(worker.Create(OpCodes.Call, countref));
            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, GetWriteFunc(Weaver.int32Type)));

            // Loop through the ArraySegment<T> and call the writer for each element.
            // generates this:
            // for (int i=0; i< length; i++)
            // {
            //    writer.Write(value.Array[i + value.Offset]);
            // }
            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.Array[i + value.Offset]);
                worker.Append(worker.Create(OpCodes.Ldarg_0));
                worker.Append(worker.Create(OpCodes.Ldarga_S, (byte)1));
                worker.Append(worker.Create(OpCodes.Call, Weaver.ArraySegmentArrayReference.MakeHostInstanceGeneric(genericInstance)));
                worker.Append(worker.Create(OpCodes.Ldloc_1));
                worker.Append(worker.Create(OpCodes.Ldarga_S, (byte)1));
                worker.Append(worker.Create(OpCodes.Call, Weaver.ArraySegmentOffsetReference.MakeHostInstanceGeneric(genericInstance)));
                worker.Append(worker.Create(OpCodes.Add));
                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.Ldloc_0));
            worker.Append(worker.Create(OpCodes.Blt, labelBody));

            // return
            worker.Append(worker.Create(OpCodes.Ret));
            return(writerFunc);
        }
Example #8
0
        void DeserializeField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize)
        {
            // check for Hook function
            MethodDefinition hookMethod = SyncVarProcessor.GetHookMethod(netBehaviourSubclass, syncVar);

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

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

                    // Generates: if (!SyncVarEqual);
                    Instruction syncVarEqualLabel = 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
                    var 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, hookMethod));

                    // 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;
                var 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
                    var 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, hookMethod));

                    // Generates: end if (!SyncVarEqual);
                    serWorker.Append(syncVarEqualLabel);
                }
            }
        }
Example #9
0
        void GenerateDeSerialization()
        {
            Weaver.DLog(netBehaviourSubclass, "  GenerateDeSerialization");

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

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

            var 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;
            var dirtyBitsLocal = new VariableDefinition(Weaver.int64Type);

            serialize.Body.Variables.Add(dirtyBitsLocal);

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

            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(Weaver.uint64Type)));
            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;
            }

            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);
        }
Example #10
0
        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      = 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} 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 = netBehaviourSubclass.GetMethod(".ctor");

            if (ctor == null)
            {
                Weaver.Error($"{netBehaviourSubclass} has invalid constructor");
                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} 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;
        }
Example #11
0
        void GenerateSerialization()
        {
            Weaver.DLog(netBehaviourSubclass, "  GenerateSerialization");

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

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

            var 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
            var dirtyLocal = new VariableDefinition(Weaver.boolType);

            serialize.Body.Variables.Add(dirtyLocal);

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

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

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

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

            foreach (FieldDefinition syncVar in syncVars)
            {
                // Generates a writer call for each sync variable
                // writer
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                // this
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                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 ());
            // writer
            serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
            // base
            serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
            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)
                // base
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.NetworkBehaviourDirtyBitsReference));
                // 8 bytes = long
                serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit));
                serWorker.Append(serWorker.Create(OpCodes.And));
                serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel));

                // Generates a call to the writer for that field
                // writer
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
                // base
                serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
                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));
                // set dirtyLocal to true
                serWorker.Append(serWorker.Create(OpCodes.Stloc_0));

                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);
        }
Example #12
0
 public static void WriteRecycleWriter(ILProcessor worker)
 {
     // NetworkWriterPool.Recycle(writer);
     worker.Append(worker.Create(OpCodes.Ldloc_0));
     worker.Append(worker.Create(OpCodes.Call, Weaver.RecycleWriterReference));
 }
Example #13
0
 public static void WriteCreateWriter(ILProcessor worker)
 {
     // create writer
     worker.Append(worker.Create(OpCodes.Call, Weaver.GetPooledWriterReference));
     worker.Append(worker.Create(OpCodes.Stloc_0));
 }
Example #14
0
 public static void WriteSetupLocals(ILProcessor worker)
 {
     worker.Body.InitLocals = true;
     worker.Body.Variables.Add(new VariableDefinition(Weaver.PooledNetworkWriterType));
 }
        public static Instruction[] FindInstrSeq(this CilBody body, ILProcessor p, OpCode[] instrs, int amt)
        {
            Instruction[] result = new Instruction[amt];

            for (int i = 0; i < result.Length; i++)
                result[i] = i == 0 ? body.FindInstrSeqStart(instrs) : result[i - 1] != null ? result[i - 1].Next(p) : null;

            return result;
        }
 /// <summary>
 /// Appends a basic op code.
 /// </summary>
 /// <param name="il">The il.</param>
 /// <param name="opCode">The op code.</param>
 public static void Append(this ILProcessor il, OpCode opCode)
 {
     il.Append(il.Create(opCode));
 }
Example #17
0
        /// <inheritdoc />
        public void Rewrite(MethodDefinition methodDefinition, ILProcessor il, ObserverRewriterContext context)
        {
            List <Instruction> branches  = GetBranchingOps(methodDefinition).ToList();
            List <Instruction> branchTos = branches.Select(x => (Instruction)x.Operand).ToList();

            // Start from 2 because we setup Observer in first 2 instructions
            int position = 2;

            List <CodeSegment> segments = new List <CodeSegment>();

            var codeSegment = new CodeSegment(methodDefinition);

            while (position < methodDefinition.Body.Instructions.Count)
            {
                Instruction instruction = methodDefinition.Body.Instructions[position];

                bool added = false;

                // Start of a new segment. End last segment and start new one with this as the first instruction
                if (branchTos.Contains(instruction))
                {
                    if (codeSegment.Instructions.Any())
                    {
                        segments.Add(codeSegment);
                    }
                    codeSegment = new CodeSegment(methodDefinition);
                    codeSegment.Instructions.Add(instruction);
                    added = true;
                }

                // End of a segment. Add this as the last instruction and move onwards with a new segment
                if (branches.Contains(instruction))
                {
                    codeSegment.Instructions.Add(instruction);
                    segments.Add(codeSegment);
                    codeSegment = new CodeSegment(methodDefinition);
                    added       = true;
                }

                // Just an in-between instruction. Add to current segment.
                if (!added)
                {
                    codeSegment.Instructions.Add(instruction);
                }

                position++;
            }

            // Got to end of the method. Add the last one if necessary
            if (!segments.Contains(codeSegment) && codeSegment.Instructions.Any())
            {
                segments.Add(codeSegment);
            }

            foreach (CodeSegment segment in segments)
            {
                AddSpendGasMethodBeforeInstruction(il, context.Observer, context.ObserverVariable, segment);
            }

            // All of the branches now need to point to the place 3 instructions earlier!
            foreach (Instruction branch in branches)
            {
                Instruction currentlyPointingTo = (Instruction)branch.Operand;
                branch.Operand = currentlyPointingTo.Previous.Previous.Previous;
            }
        }
    public static void AdjustOffsets(this ILProcessor il, MethodBody body, IList <int> offsets, int adjustBy)
    {
        //Unfortunately one thing Mono.Cecil doesn't do is adjust instruction offsets for branch statements
        //and exception handling start points. We need to fix these manually
        if (offsets.Count == 0)
        {
            return;
        }

        //We need to make sure we don't fix any instructions twice!
        List <int> seenHashCodes = new List <int>();

        //Fix all branch statements
        for (int i = 0; i < body.Instructions.Count; i++)
        {
            //Get the instruction
            Instruction instruction = body.Instructions[i];

            //We need to find the target as it may have changed
            if (instruction.Operand is Instruction)
            {
                Instruction target   = (Instruction)instruction.Operand;
                int         hashCode = target.GetHashCode();
                if (seenHashCodes.Contains(hashCode))
                {
                    continue;
                }
                seenHashCodes.Add(hashCode);

                OpCode opCode = instruction.OpCode;

                //Work out the new offset
                int originalOffset = target.Offset;
                int offset         = target.Offset;
                foreach (int movedOffsets in offsets)
                {
                    if (originalOffset > movedOffsets)
                    {
                        offset += adjustBy;
                    }
                }
                target.Offset = offset;

                Log.Debug("Shifting {0} from {1:x} to {2:x}", opCode.Name, originalOffset, offset);

                Instruction newInstr = il.Create(opCode, target);
                il.Replace(instruction, newInstr);
            }
            else if (instruction.Operand is Instruction[]) //e.g. Switch statements
            {
                Instruction[] targets = (Instruction[])instruction.Operand;

                Log.Debug("Shifting {0} from:", instruction.OpCode.Name);

                foreach (Instruction target in targets)
                {
                    int hashCode = target.GetHashCode();
                    if (seenHashCodes.Contains(hashCode))
                    {
                        continue;
                    }
                    seenHashCodes.Add(hashCode);

                    //Work out the new offset
                    int originalOffset = target.Offset;
                    int offset         = target.Offset;
                    foreach (int movedOffsets in offsets)
                    {
                        if (originalOffset > movedOffsets)
                        {
                            offset += adjustBy;
                        }
                    }
                    target.Offset = offset;

                    Log.Debug("\t{0:x} to {1:x}", originalOffset, offset);
                }
                Instruction newInstr = il.Create(instruction.OpCode, targets);
                il.Replace(instruction, newInstr);
            }
        }
        //If there is a try adjust the starting point also
        foreach (ExceptionHandler handler in body.ExceptionHandlers)
        {
            //Work out the new offset
            Instruction target   = handler.TryStart;
            int         hashCode = target.GetHashCode();
            if (seenHashCodes.Contains(hashCode))
            {
                continue;
            }
            seenHashCodes.Add(hashCode);

            int originalOffset = target.Offset;
            int offset         = target.Offset;
            foreach (int movedOffsets in offsets)
            {
                if (originalOffset > movedOffsets)
                {
                    offset += adjustBy;
                }
            }

            Log.Debug("Shifting try start from {0:x} to {1:x}", originalOffset, offset);

            target.Offset = offset;
        }
    }
Example #19
0
        static MethodDefinition GenerateArrayWriteFunc(TypeReference variable, int recursionCount)
        {
            if (!variable.IsArrayType())
            {
                Weaver.Error($"{variable} is an unsupported type. Jagged and multidimensional arrays are not supported");
                return(null);
            }

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

            if (elementWriteFunc == null)
            {
                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.int32Type));
            writerFunc.Body.Variables.Add(new VariableDefinition(Weaver.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, GetWriteFunc(Weaver.int32Type)));
            worker.Append(worker.Create(OpCodes.Ret));

            // int length = value.Length;
            worker.Append(labelNull);
            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, GetWriteFunc(Weaver.int32Type)));

            // 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, 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.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);
        }
Example #20
0
        private void InjectForeach <T>(InjectorArgs args, VariableDefinition list, Action <VariableDefinition> action)
        {
            MethodDefinition method   = args.OriginMethod;
            ILProcessor      ilprosor = args.ILProcessor;

            // 初始化变量
            if (!_foreachItemDic.ContainsKey(typeof(IEnumerator <T>)) || args.VarEnumerator == null)
            {
                args.VarEnumerator = new VariableDefinition(method.Module.Import(typeof(IEnumerator <T>)));
                method.Body.Variables.Add(args.VarEnumerator);
                if (!_foreachItemDic.ContainsKey(typeof(IEnumerator <T>)))
                {
                    _foreachItemDic.Add(typeof(IEnumerator <T>), true);
                }
            }
            if (args.VarHasNext == null)
            {
                args.VarHasNext = new VariableDefinition(method.Module.Import(typeof(bool)));
                method.Body.Variables.Add(args.VarHasNext);
            }
            if (!_foreachItemDic.ContainsKey(typeof(T)) || args.VarItem == null)
            {
                args.VarItem = new VariableDefinition(method.Module.Import(typeof(T)));
                method.Body.Variables.Add(args.VarItem);
                if (!_foreachItemDic.ContainsKey(typeof(T)))
                {
                    _foreachItemDic.Add(typeof(T), true);
                }
            }

            var tryStart   = ilprosor.Create(OpCodes.Nop);
            var handlerEnd = ilprosor.Create(OpCodes.Nop);
            var tryEnd     = ilprosor.Create(OpCodes.Nop);
            //var tryEnd = ilprosor.Create(OpCodes.Leave, handlerEnd);
            var finallyStart = ilprosor.Create(OpCodes.Nop);
            var finallyEnd   = ilprosor.Create(OpCodes.Endfinally);

            //  list.GetEnumerator()
            ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, list));
            ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(MAList).GetMethod("GetEnumerator", new Type[] { }))));
            ilprosor.Append(ilprosor.Create(OpCodes.Stloc, args.VarEnumerator));
            // 注意顺序 只有获取到VarEnumerator才可以初始化以下指令
            var loopStart     = ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator);
            var moveNextStart = ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator);

            // try{  while(list.MoveNext()){  var item = list.get_Current();  action(item);   }   }
            ilprosor.Append(tryStart);
            ilprosor.Append(ilprosor.Create(OpCodes.Br, moveNextStart));
            ilprosor.Append(loopStart);
            ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(IEnumerator <T>).GetMethod("get_Current", new Type[] { }))));
            ilprosor.Append(ilprosor.Create(OpCodes.Stloc, args.VarItem));

            if (action != null)
            {
                action(args.VarItem);
            }

            ilprosor.Append(moveNextStart);
            ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(IEnumerator).GetMethod("MoveNext", new Type[] { }))));
            ilprosor.Append(ilprosor.Create(OpCodes.Stloc, args.VarHasNext));
            ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, args.VarHasNext));
            ilprosor.Append(ilprosor.Create(OpCodes.Brtrue, loopStart));
            ilprosor.Append(ilprosor.Create(OpCodes.Leave, handlerEnd));

            //
            // finally{
            //  if( VarEnumerator!=null )
            //  { VarEnumerator.Dispose(); }
            // }
            ilprosor.Append(finallyStart);
            ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator));
            ilprosor.Append(ilprosor.Create(OpCodes.Ldnull));
            ilprosor.Append(ilprosor.Create(OpCodes.Ceq));
            ilprosor.Append(ilprosor.Create(OpCodes.Stloc_S, args.VarHasNext));
            ilprosor.Append(ilprosor.Create(OpCodes.Ldloc_S, args.VarHasNext));
            ilprosor.Append(ilprosor.Create(OpCodes.Brtrue_S, handlerEnd));
            ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator));
            ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(System.IDisposable).GetMethod("Dispose", new Type[] { }))));
            ilprosor.Append(ilprosor.Create(OpCodes.Nop));
            ilprosor.Append(finallyEnd);
            ilprosor.Append(handlerEnd);

            //ilprosor.Append(end);
            ilprosor.Append(ilprosor.Create(OpCodes.Nop));
            // HandlerStart HandlerEnd 表示ExceptionHandlerType处理的开始及结束位置
            method.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Finally)
            {
                HandlerEnd   = handlerEnd,
                HandlerStart = finallyStart,
                TryEnd       = finallyStart,
                TryStart     = tryStart
            });
        }
Example #21
0
 public static void InsertBefore(this ILProcessor ilProcessor, Instruction target, params Instruction[] instructions)
 {
     InsertBefore(ilProcessor, target, instructions.ToList());
 }
Example #22
0
 public override void EmitAddress(ILProcessor ilProcessor)
 {
     Previous.Emit(ilProcessor);
 }
 public static void WriteCallHookMethodUsingArgument(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue)
 {
     WriteCallHookMethod(worker, hookMethod, oldValue, null);
 }
Example #24
0
 public override void StoreEnd(ILProcessor ilProcessor)
 {
     throw new NotImplementedException();
 }
Example #25
0
        public static Instruction Append(this Instruction instruction, Instruction instructionAfter, ILProcessor processor)
        {
            processor.InsertAfter(instruction, instructionAfter);

            return(instructionAfter);
        }
Example #26
0
 /// <summary>
 ///     Replaces the <paramref name="oldInstruction" /> with a new set of <paramref name="IL" /> instructions..
 /// </summary>
 /// <param name="oldInstruction">The instruction currently being evaluated.</param>
 /// <param name="hostMethod">The method that contains the target instruction.</param>
 /// <param name="IL">The ILProcessor for the target method body.</param>
 protected abstract void Replace(Instruction oldInstruction, MethodDefinition hostMethod, ILProcessor IL);
Example #27
0
 public static Instruction AppendLdcI4(this Instruction instruction, ILProcessor processor, int value)
 {
     return(instruction.Append(processor.Create(OpCodes.Ldc_I4, value), processor));
 }
Example #28
0
 public Instruction CreateInstruction(ILProcessor worker, OpCode opcode)
 {
     return(worker.Create(opcode));
 }
Example #29
0
 public static Instruction AppendLdstr(this Instruction instruction, ILProcessor processor, string str)
 {
     return(instruction.Append(processor.Create(OpCodes.Ldstr, str), processor));
 }
Example #30
0
        public static void Main(string[] args)
        {
            try
            {
                ManagedPath = Environment.GetEnvironmentVariable("DOORSTOP_MANAGED_FOLDER_DIR");
                GamePath    = Path.GetFullPath(ManagedPath + @"\..\..\");

                string ConfigPath = Path.GetFullPath(Path.Combine(ManagedPath, @"..\..\UULoader\config.json"));
                if (File.Exists(ConfigPath))
                {
                    string lines = File.ReadAllText(ConfigPath);
                    config = JsonConvert.DeserializeObject <Config>(lines);
                }
                else
                {
                    config = new Config();
                }


                using (AssemblyDefinition mainDef = AssemblyDefinition.ReadAssembly(GamePath + "UULoader\\UULoader.dll"))
                {
                    if (config.useDefault)
                    {
                        string dll = "UnityEngine.CoreModule.dll";
                        if (!File.Exists(Path.Combine(ManagedPath, "UnityEngine.CoreModule.dll")))
                        {
                            dll = "UnityEngine.dll";
                        }

                        using (AssemblyDefinition unityCoreDef =
                                   AssemblyDefinition.ReadAssembly(Path.Combine(ManagedPath, dll)))
                        {
                            TypeDefinition applicationDef =
                                unityCoreDef.MainModule.Types.FirstOrDefault((TypeDefinition x) =>
                                                                             x.Name == "Application");

                            MethodDefinition startInfo = mainDef.MainModule.Types
                                                         .First((TypeDefinition x) => x.Name == "UULoaderMain")
                                                         .Methods.First((MethodDefinition x) => x.Name == "Start");
                            MethodReference startMethod = unityCoreDef.MainModule.ImportReference(startInfo);

                            MethodDefinition constructor =
                                applicationDef.Methods.FirstOrDefault((MethodDefinition m) =>
                                                                      m.IsConstructor && m.IsStatic);
                            if (constructor == null)
                            {
                                constructor = new MethodDefinition(".cctor",
                                                                   MethodAttributes.Private | MethodAttributes.HideBySig |
                                                                   MethodAttributes.SpecialName |
                                                                   MethodAttributes.RTSpecialName | MethodAttributes.Static,
                                                                   unityCoreDef.MainModule.ImportReference(typeof(void)));
                                applicationDef.Methods.Add(constructor);
                                ILProcessor ilprocessor = constructor.Body.GetILProcessor();
                                ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
                            }

                            ILProcessor ilprocessor2 = constructor.Body.GetILProcessor();
                            Instruction instruction  = ilprocessor2.Body.Instructions.First <Instruction>();
                            ilprocessor2.InsertBefore(instruction, ilprocessor2.Create(OpCodes.Call, startMethod));

                            using (MemoryStream memoryStream = new MemoryStream())
                            {
                                unityCoreDef.Write(memoryStream);
                                Assembly.Load(memoryStream.ToArray());
                            }
                        }
                    }
                    else
                    {
                        using (AssemblyDefinition unityCoreDef =
                                   AssemblyDefinition.ReadAssembly(Path.Combine(ManagedPath, config.assembly)))
                        {
                            TypeDefinition applicationDef =
                                unityCoreDef.MainModule.Types.FirstOrDefault((TypeDefinition x) =>
                                                                             x.Name == config.type);

                            MethodDefinition startInfo = mainDef.MainModule.Types
                                                         .First((TypeDefinition x) => x.Name == "UULoaderMain")
                                                         .Methods.First((MethodDefinition x) => x.Name == "Start");
                            MethodReference startMethod = unityCoreDef.MainModule.ImportReference(startInfo);

                            MethodDefinition constructor =
                                applicationDef.Methods.FirstOrDefault((MethodDefinition m) =>
                                                                      m.Name == config.method);

                            ILProcessor ilprocessor2 = constructor.Body.GetILProcessor();
                            Instruction instruction  = ilprocessor2.Body.Instructions.First <Instruction>();
                            ilprocessor2.InsertBefore(instruction, ilprocessor2.Create(OpCodes.Call, startMethod));

                            using (MemoryStream memoryStream = new MemoryStream())
                            {
                                unityCoreDef.Write(memoryStream);
                                Assembly.Load(memoryStream.ToArray());
                            }
                        }
                    }

                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        mainDef.Write(memoryStream);
                        Assembly.Load(memoryStream.ToArray());
                    }
                }
            }
            catch (Exception e)
            {
                using (TextWriter textWriter = File.CreateText("ERROR.txt"))
                {
                    textWriter.WriteLine(e);
                    textWriter.Flush();
                }
            }
        }
    private bool ModifyGameMode(ModuleDefinition module)
    {
        TypeDefinition gameMode = module.Types.FirstOrDefault(c => c.Name == "GameMode");

        if (gameMode == null)
        {
            Logging.LogError("Failed to find the class GameMode.");
            return(false);
        }

        foreach (MethodDefinition md in gameMode.Methods)
        {
            Logging.LogInfo(string.Format("Method: {0}, FullName: {1}", md.Name, md.FullName));
        }

        MethodDefinition staticConstructor = gameMode.Methods.FirstOrDefault(m => m.Name == ".cctor");

        if (staticConstructor == null)
        {
            Logging.LogError("Failed to find the method staticConstructor.");
            return(false);
        }

        TypeDefinition gmEditWorld = module.Types.FirstOrDefault(c => c.Name == "GameModeEditWorld");

        if (gmEditWorld == null)
        {
            Logging.LogError("Failed to find the class GameModeEditWorld.");
            return(false);
        }

        MethodDefinition gmEWctor = gmEditWorld.Methods.FirstOrDefault(m => m.Name == ".ctor");

        if (gmEWctor == null)
        {
            Logging.LogError("Failed to find the method GameModeEditWorld::.ctor().");
            return(false);
        }

        bool modifiedCode = false;
        var  instx        = staticConstructor.Body.Instructions;

        if (instx[0].OpCode == OpCodes.Ldc_I4_3 && instx[1].OpCode == OpCodes.Newarr)
        {
            instx[0].OpCode = OpCodes.Ldc_I4_4;
            if (instx[14].OpCode == OpCodes.Stsfld && instx[14].Operand.ToString() == "GameMode[] GameMode::AvailGameModes")
            {
                Instruction gmCollection = instx[14];
                ILProcessor il           = staticConstructor.Body.GetILProcessor();
                il.InsertBefore(gmCollection, Instruction.Create(OpCodes.Dup));
                il.InsertBefore(gmCollection, Instruction.Create(OpCodes.Ldc_I4_3));
                il.InsertBefore(gmCollection, Instruction.Create(OpCodes.Newobj, gmEWctor));
                il.InsertBefore(gmCollection, Instruction.Create(OpCodes.Stelem_Ref));

                modifiedCode = true;
            }
            else
            {
                Logging.LogError(string.Format("Failed to modify code:  Instruction 13 is {0}--{1}", instx[13].OpCode, instx[13].Operand));
            }
        }

        if (modifiedCode)
        {
            return(true);
        }

        return(false);
    }
 public static Instruction[] FindInstrSeq(this CilBody body, ILProcessor p, OpCode[] instrs)
 {
     return body.FindInstrSeq(p, instrs, instrs.Length);
 }