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 jakarta.annotation.Nonnull; 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.RegistryAccess; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; 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.Supplier; import java.util.stream.Stream; public record SimplifiedRecipe(ItemStack output, List remainder, List> input) { public SimplifiedRecipe(Recipe recipe, RegistryAccess registryAccess) { this( 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() { var outputId = ItemUtils.getId(output); var outputsInRemainder = remainder.stream() .map(ItemUtils::getId) .anyMatch(id -> Objects.equals(id, outputId)); if (outputsInRemainder) { return true; } return 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)); } } } }