From dc7613dd4669393a313b270b55cfaaa3ff8c94a3 Mon Sep 17 00:00:00 2001 From: Uko Kokņevičs Date: Sat, 13 Jan 2024 01:12:12 +0100 Subject: Toposort recipes so it's actually usably fast --- .../mc/eris_alchemy/recipe/SimplifiedRecipe.java | 123 +++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/main/java/lv/enes/mc/eris_alchemy/recipe/SimplifiedRecipe.java (limited to 'src/main/java/lv/enes/mc/eris_alchemy/recipe/SimplifiedRecipe.java') diff --git a/src/main/java/lv/enes/mc/eris_alchemy/recipe/SimplifiedRecipe.java b/src/main/java/lv/enes/mc/eris_alchemy/recipe/SimplifiedRecipe.java new file mode 100644 index 0000000..469ed52 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/recipe/SimplifiedRecipe.java @@ -0,0 +1,123 @@ +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)); + } + } + } +} -- cgit v1.2.3