diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 124e7b3b8..5e16c9afd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ # Remember to update corresponding versions in fabric.mod.json/neoforge.mods.toml fabric-api = "0.128.0+1.21.7" fabric-loader = "0.16.14" -neoForge = "21.7.1-beta" +neoForge = "21.7.19-beta" neoMergeTool = "2.0.0" mixin = "0.8.5" parchment = "2025.06.29" @@ -40,7 +40,7 @@ emi = "1.1.7+1.21" fabricPermissions = "0.3.3" iris-fabric = "1.9.1+1.21.7-fabric" iris-forge = "1.9.1+1.21.7-neoforge" -jei = "19.8.2.99" +jei = "23.1.0.4" modmenu = "13.0.2" moreRed = "6.0.0.3" rei = "18.0.800" @@ -69,7 +69,7 @@ ideaExt = "1.1.7" illuaminate = "0.1.0-83-g1131f68" lwjgl = "3.3.3" minotaur = "2.8.7" -modDevGradle = "2.0.95" +modDevGradle = "2.0.99" nullAway = "0.12.7" shadow = "8.3.1" spotless = "7.0.2" @@ -113,9 +113,9 @@ fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-l fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" } iris-fabric = { module = "maven.modrinth:iris", version.ref = "iris-fabric" } iris-forge = { module = "maven.modrinth:iris", version.ref = "iris-forge" } -jei-api = { module = "mezz.jei:jei-1.21-common-api", version.ref = "jei" } -jei-fabric = { module = "mezz.jei:jei-1.21-fabric", version.ref = "jei" } -jei-forge = { module = "mezz.jei:jei-1.21-neoforge", version.ref = "jei" } +jei-api = { module = "mezz.jei:jei-1.21.7-common-api", version.ref = "jei" } +jei-fabric = { module = "mezz.jei:jei-1.21.7-fabric", version.ref = "jei" } +jei-forge = { module = "mezz.jei:jei-1.21.7-neoforge", version.ref = "jei" } mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" } mixinExtra = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixinExtra" } modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" } @@ -186,7 +186,7 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"] # Minecraft externalMods-common = ["iris-forge", "jei-api", "nightConfig-core", "nightConfig-toml"] externalMods-forge-compile = ["moreRed", "iris-forge", "jei-api"] -externalMods-forge-runtime = [] +externalMods-forge-runtime = ["jei-forge"] externalMods-fabric-compile = ["fabricPermissions", "iris-fabric", "jei-api", "rei-api", "rei-builtin"] externalMods-fabric-runtime = [] diff --git a/projects/common/build.gradle.kts b/projects/common/build.gradle.kts index 85fe03ce4..f3f4c6a32 100644 --- a/projects/common/build.gradle.kts +++ b/projects/common/build.gradle.kts @@ -14,7 +14,6 @@ plugins { sourceSets.client { java { exclude("dan200/computercraft/client/integration/emi") - exclude("dan200/computercraft/client/integration/jei") } } diff --git a/projects/common/src/client/java/dan200/computercraft/client/integration/jei/JEIComputerCraft.java b/projects/common/src/client/java/dan200/computercraft/client/integration/jei/JEIComputerCraft.java index 2f4c2e5b4..7b698cd66 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/integration/jei/JEIComputerCraft.java +++ b/projects/common/src/client/java/dan200/computercraft/client/integration/jei/JEIComputerCraft.java @@ -8,13 +8,14 @@ import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.integration.RecipeModHelpers; +import dan200.computercraft.shared.pocket.core.PocketSide; import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.turtle.items.TurtleItem; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.RecipeTypes; import mezz.jei.api.constants.VanillaTypes; -import mezz.jei.api.ingredients.subtypes.IIngredientSubtypeInterpreter; +import mezz.jei.api.ingredients.subtypes.ISubtypeInterpreter; import mezz.jei.api.registration.IAdvancedRegistration; import mezz.jei.api.registration.ISubtypeRegistration; import mezz.jei.api.runtime.IJeiRuntime; @@ -46,7 +47,7 @@ public class JEIComputerCraft implements IModPlugin { @Override public void registerAdvanced(IAdvancedRegistration registry) { - registry.addRecipeManagerPlugin(new RecipeResolver(getRegistryAccess())); + registry.addSimpleRecipeManagerPlugin(RecipeTypes.CRAFTING, new RecipeResolver(getRegistryAccess())); } @Override @@ -62,7 +63,7 @@ public class JEIComputerCraft implements IModPlugin { // Hide all upgrade recipes var category = registry.createRecipeLookup(RecipeTypes.CRAFTING); category.get().forEach(wrapper -> { - if (RecipeModHelpers.shouldRemoveRecipe(wrapper.id())) { + if (RecipeModHelpers.shouldRemoveRecipe(wrapper.id().location())) { registry.hideRecipes(RecipeTypes.CRAFTING, List.of(wrapper)); } }); @@ -71,7 +72,7 @@ public class JEIComputerCraft implements IModPlugin { /** * Distinguishes turtles by upgrades and family. */ - private static final IIngredientSubtypeInterpreter turtleSubtype = (stack, ctx) -> { + private static final ISubtypeInterpreter turtleSubtype = (stack, ctx) -> { var name = new StringBuilder("turtle:"); // Add left and right upgrades to the identifier @@ -87,12 +88,15 @@ public class JEIComputerCraft implements IModPlugin { /** * Distinguishes pocket computers by upgrade and family. */ - private static final IIngredientSubtypeInterpreter pocketSubtype = (stack, ctx) -> { + private static final ISubtypeInterpreter pocketSubtype = (stack, ctx) -> { var name = new StringBuilder("pocket:"); // Add the upgrade to the identifier - var upgrade = PocketComputerItem.getUpgradeWithData(stack); - if (upgrade != null) name.append(upgrade.holder().key().location()); + var back = PocketComputerItem.getUpgradeWithData(stack, PocketSide.BACK); + var bottom = PocketComputerItem.getUpgradeWithData(stack, PocketSide.BOTTOM); + if (back != null) name.append(back.holder().key().location()); + if (back != null && bottom != null) name.append('|'); + if (bottom != null) name.append(bottom.holder().key().location()); return name.toString(); }; @@ -100,7 +104,7 @@ public class JEIComputerCraft implements IModPlugin { /** * Distinguishes disks by colour. */ - private static final IIngredientSubtypeInterpreter diskSubtype = (stack, ctx) -> Integer.toString(DyedItemColor.getOrDefault(stack, -1)); + private static final ISubtypeInterpreter diskSubtype = (stack, ctx) -> Integer.toString(DyedItemColor.getOrDefault(stack, -1)); private static RegistryAccess getRegistryAccess() { return Minecraft.getInstance().level.registryAccess(); diff --git a/projects/common/src/client/java/dan200/computercraft/client/integration/jei/RecipeResolver.java b/projects/common/src/client/java/dan200/computercraft/client/integration/jei/RecipeResolver.java index 5e88e2fcc..c19eacbba 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/integration/jei/RecipeResolver.java +++ b/projects/common/src/client/java/dan200/computercraft/client/integration/jei/RecipeResolver.java @@ -8,71 +8,90 @@ import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.shared.integration.UpgradeRecipeGenerator; import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.turtle.items.TurtleItem; -import mezz.jei.api.constants.RecipeTypes; -import mezz.jei.api.recipe.IFocus; -import mezz.jei.api.recipe.RecipeType; -import mezz.jei.api.recipe.advanced.IRecipeManagerPlugin; -import mezz.jei.api.recipe.category.IRecipeCategory; +import mezz.jei.api.ingredients.ITypedIngredient; +import mezz.jei.api.recipe.advanced.ISimpleRecipeManagerPlugin; import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.CraftingRecipe; -import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.*; +import net.minecraft.world.item.crafting.display.RecipeDisplay; +import net.minecraft.world.level.Level; import java.util.List; -class RecipeResolver implements IRecipeManagerPlugin { - private final UpgradeRecipeGenerator> resolver; - +class RecipeResolver implements ISimpleRecipeManagerPlugin> { /** * We need to generate unique ids for each recipe, as JEI will attempt to deduplicate them otherwise. */ private int nextId = 0; + private final UpgradeRecipeGenerator> resolver; + RecipeResolver(HolderLookup.Provider registries) { - resolver = new UpgradeRecipeGenerator<>( - x -> new RecipeHolder<>(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "upgrade_" + nextId++), x), - registries - ); + resolver = new UpgradeRecipeGenerator<>(x -> new RecipeHolder<>( + ResourceKey.create(Registries.RECIPE, ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "upgrade_" + nextId++)), + new CraftingWrapper(x) + ), registries); } @Override - public List> getRecipeTypes(IFocus focus) { - var value = focus.getTypedValue().getIngredient(); - if (!(value instanceof ItemStack stack)) return List.of(); - - return switch (focus.getRole()) { - case INPUT -> - stack.getItem() instanceof TurtleItem || stack.getItem() instanceof PocketComputerItem || resolver.isUpgrade(stack) - ? List.of(RecipeTypes.CRAFTING) - : List.of(); - case OUTPUT -> stack.getItem() instanceof TurtleItem || stack.getItem() instanceof PocketComputerItem - ? List.of(RecipeTypes.CRAFTING) - : List.of(); - default -> List.of(); - }; + public boolean isHandledInput(ITypedIngredient input) { + return input.getIngredient() instanceof ItemStack stack + && (stack.getItem() instanceof TurtleItem || stack.getItem() instanceof PocketComputerItem || resolver.isUpgrade(stack)); } @Override - public List getRecipes(IRecipeCategory recipeCategory, IFocus focus) { - if (!(focus.getTypedValue().getIngredient() instanceof ItemStack stack) || recipeCategory.getRecipeType() != RecipeTypes.CRAFTING) { - return List.of(); - } - - return switch (focus.getRole()) { - case INPUT -> cast(RecipeTypes.CRAFTING, resolver.findRecipesWithInput(stack)); - case OUTPUT -> cast(RecipeTypes.CRAFTING, resolver.findRecipesWithOutput(stack)); - default -> List.of(); - }; + public boolean isHandledOutput(ITypedIngredient output) { + return output.getIngredient() instanceof ItemStack stack + && (stack.getItem() instanceof TurtleItem || stack.getItem() instanceof PocketComputerItem); } @Override - public List getRecipes(IRecipeCategory recipeCategory) { + public List> getRecipesForInput(ITypedIngredient input) { + return input.getIngredient() instanceof ItemStack stack ? resolver.findRecipesWithInput(stack) : List.of(); + } + + @Override + public List> getRecipesForOutput(ITypedIngredient output) { + return output.getIngredient() instanceof ItemStack stack ? resolver.findRecipesWithOutput(stack) : List.of(); + } + + @Override + public List> getAllRecipes() { return List.of(); } - @SuppressWarnings({ "unchecked", "rawtypes", "UnusedVariable" }) - private static List cast(RecipeType ignoredType, List from) { - return (List) from; + private record CraftingWrapper(RecipeDisplay recipes) implements CraftingRecipe { + @Override + public RecipeSerializer getSerializer() { + throw new IllegalStateException("Should not serialise CraftingWrapper"); + } + + @Override + public CraftingBookCategory category() { + return CraftingBookCategory.MISC; + } + + @Override + public boolean matches(CraftingInput input, Level level) { + return false; + } + + @Override + public ItemStack assemble(CraftingInput input, HolderLookup.Provider registries) { + return ItemStack.EMPTY; + } + + @Override + public PlacementInfo placementInfo() { + return PlacementInfo.NOT_PLACEABLE; + } + + @Override + public List display() { + return List.of(recipes); + } } }