Exemplo n.º 1
0
        public override void Run()
        {
            var vanilla  = SourceDefinition.Type("Terraria.Netplay").Method("ServerLoop");
            var callback = this.Method(() => OTAPI.Callbacks.Terraria.Netplay.ServerSocketCreate());

            var iTcpSocket = vanilla.Body.Instructions.Single(x => x.OpCode == OpCodes.Newobj &&
                                                              x.Operand is MethodReference &&
                                                              (x.Operand as MethodReference).Name == ".ctor" &&
                                                              (x.Operand as MethodReference).DeclaringType.Name == "TcpSocket"
                                                              );

            iTcpSocket.OpCode  = OpCodes.Call;                    //Replace newobj to be call as we need to execute out callback instead
            iTcpSocket.Operand = vanilla.Module.Import(callback); //Replace the method reference from the TcpSocket constructor, to our callback
        }
Exemplo n.º 2
0
        public override void Run()
        {
            var vanilla  = SourceDefinition.Type("Terraria.NetMessage").Method("SendData");
            var callback = vanilla.Module.Import(ModificationDefinition.Type("OTAPI.Callbacks.Terraria.NetMessage").Method("SendBytes"));

            //Hooking send bytes should be as simple as replacing each AsyncSend call with
            //the OTAPI callback as well as removing the socket instance and leaving the
            //remoteClient/num variable.
            //TODO: Netplay.Connection.Socket AsyncSend for client

            //Find all calls to AsyncSend
            var asyncSendCalls = vanilla.Body.Instructions.Where(x => x.OpCode == OpCodes.Callvirt &&
                                                                 x.Operand is MethodReference &&
                                                                 (x.Operand as MethodReference).Name == "AsyncSend" &&
                                                                 x.Previous(6).Operand is FieldReference &&
                                                                 (x.Previous(6).Operand as FieldReference).Name == "Clients")
                                 .ToArray();

            var il = vanilla.Body.GetILProcessor();

            foreach (var call in asyncSendCalls)
            {
                //Replace the call with our OTAPI callback
                call.OpCode  = OpCodes.Call;
                call.Operand = callback;

                //Wind back to the first Netplay.Clients (there are two, we want the one before the Socket reference)
                var clients = call.Previous(x => x.OpCode == OpCodes.Ldfld &&
                                            x.Operand is FieldReference &&
                                            (x.Operand as FieldReference).Name == "Socket")
                              .Previous(x => x.OpCode == OpCodes.Ldsfld &&
                                        x.Operand is FieldReference &&
                                        (x.Operand as FieldReference).Name == "Clients");

                //Remove the Socket call
                if (clients.Next.Next.OpCode != OpCodes.Ldelem_Ref)
                {
                    throw new InvalidOperationException($"{clients.Next.Next.OpCode} was not expected.");
                }
                il.Remove(clients.Next.Next);                 //ldelem.ref
                if (clients.Next.Next.OpCode != OpCodes.Ldfld)
                {
                    throw new InvalidOperationException($"{clients.Next.Next.OpCode} was not expected.");
                }
                il.Remove(clients.Next.Next);                 //ldfld

                //Remove the client call
                il.Remove(clients);
            }
        }
