diff --git a/src/main/java/choonster/testmod3/init/ModCrafting.java b/src/main/java/choonster/testmod3/init/ModCrafting.java index 8cd6e357..8411486e 100644 --- a/src/main/java/choonster/testmod3/init/ModCrafting.java +++ b/src/main/java/choonster/testmod3/init/ModCrafting.java @@ -1,18 +1,19 @@ package choonster.testmod3.init; import choonster.testmod3.TestMod3; -import choonster.testmod3.util.RegistryUtil; import choonster.testmod3.world.item.crafting.ingredient.ConditionalIngredient; import choonster.testmod3.world.item.crafting.ingredient.FluidContainerIngredient; import choonster.testmod3.world.item.crafting.ingredient.MobSpawnerIngredient; import choonster.testmod3.world.item.crafting.ingredient.NeverIngredient; import choonster.testmod3.world.item.crafting.recipe.*; -import com.google.common.collect.ImmutableMap; import com.mojang.logging.LogUtils; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.SimplePreparableReloadListener; import net.minecraft.tags.TagKey; +import net.minecraft.util.Unit; +import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; import net.minecraft.world.item.alchemy.Potion; @@ -20,45 +21,35 @@ import net.minecraft.world.item.alchemy.Potions; import net.minecraft.world.item.crafting.*; import net.minecraftforge.common.crafting.ingredients.IIngredientSerializer; -import net.minecraftforge.event.server.ServerStartedEvent; +import net.minecraftforge.event.AddReloadListenerEvent; +import net.minecraftforge.event.brewing.BrewingRecipeRegisterEvent; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; -import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; -import net.minecraftforge.fml.util.ObfuscationReflectionHelper; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; import org.slf4j.Logger; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Map; +import java.util.ArrayList; +import java.util.Collection; import java.util.function.Predicate; /** * Manages this mod's recipes and ingredients and removes recipes. */ public class ModCrafting { - @Mod.EventBusSubscriber(modid = TestMod3.MODID, bus = Bus.MOD) public static class Brewing { - private static final Logger LOGGER = LogUtils.getLogger(); - - private static final Method ADD_MIX = ObfuscationReflectionHelper.findMethod(PotionBrewing.class, /* addMix */ "m_43513_", Potion.class, Item.class, Potion.class); - /** * Add this mod's brewing recipes. * * @param event The common setup event */ @SubscribeEvent - public static void registerBrewingRecipes(final FMLCommonSetupEvent event) { - event.enqueueWork(() -> - addStandardConversionRecipes(ModPotions.TEST.get(), ModPotions.LONG_TEST.get(), ModPotions.STRONG_TEST.get(), ModItems.ARROW.get()) - ); + public static void registerBrewingRecipes(final BrewingRecipeRegisterEvent event) { + addStandardConversionRecipes(event.getBuilder(), ModPotions.TEST, ModPotions.LONG_TEST, ModPotions.STRONG_TEST, ModItems.ARROW); } /** @@ -69,19 +60,49 @@ public static void registerBrewingRecipes(final FMLCommonSetupEvent event) { *
  • Standard + Glowstone = Strong
  • * * - * @param standardPotionType The standard PotionType - * @param longPotionType The long PotionType - * @param strongPotionType The strong PotionType - * @param ingredient The ingredient + * @param builder The PotionBrewing builder + * @param standardPotion The standard Potion + * @param longPotion The long Potion + * @param strongPotion The strong Potion + * @param ingredient The ingredient */ - private static void addStandardConversionRecipes(final Potion standardPotionType, final Potion longPotionType, final Potion strongPotionType, final Item ingredient) { - try { - ADD_MIX.invoke(null, Potions.AWKWARD, ingredient, standardPotionType); - ADD_MIX.invoke(null, standardPotionType, Items.REDSTONE, longPotionType); - ADD_MIX.invoke(null, standardPotionType, Items.GLOWSTONE_DUST, strongPotionType); - } catch (final IllegalAccessException | InvocationTargetException e) { - LOGGER.error("Failed to add potion recipes for potion type {}/ingredient item {}", RegistryUtil.getKey(standardPotionType), RegistryUtil.getKey(ingredient), e); - } + private static void addStandardConversionRecipes( + final PotionBrewing.Builder builder, + final Holder standardPotion, + final Holder longPotion, + final Holder strongPotion, + final Item ingredient + ) { + builder.addMix(Potions.AWKWARD, ingredient, standardPotion); + builder.addMix(standardPotion, Items.REDSTONE, longPotion); + builder.addMix(standardPotion, Items.GLOWSTONE_DUST, strongPotion); + } + + /** + * Overload of {@link #addStandardConversionRecipes(PotionBrewing.Builder, Holder, Holder, Holder, Item)} that + * accepts {@link RegistryObject} parameters for convenience. + * + * @param builder The PotionBrewing builder + * @param standardPotion The standard Potion + * @param longPotion The long Potion + * @param strongPotion The strong Potion + * @param ingredient The ingredient + */ + @SuppressWarnings("SameParameterValue") + private static void addStandardConversionRecipes( + final PotionBrewing.Builder builder, + final RegistryObject standardPotion, + final RegistryObject longPotion, + final RegistryObject strongPotion, + final RegistryObject ingredient + ) { + addStandardConversionRecipes( + builder, + standardPotion.getHolder().orElseThrow(), + longPotion.getHolder().orElseThrow(), + strongPotion.getHolder().orElseThrow(), + ingredient.get() + ); } } @@ -168,37 +189,58 @@ public static void initialise(final IEventBus modEventBus) { } @Mod.EventBusSubscriber(modid = TestMod3.MODID) - public static class RecipeRemover { + public static class RecipeRemover extends SimplePreparableReloadListener { private static final Logger LOGGER = LogUtils.getLogger(); - private static final Field RECIPES = ObfuscationReflectionHelper.findField(RecipeManager.class, /* recipes */ "f_44007_"); + private final RecipeManager recipeManager; + private final RegistryAccess registryAccess; + + public RecipeRemover(final RecipeManager recipeManager, final RegistryAccess registryAccess) { + this.recipeManager = recipeManager; + this.registryAccess = registryAccess; + } /** - * Removes recipes from the server's recipe manager when it starts up. + * Adds this listener to the ResourceManager's list. * - * @param event The server starting event + * @param event The event */ @SubscribeEvent - public static void removeRecipes(final ServerStartedEvent event) { - final var recipeManager = event.getServer().getRecipeManager(); - final var registryAccess = event.getServer().registryAccess(); - - removeRecipes(recipeManager, FireworkRocketRecipe.class); - removeRecipes(recipeManager, FireworkStarRecipe.class); - removeRecipes(recipeManager, FireworkStarFadeRecipe.class); - removeRecipes(recipeManager, registryAccess, ModTags.Items.VANILLA_DYES); - removeRecipes(recipeManager, registryAccess, ModTags.Items.VANILLA_TERRACOTTA); + public static void addReloadListener(final AddReloadListenerEvent event) { + event.addListener( + new RecipeRemover(event.getServerResources().getRecipeManager(), event.getRegistryAccess()) + ); + } + + @Override + protected Unit prepare(final ResourceManager resourceManager, final ProfilerFiller profilerFiller) { + return Unit.INSTANCE; + } + + /** + * Removes recipes from the recipe manager after it's reloaded. + */ + @Override + protected void apply(final Unit unit, final ResourceManager resourceManager, final ProfilerFiller profilerFiller) { + final var recipes = new ArrayList<>(recipeManager.getRecipes()); + + removeRecipes(recipes, FireworkRocketRecipe.class); + removeRecipes(recipes, FireworkStarRecipe.class); + removeRecipes(recipes, FireworkStarFadeRecipe.class); + removeRecipes(recipes, ModTags.Items.VANILLA_DYES); + removeRecipes(recipes, ModTags.Items.VANILLA_TERRACOTTA); + + recipeManager.replaceRecipes(recipes); } /** * Removes all crafting recipes with an output item contained in the specified tag. * - * @param recipeManager The recipe manager - * @param registryAccess The registry access - * @param tag The tag + * @param recipes The recipe list + * @param tag The tag */ - private static void removeRecipes(final RecipeManager recipeManager, final RegistryAccess registryAccess, final TagKey tag) { - final var recipesRemoved = removeRecipes(recipeManager, recipe -> { + private void removeRecipes(final Collection> recipes, final TagKey tag) { + final var recipesRemoved = removeRecipes(recipes, recipe -> { final var resultItem = recipe.getResultItem(registryAccess); return !resultItem.isEmpty() && resultItem.is(tag); }); @@ -212,11 +254,11 @@ private static void removeRecipes(final RecipeManager recipeManager, final Regis * Test for this thread: * https://www.minecraftforge.net/forum/topic/33420-removing-vanilla-recipes/ * - * @param recipeManager The recipe manager - * @param recipeClass The recipe class + * @param recipes The recipe list + * @param recipeClass The recipe class */ - private static void removeRecipes(final RecipeManager recipeManager, final Class> recipeClass) { - final var recipesRemoved = removeRecipes(recipeManager, recipeClass::isInstance); + private void removeRecipes(final Collection> recipes, final Class> recipeClass) { + final var recipesRemoved = removeRecipes(recipes, recipeClass::isInstance); LOGGER.info("Removed {} recipe(s) for class {}", recipesRemoved, recipeClass); } @@ -224,42 +266,20 @@ private static void removeRecipes(final RecipeManager recipeManager, final Class /** * Remove all crafting recipes that match the specified predicate. * - * @param recipeManager The recipe manager - * @param predicate The predicate + * @param recipes The recipe list + * @param predicate The predicate * @return The number of recipes removed */ - private static int removeRecipes(final RecipeManager recipeManager, final Predicate> predicate) { - final Map, Map>> existingRecipes; - try { - @SuppressWarnings("unchecked") final var recipesMap = (Map, Map>>) RECIPES.get(recipeManager); - existingRecipes = recipesMap; - } catch (final IllegalAccessException e) { - throw new RuntimeException("Couldn't get recipes map while removing recipes", e); - } - - final var removedCounts = new Object2IntOpenHashMap>(); - final var newRecipes = ImmutableMap., Map>>builder(); + private int removeRecipes(final Collection> recipes, final Predicate> predicate) { + // Get the list of recipes to remove + final var recipesToRemove = recipes.stream() + .filter(holder -> predicate.test(holder.value())) + .toList(); - // For each recipe type, create a new map that doesn't contain the recipes to be removed - existingRecipes.forEach((recipeType, existingRecipesForType) -> { - final ImmutableMap> newRecipesForType = existingRecipesForType.entrySet() - .stream() - .filter(entry -> !predicate.test(entry.getValue().value())) - .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); - - removedCounts.put(recipeType, existingRecipesForType.size() - newRecipesForType.size()); - newRecipes.put(recipeType, newRecipesForType); - }); - - final var removedCount = removedCounts.values().intStream().reduce(0, Integer::sum); - - try { - RECIPES.set(recipeManager, newRecipes.build()); - } catch (final IllegalAccessException e) { - throw new RuntimeException("Couldn't replace recipes map while removing recipes", e); - } + // Remove the recipes from the list + recipes.removeAll(recipesToRemove); - return removedCount; + return recipesToRemove.size(); } } }