Skip to content

Commit

Permalink
Fix #2968 Add Fabric support for Potion recipes
Browse files Browse the repository at this point in the history
  • Loading branch information
mezz committed Sep 5, 2022
1 parent 5662c0b commit 730f667
Show file tree
Hide file tree
Showing 14 changed files with 306 additions and 193 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;

import java.util.List;

public interface IPlatformIngredientHelper {
Ingredient createShulkerDyeIngredient(DyeColor color);

Ingredient createNbtIngredient(ItemStack stack, IStackHelper stackHelper);

List<Ingredient> getPotionContainers();
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package mezz.jei.common.platform;

import mezz.jei.api.recipe.vanilla.IJeiBrewingRecipe;
import mezz.jei.api.recipe.vanilla.IVanillaRecipeFactory;
import mezz.jei.api.runtime.IIngredientManager;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.crafting.CraftingRecipe;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.UpgradeRecipe;
import org.jetbrains.annotations.Nullable;

import java.util.List;

public interface IPlatformRecipeHelper {
<T extends CraftingRecipe> int getWidth(T recipe);
<T extends CraftingRecipe> int getHeight(T recipe);
Expand All @@ -15,4 +20,6 @@ public interface IPlatformRecipeHelper {

@Nullable
ResourceLocation getRegistryNameForRecipe(Object object);

List<IJeiBrewingRecipe> getBrewingRecipes(IIngredientManager ingredientManager, IVanillaRecipeFactory vanillaRecipeFactory);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import mezz.jei.api.recipe.category.extensions.IExtendableRecipeCategory;
import mezz.jei.api.recipe.category.extensions.vanilla.crafting.ICraftingCategoryExtension;
import mezz.jei.api.recipe.transfer.IRecipeTransferHandlerHelper;
import mezz.jei.api.recipe.vanilla.IJeiBrewingRecipe;
import mezz.jei.api.recipe.vanilla.IVanillaRecipeFactory;
import mezz.jei.api.registration.IGuiHandlerRegistration;
import mezz.jei.api.registration.IModIngredientRegistration;
Expand All @@ -30,6 +31,7 @@
import mezz.jei.common.ingredients.fluid.FluidIngredientHelper;
import mezz.jei.common.ingredients.fluid.FluidStackListFactory;
import mezz.jei.common.platform.IPlatformFluidHelperInternal;
import mezz.jei.common.platform.IPlatformRecipeHelper;
import mezz.jei.common.platform.IPlatformRegistry;
import mezz.jei.common.platform.Services;
import mezz.jei.common.plugins.vanilla.anvil.AnvilRecipeCategory;
Expand Down Expand Up @@ -104,6 +106,7 @@
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -246,6 +249,11 @@ public void registerRecipes(IRecipeRegistration registration) {
registration.addRecipes(RecipeTypes.ANVIL, AnvilRecipeMaker.getAnvilRecipes(vanillaRecipeFactory, ingredientManager));
registration.addRecipes(RecipeTypes.SMITHING, vanillaRecipes.getSmithingRecipes(smithingCategory));
registration.addRecipes(RecipeTypes.COMPOSTING, CompostingRecipeMaker.getRecipes(ingredientManager));

IPlatformRecipeHelper recipeHelper = Services.PLATFORM.getRecipeHelper();
List<IJeiBrewingRecipe> brewingRecipes = recipeHelper.getBrewingRecipes(ingredientManager, vanillaRecipeFactory);
brewingRecipes.sort(Comparator.comparingInt(IJeiBrewingRecipe::getBrewingSteps));
registration.addRecipes(RecipeTypes.BREWING, brewingRecipes);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package mezz.jei.common.util;

import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.ingredients.IIngredientHelper;
import mezz.jei.api.ingredients.subtypes.UidContext;
import mezz.jei.api.recipe.vanilla.IJeiBrewingRecipe;
import mezz.jei.api.recipe.vanilla.IVanillaRecipeFactory;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.common.ingredients.IngredientSet;
import mezz.jei.common.platform.IPlatformIngredientHelper;
import mezz.jei.common.platform.IPlatformRegistry;
import mezz.jei.common.platform.Services;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.alchemy.Potion;
import net.minecraft.world.item.alchemy.PotionBrewing;
import net.minecraft.world.item.alchemy.PotionUtils;
import net.minecraft.world.item.alchemy.Potions;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

public class BrewingRecipeMakerCommon {
private static final Logger LOGGER = LogManager.getLogger();

public static Set<IJeiBrewingRecipe> getVanillaBrewingRecipes(
IVanillaRecipeFactory recipeFactory,
IIngredientManager ingredientManager,
IVanillaPotionOutputSupplier vanillaOutputSupplier
) {
Set<IJeiBrewingRecipe> recipes = new HashSet<>();
IPlatformRegistry<Potion> potionRegistry = Services.PLATFORM.getRegistry(Registry.POTION_REGISTRY);
IngredientSet<ItemStack> knownPotions = getBaseKnownPotions(ingredientManager, potionRegistry);

List<ItemStack> potionReagents = ingredientManager.getAllItemStacks().stream()
.filter(BrewingRecipeMakerCommon::isIngredient)
.toList();

boolean foundNewPotions;
do {
List<ItemStack> newPotions = getNewPotions(
recipeFactory,
potionRegistry,
knownPotions,
potionReagents,
vanillaOutputSupplier,
recipes
);
foundNewPotions = !newPotions.isEmpty();
knownPotions.addAll(newPotions);
} while (foundNewPotions);

return recipes;
}

private static boolean isIngredient(ItemStack itemStack) {
try {
return PotionBrewing.isIngredient(itemStack);
} catch (RuntimeException | LinkageError e) {
String itemStackInfo = ErrorUtil.getItemStackInfo(itemStack);
LOGGER.error("Failed to check if item is a potion reagent {}.", itemStackInfo, e);
return false;
}
}

private static IngredientSet<ItemStack> getBaseKnownPotions(IIngredientManager ingredientManager, IPlatformRegistry<Potion> potionRegistry) {
IPlatformIngredientHelper ingredientHelper = Services.PLATFORM.getIngredientHelper();
List<ItemStack> potionContainers = ingredientHelper.getPotionContainers().stream()
.flatMap(potionItem -> Arrays.stream(potionItem.getItems()))
.toList();

IIngredientHelper<ItemStack> itemStackHelper = ingredientManager.getIngredientHelper(VanillaTypes.ITEM_STACK);
IngredientSet<ItemStack> knownPotions = IngredientSet.create(itemStackHelper, UidContext.Ingredient);

potionRegistry.getValues()
.filter(potion -> potion != Potions.EMPTY) // skip the "un-craft-able" vanilla potions
.forEach(potion -> {
for (ItemStack potionContainer : potionContainers) {
ItemStack result = PotionUtils.setPotion(potionContainer.copy(), potion);
knownPotions.add(result);
}
});
return knownPotions;
}

private static List<ItemStack> getNewPotions(
IVanillaRecipeFactory recipeFactory,
IPlatformRegistry<Potion> potionRegistry,
Collection<ItemStack> knownPotions,
List<ItemStack> potionReagents,
IVanillaPotionOutputSupplier vanillaOutputSupplier,
Collection<IJeiBrewingRecipe> recipes
) {
List<ItemStack> newPotions = new ArrayList<>();
for (ItemStack potionInput : knownPotions) {
for (ItemStack potionReagent : potionReagents) {
ItemStack potionOutput = vanillaOutputSupplier.getOutput(potionInput.copy(), potionReagent);
if (potionOutput.isEmpty()) {
continue;
}

if (potionInput.getItem() == potionOutput.getItem()) {
Potion potionOutputType = PotionUtils.getPotion(potionOutput);
if (potionOutputType == Potions.WATER) {
continue;
}

Potion potionInputType = PotionUtils.getPotion(potionInput);
ResourceLocation inputId = potionRegistry.getRegistryName(potionInputType);
ResourceLocation outputId = potionRegistry.getRegistryName(potionOutputType);
if (Objects.equals(inputId, outputId)) {
continue;
}
}

IJeiBrewingRecipe recipe = recipeFactory.createBrewingRecipe(List.of(potionReagent), potionInput.copy(), potionOutput);
if (!recipes.contains(recipe)) {
recipes.add(recipe);
newPotions.add(potionOutput);
}
}
}
return newPotions;
}

@FunctionalInterface
public interface IVanillaPotionOutputSupplier {
ItemStack getOutput(ItemStack input, ItemStack ingredient);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package mezz.jei.fabric.platform;

import mezz.jei.api.recipe.vanilla.IJeiBrewingRecipe;
import mezz.jei.api.recipe.vanilla.IVanillaRecipeFactory;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.common.util.BrewingRecipeMakerCommon;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.alchemy.PotionBrewing;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

public class BrewingRecipeMaker {
public static List<IJeiBrewingRecipe> getBrewingRecipes(IIngredientManager ingredientManager, IVanillaRecipeFactory vanillaRecipeFactory) {
Set<IJeiBrewingRecipe> recipes = BrewingRecipeMakerCommon.getVanillaBrewingRecipes(
vanillaRecipeFactory,
ingredientManager,
BrewingRecipeMaker::getOutput
);

List<IJeiBrewingRecipe> recipeList = new ArrayList<>(recipes);
recipeList.sort(Comparator.comparingInt(IJeiBrewingRecipe::getBrewingSteps));

return recipeList;
}

private static ItemStack getOutput(ItemStack input, ItemStack ingredient) {
ItemStack result = PotionBrewing.mix(ingredient, input);
if (result != input) {
return result;
}
return ItemStack.EMPTY;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.DyeItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.alchemy.PotionBrewing;
import net.minecraft.world.item.crafting.Ingredient;

import java.util.List;

public class IngredientHelper implements IPlatformIngredientHelper {
@Override
public Ingredient createShulkerDyeIngredient(DyeColor color) {
Expand All @@ -19,4 +22,9 @@ public Ingredient createNbtIngredient(ItemStack stack, IStackHelper stackHelper)
// TODO: Implement Fabric NBT-aware ingredients
return Ingredient.of(stack);
}

@Override
public List<Ingredient> getPotionContainers() {
return PotionBrewing.ALLOWED_CONTAINERS;
}
}
10 changes: 10 additions & 0 deletions Fabric/src/main/java/mezz/jei/fabric/platform/RecipeHelper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package mezz.jei.fabric.platform;

import mezz.jei.api.recipe.vanilla.IJeiBrewingRecipe;
import mezz.jei.api.recipe.vanilla.IVanillaRecipeFactory;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.common.platform.IPlatformRecipeHelper;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.crafting.CraftingRecipe;
Expand All @@ -9,6 +12,8 @@
import net.minecraft.world.item.crafting.UpgradeRecipe;
import org.jetbrains.annotations.Nullable;

import java.util.List;

public class RecipeHelper implements IPlatformRecipeHelper {
@Override
public <T extends CraftingRecipe> int getWidth(T recipe) {
Expand Down Expand Up @@ -44,4 +49,9 @@ public ResourceLocation getRegistryNameForRecipe(Object object) {
}
return null;
}

@Override
public List<IJeiBrewingRecipe> getBrewingRecipes(IIngredientManager ingredientManager, IVanillaRecipeFactory vanillaRecipeFactory) {
return BrewingRecipeMaker.getBrewingRecipes(ingredientManager, vanillaRecipeFactory);
}
}
4 changes: 3 additions & 1 deletion Fabric/src/main/resources/jei.accesswidener
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ accessible field net/minecraft/client/gui/screens/recipebook/RecipeBookComponent
accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite mainImage [Lcom/mojang/blaze3d/platform/NativeImage;

accessible field net/minecraft/world/item/crafting/UpgradeRecipe base Lnet/minecraft/world/item/crafting/Ingredient;
accessible field net/minecraft/world/item/crafting/UpgradeRecipe addition Lnet/minecraft/world/item/crafting/Ingredient;
accessible field net/minecraft/world/item/crafting/UpgradeRecipe addition Lnet/minecraft/world/item/crafting/Ingredient;

accessible field net/minecraft/world/item/alchemy/PotionBrewing ALLOWED_CONTAINERS Ljava/util/List;
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package mezz.jei.forge.platform;

import mezz.jei.api.recipe.vanilla.IJeiBrewingRecipe;
import mezz.jei.api.recipe.vanilla.IVanillaRecipeFactory;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.common.util.BrewingRecipeMakerCommon;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraftforge.common.brewing.BrewingRecipe;
import net.minecraftforge.common.brewing.BrewingRecipeRegistry;
import net.minecraftforge.common.brewing.IBrewingRecipe;
import net.minecraftforge.common.brewing.VanillaBrewingRecipe;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class BrewingRecipeMaker {
private static final Logger LOGGER = LogManager.getLogger();

public static List<IJeiBrewingRecipe> getBrewingRecipes(IIngredientManager ingredientManager, IVanillaRecipeFactory vanillaRecipeFactory) {
Collection<IBrewingRecipe> brewingRecipes = BrewingRecipeRegistry.getRecipes();

Set<IJeiBrewingRecipe> recipes = brewingRecipes.stream()
.filter(VanillaBrewingRecipe.class::isInstance)
.map(VanillaBrewingRecipe.class::cast)
.findFirst()
.map(vanillaBrewingRecipe ->
BrewingRecipeMakerCommon.getVanillaBrewingRecipes(
vanillaRecipeFactory,
ingredientManager,
vanillaBrewingRecipe::getOutput
)
)
.orElseGet(HashSet::new);

addModdedBrewingRecipes(
vanillaRecipeFactory,
brewingRecipes,
recipes
);

return new ArrayList<>(recipes);
}

private static void addModdedBrewingRecipes(
IVanillaRecipeFactory vanillaRecipeFactory,
Collection<IBrewingRecipe> brewingRecipes,
Collection<IJeiBrewingRecipe> recipes
) {
Set<Class<?>> unhandledRecipeClasses = new HashSet<>();
for (IBrewingRecipe iBrewingRecipe : brewingRecipes) {
if (iBrewingRecipe instanceof BrewingRecipe brewingRecipe) {
ItemStack[] ingredients = brewingRecipe.getIngredient().getItems();
if (ingredients.length > 0) {
Ingredient inputIngredient = brewingRecipe.getInput();
ItemStack output = brewingRecipe.getOutput();
ItemStack[] inputs = inputIngredient.getItems();
IJeiBrewingRecipe recipe = vanillaRecipeFactory.createBrewingRecipe(List.of(ingredients), List.of(inputs), output);
recipes.add(recipe);
}
} else if (!(iBrewingRecipe instanceof VanillaBrewingRecipe)) {
Class<?> recipeClass = iBrewingRecipe.getClass();
if (!unhandledRecipeClasses.contains(recipeClass)) {
unhandledRecipeClasses.add(recipeClass);
LOGGER.debug("Can't handle brewing recipe class: {}", recipeClass);
}
}
}
}
}
Loading

0 comments on commit 730f667

Please sign in to comment.