User Tools

Site Tools


tutorial:mixin_your_first_mixin

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
tutorial:mixin_your_first_mixin [2025/10/10 15:10] – ↷ Page name changed from tutorial:mixin_making_your_first_mixin to tutorial:mixin_your_first_mixin gauntreclusetutorial:mixin_your_first_mixin [2025/10/10 16:00] (current) – Misc. changes, elaborate closing thoughts gauntrecluse
Line 6: Line 6:
 ===== Preamble ===== ===== Preamble =====
  
-This is meant to be complementary page to the [[tutorial:mixin_introduction|Introduction to Mixins]] page, which you should read first to get an understanding of what Mixin is. It intends to hold a newcomer's hand through each step of creating an elementary Mixin, teaching what each element does and what it is for along the way. See [[tutorial:mixin_registration|Registering Mixins]] for setting up Mixins on a Fabric project if you intend to follow along with this.+This is meant to be complementary to the [[tutorial:mixin_introduction|Introduction to Mixins]] page, it is not required but highly recommended to read itThis tutorial intends to hold a newcomer's hand through each step of creating an elementary Mixin, teaching what each element does and what it is for along the way. See [[tutorial:mixin_registration|Registering Mixins]] for setting up Mixins on a Fabric project if you intend to follow along with this.
  
 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://mcdev.io/|Minecraft Development plugin]] by demonwav AKA MCDev. Yarn mappings will be used. If you wish to follow along with Mojang mappings, you may use a tool such as [[https://mappings.dev/|mappings.dev]] or [[https://linkie.shedaniel.dev/mappings|Shedaniel.dev's mappings tool]] to translate between the Yarn mappings showcased and the ones in your development environment as needed. 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://mcdev.io/|Minecraft Development plugin]] by demonwav AKA MCDev. Yarn mappings will be used. If you wish to follow along with Mojang mappings, you may use a tool such as [[https://mappings.dev/|mappings.dev]] or [[https://linkie.shedaniel.dev/mappings|Shedaniel.dev's mappings tool]] to translate between the Yarn mappings showcased and the ones in your development environment as needed.
Line 96: Line 96:
     * ''at = @At'' is used to specify what to look for in the targeted method(s)     * ''at = @At'' is used to specify what to look for in the targeted method(s)
       * ''value = "HEAD"'' specifies that Mixin should try to inject at the very start of the method at the point of injection.       * ''value = "HEAD"'' specifies that Mixin should try to inject at the very start of the method at the point of injection.
 +
 +//Sidenote: one does not need to put ''value ='' if it is the only argument passed to ''@At'', so one may also write it here as ''@At("HEAD")''.//
  
 Now, if you're following along with MCDev you'll likely notice that our handler method, ''addLoggerAtHead'', does not have the correct signature for the target. In most cases, using MCDev's context action that quickly fixes it should work. For the sake of this tutorial, an ''@Inject'' injector's handler method must contain the target method's parameters -- here there are none -- and a ''CallbackInfo'' parameter, often called ''ci'' if injecting into a method of ''void'' return type. If we were injecting into a method that returns a value, it would instead be ''CallbackInfoReturnable<T> cir'' where ''T'' is the return type of the target method, such as a ''Boolean''. Now, if you're following along with MCDev you'll likely notice that our handler method, ''addLoggerAtHead'', does not have the correct signature for the target. In most cases, using MCDev's context action that quickly fixes it should work. For the sake of this tutorial, an ''@Inject'' injector's handler method must contain the target method's parameters -- here there are none -- and a ''CallbackInfo'' parameter, often called ''ci'' if injecting into a method of ''void'' return type. If we were injecting into a method that returns a value, it would instead be ''CallbackInfoReturnable<T> cir'' where ''T'' is the return type of the target method, such as a ''Boolean''.
Line 137: Line 139:
 </yarncode> </yarncode>
  
-The constructor is irrelevant to actual Mixin work, it's only there for the sake of extensionA perceptive reader might notice that we didn't implement any methods. That is another benefit of making our class abstract. Abstract classes, because they are never instantiated, do not need to implement all methods from their parents. Do note if you implement an interface not implemented by the target class, that will be merged into the target class at runtime aswell.+The constructor is irrelevant here, it's only there for the sake of extending without errorsOne may notice that we didn't implement any methods. That is another benefit of making our class abstract. Abstract classes, because they are never instantiated, do not need to implement methods from parents, allowing us to not have to handle all the overrides you would normally get. Do note if you implement an interface not implemented by the target class, Mixin will try to make the target class implement that interface as a part of merging.
  
 ---- ----