Exemplo n.º 3
0
        public override void Run()
        {
            var netMessage = SourceDefinition.Type <NetMessage>();
            var sendData   = netMessage.Method("SendData");

            // Create new method
            var overloading = new MethodDefinition(sendData.Name, sendData.Attributes, sendData.ReturnType);

            foreach (var p in sendData.Parameters)
            {
                var prm = new ParameterDefinition(p.Name, p.Attributes, p.ParameterType);
                prm.Constant = p.Constant;
                if (prm.ParameterType == SourceDefinition.MainModule.TypeSystem.String)
                {
                    prm.ParameterType = Type <NetworkText>();
                    prm.Constant      = NetworkText.Empty;
                }
                overloading.Parameters.Add(prm);
            }

            // Create method body
            var ilprocessor          = overloading.Body.GetILProcessor();
            ParameterDefinition text = null;

            for (int i = 0; i < overloading.Parameters.Count; i++)
            {
                ilprocessor.Append(Instruction.Create(OpCodes.Ldarg, overloading.Parameters[i]));
                if (overloading.Parameters[i].ParameterType == Type <NetworkText>())
                {
                    text = overloading.Parameters[i];
                    ilprocessor.Append(Instruction.Create(OpCodes.Callvirt, Type <NetworkText>().Method("ToString")));
                }
            }
            ilprocessor.Append(Instruction.Create(OpCodes.Call, sendData));
            ilprocessor.Append(Instruction.Create(OpCodes.Ret));

            foreach (var i in new []
            {
                Instruction.Create(OpCodes.Ldarg, text),
                Instruction.Create(OpCodes.Brtrue_S, overloading.Body.Instructions[0]),
                Instruction.Create(OpCodes.Ldsfld, Type <NetworkText>().Field("Empty")),
                Instruction.Create(OpCodes.Starg, text),
            }.Reverse())
            {
                ilprocessor.InsertBefore(overloading.Body.Instructions[0], i);
            }

            netMessage.Methods.Add(overloading);
        }
Exemplo n.º 4
0
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.Projectile").Method("Kill");

            var cbkBegin = this.Method(() => Callbacks.Terraria.Projectile.KillBegin(null));
            var cbkEnd   = this.Method(() => Callbacks.Terraria.Projectile.KillEnd(null));

            vanilla.Wrap
            (
                beginCallback: cbkBegin,
                endCallback: cbkEnd,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: true
            );
        }
Exemplo n.º 5
0
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.Player")
                          .Method("Hurt");

            int    tmp      = 0;
            bool   tmp1     = false;
            string tmp2     = null;
            double tmp3     = 0;
            var    cbkBegin =          //this.SourceDefinition.MainModule.Import(
                              this.Method(() => OTAPI.Callbacks.Terraria.Player.HurtBegin(ref tmp3, null, ref tmp, ref tmp, ref tmp1, ref tmp1, ref tmp2, ref tmp1, ref tmp));

            //);

            vanilla.InjectNonVoidBeginCallback(cbkBegin);
        }
Exemplo n.º 6
0
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.RemoteClient").Method("Reset");

            var cbkBegin = this.Method(() => OTAPI.Callbacks.Terraria.RemoteClient.PreReset(null));
            var cbkEnd   = this.Method(() => OTAPI.Callbacks.Terraria.RemoteClient.PostReset(null));

            vanilla.Wrap
            (
                beginCallback: cbkBegin,
                endCallback: cbkEnd,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: true
            );
        }
Exemplo n.º 7
0
        public override void Run()
        {
            var vanilla  = SourceDefinition.Type("Terraria.Netplay").Method("OnConnectionAccepted");
            var callback = SourceDefinition.MainModule.Import(
                this.Method(() => OTAPI.Callbacks.Terraria.Netplay.OnConnectionAccepted(null))
                );

            vanilla.Wrap
            (
                beginCallback: callback,
                endCallback: null,
                beginIsCancellable: true,
                noEndHandling: true,
                allowCallbackInstance: false
            );
        }
Exemplo n.º 8
0
        public override void Run()
        {
            var vanilla  = SourceDefinition.Type("Terraria.NetMessage").Method("SendData");
            var callback = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.NetMessage").Method("SendData");

            //Few stack issues arose trying to inject a callback before for lock, so i'll resort to
            //wrapping the method;

            vanilla.Wrap
            (
                beginCallback: callback,
                endCallback: null,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: false
            );
        }
