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.recipes.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.Items; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.Recipe; import java.lang.reflect.Type; import java.util.*; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; public record SimplifiedRecipe( ItemStack output, List remainder, List> input, boolean fromChipped ) { public static List of(Recipe recipe, RegistryAccess registryAccess) { if (ForeignUtils.isClassAvailable("earth.terrarium.chipped.common.recipes.ChippedRecipe") && 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, true)) ); }) .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(), false )); } 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")).orElseGet(Items.AIR::getDefaultInstance); var remainder = parseRemainders(obj.get("remainder")) .orElseGet(() -> List.of(Items.AIR.getDefaultInstance())); var input = parseInputs(obj.get("input")); return new SimplifiedRecipe(output, remainder, input, false); } private List> parseInputs(JsonElement el) { if (el.isJsonArray()) { return el.getAsJsonArray().asList().stream().map(IngredientProvider::deserialize).toList(); } return List.of(IngredientProvider.deserialize(el)); } private Optional parseOutputOrRemainder(JsonElement el) { if (el.isJsonObject()) { return ItemUtils.itemStackFromJson(el.getAsJsonObject()); } else if (el.isJsonPrimitive() && el.getAsJsonPrimitive().isString()) { var id = new ResourceLocation(el.getAsString()); return Optional.of(BuiltInRegistries.ITEM.get(id).getDefaultInstance()); } else { throw new JsonParseException("Recipe's output or remainder must be an object or a string"); } } private Optional> parseRemainders(JsonElement el) { if (el == null) { return Optional.of(List.of()); } else if (el.isJsonArray()) { var optList = el.getAsJsonArray().asList().stream().map(this::parseOutputOrRemainder).toList(); var retList = new ArrayList(); for (var opt : optList) { if (opt.isPresent()) { retList.add(opt.get()); } else { return Optional.empty(); } } return Optional.of(retList); } else { return parseOutputOrRemainder(el).map(List::of); } } } }