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 15:10] – removed - external edit (Unknown date) 127.0.0.1 | 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: | ||
| + | ====== Tutorial: Making your first Mixin ====== | ||
| + | ===== Preamble ===== | ||
| + | |||
| + | 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:// | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Creating the Mixin Class ===== | ||
| + | For this tutorial, we'll add a Logger call at the head of the '' | ||
| + | |||
| + | For this task, we'll use the injector '' | ||
| + | |||
| + | It is important to note that we use '' | ||
| + | The reason '' | ||
| + | |||
| + | After following the necessary steps described in [[tutorial: | ||
| + | < | ||
| + | @Mixin(MinecraftServer.class) | ||
| + | abstract class TutorialFirstMixin { | ||
| + | |||
| + | } | ||
| + | </ | ||
| + | |||
| + | It is conventional practice to name your Mixin classes the target class with '' | ||
| + | |||
| + | You'll notice we made the Mixin class '' | ||
| + | |||
| + | Accordingly, | ||
| + | <code json> | ||
| + | { | ||
| + | ... | ||
| + | " | ||
| + | " | ||
| + | ], | ||
| + | ... | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | And that's all there is to actually creating the Mixin Class, but it won't do anything on its own. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Creating the injector ===== | ||
| + | |||
| + | We have a Mixin class, now let's use it to do something. | ||
| + | |||
| + | The first step, assuming nothing has caused a crash or gone to hell yet, is to create a " | ||
| + | < | ||
| + | @Mixin(MinecraftServer.class) | ||
| + | abstract class TutorialFirstMixin { | ||
| + | |||
| + | private void logOnWorldLoad() { | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Your Mixin injectors' | ||
| + | |||
| + | Next, we add the annotation: | ||
| + | < | ||
| + | abstract class TutorialFirstMixin { | ||
| + | |||
| + | @Inject(method = " | ||
| + | private void logOnWorldLoad() { | ||
| + | | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | 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 '' | ||
| + | < | ||
| + | abstract class TutorialFirstMixin { | ||
| + | |||
| + | @Inject(method = " | ||
| + | private void logOnWorldLoad() { | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | 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: | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | //Sidenote: one does not need to put '' | ||
| + | |||
| + | Now, if you're following along with MCDev you'll likely notice that our handler method, '' | ||
| + | |||
| + | If we fix the signature and add a call to our mod's logger, the full class becomes: | ||
| + | < | ||
| + | @Mixin(MinecraftServer.class) | ||
| + | abstract class TutorialFirstMixin { | ||
| + | |||
| + | @Inject(method = " | ||
| + | private void logOnWorldLoad(CallbackInfo ci) { | ||
| + | ExampleMod.LOGGER.info(" | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | And that's it for making this Mixin! | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== General Advice ===== | ||
| + | |||
| + | ==== 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: | ||
| + | < | ||
| + | abstract class TutorialFirstMixin extends class_4093< | ||
| + | public TutorialFirstMixin(String string) { | ||
| + | super(string) | ||
| + | } | ||
| + | |||
| + | @Inject(method = " | ||
| + | private void logOnWorldLoad(CallbackInfo ci) { | ||
| + | ExampleMod.LOGGER.info(" | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | The constructor does not impact functionality, | ||
| + | |||
| + | ==== Debugging Mixins ==== | ||
| + | |||
| + | === Exporting === | ||
| + | Mixin provides debugging utilities, one of them is the ability to export the transformed classes once they' | ||
| + | This section' | ||
| + | |||
| + | Exporting our Mixin would look like: | ||
| + | < | ||
| + | @Debug(export = true) | ||
| + | @Mixin(MinecraftServer.class) | ||
| + | abstract class TutorialFirstMixin extends class_4093< | ||
| + | |||
| + | @Inject(method = " | ||
| + | private void logOnWorldLoad(CallbackInfo ci) { | ||
| + | ExampleMod.LOGGER.info(" | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | 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' | ||
| + | |||
| + | One thing you will notice in an exported class is that your Mixin handler methods' | ||
| + | |||
| + | === 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. | ||
| + | |||
| + | === 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' | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Closing Thoughts and where to go next ===== | ||
| + | |||
| + | Assuming nothing blew up, you've completed your first functioning Mixin, and hopefully you learned a bit about how Mixin is used and broadly works along the way. //What now?// | ||
| + | |||
| + | Some principles to keep in mind are that Mixin is a very case-by-case tool, so this injector example was tailored to its specific use-case, but Mixin and MixinExtras provide a multitude of tools to use for different scenarios. Knowing which one to use comes down to knowing what effect you want to have in the code, and what is the most compatible and smallest change you can make to the target for your goal. You must also remember that Mixin is not always the solution, there are times where events or other modding framework tools outside of Mixins are already available, and you should prioritize using those rather than Mixins if you can afford to, as it is typically safer for compatibility. | ||
| + | |||
| + | ==== Further Reading ==== | ||
| + | |||
| + | For more thorough and formal intros to the topic, you should read through the [[tutorial: | ||
| + | Namely, those are the [[https:// | ||
| + | |||
| + | Other than that, the most important step from here is trial, error, and asking for help/ | ||