Exemplo n.º 9
0
        public override void Run()
        {
            var vanilla      = SourceDefinition.Type("Terraria.NPC").Method("Transform");
            int tmp          = 0;
            var preCallback  = vanilla.Module.Import(this.Method(() => OTAPI.Callbacks.Terraria.Npc.PreTransform(null, ref tmp)));
            var postCallback = vanilla.Module.Import(this.Method(() => OTAPI.Callbacks.Terraria.Npc.PostTransform(null)));

            //We could wrap this method, but this hooks is to inform when an npc transforms, not
            //an occurrence of the call.
            //Anyway, this instance we need to insert before the netMode == 2 check.

            //Get the IL processor instance so we can modify IL
            var processor = vanilla.Body.GetILProcessor();

            //Pre callback section
            {
                var first = vanilla.Body.Instructions.First();
                processor.InsertBefore(first,
                                       new { OpCodes.Ldarg_0 },
                                       new { OpCodes.Ldarga, Operand = vanilla.Parameters.Single() },
                                       new { OpCodes.Call, Operand = preCallback },
                                       new { OpCodes.Brtrue_S, Operand = first },
                                       new { OpCodes.Ret }
                                       );
            }

            //Post callback section
            {
                var insertionPoint = vanilla.Body.Instructions.Single(x => x.OpCode == OpCodes.Ldsfld &&
                                                                      x.Operand is FieldReference &&
                                                                      (x.Operand as FieldReference).Name == "netMode" &&
                                                                      x.Next.OpCode == OpCodes.Ldc_I4_2
                                                                      );

                //Insert our callback before the if block, ensuring we consider that the if block may be referenced elsewhere
                Instruction ourEntry;
                processor.InsertBefore(insertionPoint, ourEntry = processor.Create(OpCodes.Ldarg_0));                 //Add the current instance (this in C#) to the callback
                processor.InsertBefore(insertionPoint, processor.Create(OpCodes.Call, postCallback));

                //Replace transfers
                insertionPoint.ReplaceTransfer(ourEntry, vanilla);
            }
        }
Exemplo n.º 10
0
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.Projectile").Methods.Single(
                x => x.Name == "AI" &&
                x.Parameters.Count() == 0
                );

            var cbkBegin = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.Projectile").Method("AIBegin", parameters: vanilla.Parameters);
            var cbkEnd   = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.Projectile").Method("AIEnd", parameters: vanilla.Parameters);

            vanilla.Wrap
            (
                beginCallback: cbkBegin,
                endCallback: cbkEnd,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: true
            );
        }
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.NPC").Methods.Single(
                x => x.Name == "SetDefaults" &&
                x.Parameters.First().ParameterType == SourceDefinition.MainModule.TypeSystem.String
                );

            var cbkBegin = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.Npc").Method("SetDefaultsByNameBegin", parameters: vanilla.Parameters);
            var cbkEnd   = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.Npc").Method("SetDefaultsByNameEnd", parameters: vanilla.Parameters);

            vanilla.Wrap
            (
                beginCallback: cbkBegin,
                endCallback: cbkEnd,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: true
            );
        }
Exemplo n.º 12
0
        public override void Run()
        {
            foreach (var type in new[]
            {
                new { TypeDef = SourceDefinition.Type("Terraria.NPC"), MechType = OpCodes.Ldc_I4_1 },
                new { TypeDef = SourceDefinition.Type("Terraria.Item"), MechType = OpCodes.Ldc_I4_2 }
            })
            {
                var vanilla = type.TypeDef.Methods.Single(x => x.Name == "MechSpawn");
                var hook    = SourceDefinition.MainModule.Import(ModificationDefinition.Type("OTAPI.Callbacks.Terraria.World").Method("MechSpawn"));

                //Here we find the insertion point where we want to inject our callback at.
                var iInsertionPoint = vanilla.Body.Instructions.Last(x => x.OpCode == OpCodes.Ldloc_1);
                //If the result of the callback instructs us to cancel, this instruction will transfer us to the existing "return false"
                var iContinuePoint = vanilla.Body.Instructions.Last(x => x.OpCode == OpCodes.Ldc_I4_0);

                var il = vanilla.Body.GetILProcessor();

                //Add all the parameters
                foreach (var prm in vanilla.Parameters)
                {
                    il.InsertBefore(iInsertionPoint, il.Create(OpCodes.Ldarg, prm));
                }

                //Add the first three variables by reference
                for (var x = 0; x < 3; x++)
                {
                    il.InsertBefore(iInsertionPoint, il.Create(OpCodes.Ldloca, vanilla.Body.Variables[x]));
                }

                //Append our mech type enum value to the method call
                il.InsertBefore(iInsertionPoint, il.Create(type.MechType));

                //Trigger the method to run with the parameters on the stck
                il.InsertBefore(iInsertionPoint, il.Create(OpCodes.Call, hook));

                //If the result left on the stack is false, then jump to the "return false"
                il.InsertBefore(iInsertionPoint, il.Create(OpCodes.Brfalse_S, iContinuePoint));

                vanilla.Body.OptimizeMacros();
            }
        }
