Skip to content

Extending enums (including abstract enum classes) since 2022.

License

Notifications You must be signed in to change notification settings

Fuzss/extensibleenums

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Extensible Enums

A Minecraft modding library for Fabric, no direct downloads are available. Solely intended to be included in mod jars via jar-in-jar.

ABOUT THE PROJECT

This is a teeny tiny library for extending enums using the Unsafe class.

The main use case for this is extending the EnchantmentType/EnchantmentCategory enum which additionally to being an enum is also an anonymous class, making extending even more difficult. Now it's just a single line and you already have your very own EnchantmentType/EnchantmentCategory:

public static final EnchantmentTarget WATERING_CAN = ExtensibleEnchantmentTarget.create("WATERING_CAN", item -> item instanceof WateringCan);

WARNING

First of all be aware this library is just one big hack intended as a last resort when you've run out of options (such as the EnchantmentType/EnchantmentCategory case illustrated above). Due to all the restrictions in newer Java versions (the module system and restrictions on reflection) the only way something like this seems to be possible is using the Unsafe class and hacking our way into an enum by directly manipulating memory addresses. So think thoroughly if extending an enum is really your only option!

Also make sure to never extend an enum that might be used in a switch expression anywhere (even if that's not the case in vanilla, another mod might use it there), since extending that enum will break most switch expressions (switch statements are affected too, but do not break as easily).

DEVELOPER INFORMATION

Adding to your workspace

In your build.gradle file make sure you include the following lines to add the library. Extensible Enums is not hosted anywhere as a production jar, it is not meant to be an external dependency. When compiling your mod, Extensible Enums will simply be included as a nested jar (due to include below).

repositories {
    maven {
        name = "Fuzs Mod Resources"
        url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven/"
    }
}

dependencies {
    modImplementation include("fuzs.extensibleenums:extensibleenums-fabric:<modVersion>")   // e.g. 4.0.0 for Minecraft 1.19
}

Adding to your mod

Then finally in your fabric.mod.json add Extensible Enums as a hard-dependency by including it in the depends block. This is important, as otherwise your mod could be loaded before Extensible Enums is.

  "depends": {
    "extensibleenums": "*"
  }

Working with the library

Relevant classes are found in the fuzs.extensibleenums.core package, nothing outside should be touched!

Adding an enum value

Simply create your new enum value by calling UnsafeExtensibleEnum::invokeEnumConstructor. Your new enum value will automatically be added to the internal enum array and will also be given the next available ordinal. Make sure the name you choose does not exist in the enum yet, otherwise an exception will be thrown. A few overloads exist for this method which you should probably use, since some parameters are not required most of the time:

  • Class<? extends T> enumConcreteClass is only required when enumMainClass is an abstract class, you then need to supply the class of any anonymous implementation taken from any enum value (by calling Enum::getClass).
  • int internalId is used for manually setting your new enum value's ordinal and will prevent it from being added to the internal enum array. Overloads set this value to -1 allowing an ordinal to be assigned automatically and the new value to be added to the enum value array.
    /**
     * create a new enum constant, set <code>name</code> and <code>ordinal</code> fields accordingly (since we invoke the constant using unsafe, no fields will be initialized)
     * and also add it to the enum values array, clearing enum cache (used for switch statements) afterwards
     * @param enumMainClass the enum class to add a constant to
     * @param enumConcreteClass the enum class to create the constant from, should usually be the same as <code>enumMainClass</code>, but in case of an abstract enum supply the class of any of its constants
     * @param internalName name of the new enum value
     * @param internalId ordinal id, controls if the new enum constant is properly added to enum values, set this as -1 to do so, specify a different value to skip it
     * @param <T> enum type
     * @return the new enum constant
     *
     * @throws Throwable something went wrong during unsafe operations oh no
     */
    public static <T extends Enum<T>> T invokeEnumConstructor(Class<T> enumMainClass, Class<? extends T> enumConcreteClass, String internalName, int internalId) throws Throwable

Adding an EnchantmentCategory/EnchantmentType value

Simply create your new enum value by calling ExtensibleEnchantmentCategory::create and supplying a name and a Predicate<Item>. The new value will not be created directly from EnchantmentCategory/EnchantmentType since the class is abstract, but instead one of the anonymous classes created by already defined enum values is used. The predicate is then added via a mixin which overrides the functionality of EnchantmentCategory::canEnchant/EnchantmentType::isAcceptableItem if a predicate is present.

    /**
     * create a new {@link net.minecraft.world.item.enchantment.EnchantmentCategory} enum constant
     * @param internalName name of enum constant
     * @param canApplyTo which item this type can be applied to
     * @return new enum constant
     */
    static EnchantmentCategory create(String internalName, Predicate<Item> canApplyTo)

Adding a value to another anonymous enum class

As with EnchantmentCategory/EnchantmentType you'll also have to use a custom mixin to add relevant functionality to an anonymous class defined by one of the enum values. Check the implementation descrived above, it should be relatively straight forward to adopt.

If you have any suggestion for more enum values that should be included in the library itself feel free to contact me with your suggestion or make a pull request.

About

Extending enums (including abstract enum classes) since 2022.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages