From 350176d6f91cec7ce23b2cd0a16e45cf2da6b79f Mon Sep 17 00:00:00 2001 From: Micle Date: Sun, 16 Jan 2022 16:46:37 +0000 Subject: [PATCH] Created an ExtendedShapedRecipe and Builder. --- .../item/crafting/ExtendedShapedRecipe.java | 88 +++++++++ .../crafting/ExtendedShapedRecipeBuilder.java | 184 ++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 src/main/java/dev/micle/totemofreviving/item/crafting/ExtendedShapedRecipe.java create mode 100644 src/main/java/dev/micle/totemofreviving/item/crafting/ExtendedShapedRecipeBuilder.java diff --git a/src/main/java/dev/micle/totemofreviving/item/crafting/ExtendedShapedRecipe.java b/src/main/java/dev/micle/totemofreviving/item/crafting/ExtendedShapedRecipe.java new file mode 100644 index 0000000..e8fdf0e --- /dev/null +++ b/src/main/java/dev/micle/totemofreviving/item/crafting/ExtendedShapedRecipe.java @@ -0,0 +1,88 @@ +package dev.micle.totemofreviving.item.crafting; + +import com.google.gson.JsonObject; +import net.minecraft.inventory.CraftingInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipeSerializer; +import net.minecraft.item.crafting.ShapedRecipe; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.registries.ForgeRegistryEntry; + +import javax.annotation.Nullable; +import java.util.function.BiConsumer; +import java.util.function.Function; + +public abstract class ExtendedShapedRecipe extends ShapedRecipe { + private static final IRecipeSerializer BASE_SERIALIZER = IRecipeSerializer.SHAPED_RECIPE; + + private final ShapedRecipe recipe; + + public ExtendedShapedRecipe(ShapedRecipe recipe) { + super(recipe.getId(), recipe.getGroup(), recipe.getRecipeWidth(), recipe.getRecipeHeight(), recipe.getIngredients(), recipe.getResultItem()); + this.recipe = recipe; + } + + public ShapedRecipe getBaseRecipe() { + return this.recipe; + } + + @Override + public abstract IRecipeSerializer getSerializer(); + + @Override + public abstract boolean matches(CraftingInventory inventory, World world); + + @Override + public abstract ItemStack assemble(CraftingInventory inventory); + + public static class Serializer extends ForgeRegistryEntry> implements IRecipeSerializer { + private final Function recipeFactory; + @Nullable private final BiConsumer readJson; + @Nullable private final BiConsumer readBuffer; + @Nullable private final BiConsumer writeBuffer; + + public Serializer(Function recipeFactory, + @Nullable BiConsumer readJson, + @Nullable BiConsumer readBuffer, + @Nullable BiConsumer writeBuffer) { + this.recipeFactory = recipeFactory; + this.readJson = readJson; + this.readBuffer = readBuffer; + this.writeBuffer = writeBuffer; + } + + public static Serializer basic(Function recipeFactory) { + return new Serializer<>(recipeFactory, null, null, null); + } + + @Override + public T fromJson(ResourceLocation recipeId, JsonObject json) { + ShapedRecipe recipe = BASE_SERIALIZER.fromJson(recipeId, json); + T result = this.recipeFactory.apply(recipe); + if (this.readJson != null) { + this.readJson.accept(json, result); + } + return result; + } + + @Override + public T fromNetwork(ResourceLocation recipeId, PacketBuffer buffer) { + ShapedRecipe recipe = BASE_SERIALIZER.fromNetwork(recipeId, buffer); + T result = this.recipeFactory.apply(recipe); + if (this.readBuffer != null) { + this.readBuffer.accept(buffer, result); + } + return result; + } + + @Override + public void toNetwork(PacketBuffer buffer, T recipe) { + BASE_SERIALIZER.toNetwork(buffer, recipe.getBaseRecipe()); + if (this.writeBuffer != null) { + this.writeBuffer.accept(buffer, recipe); + } + } + } +} diff --git a/src/main/java/dev/micle/totemofreviving/item/crafting/ExtendedShapedRecipeBuilder.java b/src/main/java/dev/micle/totemofreviving/item/crafting/ExtendedShapedRecipeBuilder.java new file mode 100644 index 0000000..c315b97 --- /dev/null +++ b/src/main/java/dev/micle/totemofreviving/item/crafting/ExtendedShapedRecipeBuilder.java @@ -0,0 +1,184 @@ +package dev.micle.totemofreviving.item.crafting; + +import com.google.common.collect.Sets; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementRewards; +import net.minecraft.advancements.ICriterionInstance; +import net.minecraft.advancements.IRequirementsStrategy; +import net.minecraft.advancements.criterion.EntityPredicate; +import net.minecraft.advancements.criterion.RecipeUnlockedTrigger; +import net.minecraft.data.IFinishedRecipe; +import net.minecraft.item.Item; +import net.minecraft.item.crafting.IRecipeSerializer; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.tags.ITag; +import net.minecraft.util.IItemProvider; +import net.minecraft.util.ResourceLocation; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Consumer; + +public class ExtendedShapedRecipeBuilder { + private final IRecipeSerializer serializer; + private final Item result; + private final int count; + private final List pattern = new ArrayList<>(); + private final Map key = new LinkedHashMap<>(); + private final Advancement.Builder advancementBuilder = Advancement.Builder.advancement(); + private boolean hasAdvancementCriterion = false; + private String group = ""; + + private ExtendedShapedRecipeBuilder(IRecipeSerializer serializer, IItemProvider result, int count) { + this.serializer = serializer; + this.result = result.asItem(); + this.count = count; + } + + public static ExtendedShapedRecipeBuilder shaped(IRecipeSerializer serializer, IItemProvider result) { + return shaped(serializer, result, 1); + } + + public static ExtendedShapedRecipeBuilder shaped(IRecipeSerializer serializer, IItemProvider result, int count) { + return new ExtendedShapedRecipeBuilder(serializer, result, count); + } + + public ExtendedShapedRecipeBuilder define(Character symbol, ITag tagIn) { + return this.define(symbol, Ingredient.of(tagIn)); + } + + public ExtendedShapedRecipeBuilder define(Character symbol, IItemProvider itemIn) { + return this.define(symbol, Ingredient.of(itemIn)); + } + + public ExtendedShapedRecipeBuilder define(Character symbol, Ingredient ingredientIn) { + if (this.key.containsKey(symbol)) { + throw new IllegalArgumentException("Symbol '" + symbol + "' is already defined!"); + } else if (symbol == ' ') { + throw new IllegalArgumentException("Symbol ' ' (whitespace) is reserved and cannot be defined"); + } else { + this.key.put(symbol, ingredientIn); + return this; + } + } + + public ExtendedShapedRecipeBuilder pattern(String patternIn) { + if (!this.pattern.isEmpty() && patternIn.length() != this.pattern.get(0).length()) { + throw new IllegalArgumentException("Pattern must be the same width on every line!"); + } else { + this.pattern.add(patternIn); + return this; + } + } + + public ExtendedShapedRecipeBuilder unlockedBy(String name, ICriterionInstance criterionIn) { + this.advancementBuilder.addCriterion(name, criterionIn); + this.hasAdvancementCriterion = true; + return this; + } + + public ExtendedShapedRecipeBuilder group(String groupIn) { + this.group = groupIn; + return this; + } + + public void save(Consumer consumer) { + save(consumer, this.result.getRegistryName()); + } + + public void save(Consumer consumer, ResourceLocation id) { + this.validate(id); + if (this.hasAdvancementCriterion && !this.advancementBuilder.getCriteria().isEmpty()) { + this.advancementBuilder.parent(new ResourceLocation("recipes/root")) + .addCriterion("has_the_recipe", new RecipeUnlockedTrigger.Instance(EntityPredicate.AndPredicate.ANY, id)) + .rewards(AdvancementRewards.Builder.recipe(id)) + .requirements(IRequirementsStrategy.OR); + } + ResourceLocation advancementId = new ResourceLocation(id.getNamespace(), "recipes/" + this.result.getItemCategory().getRecipeFolderName() + "/" + id.getPath()); + consumer.accept(new Result(id, this, advancementId)); + } + + private void validate(ResourceLocation id) { + if (this.pattern.isEmpty()) { + throw new IllegalStateException("No pattern is defined for shaped recipe " + id + "!"); + } else { + Set set = Sets.newHashSet(this.key.keySet()); + set.remove(' '); + + for (String s : this.pattern) { + for (int i = 0; i < s.length(); ++i) { + char c0 = s.charAt(i); + if (!this.key.containsKey(c0) && c0 != ' ') { + throw new IllegalStateException("Pattern in recipe " + id + " uses undefined symbol '" + c0 + "'"); + } + + set.remove(c0); + } + } + + if (!set.isEmpty()) { + throw new IllegalStateException("Ingredients are defined but not used in pattern for recipe " + id); + } else if (this.pattern.size() == 1 && this.pattern.get(0).length() == 1) { + throw new IllegalStateException("Shaped recipe " + id + " only takes in a single item - should it be a shapeless recipe instead?"); + } + } + } + + public static class Result implements IFinishedRecipe { + private final ResourceLocation id; + private final ExtendedShapedRecipeBuilder builder; + private final ResourceLocation advancementId; + + public Result(ResourceLocation id, ExtendedShapedRecipeBuilder builder, ResourceLocation advancementId) { + this.id = id; + this.builder = builder; + this.advancementId = advancementId; + } + + @Override + public void serializeRecipeData(JsonObject json) { + if (!builder.group.isEmpty()) { + json.addProperty("group", builder.group); + } + + JsonArray pattern = new JsonArray(); + builder.pattern.forEach(pattern::add); + json.add("pattern", pattern); + + JsonObject key = new JsonObject(); + builder.key.forEach((c, ingredient) -> key.add(String.valueOf(c), ingredient.toJson())); + json.add("key", key); + + JsonObject result = new JsonObject(); + result.addProperty("item", builder.result.getRegistryName().toString()); + if (builder.count > 1) { + result.addProperty("count", builder.count); + } + json.add("result", result); + } + + @Override + public IRecipeSerializer getType() { + return builder.serializer; + } + + @Override + public ResourceLocation getId() { + return id; + } + + @Nullable + @Override + public JsonObject serializeAdvancement() { + return builder.hasAdvancementCriterion ? builder.advancementBuilder.serializeToJson() : null; + } + + @Nullable + @Override + public ResourceLocation getAdvancementId() { + return builder.hasAdvancementCriterion ? advancementId : null; + } + } +}