From 02c31cc86718a9f8e5b3558345c21ac79e867c48 Mon Sep 17 00:00:00 2001 From: LexManos Date: Mon, 17 Sep 2018 03:41:16 -0700 Subject: [PATCH] More work on recipes, custom ingredients, recipes, and constants should work now. --- .../item/crafting/FurnaceRecipe.java.patch | 20 + .../crafting/IRecipeSerializer.java.patch | 15 + .../item/crafting/Ingredient.java.patch | 78 +- .../item/crafting/RecipeManager.java.patch | 20 + .../crafting/RecipeSerializers.java.patch | 69 ++ .../item/crafting/RecipesArmorDyes.java.patch | 2 +- .../item/crafting/ShapedRecipe.java.patch | 46 +- .../item/crafting/ShapelessRecipe.java.patch | 20 +- .../ShulkerBoxColoringRecipe.java.patch | 2 +- .../common/ForgeChunkManager.java | 6 +- .../net/minecraftforge/common/ForgeHooks.java | 13 +- .../java/net/minecraftforge/common/Tags.java | 2 +- .../common/crafting/CompoundIngredient.java | 50 +- .../common/crafting/CraftingHelper.java | 709 +++++------------- ...Factory.java => IConditionSerializer.java} | 6 +- ...actory.java => IIngredientSerializer.java} | 12 +- .../common/crafting/IRecipeFactory.java | 28 - .../common/crafting/IngredientNBT.java | 27 +- .../common/crafting/JsonContext.java | 73 -- .../common/crafting/StackList.java | 67 ++ .../common/crafting/package-info.java | 7 + src/main/resources/forge_at.cfg | 10 +- 22 files changed, 599 insertions(+), 683 deletions(-) create mode 100644 patches/minecraft/net/minecraft/item/crafting/IRecipeSerializer.java.patch create mode 100644 patches/minecraft/net/minecraft/item/crafting/RecipeManager.java.patch create mode 100644 patches/minecraft/net/minecraft/item/crafting/RecipeSerializers.java.patch rename src/main/java/net/minecraftforge/common/crafting/{IConditionFactory.java => IConditionSerializer.java} (89%) rename src/main/java/net/minecraftforge/common/crafting/{IIngredientFactory.java => IIngredientSerializer.java} (80%) delete mode 100644 src/main/java/net/minecraftforge/common/crafting/IRecipeFactory.java delete mode 100644 src/main/java/net/minecraftforge/common/crafting/JsonContext.java create mode 100644 src/main/java/net/minecraftforge/common/crafting/StackList.java create mode 100644 src/main/java/net/minecraftforge/common/crafting/package-info.java diff --git a/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipe.java.patch b/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipe.java.patch index ec5b0533c..a7bce1101 100644 --- a/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipe.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipe.java.patch @@ -25,3 +25,23 @@ public String getGroup() { return this.field_201833_b; } +@@ -75,6 +71,7 @@ + } + + public static class Serializer implements IRecipeSerializer { ++ private static ResourceLocation NAME = new ResourceLocation("minecraft", "smelting"); + public FurnaceRecipe func_199425_a_(ResourceLocation p_199425_1_, JsonObject p_199425_2_) { + String s = JsonUtils.getString(p_199425_2_, "group", ""); + Ingredient ingredient; +@@ -113,8 +110,9 @@ + p_199427_1_.writeVarInt(p_199427_2_.field_201837_f); + } + +- public String func_199567_a() { +- return "smelting"; ++ @Override ++ public ResourceLocation getName() { ++ return NAME; + } + } + } diff --git a/patches/minecraft/net/minecraft/item/crafting/IRecipeSerializer.java.patch b/patches/minecraft/net/minecraft/item/crafting/IRecipeSerializer.java.patch new file mode 100644 index 000000000..c482a80fd --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/IRecipeSerializer.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/item/crafting/IRecipeSerializer.java ++++ b/net/minecraft/item/crafting/IRecipeSerializer.java +@@ -11,5 +11,11 @@ + + void func_199427_a_(PacketBuffer p_199427_1_, T p_199427_2_); + +- String func_199567_a(); ++ @Deprecated //Modders, do not use, this is un-namespaced and thus could cause clashes. ++ default String func_199567_a() { ++ ResourceLocation name = getName(); //To keep compatibility with vanilla, anything in the "minecraft" namespace doesn't get a prefix. ++ return name.getNamespace().equals("minecraft") ? name.getPath() : name.toString(); ++ } ++ ++ ResourceLocation getName(); + } diff --git a/patches/minecraft/net/minecraft/item/crafting/Ingredient.java.patch b/patches/minecraft/net/minecraft/item/crafting/Ingredient.java.patch index bf218d216..4c2f43d3d 100644 --- a/patches/minecraft/net/minecraft/item/crafting/Ingredient.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/Ingredient.java.patch @@ -7,7 +7,7 @@ -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - public final class Ingredient implements Predicate { + public class Ingredient implements Predicate { + //Because Mojang caches things... we need to invalidate them.. so... here we go.. + private static final java.util.Set INSTANCES = java.util.Collections.newSetFromMap(new java.util.WeakHashMap()); + public static void invalidateAll() { @@ -23,7 +23,7 @@ private IntList matchingStacksPacked; + private final boolean isSimple; - private Ingredient(Stream p_i49381_1_) { + protected Ingredient(Stream p_i49381_1_) { this.field_199807_b = (Ingredient.IItemList[])p_i49381_1_.filter(field_209362_b).toArray((p_209360_0_) -> { return new Ingredient.IItemList[p_209360_0_]; }); @@ -35,7 +35,18 @@ public ItemStack[] getMatchingStacks() { this.func_199806_d(); return this.matchingStacks; -@@ -121,6 +127,15 @@ +@@ -95,6 +101,10 @@ + + public final void func_199564_a(PacketBuffer p_199564_1_) { + this.func_199806_d(); ++ if (!this.isVanilla()) { ++ net.minecraftforge.common.crafting.CraftingHelper.write(p_199564_1_, this); ++ return; ++ } + p_199564_1_.writeVarInt(this.matchingStacks.length); + + for(int i = 0; i < this.matchingStacks.length; ++i) { +@@ -121,6 +131,25 @@ return this.field_199807_b.length == 0 && (this.matchingStacks == null || this.matchingStacks.length == 0) && (this.matchingStacksPacked == null || this.matchingStacksPacked.isEmpty()); } @@ -48,10 +59,20 @@ + return isSimple || this == EMPTY; + } + - private static Ingredient func_209357_a(Stream p_209357_0_) { ++ private final boolean isVanilla = this.getClass() == Ingredient.class; ++ public final boolean isVanilla() { ++ return isVanilla; ++ } ++ ++ public net.minecraftforge.common.crafting.IIngredientSerializer getSerializer() { ++ if (!isVanilla()) throw new IllegalStateException("Modderrs must implement Ingredient.getSerializer in their custom Ingredients: " + this); ++ return net.minecraftforge.common.crafting.CraftingHelper.INGREDIENT_VANILLA; ++ } ++ + public static Ingredient func_209357_a(Stream p_209357_0_) { Ingredient ingredient = new Ingredient(p_209357_0_); return ingredient.field_199807_b.length == 0 ? EMPTY : ingredient; -@@ -132,7 +147,6 @@ +@@ -132,7 +161,6 @@ })); } @@ -59,3 +80,50 @@ public static Ingredient fromStacks(ItemStack... stacks) { return func_209357_a(Arrays.stream(stacks).map((p_209356_0_) -> { return new Ingredient.SingleItemList(p_209356_0_); +@@ -144,7 +172,12 @@ + } + + public static Ingredient func_199566_b(PacketBuffer p_199566_0_) { ++ p_199566_0_.markReaderIndex(); + int i = p_199566_0_.readVarInt(); ++ if (i == -1) { ++ p_199566_0_.resetReaderIndex(); ++ return net.minecraftforge.common.crafting.CraftingHelper.getIngredient(p_199566_0_.readResourceLocation(), p_199566_0_); ++ } + return func_209357_a(Stream.generate(() -> { + return new Ingredient.SingleItemList(p_199566_0_.readItemStack()); + }).limit((long)i)); +@@ -152,6 +185,8 @@ + + public static Ingredient func_199802_a(@Nullable JsonElement p_199802_0_) { + if (p_199802_0_ != null && !p_199802_0_.isJsonNull()) { ++ Ingredient ret = net.minecraftforge.common.crafting.CraftingHelper.getIngredient(p_199802_0_); ++ if (ret != null) return ret; + if (p_199802_0_.isJsonObject()) { + return func_209357_a(Stream.of(func_199803_a(p_199802_0_.getAsJsonObject()))); + } else if (p_199802_0_.isJsonArray()) { +@@ -172,6 +207,11 @@ + } + + public static Ingredient.IItemList func_199803_a(JsonObject p_199803_0_) { ++ if (p_199803_0_.has("constant")) { ++ Ingredient.IItemList ret = net.minecraftforge.common.crafting.CraftingHelper.getConstant(new ResourceLocation(JsonUtils.getString(p_199803_0_, "constant"))); ++ if (ret == null) throw new JsonSyntaxException("Ingredient referenced invalid constant: " + JsonUtils.getString(p_199803_0_, "constant")); ++ return ret; ++ } + if (p_199803_0_.has("item") && p_199803_0_.has("tag")) { + throw new JsonParseException("An ingredient entry is either a tag or an item, not both"); + } else if (p_199803_0_.has("item")) { +@@ -195,6 +235,12 @@ + } + } + ++ //Merges several vanilla Ingredients together. As a qwerk of how the json is structured, we can't tell if its a single Ingredient type or multiple so we split per item and remerge here. ++ //Only public for internal use, so we can access a private field in here. ++ public static Ingredient merge(Collection parts) { ++ return func_209357_a(parts.stream().flatMap(i -> Arrays.stream(i.field_199807_b))); ++ } ++ + public interface IItemList { + Collection func_199799_a(); + diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeManager.java.patch b/patches/minecraft/net/minecraft/item/crafting/RecipeManager.java.patch new file mode 100644 index 000000000..9d96a9d2e --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeManager.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/item/crafting/RecipeManager.java ++++ b/net/minecraft/item/crafting/RecipeManager.java +@@ -37,6 +37,8 @@ + this.field_199523_e = false; + this.field_199522_d.clear(); + ++ net.minecraftforge.common.crafting.CraftingHelper.reloadConstants(p_195410_1_); ++ + for(ResourceLocation resourcelocation : p_195410_1_.func_199003_a("recipes", (p_199516_0_) -> { + return p_199516_0_.endsWith(".json"); + })) { +@@ -47,6 +49,8 @@ + JsonObject jsonobject = (JsonObject)JsonUtils.gsonDeserialize(gson, IOUtils.toString(iresource.func_199027_b(), StandardCharsets.UTF_8), JsonObject.class); + if (jsonobject == null) { + field_199521_c.error("Couldn't load recipe {} as it's null or empty", (Object)resourcelocation1); ++ } else if (jsonobject.has("conditions") && !net.minecraftforge.common.crafting.CraftingHelper.processConditions(JsonUtils.getJsonArray(jsonobject, "conditions"))) { ++ field_199521_c.info("Skipping loading recipe {} as it's conditions were not met", resourcelocation1); + } else { + this.func_199509_a(RecipeSerializers.func_199572_a(resourcelocation1, jsonobject)); + } diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeSerializers.java.patch b/patches/minecraft/net/minecraft/item/crafting/RecipeSerializers.java.patch new file mode 100644 index 000000000..b769c35cb --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeSerializers.java.patch @@ -0,0 +1,69 @@ +--- a/net/minecraft/item/crafting/RecipeSerializers.java ++++ b/net/minecraft/item/crafting/RecipeSerializers.java +@@ -10,7 +10,7 @@ + import net.minecraft.util.ResourceLocation; + + public class RecipeSerializers { +- private static final Map> field_199590_p = Maps.>newHashMap(); ++ private static final Map> field_199590_p = Maps.newHashMap(); + public static final IRecipeSerializer field_199575_a = func_199573_a(new ShapedRecipe.Serializer()); + public static final IRecipeSerializer field_199576_b = func_199573_a(new ShapelessRecipe.Serializer()); + public static final RecipeSerializers.SimpleSerializer field_199577_c = func_199573_a(new RecipeSerializers.SimpleSerializer("crafting_special_armordye", RecipesArmorDyes::new)); +@@ -27,18 +27,19 @@ + public static final RecipeSerializers.SimpleSerializer field_199588_n = func_199573_a(new RecipeSerializers.SimpleSerializer("crafting_special_shielddecoration", ShieldRecipes::new)); + public static final RecipeSerializers.SimpleSerializer field_199589_o = func_199573_a(new RecipeSerializers.SimpleSerializer("crafting_special_shulkerboxcoloring", ShulkerBoxColoringRecipe::new)); + public static final IRecipeSerializer field_201839_p = func_199573_a(new FurnaceRecipe.Serializer()); ++ private static final java.util.Set VANILLA_TYPES = new java.util.HashSet<>(field_199590_p.keySet()); //Copy of vanilla so we can keep track for compatibility. + + public static , T extends IRecipe> S func_199573_a(S p_199573_0_) { +- if (field_199590_p.containsKey(p_199573_0_.func_199567_a())) { +- throw new IllegalArgumentException("Duplicate recipe serializer " + p_199573_0_.func_199567_a()); ++ if (field_199590_p.containsKey(p_199573_0_.getName())) { ++ throw new IllegalArgumentException("Duplicate recipe serializer " + p_199573_0_.getName()); + } else { +- field_199590_p.put(p_199573_0_.func_199567_a(), p_199573_0_); ++ field_199590_p.put(p_199573_0_.getName(), p_199573_0_); + return p_199573_0_; + } + } + + public static IRecipe func_199572_a(ResourceLocation p_199572_0_, JsonObject p_199572_1_) { +- String s = JsonUtils.getString(p_199572_1_, "type"); ++ ResourceLocation s = new ResourceLocation(JsonUtils.getString(p_199572_1_, "type")); + IRecipeSerializer irecipeserializer = field_199590_p.get(s); + if (irecipeserializer == null) { + throw new JsonSyntaxException("Invalid or unsupported recipe type '" + s + "'"); +@@ -49,7 +50,7 @@ + + public static IRecipe func_199571_a(PacketBuffer p_199571_0_) { + ResourceLocation resourcelocation = p_199571_0_.readResourceLocation(); +- String s = p_199571_0_.readString(32767); ++ ResourceLocation s = new ResourceLocation(p_199571_0_.readString(32767)); + IRecipeSerializer irecipeserializer = field_199590_p.get(s); + if (irecipeserializer == null) { + throw new IllegalArgumentException("Unknown recipe serializer " + s); +@@ -68,10 +69,12 @@ + public static final class SimpleSerializer implements IRecipeSerializer { + private final String field_199569_a; + private final Function field_199570_b; ++ private final ResourceLocation name; + + public SimpleSerializer(String p_i48188_1_, Function p_i48188_2_) { + this.field_199569_a = p_i48188_1_; + this.field_199570_b = p_i48188_2_; ++ this.name = new ResourceLocation(field_199569_a); + } + + public T func_199425_a_(ResourceLocation p_199425_1_, JsonObject p_199425_2_) { +@@ -85,8 +88,9 @@ + public void func_199427_a_(PacketBuffer p_199427_1_, T p_199427_2_) { + } + +- public String func_199567_a() { +- return this.field_199569_a; ++ @Override ++ public ResourceLocation getName() { ++ return this.name; + } + } + } diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java.patch b/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java.patch index 697877a9b..2c7aaed32 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java.patch @@ -14,7 +14,7 @@ itemstack = itemstack1; } else { - if (!(itemstack1.getItem() instanceof ItemDye)) { -+ if (!itemstack1.getItem().func_206844_a(net.minecraft.tags.ItemTags.DYES)) { ++ if (!itemstack1.getItem().func_206844_a(net.minecraftforge.common.Tags.Items.DYES)) { return false; } diff --git a/patches/minecraft/net/minecraft/item/crafting/ShapedRecipe.java.patch b/patches/minecraft/net/minecraft/item/crafting/ShapedRecipe.java.patch index 5ef53bfda..ba538defd 100644 --- a/patches/minecraft/net/minecraft/item/crafting/ShapedRecipe.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/ShapedRecipe.java.patch @@ -7,7 +7,8 @@ -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - public class ShapedRecipe implements IRecipe { +-public class ShapedRecipe implements IRecipe { ++public class ShapedRecipe implements IRecipe, net.minecraftforge.common.crafting.IShapedRecipe { + static int MAX_WIDTH = 3; + static int MAX_HEIGHT = 3; + /** @@ -40,7 +41,28 @@ public boolean canFit(int width, int height) { return width >= this.recipeWidth && height >= this.recipeHeight; } -@@ -202,15 +211,15 @@ +@@ -117,10 +126,20 @@ + return this.recipeWidth; + } + ++ @Override ++ public int getRecipeWidth() { ++ return getWidth(); ++ } ++ + public int getHeight() { + return this.recipeHeight; + } + ++ @Override ++ public int getRecipeHeight() { ++ return getHeight(); ++ } ++ + private static NonNullList deserializeIngredients(String[] pattern, Map keys, int patternWidth, int patternHeight) { + NonNullList nonnulllist = NonNullList.withSize(patternWidth * patternHeight, Ingredient.EMPTY); + Set set = Sets.newHashSet(keys.keySet()); +@@ -202,15 +221,15 @@ private static String[] patternFromJson(JsonArray jsonArr) { String[] astring = new String[jsonArr.size()]; @@ -60,3 +82,23 @@ } if (i > 0 && astring[0].length() != s.length()) { +@@ -257,6 +276,7 @@ + } + + public static class Serializer implements IRecipeSerializer { ++ private static final ResourceLocation NAME = new ResourceLocation("minecraft", "crafting_shaped"); + public ShapedRecipe func_199425_a_(ResourceLocation p_199425_1_, JsonObject p_199425_2_) { + String s = JsonUtils.getString(p_199425_2_, "group", ""); + Map map = ShapedRecipe.deserializeKey(JsonUtils.getJsonObject(p_199425_2_, "key")); +@@ -268,8 +288,9 @@ + return new ShapedRecipe(p_199425_1_, s, i, j, nonnulllist, itemstack); + } + +- public String func_199567_a() { +- return "crafting_shaped"; ++ @Override ++ public ResourceLocation getName() { ++ return NAME; + } + + public ShapedRecipe func_199426_a_(ResourceLocation p_199426_1_, PacketBuffer p_199426_2_) { diff --git a/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipe.java.patch b/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipe.java.patch index 5f1851fd2..ced9d87d4 100644 --- a/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipe.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipe.java.patch @@ -56,7 +56,7 @@ } } -@@ -74,7 +77,6 @@ +@@ -74,19 +77,19 @@ return this.recipeOutput.copy(); } @@ -64,7 +64,11 @@ public boolean canFit(int width, int height) { return width * height >= this.recipeItems.size(); } -@@ -85,8 +87,8 @@ + + public static class Serializer implements IRecipeSerializer { ++ private static final ResourceLocation NAME = new ResourceLocation("minecraft", "crafting_shapeless"); + public ShapelessRecipe func_199425_a_(ResourceLocation p_199425_1_, JsonObject p_199425_2_) { + String s = JsonUtils.getString(p_199425_2_, "group", ""); NonNullList nonnulllist = func_199568_a(JsonUtils.getJsonArray(p_199425_2_, "ingredients")); if (nonnulllist.isEmpty()) { throw new JsonParseException("No ingredients for shapeless recipe"); @@ -75,3 +79,15 @@ } else { ItemStack itemstack = ShapedRecipe.func_199798_a(JsonUtils.getJsonObject(p_199425_2_, "result")); return new ShapelessRecipe(p_199425_1_, s, itemstack, nonnulllist); +@@ -106,8 +109,9 @@ + return nonnulllist; + } + +- public String func_199567_a() { +- return "crafting_shapeless"; ++ @Override ++ public ResourceLocation getName() { ++ return NAME; + } + + public ShapelessRecipe func_199426_a_(ResourceLocation p_199426_1_, PacketBuffer p_199426_2_) { diff --git a/patches/minecraft/net/minecraft/item/crafting/ShulkerBoxColoringRecipe.java.patch b/patches/minecraft/net/minecraft/item/crafting/ShulkerBoxColoringRecipe.java.patch index 58b20f27c..b8475105d 100644 --- a/patches/minecraft/net/minecraft/item/crafting/ShulkerBoxColoringRecipe.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/ShulkerBoxColoringRecipe.java.patch @@ -14,7 +14,7 @@ ++i; } else { - if (!(itemstack.getItem() instanceof ItemDye)) { -+ if (!itemstack.getItem().func_206844_a(net.minecraft.tags.ItemTags.DYES)) { ++ if (!itemstack.getItem().func_206844_a(net.minecraftforge.common.Tags.Items.DYES)) { return false; } diff --git a/src/main/java/net/minecraftforge/common/ForgeChunkManager.java b/src/main/java/net/minecraftforge/common/ForgeChunkManager.java index f56de6f9f..256e7236b 100644 --- a/src/main/java/net/minecraftforge/common/ForgeChunkManager.java +++ b/src/main/java/net/minecraftforge/common/ForgeChunkManager.java @@ -939,8 +939,8 @@ public class ForgeChunkManager { ticket.setInteger("chunkX", MathHelper.floor(tick.entity.chunkCoordX)); ticket.setInteger("chunkZ", MathHelper.floor(tick.entity.chunkCoordZ)); - ticket.setLong("PersistentIDMSB", tick.entity.getPersistentID().getMostSignificantBits()); - ticket.setLong("PersistentIDLSB", tick.entity.getPersistentID().getLeastSignificantBits()); + ticket.setLong("PersistentIDMSB", tick.entity.getUniqueID().getMostSignificantBits()); + ticket.setLong("PersistentIDLSB", tick.entity.getUniqueID().getLeastSignificantBits()); tickets.add(ticket); } else if (tick.ticketType != Type.ENTITY) @@ -966,7 +966,7 @@ public class ForgeChunkManager static void loadEntity(Entity entity) { - UUID id = entity.getPersistentID(); + UUID id = entity.getUniqueID(); Ticket tick = pendingEntities.get(id); if (tick != null) { diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index a3efc1e85..26dc2a251 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -1014,7 +1014,7 @@ public class ForgeHooks if (stack.getItem().hasContainerItem(stack)) { stack = stack.getItem().getContainerItem(stack); - if (!stack.isEmpty() && stack.isItemStackDamageable() && stack.getMetadata() > stack.getMaxDamage()) + if (!stack.isEmpty() && stack.isItemStackDamageable() && stack.getItemDamage() > stack.getMaxDamage()) { ForgeEventFactory.onPlayerDestroyItem(craftingPlayer.get(), stack, null); return ItemStack.EMPTY; @@ -1035,10 +1035,10 @@ public class ForgeHooks { filled = ((IFluidBlock)block).getFilledPercentage(entity.world, pos); } - else if (block instanceof BlockLiquid) + /*else if (block instanceof BlockLiquid) { filled = 1.0 - (BlockLiquid.getLiquidHeightPercent(block.getMetaFromState(state)) - (1.0 / 9.0)); - } + }*/ if (filled < 0) { @@ -1271,13 +1271,6 @@ public class ForgeHooks public static LootEntry deserializeJsonLootEntry(String type, JsonObject json, int weight, int quality, LootCondition[] conditions){ return null; } public static String getLootEntryType(LootEntry entry){ return null; } //Companion to above function - /** @deprecated use {@link ForgeEventFactory#onProjectileImpact(EntityThrowable, RayTraceResult)} */ - @Deprecated // TODO: remove (1.13) - public static boolean onThrowableImpact(EntityThrowable throwable, RayTraceResult ray) - { - return ForgeEventFactory.onProjectileImpact(throwable, ray); - } - public static boolean onCropsGrowPre(World worldIn, BlockPos pos, IBlockState state, boolean def) { BlockEvent ev = new BlockEvent.CropGrowEvent.Pre(worldIn,pos,state); diff --git a/src/main/java/net/minecraftforge/common/Tags.java b/src/main/java/net/minecraftforge/common/Tags.java index 1605fd329..0fe1632c1 100644 --- a/src/main/java/net/minecraftforge/common/Tags.java +++ b/src/main/java/net/minecraftforge/common/Tags.java @@ -106,7 +106,7 @@ public class Tags public static final Tag GEMS_PRISMARINE = tag("gems/prismarine"); public static final Tag GEMS_QUARRTZ = tag("gems/quartz"); public static final Tag INGOTS = tag("ingots"); - public static final Tag INGOTS_BRICK = tag("ingots/brrick"); + public static final Tag INGOTS_BRICK = tag("ingots/brick"); public static final Tag INGOTS_GOLD = tag("ingots/gold"); public static final Tag INGOTS_IRON = tag("ingots/iron"); public static final Tag INGOTS_NETHER_BRICK = tag("ingots/nether_brick"); diff --git a/src/main/java/net/minecraftforge/common/crafting/CompoundIngredient.java b/src/main/java/net/minecraftforge/common/crafting/CompoundIngredient.java index 05f3067e7..a4faa6214 100644 --- a/src/main/java/net/minecraftforge/common/crafting/CompoundIngredient.java +++ b/src/main/java/net/minecraftforge/common/crafting/CompoundIngredient.java @@ -22,17 +22,22 @@ package net.minecraftforge.common.crafting; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.google.common.collect.Lists; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntComparators; import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.Ingredient; +import net.minecraft.network.PacketBuffer; public class CompoundIngredient extends Ingredient { @@ -41,15 +46,11 @@ public class CompoundIngredient extends Ingredient private IntList itemIds; private final boolean isSimple; - protected CompoundIngredient(Collection children) + protected CompoundIngredient(Stream children) { - super(0); - this.children = children; - - boolean simple = true; - for (Ingredient child : children) - simple &= child.isSimple(); - this.isSimple = simple; + super(Stream.of()); + this.isSimple = children.allMatch(Ingredient::isSimple); + this.children = children.collect(Collectors.toList()); } @Override @@ -84,16 +85,12 @@ public class CompoundIngredient extends Ingredient } @Override - public boolean apply(@Nullable ItemStack target) + public boolean test(@Nullable ItemStack target) { if (target == null) return false; - for (Ingredient child : children) - if (child.apply(target)) - return true; - - return false; + return children.stream().anyMatch(c -> c.test(target)); } @Override @@ -109,10 +106,33 @@ public class CompoundIngredient extends Ingredient { return isSimple; } - + @Nonnull public Collection getChildren() { return Collections.unmodifiableCollection(this.children); } + + public static class Serializer implements IIngredientSerializer + { + @Override + public CompoundIngredient parse(PacketBuffer buffer) + { + return new CompoundIngredient(Stream.generate(() -> Ingredient.func_199566_b(buffer)).limit(buffer.readVarInt())); + } + + @Override + public CompoundIngredient parse(JsonObject json) + { + throw new JsonSyntaxException("CompountIngredient should not be directly referenced in json, just use an array of ingredients."); + } + + @Override + public void write(PacketBuffer buffer, CompoundIngredient ingredient) + { + buffer.writeVarInt(ingredient.children.size()); + ingredient.children.forEach(c -> c.func_199564_a(buffer)); + } + + } } diff --git a/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java b/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java index f937ddfe8..306645dcb 100644 --- a/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java +++ b/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java @@ -19,31 +19,21 @@ package net.minecraftforge.common.crafting; -import java.io.BufferedReader; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BooleanSupplier; +import java.util.stream.Stream; -import javax.annotation.Nonnull; +import javax.annotation.Nullable; -import net.minecraft.client.util.RecipeBookClient; -import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.DistExecutor; -import net.minecraftforge.fml.loading.moddiscovery.ModFile; -import org.apache.commons.io.FilenameUtils; - +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; @@ -51,92 +41,140 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; +import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.minecraft.block.Block; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.Ingredient; -import net.minecraft.item.crafting.ShapedRecipes; -import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraft.item.crafting.Ingredient.IItemList; import net.minecraft.nbt.JsonToNBT; -import net.minecraft.nbt.NBTException; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.PacketBuffer; +import net.minecraft.resources.IResource; +import net.minecraft.resources.IResourceManager; import net.minecraft.util.JsonUtils; -import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.oredict.OreDictionary; -import net.minecraftforge.oredict.OreIngredient; -import net.minecraftforge.oredict.ShapedOreRecipe; -import net.minecraftforge.oredict.ShapelessOreRecipe; import net.minecraftforge.registries.ForgeRegistries; -import net.minecraftforge.registries.ForgeRegistry; -import net.minecraftforge.registries.GameData; -import net.minecraftforge.registries.RegistryManager; + +import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -public class CraftingHelper { - - private static final boolean DEBUG_LOAD_MINECRAFT = false; +public class CraftingHelper +{ private static final Logger LOGGER = LogManager.getLogger(); private static final Marker CRAFTHELPER = MarkerManager.getMarker("CRAFTHELPER"); private static Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); - private static Map conditions = new HashMap<>(); - private static Map ingredients = new HashMap<>(); - private static Map recipes = new HashMap<>(); + private static final Map conditions = new HashMap<>(); + private static final BiMap> ingredients = HashBiMap.create(); + private static Map constants = new HashMap<>(); - static { - init(); - } + public static final IConditionSerializer CONDITION_MOD_LOADED = condition("mod_loaded", json -> { + String modid = JsonUtils.getString(json, "modid"); + return () -> ModList.get().isLoaded(modid); + }); + public static final IConditionSerializer CONDITION_ITEM_EXISTS = condition("item_exists", json -> { + String itemName = JsonUtils.getString(json, "item"); + return () -> ForgeRegistries.ITEMS.containsKey(new ResourceLocation(itemName)); + }); + public static final IConditionSerializer CONDITION_NOT = condition("not", json -> { + BooleanSupplier child = CraftingHelper.getCondition(JsonUtils.getJsonObject(json, "value")); + return () -> !child.getAsBoolean(); + }); + public static final IConditionSerializer CONDITION_OR = condition("or", json -> { + JsonArray values = JsonUtils.getJsonArray(json, "values"); + List children = Lists.newArrayList(); + for (JsonElement j : values) + { + if (!j.isJsonObject()) + throw new JsonSyntaxException("Or condition values must be an array of JsonObjects"); + children.add(CraftingHelper.getCondition(j.getAsJsonObject())); + } + return () -> children.stream().anyMatch(BooleanSupplier::getAsBoolean); + }); + public static final IConditionSerializer CONDITION_AND = condition("and", json -> { + JsonArray values = JsonUtils.getJsonArray(json, "values"); + List children = Lists.newArrayList(); + for (JsonElement j : values) + { + if (!j.isJsonObject()) + throw new JsonSyntaxException("And condition values must be an array of JsonObjects"); + children.add(CraftingHelper.getCondition(j.getAsJsonObject())); + } + return () -> children.stream().allMatch(c -> c.getAsBoolean()); + }); + public static final IConditionSerializer CONDITION_FALSE = condition("false", json -> () -> false); - public static void register(ResourceLocation key, IConditionFactory factory) + public static final IIngredientSerializer INGREDIENT_NBT = register(new ResourceLocation("forge", "nbt"), new IngredientNBT.Serializer()); + public static final IIngredientSerializer INGREDIENT_COMPOUND = register(new ResourceLocation("forge", "nbt"), new CompoundIngredient.Serializer()); + public static final IIngredientSerializer INGREDIENT_VANILLA = register(new ResourceLocation("minecraft", "item"), new IIngredientSerializer() { + public Ingredient parse(PacketBuffer buffer) { + return Ingredient.func_209357_a(Stream.generate(() -> new Ingredient.SingleItemList(buffer.readItemStack())).limit(buffer.readVarInt())); + } + + public Ingredient parse(JsonObject json) { + return Ingredient.func_209357_a(Stream.of(Ingredient.func_199803_a(json))); + } + + public void write(PacketBuffer buffer, Ingredient ingredient) { + ItemStack[] items = ingredient.getMatchingStacks(); + buffer.writeVarInt(items.length); + + for (ItemStack stack : items) + buffer.writeItemStack(stack); + } + }); + + public static IConditionSerializer register(ResourceLocation key, IConditionSerializer serializer) { if (conditions.containsKey(key)) - throw new IllegalStateException("Duplicate recipe condition factory: " + key); - conditions.put(key, factory); + throw new IllegalStateException("Duplicate recipe condition serializer: " + key); + conditions.put(key, serializer); + return serializer; } - public static void register(ResourceLocation key, IRecipeFactory factory) - { - if (recipes.containsKey(key)) - throw new IllegalStateException("Duplicate recipe factory: " + key); - recipes.put(key, factory); - } - public static void register(ResourceLocation key, IIngredientFactory factory) + public static IIngredientSerializer register(ResourceLocation key, IIngredientSerializer serializer) { if (ingredients.containsKey(key)) - throw new IllegalStateException("Duplicate recipe ingredient factory: " + key); - ingredients.put(key, factory); + throw new IllegalStateException("Duplicate recipe ingredient serializer: " + key); + if (ingredients.containsValue(serializer)) + throw new IllegalStateException("Duplicate recipe ingredient serializer: " + key + " " + serializer); + ingredients.put(key, serializer); + return serializer; } + private static IConditionSerializer condition(String name, IConditionSerializer serializer) { + return register(new ResourceLocation("forge", name), serializer); + } - public static Ingredient getIngredient(Object obj) + public static void write(PacketBuffer buffer, T ingredient) { - if (obj instanceof Ingredient) - return (Ingredient)obj; - else if (obj instanceof ItemStack) - return Ingredient.fromStacks(((ItemStack)obj).copy()); - else if (obj instanceof Item) - return Ingredient.fromItem((Item)obj); - else if (obj instanceof Block) - return Ingredient.fromStacks(new ItemStack((Block)obj, 1, OreDictionary.WILDCARD_VALUE)); - else if (obj instanceof String) - return new OreIngredient((String)obj); - else if (obj instanceof JsonElement) - throw new IllegalArgumentException("JsonObjects must use getIngredient(JsonObject, JsonContext)"); - - return null; + @SuppressWarnings("unchecked") //I wonder if there is a better way generic wise... + IIngredientSerializer serializer = (IIngredientSerializer)ingredient.getSerializer(); + ResourceLocation key = ingredients.inverse().get(serializer); + if (key == null) + throw new IllegalArgumentException("Tried to serialize unregistered Ingredient: " + ingredient + " " + serializer); + if (serializer != INGREDIENT_VANILLA) + { + buffer.writeVarInt(-1); //Marker to know there is a custom ingredient + buffer.writeResourceLocation(key); + } + serializer.write(buffer, ingredient); } - @Nonnull - public static Ingredient getIngredient(JsonElement json, JsonContext context) + public static Ingredient getIngredient(ResourceLocation type, PacketBuffer buffer) + { + IIngredientSerializer serializer = ingredients.get(type); + if (serializer == null) + throw new IllegalArgumentException("Can not deserialize unknown Ingredient type: " + type); + return serializer.parse(buffer); + } + + public static Ingredient getIngredient(JsonElement json) { if (json == null || json.isJsonNull()) throw new JsonSyntaxException("Json cannot be null"); - if (context == null) - throw new IllegalArgumentException("getIngredient Context cannot be null"); if (json.isJsonArray()) { @@ -144,23 +182,16 @@ public class CraftingHelper { List vanilla = Lists.newArrayList(); json.getAsJsonArray().forEach((ele) -> { - Ingredient ing = CraftingHelper.getIngredient(ele, context); + Ingredient ing = CraftingHelper.getIngredient(ele); - if (ing.getClass() == Ingredient.class) - { - //Vanilla, Due to how we read it splits each itemstack, so we pull out to re-merge later + if (ing.getClass() == Ingredient.class) //Vanilla, Due to how we read it splits each itemstack, so we pull out to re-merge later vanilla.add(ing); - } else - { ingredients.add(ing); - } }); if (!vanilla.isEmpty()) - { ingredients.add(Ingredient.merge(vanilla)); - } if (ingredients.size() == 0) throw new JsonSyntaxException("Item array cannot be empty, at least one item must be defined"); @@ -168,7 +199,7 @@ public class CraftingHelper { if (ingredients.size() == 1) return ingredients.get(0); - return new CompoundIngredient(ingredients); + return new CompoundIngredient(ingredients.stream()); } if (!json.isJsonObject()) @@ -176,42 +207,27 @@ public class CraftingHelper { JsonObject obj = (JsonObject)json; - String type = context.appendModId(JsonUtils.getString(obj, "type", "minecraft:item")); + String type = JsonUtils.getString(obj, "type", "minecraft:item"); if (type.isEmpty()) throw new JsonSyntaxException("Ingredient type can not be an empty string"); - if (type.equals("minecraft:item")) - { - String item = JsonUtils.getString(obj, "item"); - if (item.startsWith("#")) - { - Ingredient constant = context.getConstant(item.substring(1)); - if (constant == null) - throw new JsonSyntaxException("Ingredient referenced invalid constant: " + item); - return constant; - } - } - - IIngredientFactory factory = ingredients.get(new ResourceLocation(type)); - if (factory == null) + IIngredientSerializer serializer = ingredients.get(new ResourceLocation(type)); + if (serializer == null) throw new JsonSyntaxException("Unknown ingredient type: " + type); - return factory.parse(context, obj); + return serializer.parse(obj); } - public static ItemStack getItemStack(JsonObject json, JsonContext context) + public static ItemStack getItemStack(JsonObject json, boolean readNBT) { - String itemName = context.appendModId(JsonUtils.getString(json, "item")); + String itemName = JsonUtils.getString(json, "item"); Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(itemName)); if (item == null) throw new JsonSyntaxException("Unknown item '" + itemName + "'"); - if (item.getHasSubtypes() && !json.has("data")) - throw new JsonParseException("Missing data for item '" + itemName + "'"); - - if (json.has("nbt")) + if (readNBT && json.has("nbt")) { // Lets hope this works? Needs test try @@ -233,138 +249,19 @@ public class CraftingHelper { tmp.setTag("tag", nbt); tmp.setString("id", itemName); tmp.setInteger("Count", JsonUtils.getInt(json, "count", 1)); - tmp.setInteger("Damage", JsonUtils.getInt(json, "data", 0)); - return new ItemStack(tmp); + return ItemStack.func_199557_a(tmp); } - catch (NBTException e) + catch (CommandSyntaxException e) { throw new JsonSyntaxException("Invalid NBT Entry: " + e.toString()); } } - return new ItemStack(item, JsonUtils.getInt(json, "count", 1), JsonUtils.getInt(json, "data", 0)); + return new ItemStack(item, JsonUtils.getInt(json, "count", 1)); } - - public static ItemStack getItemStackBasic(JsonObject json, JsonContext context) - { - String itemName = context.appendModId(JsonUtils.getString(json, "item")); - - Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(itemName)); - - if (item == null) - throw new JsonSyntaxException("Unknown item '" + itemName + "'"); - - if (item.getHasSubtypes() && !json.has("data")) - throw new JsonParseException("Missing data for item '" + itemName + "'"); - - return new ItemStack(item, 1, JsonUtils.getInt(json, "data", 0)); - } - - public static class ShapedPrimer { - public int height, width; - public boolean mirrored = true; - public NonNullList input; - } - - public static ShapedPrimer parseShaped(Object... recipe) - { - ShapedPrimer ret = new ShapedPrimer(); - String shape = ""; - int idx = 0; - - if (recipe[idx] instanceof Boolean) - { - ret.mirrored = (Boolean)recipe[idx]; - if (recipe[idx+1] instanceof Object[]) - recipe = (Object[])recipe[idx+1]; - else - idx = 1; - } - - if (recipe[idx] instanceof String[]) - { - String[] parts = ((String[])recipe[idx++]); - - for (String s : parts) - { - ret.width = s.length(); - shape += s; - } - - ret.height = parts.length; - } - else - { - while (recipe[idx] instanceof String) - { - String s = (String)recipe[idx++]; - shape += s; - ret.width = s.length(); - ret.height++; - } - } - - if (ret.width * ret.height != shape.length() || shape.length() == 0) - { - String err = "Invalid shaped recipe: "; - for (Object tmp : recipe) - { - err += tmp + ", "; - } - throw new RuntimeException(err); - } - - HashMap itemMap = Maps.newHashMap(); - itemMap.put(' ', Ingredient.EMPTY); - - for (; idx < recipe.length; idx += 2) - { - Character chr = (Character)recipe[idx]; - Object in = recipe[idx + 1]; - Ingredient ing = CraftingHelper.getIngredient(in); - - if (' ' == chr.charValue()) - throw new JsonSyntaxException("Invalid key entry: ' ' is a reserved symbol."); - - if (ing != null) - { - itemMap.put(chr, ing); - } - else - { - String err = "Invalid shaped ore recipe: "; - for (Object tmp : recipe) - { - err += tmp + ", "; - } - throw new RuntimeException(err); - } - } - - ret.input = NonNullList.withSize(ret.width * ret.height, Ingredient.EMPTY); - - Set keys = Sets.newHashSet(itemMap.keySet()); - keys.remove(' '); - - int x = 0; - for (char chr : shape.toCharArray()) - { - Ingredient ing = itemMap.get(chr); - if (ing == null) - throw new IllegalArgumentException("Pattern references symbol '" + chr + "' but it's not defined in the key"); - ret.input.set(x++, ing); - keys.remove(chr); - } - - if (!keys.isEmpty()) - throw new IllegalArgumentException("Key defines symbols that aren't used in pattern: " + keys); - - return ret; - } - - public static boolean processConditions(JsonArray conditions, JsonContext context) + public static boolean processConditions(JsonArray conditions) { for (int x = 0; x < conditions.size(); x++) { @@ -372,334 +269,82 @@ public class CraftingHelper { throw new JsonSyntaxException("Conditions must be an array of JsonObjects"); JsonObject json = conditions.get(x).getAsJsonObject(); - BooleanSupplier cond = CraftingHelper.getCondition(json, context); + BooleanSupplier cond = CraftingHelper.getCondition(json); if (!cond.getAsBoolean()) return false; } return true; } - public static BooleanSupplier getCondition(JsonObject json, JsonContext context) + public static BooleanSupplier getCondition(JsonObject json) { - ResourceLocation type = new ResourceLocation(context.appendModId(JsonUtils.getString(json, "type"))); - IConditionFactory factory = conditions.get(type); - if (factory == null) + ResourceLocation type = new ResourceLocation(JsonUtils.getString(json, "type")); + IConditionSerializer serrializer = conditions.get(type); + if (serrializer == null) throw new JsonSyntaxException("Unknown condition type: " + type.toString()); - return factory.parse(context, json); + return serrializer.parse(json); } - public static IRecipe getRecipe(JsonObject json, JsonContext context) - { - if (json == null || json.isJsonNull()) - throw new JsonSyntaxException("Json cannot be null"); - if (context == null) - throw new IllegalArgumentException("getRecipe Context cannot be null"); - - String type = context.appendModId(JsonUtils.getString(json, "type")); - if (type.isEmpty()) - throw new JsonSyntaxException("Recipe type can not be an empty string"); - - IRecipeFactory factory = recipes.get(new ResourceLocation(type)); - if (factory == null) - throw new JsonSyntaxException("Unknown recipe type: " + type); - - return factory.parse(context, json); + @Nullable + public static IItemList getConstant(ResourceLocation key) { + return constants.get(key); } + public static void reloadConstants(IResourceManager manager) { + Map ret = new HashMap<>(); + for(ResourceLocation key : manager.func_199003_a("recipes", filename -> filename.equals("_constants.json"))) + { + String path = key.getPath(); + if (!path.equals("rrecipes/_constants.json")) //Top level only + continue; - //======================================================= - // INTERNAL - //======================================================= - - private static void init() - { - conditions.clear(); - ingredients.clear(); - recipes.clear(); - - registerC("forge:mod_loaded", (context, json) -> { - String modid = JsonUtils.getString(json, "modid"); - return () -> ModList.get().isLoaded(modid); - }); - registerC("minecraft:item_exists", (context, json) -> { - String itemName = context.appendModId(JsonUtils.getString(json, "item")); - return () -> ForgeRegistries.ITEMS.containsKey(new ResourceLocation(itemName)); - }); - registerC("forge:not", (context, json) -> { - BooleanSupplier child = CraftingHelper.getCondition(JsonUtils.getJsonObject(json, "value"), context); - return () -> !child.getAsBoolean(); - }); - registerC("forge:or", (context, json) -> { - JsonArray values = JsonUtils.getJsonArray(json, "values"); - List children = Lists.newArrayList(); - for (JsonElement j : values) + try (IResource iresource = manager.func_199002_a(key)) { - if (!j.isJsonObject()) - throw new JsonSyntaxException("Or condition values must be an array of JsonObjects"); - children.add(CraftingHelper.getCondition(j.getAsJsonObject(), context)); - } - return () -> children.stream().anyMatch(BooleanSupplier::getAsBoolean); - }); - registerC("forge:and", (context, json) -> { - JsonArray values = JsonUtils.getJsonArray(json, "values"); - List children = Lists.newArrayList(); - for (JsonElement j : values) - { - if (!j.isJsonObject()) - throw new JsonSyntaxException("And condition values must be an array of JsonObjects"); - children.add(CraftingHelper.getCondition(j.getAsJsonObject(), context)); - } - return () -> children.stream().allMatch(c -> c.getAsBoolean()); - }); - registerC("forge:false", (context, json) -> { - return () -> false; - }); - - registerR("minecraft:crafting_shaped", (context, json) -> { - String group = JsonUtils.getString(json, "group", ""); - //if (!group.isEmpty() && group.indexOf(':') == -1) - // group = context.getModId() + ":" + group; - - Map ingMap = Maps.newHashMap(); - for (Entry entry : JsonUtils.getJsonObject(json, "key").entrySet()) - { - if (entry.getKey().length() != 1) - throw new JsonSyntaxException("Invalid key entry: '" + entry.getKey() + "' is an invalid symbol (must be 1 character only)."); - if (" ".equals(entry.getKey())) - throw new JsonSyntaxException("Invalid key entry: ' ' is a reserved symbol."); - - ingMap.put(entry.getKey().toCharArray()[0], CraftingHelper.getIngredient(entry.getValue(), context)); - } - ingMap.put(' ', Ingredient.EMPTY); - - JsonArray patternJ = JsonUtils.getJsonArray(json, "pattern"); - - if (patternJ.size() == 0) - throw new JsonSyntaxException("Invalid pattern: empty pattern not allowed"); - if (patternJ.size() > 3) - throw new JsonSyntaxException("Invalid pattern: too many rows, 3 is maximum"); - - String[] pattern = new String[patternJ.size()]; - for (int x = 0; x < pattern.length; ++x) - { - String line = JsonUtils.getString(patternJ.get(x), "pattern[" + x + "]"); - if (line.length() > 3) - throw new JsonSyntaxException("Invalid pattern: too many columns, 3 is maximum"); - if (x > 0 && pattern[0].length() != line.length()) - throw new JsonSyntaxException("Invalid pattern: each row must be the same width"); - pattern[x] = line; - } - - NonNullList input = NonNullList.withSize(pattern[0].length() * pattern.length, Ingredient.EMPTY); - Set keys = Sets.newHashSet(ingMap.keySet()); - keys.remove(' '); - - int x = 0; - for (String line : pattern) - { - for (char chr : line.toCharArray()) + JsonObject[] elements = JsonUtils.gsonDeserialize(GSON, IOUtils.toString(iresource.func_199027_b(), StandardCharsets.UTF_8), JsonObject[].class); + for (int x = 0; x < elements.length; x++) { - Ingredient ing = ingMap.get(chr); - if (ing == null) - throw new JsonSyntaxException("Pattern references symbol '" + chr + "' but it's not defined in the key"); - input.set(x++, ing); - keys.remove(chr); - } - } - - if (!keys.isEmpty()) - throw new JsonSyntaxException("Key defines symbols that aren't used in pattern: " + keys); - - ItemStack result = CraftingHelper.getItemStack(JsonUtils.getJsonObject(json, "result"), context); - return new ShapedRecipes(group, pattern[0].length(), pattern.length, input, result); - }); - registerR("minecraft:crafting_shapeless", (context, json) -> { - String group = JsonUtils.getString(json, "group", ""); - - NonNullList ings = NonNullList.create(); - for (JsonElement ele : JsonUtils.getJsonArray(json, "ingredients")) - ings.add(CraftingHelper.getIngredient(ele, context)); - - if (ings.isEmpty()) - throw new JsonParseException("No ingredients for shapeless recipe"); - if (ings.size() > 9) - throw new JsonParseException("Too many ingredients for shapeless recipe"); - - ItemStack itemstack = CraftingHelper.getItemStack(JsonUtils.getJsonObject(json, "result"), context); - return new ShapelessRecipes(group, itemstack, ings); - }); - registerR("forge:ore_shaped", ShapedOreRecipe::factory); - registerR("forge:ore_shapeless", ShapelessOreRecipe::factory); - - registerI("minecraft:item", (context, json) -> Ingredient.fromStacks(CraftingHelper.getItemStackBasic(json, context))); - registerI("minecraft:empty", (context, json) -> Ingredient.EMPTY); - registerI("minecraft:item_nbt", (context, json) -> new IngredientNBT(CraftingHelper.getItemStack(json, context))); - registerI("forge:ore_dict", (context, json) -> new OreIngredient(JsonUtils.getString(json, "ore"))); - } - - private static void registerC(String name, IConditionFactory fac) { - register(new ResourceLocation(name), fac); - } - private static void registerR(String name, IRecipeFactory fac) { - register(new ResourceLocation(name), fac); - } - private static void registerI(String name, IIngredientFactory fac) { - register(new ResourceLocation(name), fac); - } - - private static void loadFactories(JsonObject json, JsonContext context) - { - if (json.has("ingredients")) - { - for (Entry entry : JsonUtils.getJsonObject(json, "ingredients").entrySet()) - { - ResourceLocation key = new ResourceLocation(context.getModId(), entry.getKey()); - String clsName = JsonUtils.getString(entry.getValue(), "ingredients[" + entry.getValue() + "]"); - register(key, getClassInstance(clsName, IIngredientFactory.class)); - } - } - - if (json.has("recipes")) - { - for (Entry entry : JsonUtils.getJsonObject(json, "recipes").entrySet()) - { - ResourceLocation key = new ResourceLocation(context.getModId(), entry.getKey()); - String clsName = JsonUtils.getString(entry.getValue(), "recipes[" + entry.getValue() + "]"); - register(key, getClassInstance(clsName, IRecipeFactory.class)); - } - } - - if (json.has("conditions")) - { - for (Entry entry : JsonUtils.getJsonObject(json, "conditions").entrySet()) - { - ResourceLocation key = new ResourceLocation(context.getModId(), entry.getKey()); - String clsName = JsonUtils.getString(entry.getValue(), "conditions[" + entry.getValue() + "]"); - register(key, getClassInstance(clsName, IConditionFactory.class)); - } - } - } - - @SuppressWarnings("unchecked") - private static T getClassInstance(String clsName, Class expected) - { - try - { - Class cls = Class.forName(clsName, true, Thread.currentThread().getContextClassLoader()); - if (!expected.isAssignableFrom(cls)) - throw new JsonSyntaxException("Class '" + clsName + "' is not an " + expected.getSimpleName()); - return (T)cls.newInstance(); - } - catch (ClassNotFoundException e) - { - throw new JsonSyntaxException("Could not find " + expected.getSimpleName() + ": " + clsName, e); - } - catch (InstantiationException | IllegalAccessException e) - { - throw new JsonSyntaxException("Could not instantiate " + expected.getSimpleName() + ": " + clsName, e); - } - } - - public static void loadRecipes(boolean revertFrozen) - { - //TODO: If this errors in ServerInit it freezes the client at loading world, find a way to pop that up? - //TODO: Figure out how to remove recipes, and override them. This relies on cpw to help. - //For now this is only done one after mod init, I want to move this to ServerInit and re-do it many times. - init(); - ForgeRegistry reg = (ForgeRegistry)ForgeRegistries.RECIPES; - //reg.unfreeze(); - if (DEBUG_LOAD_MINECRAFT) - reg.clear(); - else if (revertFrozen) - GameData.revert(RegistryManager.FROZEN, GameData.RECIPES, false); - - ModList.get().forEachModFile(CraftingHelper::loadFactories); - ModList.get().forEachModFile(CraftingHelper::loadRecipes); - - GameData.fireRegistryEvents(rl -> rl.equals(GameData.RECIPES)); - - //reg.freeze(); - DistExecutor.runWhenOn(Dist.CLIENT, ()-> RecipeBookClient::rebuildTable); - } - - private static void loadFactories(final ModFile modFile) - { - modFile.getModInfos().forEach(modInfo-> { - final String modId = modInfo.getModId(); - JsonContext ctx = new JsonContext(modId); - final Path fPath = modFile.getLocator().findPath(modFile, "assets", modId, "recipes","_factories.json"); - if (fPath != null && Files.exists(fPath)) - { - try (final BufferedReader reader = Files.newBufferedReader(fPath)) - { - JsonObject json = JsonUtils.fromJson(GSON, reader, JsonObject.class); - loadFactories(json, ctx); - } - catch (final IOException e) - { - LOGGER.error(CRAFTHELPER,"Encountered error reading recipe factories for {}", modId, e); - } - } - }); - } - - private static boolean loadRecipes(final ModFile modFile) - { - final AtomicBoolean errored = new AtomicBoolean(false); - modFile.getModInfos().forEach(modInfo-> { - final String modId = modInfo.getModId(); - JsonContext ctx = new JsonContext(modId); - final Path root = modFile.getLocator().findPath(modFile, "assets", modId, "recipes"); - if (!Files.exists(root)) return; - final Path constants = modFile.getLocator().findPath(modFile, "assets", modId, "recipes", "_constants.json"); - if (Files.exists(constants)) - { - try (BufferedReader reader = Files.newBufferedReader(constants)) - { - JsonObject[] json = JsonUtils.fromJson(GSON, reader, JsonObject[].class); - ctx.loadConstants(json); - } - catch (final IOException e) - { - LOGGER.error(CRAFTHELPER, "Error loading _constants.json: ", e); - errored.set(true); - } - } - try - { - Files.walk(root). - filter(p -> p.getFileName().toString().startsWith("_") || Objects.equals(FilenameUtils.getExtension(p.getFileName().toString()), "json")). - forEach(p -> { - final String relative = root.relativize(p).toString(); - final String name = FilenameUtils.removeExtension(relative).replaceAll("\\\\", "/"); - final ResourceLocation key = new ResourceLocation(modId, name); - try (BufferedReader reader = Files.newBufferedReader(constants)) + JsonObject json = elements[x]; + if (json == null || json.size() == 0) + LOGGER.error(CRAFTHELPER, "Couldn't load constant #{} from {} as it's null or empty", x, key); + else if (json.has("conditions") && !processConditions(JsonUtils.getJsonArray(json, "conditions"))) + LOGGER.info(CRAFTHELPER, "Skipping loading constant #{} from {} as it's conditions were not met", x, key); + else if (!json.has("name")) + LOGGER.error(CRAFTHELPER, "Couldn't load constant #{} from {} as it's missing `name`", x, key); + else if (json.has("items")) + { + List items = new ArrayList<>(); + for (JsonElement item : JsonUtils.getJsonArray(json, "items")) + { + if (item.isJsonObject()) + items.add(getItemStack(item.getAsJsonObject(), true)); + else { - JsonObject json = JsonUtils.fromJson(GSON, reader, JsonObject.class); - if (json.has("conditions") && !CraftingHelper.processConditions(JsonUtils.getJsonArray(json, "conditions"), ctx)) - return; - IRecipe recipe = CraftingHelper.getRecipe(json, ctx); - ForgeRegistries.RECIPES.register(recipe.setRegistryName(key)); - } - catch (final JsonParseException e) - { - LOGGER.error(CRAFTHELPER, "Parsing error loading recipe {}", key, e); - errored.set(true); - } - catch (final IOException e) - { - LOGGER.error(CRAFTHELPER, "Couldn't read recipe {} from {}", key, p, e); - errored.set(true); + LOGGER.error(CRAFTHELPER, "Couldn't load constant #{} from {} as it's `items` entry is not a object", x, key); + items.clear(); + break; } + } + if (!items.isEmpty()) + constants.put(new ResourceLocation(JsonUtils.getString(json, "name")), new StackList(items)); + } + else if (json.has("tag")) + constants.put(new ResourceLocation(JsonUtils.getString(json, "name")), Ingredient.func_199803_a(json)); + else if (json.has("item")) + constants.put(new ResourceLocation(JsonUtils.getString(json, "name")), new StackList(Lists.newArrayList(getItemStack(JsonUtils.getJsonObject(json, "item"), true)))); + else + LOGGER.error(CRAFTHELPER, "Couldn't load constant #{} from {} as it's missing `item` or `items` element", x, key); + } - }); } - catch (final IOException e) + catch (IllegalArgumentException | JsonParseException e) { - LOGGER.error(CRAFTHELPER, "Error occurred walking file tree", e); - errored.set(true); + LOGGER.error(CRAFTHELPER, "Parsing error loading constants {}", key, e); } - }); - return errored.get(); + catch (IOException e) + { + LOGGER.error(CRAFTHELPER, "Couldn't read constants from {}", key, e); + } + } + constants = ret; } } diff --git a/src/main/java/net/minecraftforge/common/crafting/IConditionFactory.java b/src/main/java/net/minecraftforge/common/crafting/IConditionSerializer.java similarity index 89% rename from src/main/java/net/minecraftforge/common/crafting/IConditionFactory.java rename to src/main/java/net/minecraftforge/common/crafting/IConditionSerializer.java index 2aa25d540..55c44fd4f 100644 --- a/src/main/java/net/minecraftforge/common/crafting/IConditionFactory.java +++ b/src/main/java/net/minecraftforge/common/crafting/IConditionSerializer.java @@ -23,6 +23,8 @@ import java.util.function.BooleanSupplier; import com.google.gson.JsonObject; -public interface IConditionFactory { - BooleanSupplier parse(JsonContext context, JsonObject json); +@FunctionalInterface +public interface IConditionSerializer +{ + BooleanSupplier parse(JsonObject json); } diff --git a/src/main/java/net/minecraftforge/common/crafting/IIngredientFactory.java b/src/main/java/net/minecraftforge/common/crafting/IIngredientSerializer.java similarity index 80% rename from src/main/java/net/minecraftforge/common/crafting/IIngredientFactory.java rename to src/main/java/net/minecraftforge/common/crafting/IIngredientSerializer.java index 6f14f8464..5f84a5b6a 100644 --- a/src/main/java/net/minecraftforge/common/crafting/IIngredientFactory.java +++ b/src/main/java/net/minecraftforge/common/crafting/IIngredientSerializer.java @@ -19,14 +19,16 @@ package net.minecraftforge.common.crafting; -import javax.annotation.Nonnull; - import com.google.gson.JsonObject; import net.minecraft.item.crafting.Ingredient; +import net.minecraft.network.PacketBuffer; -public interface IIngredientFactory +public interface IIngredientSerializer { - @Nonnull //If you would return null throw JsonSyntaxException to explain why - Ingredient parse(JsonContext context, JsonObject json); + T parse(PacketBuffer buffer); + + T parse(JsonObject json); + + void write(PacketBuffer buffer, T ingredient); } diff --git a/src/main/java/net/minecraftforge/common/crafting/IRecipeFactory.java b/src/main/java/net/minecraftforge/common/crafting/IRecipeFactory.java deleted file mode 100644 index cda203de5..000000000 --- a/src/main/java/net/minecraftforge/common/crafting/IRecipeFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2018. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation version 2.1 - * of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.common.crafting; - -import com.google.gson.JsonObject; - -import net.minecraft.item.crafting.IRecipe; - -public interface IRecipeFactory { - IRecipe parse(JsonContext context, JsonObject json); -} diff --git a/src/main/java/net/minecraftforge/common/crafting/IngredientNBT.java b/src/main/java/net/minecraftforge/common/crafting/IngredientNBT.java index 584cd1b44..c51d784f9 100644 --- a/src/main/java/net/minecraftforge/common/crafting/IngredientNBT.java +++ b/src/main/java/net/minecraftforge/common/crafting/IngredientNBT.java @@ -19,22 +19,27 @@ package net.minecraftforge.common.crafting; +import java.util.stream.Stream; + import javax.annotation.Nullable; +import com.google.gson.JsonObject; + import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.Ingredient; +import net.minecraft.network.PacketBuffer; public class IngredientNBT extends Ingredient { private final ItemStack stack; protected IngredientNBT(ItemStack stack) { - super(stack); + super(Stream.of(new Ingredient.SingleItemList(stack))); this.stack = stack; } @Override - public boolean apply(@Nullable ItemStack input) + public boolean test(@Nullable ItemStack input) { if (input == null) return false; @@ -47,4 +52,22 @@ public class IngredientNBT extends Ingredient { return false; } + + public static class Serializer implements IIngredientSerializer + { + @Override + public IngredientNBT parse(PacketBuffer buffer) { + return new IngredientNBT(buffer.readItemStack()); + } + + @Override + public IngredientNBT parse(JsonObject json) { + return new IngredientNBT(CraftingHelper.getItemStack(json, true)); + } + + @Override + public void write(PacketBuffer buffer, IngredientNBT ingredient) { + buffer.writeItemStack(ingredient.stack); + } + } } diff --git a/src/main/java/net/minecraftforge/common/crafting/JsonContext.java b/src/main/java/net/minecraftforge/common/crafting/JsonContext.java deleted file mode 100644 index 196842cad..000000000 --- a/src/main/java/net/minecraftforge/common/crafting/JsonContext.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2018. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation version 2.1 - * of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.common.crafting; - -import java.util.Map; - -import javax.annotation.Nullable; - -import com.google.common.collect.Maps; -import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; - -import net.minecraft.item.crafting.Ingredient; -import net.minecraft.util.JsonUtils; - -public class JsonContext -{ - private String modId; - private Map constants = Maps.newHashMap(); - - public JsonContext(String modId) - { - this.modId = modId; - } - - public String getModId() - { - return this.modId; - } - - public String appendModId(String data) - { - if (data.indexOf(':') == -1) - return modId + ":" + data; - return data; - } - - @Nullable - public Ingredient getConstant(String name) - { - return constants.get(name); - } - - void loadConstants(JsonObject[] jsons) - { - for (JsonObject json : jsons) - { - if (json.has("conditions") && !CraftingHelper.processConditions(json.getAsJsonArray("conditions"), this)) - continue; - if (!json.has("ingredient")) - throw new JsonSyntaxException("Constant entry must contain 'ingredient' value"); - constants.put(JsonUtils.getString(json, "name"), CraftingHelper.getIngredient(json.get("ingredient"), this)); - } - - } -} diff --git a/src/main/java/net/minecraftforge/common/crafting/StackList.java b/src/main/java/net/minecraftforge/common/crafting/StackList.java new file mode 100644 index 000000000..7b35a111f --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/StackList.java @@ -0,0 +1,67 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */package net.minecraftforge.common.crafting; + +import java.util.Collection; +import java.util.Collections; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.Ingredient.IItemList; + +public class StackList implements IItemList +{ + private Collection items; + public StackList(Collection items) + { + this.items = Collections.unmodifiableCollection(items); + } + + @Override + public Collection func_199799_a() + { + return items; + } + + @Override + public JsonObject func_200303_b() + { + if (items.size() == 1) + return toJson(items.iterator().next()); + + JsonObject ret = new JsonObject(); + JsonArray array = new JsonArray(); + items.forEach(stack -> array.add(toJson(stack))); + ret.add("items", array); + return ret; + } + + private JsonObject toJson(ItemStack stack) + { + JsonObject ret = new JsonObject(); + ret.addProperty("item", stack.getItem().getRegistryName().toString()); + if (stack.getCount() != 1) + ret.addProperty("count", stack.getCount()); + if (stack.getTagCompound() != null) + ret.addProperty("nbt", stack.getTagCompound().toString()); //TODO: Better serialization? + return ret; + } + +} diff --git a/src/main/java/net/minecraftforge/common/crafting/package-info.java b/src/main/java/net/minecraftforge/common/crafting/package-info.java new file mode 100644 index 000000000..c2eca7d79 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/package-info.java @@ -0,0 +1,7 @@ +// Auto generated package-info by MCP +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package net.minecraftforge.common.crafting; + +import mcp.MethodsReturnNonnullByDefault; +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/resources/forge_at.cfg b/src/main/resources/forge_at.cfg index b32160ed9..ba7d93f56 100644 --- a/src/main/resources/forge_at.cfg +++ b/src/main/resources/forge_at.cfg @@ -345,7 +345,15 @@ protected net.minecraft.world.Teleporter field_85191_c # destinationCoordinateCa public net.minecraft.util.ResourceLocation func_177516_a(Ljava/lang/String;)[Ljava/lang/String; # splitObjectName # Ingredient -protected net.minecraft.item.crafting.Ingredient ([Lnet/minecraft/item/ItemStack;)V # Ingredient +public-f net.minecraft.item.crafting.Ingredient +protected net.minecraft.item.crafting.Ingredient (Ljava/util/stream/Stream;)V +public net.minecraft.item.crafting.Ingredient func_209357_a(Ljava/util/stream/Stream;)Lnet/minecraft/item/crafting/Ingredient; +public+f net.minecraft.item.crafting.Ingredient func_199564_a(Lnet/minecraft/network/PacketBuffer;)V +public net.minecraft.item.crafting.Ingredient$IItemList +public net.minecraft.item.crafting.Ingredient$SingleItemList +public net.minecraft.item.crafting.Ingredient$SingleItemList (Lnet/minecraft/item/ItemStack;)V +public net.minecraft.item.crafting.Ingredient$TagList +public net.minecraft.item.crafting.Ingredient$TagList (Lnet/minecraft/tags/Tag;)V # Crafting public net.minecraft.client.Minecraft func_193986_ar()V # populateSearchTreeManager