Created an ExtendedShapedRecipe and Builder.

This commit is contained in:
2022-01-16 16:46:37 +00:00
parent b06b88780f
commit 350176d6f9
2 changed files with 272 additions and 0 deletions

View File

@ -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<ShapedRecipe> 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<T extends ExtendedShapedRecipe> extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<T> {
private final Function<ShapedRecipe, T> recipeFactory;
@Nullable private final BiConsumer<JsonObject, T> readJson;
@Nullable private final BiConsumer<PacketBuffer, T> readBuffer;
@Nullable private final BiConsumer<PacketBuffer, T> writeBuffer;
public Serializer(Function<ShapedRecipe, T> recipeFactory,
@Nullable BiConsumer<JsonObject, T> readJson,
@Nullable BiConsumer<PacketBuffer, T> readBuffer,
@Nullable BiConsumer<PacketBuffer, T> writeBuffer) {
this.recipeFactory = recipeFactory;
this.readJson = readJson;
this.readBuffer = readBuffer;
this.writeBuffer = writeBuffer;
}
public static <S extends ExtendedShapedRecipe> Serializer<S> basic(Function<ShapedRecipe, S> 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);
}
}
}
}

View File

@ -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<String> pattern = new ArrayList<>();
private final Map<Character, Ingredient> 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<Item> 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<IFinishedRecipe> consumer) {
save(consumer, this.result.getRegistryName());
}
public void save(Consumer<IFinishedRecipe> 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<Character> 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;
}
}
}