aboutsummaryrefslogtreecommitdiff
path: root/docs/mixins/simple-injects.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/mixins/simple-injects.md')
-rw-r--r--docs/mixins/simple-injects.md46
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