diff options
Diffstat (limited to 'docs/mixins/simple-injects.md')
-rw-r--r-- | docs/mixins/simple-injects.md | 46 |
1 files changed, 35 insertions, 11 deletions
diff --git a/docs/mixins/simple-injects.md b/docs/mixins/simple-injects.md index eeea65e..b635973 100644 --- a/docs/mixins/simple-injects.md +++ b/docs/mixins/simple-injects.md @@ -9,26 +9,41 @@ Now we will modify an existing method in Minecrafts code. This will allow us to ## The easiest of the easiest -Let's start with probably the easiest `@Inject` out there. The HEAD inject. This mixin will inject whatever code you have inside your method at the start of the method you target. +Let's start with probably the easiest `@Inject` out there. The `HEAD` inject. This mixin will inject whatever code you have inside your method at the start of the method you target. ```java -@Mixin(PlayerControllerMP.class) +@Mixin(PlayerControllerMP.class) // (1)! public class RightClickWithItemEvent { - @Inject(method = "sendUseItem", at = @At("HEAD")) - private void onSendUseItem_mymod(EntityPlayer playerIn, World worldIn, ItemStack itemStackIn, CallbackInfoReturnable<Boolean> cir) { + @Inject( // (2)! + method = "sendUseItem", // (3)! + at = @At("HEAD")) // (4)! + private void onSendUseItem_mymod( // (5)! + EntityPlayer playerIn, World worldIn, ItemStack itemStackIn, // (6)! + CallbackInfoReturnable<Boolean> cir // (7)! + ) { MinecraftForge.EVENT_BUS.post(new SendUseItemEvent(playerIn, worldIn, itemStackIn)); } } ``` +1. First we declare which class we want to change +2. `:::java @Inject` allows us to add code into an already existing method +3. This sets the method into which we want to inject something. Be careful of overloaded methods here. Check out the [advanced tutorial](./advanced-injects.md) for more info. +4. The `:::java @At` specifies where our code will be injected. `HEAD` just means the top of the method. +5. The injected code method should be `:::java private` and `:::java void` no matter what your target method is. You might also need to make your method `:::java static` +6. You need to copy over all the parameters from your original method into which you are injecting. +7. You need one extra parameter for the callback info. + + + First we want to inject into the `PlayerControllerMP` class. We create an `@Inject`. This tells us in which method we want to inject (`sendUseItem`) and where in that method (`HEAD`, meaning the very top of the method). The actual method signature for an inject is always to return a `void`. You can make them `private` or `public`. The arguments are the same arguments as the method you want to inject into, as well as a `CallbackInfo`. -For a method returning void, you just use a `CallbackInfo`, and if the method returns something, you use `CallbackInfoReturnable<ReturnTypeOfTheInjectedIntoMethod>`. +For a method returning void, you just use a `:::java CallbackInfo`, and if the method returns something, you use `:::java CallbackInfoReturnable<ReturnTypeOfTheInjectedIntoMethod>`. Your method will now be called every time the `sendUseItem` is called with the arguments to that method and the `CallbackInfo`. @@ -50,7 +65,8 @@ Let's take this example method: ```java public void methodA() { - // ... + // Let's pretend lots of code calls methodA, so we don't want to inject + // ourselves into methodA } public void methodB() { @@ -64,18 +80,26 @@ public void methodB() { We can inject ourselves into `methodB` as well. It is *just* a bit more complicated than the `HEAD` inject. ```java -@Inject(method = "methodB", at = @At(target = "Lnet/some/Class;methodA()V", value = "INVOKE")) +@Inject( + method = "methodB", // (3)! + at = @At( + target = "Lnet/some/Class;methodA()V", // (1)! + value = "INVOKE")) // (2)! private void onMethodBJustCalledMethodA(CallbackInfo ci) { } ``` +1. This is the method call for which we are searching. This is not the method into which our code will be injected. +2. This tells mixin that we want `target` to point to a method call (not a field or anything else). +3. This is the method into which we want our code to be injected. + > **HUUUUH, where does that come from???** -Don't worry! I won't explain you how to understand these `target`s in this tutorial, but you also don't need to understand that `target`. Instead you can simply use the Minecraft Development IntelliJ Plugin to help you. Simply type `@At(value = "INVOKE", target = "")`, place your cursor inside of the target and use auto completion (<kbd>Ctrl + Space</kbd>) and the plugin will recommend you a bunch of method calls. Find whichever seems right to you and press enter. You can now (also thanks to the plugin) <kbd>Ctrl</kbd> click on the `target` string, which will take you to the decompiled code exactly to where that target will inject. +Don't worry! I won't explain you how to understand these `target`s in this tutorial, but you also don't need to understand that `target`. Instead you can simply use the Minecraft Development IntelliJ Plugin to help you. Simply type `:::java @At(value = "INVOKE", target = "")`, place your cursor inside of the target and use auto completion (++ctrl+space++) and the plugin will recommend you a bunch of method calls. Find whichever seems right to you and press enter. You can now (also thanks to the plugin) ++ctrl++ click on the `target` string, which will take you to the decompiled code exactly to where that target will inject. ## Ordinals -Let's take the method injection example from before and change it a bit: +Let's take the `INVOKE` injection example from before and change it a bit: ```java public void methodA() { @@ -93,7 +117,7 @@ public void methodB() { } ``` -We can't simply use the same `@Inject` from before, since by default a `INVOKE` inject will inject just after *every* method call. Here, we can use the `ordinal` classifier to specify which method call we want to use. Keep in mind this is about where to place our injection, so many method calls in a loop will not increment the ordinal, only unique code locations that call the function will increase the ordinal. Keep in mind: we are programmers, we start counting with `0`. +We can't simply use the same `:::java @Inject` from before, since by default a `INVOKE` inject will inject just after *every* method call. Here, we can use the `ordinal` classifier to specify which method call we want to use. Keep in mind this is about where to place our injection, so many method calls in a loop will not increment the ordinal, only unique code locations that call the function will increase the ordinal. Remember: we are programmers, we start counting with `0`. ```java @Inject(method = "methodB", at = @At(target = "Lnet/some/Class;methodA()V", value = "INVOKE", ordinal = 1)) @@ -121,7 +145,7 @@ private void onIsHittingPosition_mymod(BlockPos pos, CallbackInfoReturnable<Bool } ``` -For `void` methods you need to use `callbackInfo.cancel()`. For all other methods you need to use `callbackInfoReturnable.setReturnValue(returnValue)`. +For `void` methods you need to use `:::java callbackInfo.cancel()` which acts the same as a normal `:::java return;` would in the method you are injecting into. For all other methods you need to use `:::java callbackInfoReturnable.setReturnValue(returnValue)` which corresponds to `:::java return returnValue;`. !!! important |