Exemplo n.º 13
0
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.Projectile").Methods.Single(
                x => x.Name == "Update" &&
                x.Parameters.Single().ParameterType == SourceDefinition.MainModule.TypeSystem.Int32
                );


            var cbkBegin = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.Projectile").Method("UpdateBegin", parameters: vanilla.Parameters);
            var cbkEnd   = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.Projectile").Method("UpdateEnd", parameters: vanilla.Parameters);

            vanilla.Wrap
            (
                beginCallback: cbkBegin,
                endCallback: cbkEnd,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: true
            );
        }
Exemplo n.º 14
0
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.IO.WorldFile").Methods.Single(
                x => x.Name == "loadWorld" &&
                x.Parameters.Count() == 1 &&
                x.Parameters[0].ParameterType == SourceDefinition.MainModule.TypeSystem.Boolean
                );

            var cbkBegin = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.WorldFile").Method("LoadWorldBegin", parameters: vanilla.Parameters);
            var cbkEnd   = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.WorldFile").Method("LoadWorldEnd", parameters: vanilla.Parameters);

            vanilla.Wrap
            (
                beginCallback: cbkBegin,
                endCallback: cbkEnd,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: false
            );
        }
Exemplo n.º 15
0
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.NetMessage")
                          .Method("greetPlayer");

            var cbkBegin = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.NetMessage")
                           .Method("GreetPlayerBegin", parameters: vanilla.Parameters);

            var cbkEnd = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.NetMessage")
                         .Method("GreetPlayerEnd", parameters: vanilla.Parameters);

            vanilla.Wrap
            (
                beginCallback: cbkBegin,
                endCallback: cbkEnd,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: false
            );
        }
Exemplo n.º 16
0
        public override void Run()
        {
            //Get the vanilla method reference
            var vanilla = SourceDefinition.Type("Terraria.WorldGen").Method("hardUpdateWorld");

            //Get the OTAPI callback method reference
            //int tmp = 0;
            var callback = this.SourceDefinition.MainModule.Import(
                this.Method(() => OTAPI.Callbacks.Terraria.WorldGen.HardmodeTilePlace(0, 0, 0, false, false, 0, 0))
                );

            /* In this particular hardmode tile mod we replace all WorldGen.PlaceTile
             * calls to a custom callback version, then replace the Pop instruction
             * with cancelable IL.
             */

            var targets = vanilla.Body.Instructions.Where(instruction =>
                                                          instruction.OpCode == OpCodes.Call &&
                                                          (instruction.Operand as MethodReference).Name == "PlaceTile"

                                                          && instruction.Next.OpCode == OpCodes.Pop
                                                          ).ToArray();

            var processor = vanilla.Body.GetILProcessor();

            foreach (var replacementPoint in targets)            //.Take(1))
            {
                replacementPoint.Operand = callback;


                var insContinue = replacementPoint.Next.Next.Next(i =>
                                                                  i.OpCode == OpCodes.Call &&
                                                                  (i.Operand as MethodReference).Name == "SendTileSquare"
                                                                  ).Next;

                replacementPoint.Next.OpCode  = OpCodes.Brtrue_S;
                replacementPoint.Next.Operand = insContinue;

                processor.InsertAfter(replacementPoint.Next, processor.Create(OpCodes.Ret));
            }
        }
