From aef43236d109a1263a433d419e851ed8f54ed2d2 Mon Sep 17 00:00:00 2001 From: Waiting Idly <25394029+WaitingIdly@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:34:51 -0800 Subject: [PATCH] add JEI Category Sorting functionality (#257) * add ability to sort categories * add tip for using the command to get category uids * rename mixin class * use indexOf once instead of contains --- examples/postInit/jei.groovy | 4 ++- .../compat/mods/jei/Category.java | 32 +++++++++++++++---- .../compat/mods/jei/JeiPlugin.java | 14 ++++++++ .../core/mixin/jei/ModRegistryMixin.java | 32 +++++++++++++++++++ .../assets/groovyscript/lang/en_us.lang | 4 ++- .../resources/mixin.groovyscript.jei.json | 3 +- 6 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/cleanroommc/groovyscript/core/mixin/jei/ModRegistryMixin.java diff --git a/examples/postInit/jei.groovy b/examples/postInit/jei.groovy index 61e8cfd30..bf4fa52a5 100644 --- a/examples/postInit/jei.groovy +++ b/examples/postInit/jei.groovy @@ -16,7 +16,7 @@ mods.jei.catalyst.add('minecraft.smelting', item('minecraft:clay') * 8, item('mi // Categories: // Modify the Categories visible in JEI, each of which contain recipes and are associated with specific blocks, typically -// machines. +// machines. Can also set the order of Categories. mods.jei.category.hideCategory('minecraft.fuel') // mods.jei.category.hideAll() @@ -29,6 +29,8 @@ mods.jei.category.hideCategory('minecraft.fuel') .register()*/ +mods.jei.category.setOrder('minecraft.crafting', 'jei.information', 'minecraft.smelting', 'groovyscript:burning', 'groovyscript:explosion', 'groovyscript:fluid_recipe', 'groovyscript:piston_push', 'minecraft.anvil') + // Description Category: // Modify the description of the input items, where the description is a unique JEI tab containing text. diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/jei/Category.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/jei/Category.java index ffcb813f8..c9413dc31 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/jei/Category.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/jei/Category.java @@ -15,20 +15,19 @@ import mezz.jei.api.recipe.IRecipeWrapper; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.function.Function; -@RegistryDescription( - category = RegistryDescription.Category.ENTRIES, - admonition = @Admonition("groovyscript.wiki.jei.category.note0")) +@RegistryDescription(category = RegistryDescription.Category.ENTRIES, admonition = { + @Admonition("groovyscript.wiki.jei.category.note0"), + @Admonition(value = "groovyscript.wiki.jei.category.note1", type = Admonition.Type.TIP) +}) public class Category extends VirtualizedRegistry { private boolean hideAllCategories; private final AbstractReloadableStorage categoryStorage = new AbstractReloadableStorage<>(); + private final List categoryUidOrder = new ArrayList<>(); /** * Called by {@link JeiPlugin#afterRuntimeAvailable()} @@ -39,6 +38,14 @@ public void applyChanges(IRecipeRegistry recipeRegistry) { getBackupRecipes().forEach(recipeRegistry::hideRecipeCategory); } + /** + * Called by {@link JeiPlugin#getCategoryComparator()} + */ + @GroovyBlacklist + public List getOrder() { + return categoryUidOrder; + } + @MethodDescription public void add(CustomCategory customCategory) { categoryStorage.addScripted(customCategory); @@ -92,6 +99,17 @@ public void onReload() { restoreFromBackup(); hideAllCategories = false; categoryStorage.removeScripted(); + categoryUidOrder.clear(); + } + + @MethodDescription(type = MethodDescription.Type.VALUE) + public void setOrder(List categoryUidOrder) { + this.categoryUidOrder.addAll(categoryUidOrder); + } + + @MethodDescription(type = MethodDescription.Type.VALUE, example = @Example("'minecraft.crafting', 'jei.information', 'minecraft.smelting', 'groovyscript:burning', 'groovyscript:explosion', 'groovyscript:fluid_recipe', 'groovyscript:piston_push', 'minecraft.anvil'")) + public void setOrder(String... categoryUidOrder) { + setOrder(Arrays.asList(categoryUidOrder)); } @MethodDescription(description = "groovyscript.wiki.jei.category.hideCategory") diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/jei/JeiPlugin.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/jei/JeiPlugin.java index 48ba5ab51..569e99c8d 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/jei/JeiPlugin.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/jei/JeiPlugin.java @@ -23,6 +23,8 @@ import net.minecraft.util.text.TextComponentString; import net.minecraftforge.fluids.FluidStack; +import java.util.Comparator; + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") @GroovyBlacklist @JEIPlugin @@ -47,6 +49,18 @@ public static void afterRuntimeAvailable() { ModSupport.JEI.get().description.applyRemovals(jeiRuntime.getRecipeRegistry()); } + /** + * We want to only return the index of the category if it exists, otherwise we want it placed at the end of the list. + * If this wasn't the case, any categories not in the list (such as new ones) would be disruptive to the user experience. + */ + public static Comparator> getCategoryComparator() { + var order = ModSupport.JEI.get().category.getOrder(); + return Comparator.comparingInt(category -> { + var index = order.indexOf(category.getUid()); + return index >= 0 ? index : Integer.MAX_VALUE; + }); + } + public static boolean isLoaded() { return jeiRuntime != null; } diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/jei/ModRegistryMixin.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/jei/ModRegistryMixin.java new file mode 100644 index 000000000..3d8625d0a --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/jei/ModRegistryMixin.java @@ -0,0 +1,32 @@ +package com.cleanroommc.groovyscript.core.mixin.jei; + +import com.cleanroommc.groovyscript.compat.mods.jei.JeiPlugin; +import mezz.jei.api.recipe.IRecipeCategory; +import mezz.jei.recipes.RecipeRegistry; +import mezz.jei.startup.ModRegistry; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(value = ModRegistry.class, remap = false) +public abstract class ModRegistryMixin { + + @Shadow + @Final + private List> recipeCategories; + + /** + * @reason Sort recipe categories according to a list created and managed by GroovyScript + * to allow a custom order for JEI category tabs. + * @see JeiPlugin#getCategoryComparator() + */ + @Inject(method = "createRecipeRegistry", at = @At("HEAD")) + private void grs$sortRecipeCategories(CallbackInfoReturnable ci) { + recipeCategories.sort(JeiPlugin.getCategoryComparator()); + } +} diff --git a/src/main/resources/assets/groovyscript/lang/en_us.lang b/src/main/resources/assets/groovyscript/lang/en_us.lang index 6298f425a..84b9cd6ba 100644 --- a/src/main/resources/assets/groovyscript/lang/en_us.lang +++ b/src/main/resources/assets/groovyscript/lang/en_us.lang @@ -1423,8 +1423,9 @@ groovyscript.wiki.jei.catalyst.remove=Removes the given catalyst items from the groovyscript.wiki.jei.catalyst.removeByType=Removes all catalyst items from the given category groovyscript.wiki.jei.category.title=Categories -groovyscript.wiki.jei.category.description=Modify the Categories visible in JEI, each of which contain recipes and are associated with specific blocks, typically machines. +groovyscript.wiki.jei.category.description=Modify the Categories visible in JEI, each of which contain recipes and are associated with specific blocks, typically machines. Can also set the order of Categories. groovyscript.wiki.jei.category.note0=Hidden Categories will still take up load time, and recipes contained within can still be processed. This only prevents seeing Categories. +groovyscript.wiki.jei.category.note1=Use the command `/gs jeiCategories` to log the UIDs of all JEI Categories to the `groovy.log` file! groovyscript.wiki.jei.category.annotation=Note that `classes.GenericRecipeCategory` must be defined elsewhere, and this example presumes certain fields and methods exist. groovyscript.wiki.jei.category.id.value=Sets the ID of the Category, which must be unique among all other Categories groovyscript.wiki.jei.category.wrapper.value=Sets the `IRecipeWrapper`s used by the Category to generate entries @@ -1433,6 +1434,7 @@ groovyscript.wiki.jei.category.category.value=Sets the function used to generate groovyscript.wiki.jei.category.add=Adds a new Category to JEI in the format `id`, `category`, `catalsyts`, `wrappers` groovyscript.wiki.jei.category.hideCategory=Hides the given category from JEI groovyscript.wiki.jei.category.hideAll=Hides all categories from JEI +groovyscript.wiki.jei.category.setOrder=Sets the order of categories in JEI. Categories not included in the list will be placed at the end according to their normal order. groovyscript.wiki.jei.description.title=Description Category groovyscript.wiki.jei.description.description=Modify the description of the input items, where the description is a unique JEI tab containing text. diff --git a/src/main/resources/mixin.groovyscript.jei.json b/src/main/resources/mixin.groovyscript.jei.json index 2b32b5c24..3fe83a680 100644 --- a/src/main/resources/mixin.groovyscript.jei.json +++ b/src/main/resources/mixin.groovyscript.jei.json @@ -8,6 +8,7 @@ "IngredientInfoRecipeAccessor", "JeiProxyAccessor", "JeiStarterMixin", - "ModRegistryAccessor" + "ModRegistryAccessor", + "ModRegistryMixin" ] } \ No newline at end of file