package lv.enes.mc.eris_alchemy.recipe; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import earth.terrarium.chipped.common.recipe.ChippedRecipe; import jakarta.annotation.Nonnull; import lv.enes.mc.eris_alchemy.utils.ForeignUtils; import lv.enes.mc.eris_alchemy.utils.IngredientProvider; import lv.enes.mc.eris_alchemy.utils.ItemUtils; import lv.enes.mc.eris_alchemy.utils.RecipeUtils; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.ShapedRecipe; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; public record SimplifiedRecipe(ItemStack output, List remainder, List> input) { public static List of(Recipe recipe, RegistryAccess registryAccess) { if (ForeignUtils.IS_CHIPPED_AVAILABLE && recipe instanceof ChippedRecipe chippedRecipe) { var remainder = List.of(); return chippedRecipe.tags() .stream() .flatMap(tag -> { var items = tag.stream().map(Holder::value).toList(); Predicate isOutput = item -> ItemUtils.getId(item).getNamespace().equals("chipped"); var inputs = items.stream() .filter(item -> !isOutput.test(item)) .map(Ingredient::of) .map(x -> (Supplier) () -> x) .map(List::of) .toList(); var outputs = items.stream().filter(isOutput).map(Item::getDefaultInstance).toList(); return outputs.stream() .flatMap( output -> inputs.stream() .map(input -> new SimplifiedRecipe(output, remainder, input)) ); }) .toList(); } return List.of(new SimplifiedRecipe( RecipeUtils.getOutput(recipe, registryAccess), List.of(), // TODO RecipeUtils.getIngredients(recipe) .stream() .filter(ingredient -> !ingredient.isEmpty()) .map(x -> (Supplier)() -> x) .toList() )); } public Stream dependencies() { return Stream.concat( input.stream().map(Supplier::get).map(Ingredient::getItems).flatMap(Arrays::stream), remainder.stream() ).map(ItemUtils::getId); } public boolean hasDuplication() { return remainder.stream().anyMatch(stack -> ItemStack.isSameItem(stack, output)) || input.stream().anyMatch(ingredient -> ingredient.get().test(output)); } public boolean isAllowed(BannedRecipe ban) { if (!ban.output().get().test(output())) { return true; } return dependencies().noneMatch(dep -> ban.input().get().test(ItemUtils.get(dep).getDefaultInstance())); } public boolean isAllowed(Collection bans) { return bans.stream().allMatch(this::isAllowed); } public static class Deserializer implements JsonDeserializer { @Nonnull @Override public SimplifiedRecipe deserialize( JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext ) throws JsonParseException { if (!jsonElement.isJsonObject()) { throw new JsonParseException("Recipe must be an object"); } var obj = jsonElement.getAsJsonObject(); if (obj.get("output") == null || obj.get("input") == null) { throw new JsonParseException("Recipe must have 'output' and 'input' fields"); } var output = parseOutputOrRemainder(obj.get("output")); var remainder = parseRemainders(obj.get("remainder")); var input = parseInputs(obj.get("input")); return new SimplifiedRecipe(output, remainder, input); } private List> parseInputs(JsonElement el) { if (el.isJsonArray()) { return el.getAsJsonArray().asList().stream().map(IngredientProvider::deserialize).toList(); } return List.of(IngredientProvider.deserialize(el)); } private ItemStack parseOutputOrRemainder(JsonElement el) { if (el.isJsonObject()) { return ShapedRecipe.itemStackFromJson(el.getAsJsonObject()); } else if (el.isJsonPrimitive() && el.getAsJsonPrimitive().isString()) { var id = new ResourceLocation(el.getAsString()); return BuiltInRegistries.ITEM.get(id).getDefaultInstance(); } else { throw new JsonParseException("Recipe's output or remainder must be an object or a string"); } } private List parseRemainders(JsonElement el) { if (el == null) { return List.of(); } else if (el.isJsonArray()) { return el.getAsJsonArray().asList().stream().map(this::parseOutputOrRemainder).toList(); } else { return List.of(parseOutputOrRemainder(el)); } } } }