Exemplo n.º 17
0
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.Item").Methods.Single(
                x => x.Name == "SetDefaults" &&
                x.Parameters.First().ParameterType == this.SourceDefinition.MainModule.TypeSystem.Int32 &&
                x.Parameters.Skip(1).First().ParameterType == this.SourceDefinition.MainModule.TypeSystem.Boolean
                );


            var cbkBegin = this.ModificationDefinition.Type("OTAPI.Callbacks.Terraria.Item").Method("SetDefaultsByIdBegin", parameters: vanilla.Parameters);
            var cbkEnd   = this.ModificationDefinition.Type("OTAPI.Callbacks.Terraria.Item").Method("SetDefaultsByIdEnd", parameters: vanilla.Parameters);

            vanilla.Wrap
            (
                beginCallback: cbkBegin,
                endCallback: cbkEnd,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: true
            );
        }
Exemplo n.º 18
0
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.Player")
                          .Method("UpdateJumpHeight");

            var cbkBegin = this.SourceDefinition.MainModule.Import(
                this.Method(() => OTAPI.Callbacks.Terraria.Player.UpdateJumpHeightBegin(null))
                );

            var cbkEnd = this.SourceDefinition.MainModule.Import(
                this.Method(() => OTAPI.Callbacks.Terraria.Player.UpdateJumpHeightEnd(null))
                );

            vanilla.Wrap
            (
                beginCallback: cbkBegin,
                endCallback: cbkEnd,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: true
            );
        }
Exemplo n.º 19
0
        public override void Run()
        {
            var vanilla  = SourceDefinition.Type("Terraria.NPC").Method("DropItemInstanced");
            var callback = this.Method(
                () => OTAPI.Callbacks.Terraria.Npc.BossBagItem(0, 0, 0, 0, 0, 0, false, 0, false, false, null)
                );

            var il = vanilla.Body.GetILProcessor();

            //Grad the NewItem calls
            var instructions = vanilla.Body.Instructions.Where(x => x.OpCode == OpCodes.Call &&
                                                               x.Operand is MethodReference &&
                                                               (x.Operand as MethodReference).Name == "NewItem" &&
                                                               x.Next.OpCode == OpCodes.Stloc_0);

            //Quick validation check
            if (instructions.Count() != 1)
            {
                throw new NotSupportedException("Only one server NewItem call expected in DropBossBags.");
            }

            //The first call is in the server block. TODO: client version
            var ins = instructions.First();

            //Swap the NewItem call to our custom item call
            ins.Operand = vanilla.Module.Import(callback);
            //Our argument appends the NPC instance (this) to the arguments
            il.InsertBefore(ins, il.Create(OpCodes.Ldarg_0));             //Instance methods ldarg.0 is the instance object

            //Now we start inserting our own if block to compare the call result.
            var target = ins.Next /*stloc.0*/.Next;                        //Grabs a reference to the instruction after the stloc.1 opcode so we can insert sequentially

            il.InsertBefore(target, il.Create(OpCodes.Ldloc_0));           //Load the num variable onto the stack
            il.InsertBefore(target, il.Create(OpCodes.Ldc_I4_M1));         //Load -1 onto the stack
            il.InsertBefore(target, il.Create(OpCodes.Ceq));               //Consume & compare the two variables and push 1 (true) or 0 (false) onto the stack
            il.InsertBefore(target, il.Create(OpCodes.Brfalse_S, target)); //if the output of ceq is 0 (false) then continue back on with the [target] instruction. In code terms, if the expression is not -1 then don't exit
            il.InsertBefore(target, il.Create(OpCodes.Ret));               //If we are here, the num2 variable is equal to -1, so we can exit the function.
        }
