This is an old revision of the document!
Table of Contents
Adding to Enums
Introduction
To properly add to enums we have to edit some internal fields. There is no guarantee that this will work on all java versions. Due to this it's important to make sure adding to a enum is the correct thing to do. eg. if you need to make armor you shouldn't add to ArmorMaterials
but instead make your own class (armor). You also need to careful if your target enum is used in a switch and stop it from creating problems (you should anyways make sure that all uses are covered). If this stops working for some reason don't be surprised. It might be a small thing or the whole concept could be changed. I will be using adding a axolotl variant as an example here, but the concept should work on any enum you can mixin into.
Creating a Container Class
The purpose of this is to contain all of your custom enum entries. The entries will be contained in static non final fields.
public class CustomAxolotlVariant { static { AxolotlEntity.Variant.values(); // Ensure class is loaded before the variant is accessed } public static AxolotlEntity.Variant PURPLE; // You can add as many of these fields as you like }
Mixing Into the Target
If you're adding to enum I'm going to expect you know the basics of mixin.
Accessing the Constructor
We need an invoker for the constructor. The first two arguments should always be the internal name and id. After that we have the arguments of the visible constructor.
@SuppressWarnings("InvokerTarget") @Invoker("<init>") private static AxolotlEntity.Variant newVariant(String internalName, int internalId, int id, String name, boolean natural) { throw new AssertionError(); }
Accessing the Values Field
If we are dealing with a minecraft class then the field name will be in intermediary mappings. To find the name of the filed we have to look at bytecode. This can be done in IntelliJ by placing the cursor in the class and going to View → Show Bytecode. Scroll down until you see a line a bit like this:
private final static synthetic [Lnet/minecraft/entity/passive/AxolotlEntity$Variant; field_28350
That means that in our case the field name is field_28350
. Next we shadow it as such:
@SuppressWarnings("ShadowTarget") @Shadow @Final @Mutable private static AxolotlEntity.Variant[] field_28350;
Injecting the Entries
We are going inject after the values field is assigned. To do this we need the correct target for the for the INVOKE
injection point. We can't use MCDev since we are dealing with internals so we need to type it ourselves as such:
<classname><fieldname>:[<classname>
In our case it would be Lnet/minecraft/entity/passive/AxolotlEntity$Variant;field_28350:[Lnet/minecraft/entity/passive/AxolotlEntity$Variant;
Now we can create the inject:
@SuppressWarnings("UnresolvedMixinReference") @Inject(method = "<clinit>", at = @At(value = "FIELD", opcode = Opcodes.PUTSTATIC, target = "Lnet/minecraft/entity/passive/AxolotlEntity$Variant;field_28350:[Lnet/minecraft/entity/passive/AxolotlEntity$Variant;", shift = At.Shift.AFTER)) private static void addCustomVariant(CallbackInfo ci) { }
Now finally we can add our entries:
var variants = new ArrayList<>(Arrays.asList(field_28350)); var last = variants.get(variants.size() - 1); // This means our code will still work if other mods or Mojang add more variants! // Repeat this section if you need more than one entry. Just remember to have unique ordinals! var purple = newVariant("PURPLE", last.ordinal() + 1, last.getId() + 1, "purple", true); CustomAxolotlVariant.PURPLE = purple; variants.add(purple); field_28350 = variants.toArray(new AxolotlEntity.Variant[0]);