Line 161: Line 163:
 } }
 </yarncode> </yarncode>
- +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 version of that now transformed class. It should be exported to the directory ''run/mixin.out''. You can also configure your development runs to export //all// Mixins by adding the following argument to your VM options: ''-Dmixin.debug.export=true''
-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 version of that class, which will now contain the different transformations applied by Mixin to the class. It should be exported to the directory ''run/mixin.out''. You can also configure your development runs to export //all// Mixins by adding the following argument to your VM options: ''-Dmixin.debug.export=true''+
  
 === Why do my merged Mixins' names look so weird? === === Why do my merged Mixins' names look so weird? ===
  
-One thing you will notice in an exported class is that your Mixin handler methods' names look very odd when merged into the target class. This is the result of a process known as "name mangling" or just "mangling". This process consists of procedurally modifying the merged member names such that they are unique. This is first and foremost for compatibility reasons, as two Mixins adding methods of the same name may cause a conflict even if the injectors target entirely different things. Injector methods are also given an identifier corresponding to the type of injector as part of mangling. For our case, ''@Inject'' has the prefix ''handler''.+One thing you will notice in an exported class is that your Mixin handler methods' names look odd when merged into the target class. This is the result of a process known as "name mangling" or just "mangling". This process consists of procedurally modifying the merged member names such that they are unique. This is first and foremost for compatibility reasons, as two Mixins adding methods of the same name may cause a conflict even if the injectors target entirely different things. Injector methods are also given an identifier corresponding to the type of injector as part of mangling. For our case, ''@Inject'' has the prefix ''handler''.
  
-:!: //The ''@Debug'' annotation settings stay as they are in dev when the mod is built into a ''jar'', make sure to not accidentally export Mixin classes in the ''jar'' you publish!//+:!: //The ''@Debug'' annotation settings do not automatically turn off when the mod is built into a ''jar'', make sure to not accidentally export Mixin classes in the ''jar'' you publish!//
  
 === 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 with them by directly putting them in your Mixins rather than having to try and anticipate your injector.+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'll be called a **lot**, for performance reasons. This applies just the same with Mixins, and one should in general not use loggers as their primary form of debugging. +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'll be called very frequently, for performance and log readability reasons. This applies just the same with Mixins, and one should in general not use loggers as their primary form of debugging.
  
 ---- ----
Line 184: Line 184:
  
 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?// 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 ==== ==== Further Reading ====
  
-For more thorough and formal intros to the topic, you should read through the [[tutorial:mixin_introduction|Mixin Introduction]] page and its linked Wikis. Prioritize the latter if possible at all times as the Fabric Wiki will inherently be less reliable than the official Wikis about Mixin tools.\\ +For more thorough and formal intros to the topic, you should read through the [[tutorial:mixin_introduction|Mixin Introduction]] page and especially its linked Wikis.\\ 
-Namely, those are the [[https://github.com/SpongePowered/Mixin/wiki/|Mixin Wiki]] and the [[https://github.com/LlamaLad7/MixinExtras/wiki|MixinExtras Wiki]]. The latter should always be one's first resort for anything relating to MixinExtras. You may also find further information about specific tools in the JavaDocs in the tools' source code.+Namely, those are the [[https://github.com/SpongePowered/Mixin/wiki/|Mixin Wiki]] and the [[https://github.com/LlamaLad7/MixinExtras/wiki|MixinExtras Wiki]]. You may also find further information about specific tools in the relevant JavaDocs.
  
-Other than that, the most important step from here is trial, error, and asking for help/information when stuck. This is an entirely expected part of the learning process, and even more experienced devs frequently ask questions about how to best approach an issue with Mixins.+Other than that, the most important step from here is trial, error, and asking for help/information in the relevant support channels when stuck. This is an entirely expected part of the learning process, and even experienced devs ask questions about how to best approach an issue with Mixin.
tutorial/mixin_your_first_mixin.1760109019.txt.gz · Last modified: 2025/10/10 15:10 by gauntrecluse