Exemplo n.º 20
0
        public override void Run()
        {
            var vanilla = SourceDefinition.Type("Terraria.Player")
                          .Method("SavePlayer");

            bool tmp      = false;
            var  cbkBegin = this.SourceDefinition.MainModule.Import(
                this.Method(() => OTAPI.Callbacks.Terraria.Player.SavePlayerBegin(null, ref tmp))
                );

            var cbkEnd = this.SourceDefinition.MainModule.Import(
                this.Method(() => OTAPI.Callbacks.Terraria.Player.SavePlayerEnd(null, tmp))
                );

            vanilla.Wrap
            (
                beginCallback: cbkBegin,
                endCallback: cbkEnd,
                beginIsCancellable: true,
                noEndHandling: false,
                allowCallbackInstance: false
            );
        }
Exemplo n.º 21
0
        public override void Run()
        {
            var npcLoot  = SourceDefinition.Type("Terraria.NPC").Method("NPCLoot");
            var dropLoot = SourceDefinition.Type("Terraria.NPC").Method("DropLoot");

            //In the NPCLoot method there is a call to send packet 88 (after item drop).
            //We will also want to hook this in the case the returned value from DropLoot
            //cancels the event.
            //Note, each update will need checking for any other calls to DropLoot that populate variables
            //as currently this is the only one.
            //TODO: write a test for this

            var il = npcLoot.Body.GetILProcessor();

            //This section will add '&& num40 >= 0' to the statement above "Main.item [num40].color = this.color;"
            var insColour         = npcLoot.Body.Instructions.Single(x => x.OpCode == OpCodes.Ldfld && x.Operand == SourceDefinition.Type("Terraria.NPC").Field("color")); //Grab where the call is located
            var insColorStart     = insColour.Previous(i => i.OpCode == OpCodes.Ldsfld);                                                                                   //Find the first instruction for the color call
            var resumeInstruction = insColorStart.Previous.Operand as Instruction;                                                                                         //Find the instruction where it should be transferred to if false is evaludated

            il = npcLoot.Body.GetILProcessor();

            //Insert the num40 variable (the result back from the DropLoot method)
            il.InsertBefore(insColorStart, il.Create(OpCodes.Ldloc, (VariableDefinition)insColorStart.Next.Operand));
            //Place 0 on the stack
            il.InsertBefore(insColorStart, il.Create(OpCodes.Ldc_I4_0));
            //Compare the current values on stack, using >=
            il.InsertBefore(insColorStart, il.Create(OpCodes.Blt, resumeInstruction));

            npcLoot.Body.OptimizeMacros();
        }
