tutorial:mixin_your_first_mixin
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| tutorial:mixin_your_first_mixin [2025/10/10 16:00] – Misc. changes, elaborate closing thoughts gauntrecluse | tutorial:mixin_your_first_mixin [2025/11/16 13:35] (current) – Minor fixes to try and simplify wordings and fix typos gauntrecluse | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | :!: //This page is currently a draft! It is not intended to be read by users of the wiki yet. Feedback will be appreciated but trust this page's information at your own risk!//\\ | + | ====== Tutorial: Making your first Mixin ====== |
| - | //Contact GauntRecluse (paleintrovert) on Discord to give feedback and suggestions on this draft// | + | |
| - | + | ||
| - | ====== Tutorial: Making your first Mixin (DRAFT) | + | |
| ===== Preamble ===== | ===== Preamble ===== | ||
| - | This is meant to be complementary to the [[tutorial: | + | This is meant to be complementary to the [[tutorial: |
| + | See [[tutorial: | ||
| - | This tutorial will be made using a 1.21.1 Fabric Project as reference. It is recommended to develop using IntelliJ IDEa Community Edition to be able to leverage the [[https:// | + | This tutorial will be made using a 1.21.1 Fabric Project as reference. It is recommended to develop using IntelliJ IDEa Community Edition to be able to leverage the [[https:// |
| - | The repository containing this tutorial' | + | ---- |
| ===== Creating the Mixin Class ===== | ===== Creating the Mixin Class ===== | ||
| For this tutorial, we'll add a Logger call at the head of the '' | For this tutorial, we'll add a Logger call at the head of the '' | ||
| - | For this task, we'll use the Mixin injector | + | For this task, we'll use the injector '' |
| - | It is important to note that we use '' | + | It is important to note that we use '' |
| - | The reason '' | + | The reason '' |
| After following the necessary steps described in [[tutorial: | After following the necessary steps described in [[tutorial: | ||
| < | < | ||
| @Mixin(MinecraftServer.class) | @Mixin(MinecraftServer.class) | ||
| - | public | + | abstract class TutorialFirstMixin { |
| } | } | ||
| </ | </ | ||
| - | It is conventional practice to name your Mixin classes the target class with '' | + | It is conventional practice to name your Mixin classes the target class with '' |
| - | You'll notice we made the Mixin class '' | + | You'll notice we made the Mixin class '' |
| - | Accordingly, | + | Accordingly, |
| - | Adding to our config file: | + | |
| <code json> | <code json> | ||
| { | { | ||
| Line 55: | Line 52: | ||
| < | < | ||
| @Mixin(MinecraftServer.class) | @Mixin(MinecraftServer.class) | ||
| - | public | + | abstract class TutorialFirstMixin { |
| - | private void addLoggerAtHead() { | + | private void logOnWorldLoad() { |
| } | } | ||
| } | } | ||
| </ | </ | ||
| - | We've named it '' | + | Your Mixin injectors' |
| - | We now add our '' | + | Next, we add the annotation: |
| < | < | ||
| - | public | + | abstract class TutorialFirstMixin { |
| @Inject(method = " | @Inject(method = " | ||
| - | private void addLoggerAtHead() { | + | private void logOnWorldLoad() { |
| | | ||
| } | } | ||
| Line 75: | Line 72: | ||
| </ | </ | ||
| - | This now specifies to Mixin that, when that method gets merged into '' | + | This now specifies to Mixin that, when this method gets merged into '' |
| Now, to specify where in the targeted method we want our injection to happen, we add something that may be a bit counterintuitive to some, an '' | Now, to specify where in the targeted method we want our injection to happen, we add something that may be a bit counterintuitive to some, an '' | ||
| < | < | ||
| - | public | + | abstract class TutorialFirstMixin { |
| - | @Inject(method = " | + | @Inject(method = " |
| - | | + | private void logOnWorldLoad() { |
| - | | + | |
| - | private void addLoggerAtHead() { | + | |
| } | } | ||
| } | } | ||
| </ | </ | ||
| - | |||
| - | It is not necessary to put the different fields on different lines in your code, but it may become useful when the individual arguments are very long or if you prefer it for organization purposes. | ||
| Now, let's look closer at the annotations we have thus far. Think of them as holding a series of hierarchized instructions. A very simplified explanation would be: | Now, let's look closer at the annotations we have thus far. Think of them as holding a series of hierarchized instructions. A very simplified explanation would be: | ||
| Line 95: | Line 88: | ||
| * '' | * '' | ||
| * '' | * '' | ||
| - | * '' | + | * '' |
| - | //Sidenote: one does not need to put '' | + | //Sidenote: one does not need to put '' |
| - | Now, if you're following along with MCDev you'll likely notice that our handler method, '' | + | Now, if you're following along with MCDev you'll likely notice that our handler method, '' |
| - | If we fix the signature and add a logger | + | If we fix the signature and add a call to our mod's logger, the full class becomes: |
| < | < | ||
| @Mixin(MinecraftServer.class) | @Mixin(MinecraftServer.class) | ||
| - | public | + | abstract class TutorialFirstMixin { |
| - | @Inject(method = " | + | @Inject(method = " |
| - | | + | private void logOnWorldLoad(CallbackInfo ci) { |
| - | | + | |
| - | private void addLoggerAtHead(CallbackInfo ci) { | + | |
| - | | + | |
| } | } | ||
| } | } | ||
| </ | </ | ||
| - | And that's it for making this Mixin work! But let's go over some general advice that will come in handy as you learn Mixins: | + | And that's it for making this Mixin! |
| ---- | ---- | ||
| Line 122: | Line 113: | ||
| ==== Extending the target class' | ==== Extending the target class' | ||
| - | |||
| When the class you're targeting extends and/or implements parents, mimicking that on your Mixin class is an overall benefit, as it allows you to get all of the parent methods directly. For our example Mixin it'll look like: | When the class you're targeting extends and/or implements parents, mimicking that on your Mixin class is an overall benefit, as it allows you to get all of the parent methods directly. For our example Mixin it'll look like: | ||
| < | < | ||
| - | public | + | abstract class TutorialFirstMixin extends class_4093< |
| public TutorialFirstMixin(String string) { | public TutorialFirstMixin(String string) { | ||
| super(string) | super(string) | ||
| } | } | ||
| - | @Inject(method = " | + | @Inject(method = " |
| - | | + | private void logOnWorldLoad(CallbackInfo ci) { |
| - | | + | |
| - | private void addLoggerAtHead(CallbackInfo ci) { | + | |
| - | | + | |
| } | } | ||
| } | } | ||
| </ | </ | ||
| - | The constructor | + | The constructor |
| - | + | ||
| - | ---- | + | |
| ==== Debugging Mixins ==== | ==== Debugging Mixins ==== | ||
| - | === The export feature | + | === Exporting |
| Mixin provides debugging utilities, one of them is the ability to export the transformed classes once they' | Mixin provides debugging utilities, one of them is the ability to export the transformed classes once they' | ||
| This section' | This section' | ||
| Line 153: | Line 139: | ||
| @Debug(export = true) | @Debug(export = true) | ||
| @Mixin(MinecraftServer.class) | @Mixin(MinecraftServer.class) | ||
| - | public | + | abstract class TutorialFirstMixin extends class_4093< |
| - | @Inject(method = " | + | @Inject(method = " |
| - | | + | private void logOnWorldLoad(CallbackInfo ci) { |
| - | | + | |
| - | private void addLoggerAtHead(CallbackInfo ci) { | + | |
| - | | + | |
| } | } | ||
| } | } | ||
| </ | </ | ||
| - | Setting a Mixin to be exported makes it so that once the targeted class has been transformed and has loaded (which could be anytime between the client starting and a world loading) Mixin will export a decompiled | + | Setting a Mixin to be exported makes it so that once the targeted class has been transformed and has loaded (which could be anytime between the client starting and a world loading) Mixin will export a version of that now transformed class. It should be exported to the directory '' |
| + | |||
| + | :!: If you use the '' | ||
| === Why do my merged Mixins' | === Why do my merged Mixins' | ||
| One thing you will notice in an exported class is that your Mixin handler methods' | One thing you will notice in an exported class is that your Mixin handler methods' | ||
| - | |||
| - | :!: //The '' | ||
| === Breakpoints and MCDev === | === Breakpoints and MCDev === | ||
| - | |||
| Breakpoints don't natively work with Mixin classes as they get merged at runtime. However, MCDev adds breakpoint support in Mixin Classes directly, allowing you to debug them by directly putting a breakpoint there, rather than having to try and anticipate your injector in the target class. | Breakpoints don't natively work with Mixin classes as they get merged at runtime. However, MCDev adds breakpoint support in Mixin Classes directly, allowing you to debug them by directly putting a breakpoint there, rather than having to try and anticipate your injector in the target class. | ||
| === Loggers === | === Loggers === | ||
| - | |||
| Logger calls can be very handy when trying to debug complex logical flows or just for general warning and error notifications. And whilst that may be useful for debugging, one should be particularly careful when using them anywhere that' | Logger calls can be very handy when trying to debug complex logical flows or just for general warning and error notifications. And whilst that may be useful for debugging, one should be particularly careful when using them anywhere that' | ||
tutorial/mixin_your_first_mixin.1760112003.txt.gz · Last modified: 2025/10/10 16:00 by gauntrecluse