Exemplo n.º 22
0
        public override void Run()
        {
            foreach (var newItem in SourceDefinition.Type("Terraria.Item").Methods.Where(
                         x => x.Name == "NewItem"
                         ))
            {
                //In this patch we create a custom DropLoot method that will be the receiver
                //of all Item.NewItem calls in NPCLoot.

                var typeNpc = this.Type <Terraria.NPC>();

                //Create the new DropLoot call in the Terraria.NPC class
                var dropLoot = new MethodDefinition("DropLoot", MethodAttributes.Public | MethodAttributes.Static, newItem.ReturnType);
                typeNpc.Methods.Add(dropLoot);

                dropLoot.Body.InitLocals = true;

                var il = dropLoot.Body.GetILProcessor();

                //Clone the parameters from the Item.NewItem method (with no byreference)
                foreach (var prm in newItem.Parameters)
                {
                    dropLoot.Parameters.Add(prm);
                }

                dropLoot.Parameters.Add(new ParameterDefinition(typeNpc)
                {
                    Name       = "npc",
                    IsOptional = true,
                    HasDefault = true,
                    Constant   = null
                });

                //Collect the hooks
                var cbkBegin = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.Npc").Method(
                    "DropLootBegin",
                    parameters: dropLoot.Parameters,
                    skipMethodParameters: 1,
                    substituteByRefs: true
                    );
                var cbkEnd = ModificationDefinition.Type("OTAPI.Callbacks.Terraria.Npc").Method(
                    "DropLootEnd",
                    parameters: dropLoot.Parameters,
                    skipMethodParameters: 0,
                    substituteByRefs: true
                    );

                //Create the value to hold the new item id
                var vrbItemId = new VariableDefinition("otaItem", (cbkBegin.Parameters[0].ParameterType as ByReferenceType).ElementType);
                dropLoot.Body.Variables.Add(vrbItemId);

                il.Emit(OpCodes.Ldloca_S, vrbItemId);                 //Loads our variable by reference so our callback and alter it.
                var beginResult = dropLoot.EmitBeginCallback(cbkBegin, false, false, false, parameterOffset: 1);

                //Inject the begin call
                var insFirstForMethod = dropLoot.EmitMethodCallback(newItem, false, false);
                //Store the result into our new variable
                il.Emit(OpCodes.Stloc, vrbItemId);

                //Set the vanilla instruction to be resumed upon continuation of the begin hook.
                if (beginResult != null && beginResult.OpCode == OpCodes.Pop)
                {
                    beginResult.OpCode  = OpCodes.Brtrue_S;
                    beginResult.Operand = insFirstForMethod;

                    //Emit the cancellation return IL
                    il.InsertAfter(beginResult, il.Create(OpCodes.Ret));
                    il.InsertAfter(beginResult, il.Create(OpCodes.Ldloc, vrbItemId));
                }

                //Inject the end callback
                dropLoot.EmitEndCallback(cbkEnd, false, false);

                //Emit the return value using the result variable we injected
                il.Emit(OpCodes.Ldloc, vrbItemId);
                il.Emit(OpCodes.Ret);
            }
        }
Exemplo n.º 23
0
        public override void Run()
        {
            //Get the vanilla callback, as well as the imported OTAPI callback method
            var vanilla  = SourceDefinition.Type("Terraria.NetMessage").Method("SendData");
            var callback = vanilla.Module.Import(this.Method(() =>
                                                             OTAPI.Callbacks.Terraria.NetMessage.SendUnknownPacket(0, null, 0, 0, 0, "", 0, 0, 0, 0, 0, 0, 0)
                                                             ));

            //Get the IL processor instance so we can alter IL
            var il = vanilla.Body.GetILProcessor();

            //Get the buffer id and writer variable references
            var vrbBufferId = vanilla.Body.Variables[0];
            var vrbWriter   = vanilla.Body.Variables[1];

            //Ensure the data types are correct
            if (vrbBufferId.VariableType != vanilla.Module.TypeSystem.Int32)
            {
                throw new NotSupportedException("Expected the first variable to be the buffer id");
            }
            if (vrbWriter.VariableType.Name != "BinaryWriter")
            {
                throw new NotSupportedException("Expected the second variable to be the writer");
            }

            //Find the switch statement we wish to add the "default" block to
            var insSwitchStatement = vanilla.Body.Instructions.Single(x => x.OpCode == OpCodes.Switch);
            //Find the instruction that is called when the switch completes
            var insInstructionAfterSwitch = insSwitchStatement.Next.Operand as Instruction;

            //Insert the jump when the last switch block ends, otherwise it will continue onto
            //our custom code we will soon inject.
            il.InsertBefore(insInstructionAfterSwitch,
                            new { OpCodes.Br_S, insInstructionAfterSwitch }
                            );

            //Start building the custom code by supplying the bufferId and writer variables
            var instructions = il.InsertBefore(insInstructionAfterSwitch,
                                               new { OpCodes.Ldloc, Operand = vrbBufferId },
                                               new { OpCodes.Ldloc, Operand = vrbWriter }
                                               );

            //Add all SendData parameters to our callback (as we also need these for the hooks)
            foreach (var parameter in vanilla.Parameters)
            {
                il.InsertBefore(insInstructionAfterSwitch, il.Create(OpCodes.Ldarg, parameter));
            }

            //Finish up injecting the custom IL.
            il.InsertBefore(insInstructionAfterSwitch,
                            //Execute the callback with whats on the stack
                            new { OpCodes.Call, Operand = callback }
                            );

            //Instead of continuing onto the [insInstructionAfterSwitch] we change this to
            //our newly injected IL, which will trigger the 'default' keyword.
            if (insSwitchStatement.Next.Operand != insInstructionAfterSwitch)
            {
                throw new NotSupportedException("Expected default switch jump to be the end");
            }
            insSwitchStatement.Next.Operand = instructions.First();
            insSwitchStatement.Next.OpCode  = OpCodes.Br;

            //Cleanup & optimize
            vanilla.Body.SimplifyMacros();
            vanilla.Body.OptimizeMacros();
        }
Exemplo n.º 24
0
        public override void Run()
        {
            //Get the vanilla method reference
            var vanilla = SourceDefinition.Type("Terraria.WorldGen").Method("hardUpdateWorld");

            //Get the OTAPI callback method reference
            //int tmp = 0;
            var callback = this.SourceDefinition.MainModule.Import(
                this.Method(() => OTAPI.Callbacks.Terraria.WorldGen.HardmodeTileUpdate(0, 0, 0))
                );

            /* In this particular tile update mod (part 1) we are
             * looking specifically for each occurrence of the below
             * pair of methods.
             * Above the if blocks, we insert callbacks
             *
             *      WorldGen.SquareTileFrame(num11, num12, true);
             *      NetMessage.SendTileSquare(-1, num11, num12, 1);
             */

            var targets = vanilla.Body.Instructions.Where(instruction =>
                                                          instruction.OpCode == OpCodes.Stfld &&
                                                          (instruction.Operand as FieldReference).Name == "type" &&
                                                          (instruction.Operand as FieldReference).DeclaringType.FullName == "Terraria.Tile"
                                                          ).ToArray();

            var processor = vanilla.Body.GetILProcessor();

            foreach (var insertionPoint in targets)            //.Take(1))
            {
                //Replace field update, Terraria.Main.tile[x,y].type = <value>
                //with our callback, so it receives the x, y, and type.
                insertionPoint.OpCode  = OpCodes.Call;
                insertionPoint.Operand = callback;

                //We now need to remove the tile instance IL, as we only need the x,y pos
                //This means the ldsfld to the tile array, and the Get accessor
                //[Part 1] Starting with the simple Get accessor, it can simply be removed as there
                //are no instruction references to it as its in the middle of items on the stack
                var getAccessor = insertionPoint.Previous(ins =>
                                                          ins.OpCode == OpCodes.Call &&
                                                          (ins.Operand as MethodReference).Name == "Get"
                                                          );
                processor.Remove(getAccessor);

                //[Part 2] Remove the tile array load instruction.
                //To do this we need to replace all transfers to the indexer arguments
                //otherwise the if blocks will be ruined.
                var tileLoader = insertionPoint.Previous(ins =>
                                                         ins.OpCode == OpCodes.Ldsfld &&
                                                         (ins.Operand as FieldReference).Name == "tile"
                                                         );

                //Replace all instruction references from the instruction we are removing,
                //onto the following instruction
                tileLoader.ReplaceTransfer(tileLoader.Next, vanilla);

                //It's safe to remove the tile load instruction now
                processor.Remove(tileLoader);

                var insContinue = insertionPoint.Next.Next(i =>
                                                           i.OpCode == OpCodes.Call &&
                                                           (i.Operand as MethodReference).Name == "SendTileSquare"
                                                           ).Next;

                processor.InsertAfter(insertionPoint,
                                      new { OpCodes.Brtrue_S, insContinue },
                                      new { OpCodes.Ret }
                                      );
            }
        }