diff --git a/jsons/1.12-rel.json b/jsons/1.12-rel.json index 3d42e73500..a7d91126de 100644 --- a/jsons/1.12-rel.json +++ b/jsons/1.12-rel.json @@ -35,7 +35,7 @@ "url" : "http://files.minecraftforge.net/maven/", "checksums" : [ "2ea49e08b876bbd33e0a7ce75c8f371d29e1f10a" ], "serverreq":true, - "serverreq":true + "clientreq":true }, { "name": "jline:jline:2.13", diff --git a/patches/minecraft/net/minecraft/item/crafting/CraftingManager.java.patch b/patches/minecraft/net/minecraft/item/crafting/CraftingManager.java.patch new file mode 100644 index 0000000000..421891dfa5 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/CraftingManager.java.patch @@ -0,0 +1,42 @@ +--- ../src-base/minecraft/net/minecraft/item/crafting/CraftingManager.java ++++ ../src-work/minecraft/net/minecraft/item/crafting/CraftingManager.java +@@ -36,7 +36,7 @@ + { + private static final Logger field_192422_a = LogManager.getLogger(); + private static int field_193381_c; +- public static final RegistryNamespaced field_193380_a = new RegistryNamespaced(); ++ public static final RegistryNamespaced field_193380_a = net.minecraftforge.fml.common.registry.GameData.getRecipeRegistry(); + + public static boolean func_193377_a() + { +@@ -173,12 +173,14 @@ + } + } + +- public static void func_193379_a(String p_193379_0_, IRecipe p_193379_1_) ++ //Forge: Made private use GameData/Registry events! ++ private static void func_193379_a(String p_193379_0_, IRecipe p_193379_1_) + { + func_193372_a(new ResourceLocation(p_193379_0_), p_193379_1_); + } + +- public static void func_193372_a(ResourceLocation p_193372_0_, IRecipe p_193372_1_) ++ //Forge: Made private use GameData/Registry events! ++ private static void func_193372_a(ResourceLocation p_193372_0_, IRecipe p_193372_1_) + { + if (field_193380_a.func_148741_d(p_193372_0_)) + { +@@ -243,11 +245,13 @@ + return field_193380_a.func_82594_a(p_193373_0_); + } + ++ @Deprecated //DO NOT USE THIS + public static int func_193375_a(IRecipe p_193375_0_) + { + return field_193380_a.func_148757_b(p_193375_0_); + } + ++ @Deprecated //DO NOT USE THIS + @Nullable + public static IRecipe func_193374_a(int p_193374_0_) + { diff --git a/patches/minecraft/net/minecraft/item/crafting/IRecipe.java.patch b/patches/minecraft/net/minecraft/item/crafting/IRecipe.java.patch index 960db18ff7..1b6969d110 100644 --- a/patches/minecraft/net/minecraft/item/crafting/IRecipe.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/IRecipe.java.patch @@ -1,15 +1,16 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/IRecipe.java +++ ../src-work/minecraft/net/minecraft/item/crafting/IRecipe.java -@@ -4,8 +4,6 @@ +@@ -4,35 +4,30 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.NonNullList; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - public interface IRecipe +-public interface IRecipe ++public interface IRecipe extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry { -@@ -13,26 +11,23 @@ + boolean func_77569_a(InventoryCrafting p_77569_1_, World p_77569_2_); ItemStack func_77572_b(InventoryCrafting p_77572_1_); diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.java.patch b/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.java.patch index 27a76532e3..b4939fa032 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.java.patch @@ -1,6 +1,18 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/RecipeBookCloning.java +++ ../src-work/minecraft/net/minecraft/item/crafting/RecipeBookCloning.java -@@ -127,7 +127,6 @@ +@@ -6,10 +6,8 @@ + import net.minecraft.item.ItemWrittenBook; + import net.minecraft.util.NonNullList; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + +-public class RecipeBookCloning implements IRecipe ++public class RecipeBookCloning extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + public boolean func_77569_a(InventoryCrafting p_77569_1_, World p_77569_2_) + { +@@ -127,7 +125,6 @@ return true; } diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.java.patch b/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.java.patch index 0631bd684d..ab0d42210f 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.java.patch @@ -1,6 +1,18 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/RecipeFireworks.java +++ ../src-work/minecraft/net/minecraft/item/crafting/RecipeFireworks.java -@@ -248,10 +248,7 @@ +@@ -10,10 +10,8 @@ + import net.minecraft.nbt.NBTTagList; + import net.minecraft.util.NonNullList; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + +-public class RecipeFireworks implements IRecipe ++public class RecipeFireworks extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + private ItemStack field_92102_a = ItemStack.field_190927_a; + +@@ -248,10 +246,7 @@ { ItemStack itemstack = p_179532_1_.func_70301_a(i); @@ -12,7 +24,7 @@ } return nonnulllist; -@@ -262,7 +259,6 @@ +@@ -262,7 +257,6 @@ return true; } diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.java.patch b/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.java.patch index d79d74d8e6..a4d76b0125 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.java.patch @@ -1,6 +1,18 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/RecipeRepairItem.java +++ ../src-work/minecraft/net/minecraft/item/crafting/RecipeRepairItem.java -@@ -28,7 +28,7 @@ +@@ -7,10 +7,8 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.util.NonNullList; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + +-public class RecipeRepairItem implements IRecipe ++public class RecipeRepairItem extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + public boolean func_77569_a(InventoryCrafting p_77569_1_, World p_77569_2_) + { +@@ -28,7 +26,7 @@ { ItemStack itemstack1 = list.get(0); @@ -9,7 +21,7 @@ { return false; } -@@ -55,7 +55,7 @@ +@@ -55,7 +53,7 @@ { ItemStack itemstack1 = list.get(0); @@ -18,7 +30,7 @@ { return ItemStack.field_190927_a; } -@@ -68,13 +68,13 @@ +@@ -68,13 +66,13 @@ ItemStack itemstack2 = list.get(0); ItemStack itemstack3 = list.get(1); @@ -38,7 +50,7 @@ if (i1 < 0) { -@@ -100,11 +100,7 @@ +@@ -100,11 +98,7 @@ for (int i = 0; i < nonnulllist.size(); ++i) { ItemStack itemstack = p_179532_1_.func_70301_a(i); @@ -51,7 +63,7 @@ } return nonnulllist; -@@ -115,7 +111,6 @@ +@@ -115,7 +109,6 @@ return true; } diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeTippedArrow.java.patch b/patches/minecraft/net/minecraft/item/crafting/RecipeTippedArrow.java.patch index 1862be061a..8fc339216d 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipeTippedArrow.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeTippedArrow.java.patch @@ -1,6 +1,18 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/RecipeTippedArrow.java +++ ../src-work/minecraft/net/minecraft/item/crafting/RecipeTippedArrow.java -@@ -83,7 +83,6 @@ +@@ -7,10 +7,8 @@ + import net.minecraft.potion.PotionUtils; + import net.minecraft.util.NonNullList; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + +-public class RecipeTippedArrow implements IRecipe ++public class RecipeTippedArrow extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + public boolean func_77569_a(InventoryCrafting p_77569_1_, World p_77569_2_) + { +@@ -83,7 +81,6 @@ return true; } diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java.patch b/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java.patch index a7880f5ab9..11ef5a6706 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java.patch @@ -1,6 +1,18 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java +++ ../src-work/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java -@@ -143,11 +143,7 @@ +@@ -9,10 +9,8 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.util.NonNullList; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + +-public class RecipesArmorDyes implements IRecipe ++public class RecipesArmorDyes extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + public boolean func_77569_a(InventoryCrafting p_77569_1_, World p_77569_2_) + { +@@ -143,11 +141,7 @@ for (int i = 0; i < nonnulllist.size(); ++i) { ItemStack itemstack = p_179532_1_.func_70301_a(i); @@ -13,7 +25,7 @@ } return nonnulllist; -@@ -158,7 +154,6 @@ +@@ -158,7 +152,6 @@ return true; } diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.java.patch b/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.java.patch index 69db92f593..b9be93622c 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.java.patch @@ -1,6 +1,20 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/RecipesBanners.java +++ ../src-work/minecraft/net/minecraft/item/crafting/RecipesBanners.java -@@ -79,9 +79,10 @@ +@@ -12,12 +12,10 @@ + import net.minecraft.tileentity.TileEntityBanner; + import net.minecraft.util.NonNullList; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + + public class RecipesBanners + { +- public static class RecipeAddPattern implements IRecipe ++ public static class RecipeAddPattern extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + public boolean func_77569_a(InventoryCrafting p_77569_1_, World p_77569_2_) + { +@@ -79,9 +77,10 @@ { ItemStack itemstack2 = p_77572_1_.func_70301_a(j); @@ -13,7 +27,7 @@ break; } } -@@ -120,11 +121,7 @@ +@@ -120,11 +119,7 @@ for (int i = 0; i < nonnulllist.size(); ++i) { ItemStack itemstack = p_179532_1_.func_70301_a(i); @@ -26,7 +40,7 @@ } return nonnulllist; -@@ -150,7 +147,7 @@ +@@ -150,7 +145,7 @@ if (!itemstack.func_190926_b() && itemstack.func_77973_b() != Items.field_179564_cE) { @@ -35,7 +49,7 @@ { if (flag2) { -@@ -190,7 +187,7 @@ +@@ -190,7 +185,7 @@ if (!itemstack1.func_190926_b() && itemstack1.func_77973_b() != Items.field_179564_cE) { @@ -44,7 +58,7 @@ { flag = false; break; -@@ -237,11 +234,42 @@ +@@ -237,14 +232,45 @@ return true; } @@ -87,8 +101,12 @@ + } } - public static class RecipeDuplicatePattern implements IRecipe -@@ -344,9 +372,9 @@ +- public static class RecipeDuplicatePattern implements IRecipe ++ public static class RecipeDuplicatePattern extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + public boolean func_77569_a(InventoryCrafting p_77569_1_, World p_77569_2_) + { +@@ -344,9 +370,9 @@ if (!itemstack.func_190926_b()) { @@ -100,7 +118,7 @@ } else if (itemstack.func_77942_o() && TileEntityBanner.func_175113_c(itemstack) > 0) { -@@ -365,7 +393,6 @@ +@@ -365,7 +391,6 @@ return true; } diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.java.patch b/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.java.patch index 284e45bcc7..537f3f7e01 100644 --- a/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.java.patch @@ -1,6 +1,18 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/RecipesMapCloning.java +++ ../src-work/minecraft/net/minecraft/item/crafting/RecipesMapCloning.java -@@ -111,11 +111,7 @@ +@@ -5,10 +5,8 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.util.NonNullList; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + +-public class RecipesMapCloning implements IRecipe ++public class RecipesMapCloning extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + public boolean func_77569_a(InventoryCrafting p_77569_1_, World p_77569_2_) + { +@@ -111,11 +109,7 @@ for (int i = 0; i < nonnulllist.size(); ++i) { ItemStack itemstack = p_179532_1_.func_70301_a(i); @@ -13,7 +25,7 @@ } return nonnulllist; -@@ -126,7 +122,6 @@ +@@ -126,7 +120,6 @@ return true; } diff --git a/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.java.patch b/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.java.patch index c6602cecdb..dbb896f9e4 100644 --- a/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.java.patch @@ -1,6 +1,18 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/ShapedRecipes.java +++ ../src-work/minecraft/net/minecraft/item/crafting/ShapedRecipes.java -@@ -39,7 +39,6 @@ +@@ -19,10 +19,8 @@ + import net.minecraft.util.NonNullList; + import net.minecraft.util.ResourceLocation; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + +-public class ShapedRecipes implements IRecipe ++public class ShapedRecipes extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + public final int field_77576_b; + public final int field_77577_c; +@@ -39,7 +37,6 @@ this.field_77575_e = p_i47501_5_; } @@ -8,7 +20,7 @@ public String func_193358_e() { return this.field_194137_e; -@@ -57,23 +56,17 @@ +@@ -57,23 +54,17 @@ for (int i = 0; i < nonnulllist.size(); ++i) { ItemStack itemstack = p_179532_1_.func_70301_a(i); @@ -33,3 +45,17 @@ public boolean func_194133_a(int p_194133_1_, int p_194133_2_) { return p_194133_1_ >= this.field_77576_b && p_194133_2_ >= this.field_77577_c; +@@ -241,13 +232,11 @@ + return i; + } + +- @SideOnly(Side.CLIENT) + public int func_192403_f() + { + return this.field_77576_b; + } + +- @SideOnly(Side.CLIENT) + public int func_192404_g() + { + return this.field_77577_c; diff --git a/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.java.patch b/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.java.patch index ea91634362..4c9e94b1f0 100644 --- a/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.java.patch @@ -1,6 +1,26 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/ShapelessRecipes.java +++ ../src-work/minecraft/net/minecraft/item/crafting/ShapelessRecipes.java -@@ -44,17 +44,12 @@ +@@ -10,10 +10,8 @@ + import net.minecraft.util.JsonUtils; + import net.minecraft.util.NonNullList; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + +-public class ShapelessRecipes implements IRecipe ++public class ShapelessRecipes extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + private final ItemStack field_77580_a; + public final NonNullList field_77579_b; +@@ -26,7 +24,6 @@ + this.field_77579_b = p_i47500_3_; + } + +- @SideOnly(Side.CLIENT) + public String func_193358_e() + { + return this.field_194138_c; +@@ -44,17 +41,12 @@ for (int i = 0; i < nonnulllist.size(); ++i) { ItemStack itemstack = p_179532_1_.func_70301_a(i); @@ -19,7 +39,7 @@ public NonNullList func_192400_c() { return this.field_77579_b; -@@ -137,7 +132,6 @@ +@@ -137,7 +129,6 @@ return nonnulllist; } diff --git a/patches/minecraft/net/minecraft/item/crafting/ShieldRecipes.java.patch b/patches/minecraft/net/minecraft/item/crafting/ShieldRecipes.java.patch index 6ef465cee5..b49212edc7 100644 --- a/patches/minecraft/net/minecraft/item/crafting/ShieldRecipes.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/ShieldRecipes.java.patch @@ -1,6 +1,20 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/ShieldRecipes.java +++ ../src-work/minecraft/net/minecraft/item/crafting/ShieldRecipes.java -@@ -128,7 +128,6 @@ +@@ -6,12 +6,10 @@ + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.util.NonNullList; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + + public class ShieldRecipes + { +- public static class Decoration implements IRecipe ++ public static class Decoration extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + public boolean func_77569_a(InventoryCrafting p_77569_1_, World p_77569_2_) + { +@@ -128,7 +126,6 @@ return true; } diff --git a/patches/minecraft/net/minecraft/item/crafting/ShulkerBoxRecipes.java.patch b/patches/minecraft/net/minecraft/item/crafting/ShulkerBoxRecipes.java.patch index e912b5a50b..9e713722ea 100644 --- a/patches/minecraft/net/minecraft/item/crafting/ShulkerBoxRecipes.java.patch +++ b/patches/minecraft/net/minecraft/item/crafting/ShulkerBoxRecipes.java.patch @@ -1,6 +1,20 @@ --- ../src-base/minecraft/net/minecraft/item/crafting/ShulkerBoxRecipes.java +++ ../src-work/minecraft/net/minecraft/item/crafting/ShulkerBoxRecipes.java -@@ -109,7 +109,6 @@ +@@ -8,12 +8,10 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.util.NonNullList; + import net.minecraft.world.World; +-import net.minecraftforge.fml.relauncher.Side; +-import net.minecraftforge.fml.relauncher.SideOnly; + + public class ShulkerBoxRecipes + { +- public static class ShulkerBoxColoring implements IRecipe ++ public static class ShulkerBoxColoring extends net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl implements IRecipe + { + public boolean func_77569_a(InventoryCrafting p_77569_1_, World p_77569_2_) + { +@@ -109,7 +107,6 @@ return true; } diff --git a/patches/minecraft/net/minecraft/stats/RecipeBook.java.patch b/patches/minecraft/net/minecraft/stats/RecipeBook.java.patch new file mode 100644 index 0000000000..22874dd0a3 --- /dev/null +++ b/patches/minecraft/net/minecraft/stats/RecipeBook.java.patch @@ -0,0 +1,10 @@ +--- ../src-base/minecraft/net/minecraft/stats/RecipeBook.java ++++ ../src-work/minecraft/net/minecraft/stats/RecipeBook.java +@@ -41,6 +41,7 @@ + this.field_194078_b.clear(i); + } + ++ @Deprecated //DO NOT USE + protected static int func_194075_d(IRecipe p_194075_0_) + { + return CraftingManager.field_193380_a.func_148757_b(p_194075_0_); diff --git a/src/main/java/net/minecraftforge/common/crafting/CompoundIngredient.java b/src/main/java/net/minecraftforge/common/crafting/CompoundIngredient.java new file mode 100644 index 0000000000..29f0330790 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/CompoundIngredient.java @@ -0,0 +1,79 @@ +package net.minecraftforge.common.crafting; + +import java.util.Collection; +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.collect.Lists; + +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.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class CompoundIngredient extends Ingredient +{ + private Collection children; + private ItemStack[] stacks; + private IntList itemIds; + + protected CompoundIngredient(Collection children) + { + super(0); + this.children = children; + } + + @Override + public ItemStack[] func_193365_a() + { + if (stacks == null) + { + List tmp = Lists.newArrayList(); + for (Ingredient child : children) + for (ItemStack stack : child.func_193365_a()) + tmp.add(stack); + stacks = tmp.toArray(new ItemStack[tmp.size()]); + + } + return stacks; + } + + @SideOnly(Side.CLIENT) + public IntList func_194139_b() + { + //TODO: Add a child.isInvalid()? + if (this.itemIds == null) + { + this.itemIds = new IntArrayList(); + for (Ingredient child : children) + this.itemIds.addAll(child.func_194139_b()); + this.itemIds.sort(IntComparators.NATURAL_COMPARATOR); + } + + return this.itemIds; + } + + + public boolean apply(@Nullable ItemStack target) + { + if (target == null) + return false; + + for (Ingredient child : children) + if (child.apply(target)) + return true; + + return false; + } + + protected void invalidate() + { + this.itemIds = null; + this.stacks = null; + //Shouldn't need to invalidate children as this is only called form invalidateAll.. + } +} diff --git a/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java b/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java new file mode 100644 index 0000000000..a534301ccc --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java @@ -0,0 +1,734 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * 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.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.BooleanSupplier; + +import javax.annotation.Nonnull; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.Level; + +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; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; +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.nbt.JsonToNBT; +import net.minecraft.nbt.NBTException; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.JsonUtils; +import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.FMLLog; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.fml.common.registry.ForgeRegistries; +import net.minecraftforge.fml.common.registry.GameData; +import net.minecraftforge.oredict.OreDictionary; +import net.minecraftforge.oredict.OreIngredient; +import net.minecraftforge.oredict.ShapedOreRecipe; +import net.minecraftforge.oredict.ShapelessOreRecipe; + +public class CraftingHelper { + + private static final boolean DEBUG_LOAD_MINECRAFT = false; + private static Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + private static Map conditions = Maps.newHashMap(); + private static Map ingredients = Maps.newHashMap(); + private static Map recipes = Maps.newHashMap(); + + static { + init(); + } + + public static void register(ResourceLocation key, IConditionFactory factory) + { + if (conditions.containsKey(key)) + throw new IllegalStateException("Duplicate recipe condition factory: " + key); + conditions.put(key, factory); + } + 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) + { + if (ingredients.containsKey(key)) + throw new IllegalStateException("Duplicate recipe ingredient factory: " + key); + ingredients.put(key, factory); + } + + + public static Ingredient getIngredient(Object obj) + { + if (obj instanceof Ingredient) + return (Ingredient)obj; + else if (obj instanceof ItemStack) + return Ingredient.func_193369_a(((ItemStack)obj).copy()); + else if (obj instanceof Item) + return Ingredient.func_193367_a((Item)obj); + else if (obj instanceof Block) + return Ingredient.func_193369_a(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; + } + + @Nonnull + public static Ingredient getIngredient(JsonElement json, JsonContext context) + { + 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()) + { + List ingredients = Lists.newArrayList(); + List vanilla = Lists.newArrayList(); + json.getAsJsonArray().forEach((ele) -> { + Ingredient ing = CraftingHelper.getIngredient(ele, context); + + if (ing.getClass() == Ingredient.class) { + //Vanilla, Due to how we read it splits each itemstack, so we pull out to re-merge later + for (ItemStack stack : ing.func_193365_a()) + vanilla.add(stack); + } + else + { + ingredients.add(ing); + } + }); + + if (!vanilla.isEmpty()) + { + ItemStack[] items = vanilla.toArray(new ItemStack[vanilla.size()]); + ingredients.add(Ingredient.func_193369_a(items)); + } + + if (ingredients.size() == 0) + throw new JsonSyntaxException("Item array cannot be empty, at least one item must be defined"); + + if (ingredients.size() == 1) + return ingredients.get(0); + + return new CompoundIngredient(ingredients); + } + + if (!json.isJsonObject()) + throw new JsonSyntaxException("Expcted ingredient to be a object or array of objects"); + + JsonObject obj = (JsonObject)json; + + String type = context.appendModId(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) + throw new JsonSyntaxException("Unknown ingredient type: " + type); + + return factory.parse(context, obj); + } + + public static ItemStack getItemStack(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 + "'"); + + if (json.has("nbt")) + { + // Lets hope this works? Needs test + try + { + NBTTagCompound nbt = JsonToNBT.getTagFromJson(GSON.toJson(json.get("nbt"))); + NBTTagCompound tmp = new NBTTagCompound(); + if (nbt.hasKey("ForgeCaps")) + { + tmp.setTag("ForgeCaps", nbt.getTag("ForgeCaps")); + nbt.removeTag("ForgeCaps"); + } + + tmp.setTag("tag", nbt); + tmp.setString("id", itemName); + tmp.setInteger("Count", JsonUtils.getInt(json, "count", 1)); + tmp.setInteger("Data", JsonUtils.getInt(json, "data", 0)); + + return new ItemStack(tmp); + } + catch (NBTException e) + { + throw new JsonSyntaxException("Invalid NBT Entry: " + e.toString()); + } + } + + return new ItemStack(item, JsonUtils.getInt(json, "count", 1), JsonUtils.getInt(json, "data", 0)); + } + + + 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(); + + for (; idx < recipe.length; idx += 2) + { + Character chr = (Character)recipe[idx]; + Object in = recipe[idx + 1]; + Ingredient ing = CraftingHelper.getIngredient(in); + + 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.field_193370_a); + int x = 0; + for (char chr : shape.toCharArray()) + { + ret.input.add(x++, itemMap.get(chr)); + } + + return ret; + } + + public static boolean processConditions(JsonArray conditions, JsonContext context) + { + for (int x = 0; x < conditions.size(); x++) + { + if (!conditions.get(x).isJsonObject()) + throw new JsonSyntaxException("Conditions must be an array of JsonObjects"); + + JsonObject json = conditions.get(x).getAsJsonObject(); + BooleanSupplier cond = CraftingHelper.getCondition(json, context); + if (!cond.getAsBoolean()) + return false; + } + return true; + } + + public static BooleanSupplier getCondition(JsonObject json, JsonContext context) + { + ResourceLocation type = new ResourceLocation(context.appendModId(JsonUtils.getString(json, "type"))); + IConditionFactory factory = conditions.get(type); + if (factory == null) + throw new JsonSyntaxException("Unknown condition type: " + type.toString()); + return factory.parse(context, 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); + } + + + + //======================================================= + // INTERNAL + //======================================================= + + private static void init() + { + conditions.clear(); + ingredients.clear(); + recipes.clear(); + + registerC("forge:mod_loaded", (context, json) -> { + String modid = JsonUtils.getString(json, "modid"); + return () -> Loader.isModLoaded(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) + { + 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(c -> c.getAsBoolean()); + }); + + 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.field_193370_a); + + 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.field_193370_a); + Set keys = Sets.newHashSet(ingMap.keySet()); + keys.remove(' '); + + int x = 0; + for (String line : pattern) + { + for (char chr : line.toCharArray()) + { + 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 = ShapedRecipes.func_192405_a(JsonUtils.getJsonObject(json, "result"), true); + return new ShapelessRecipes(group, itemstack, ings); + }); + registerR("forge:ore_shaped", ShapedOreRecipe::factory); + registerR("forge:ore_shapeless", ShapelessOreRecipe::factory); + + registerI("minecraft:item", (context, json) -> Ingredient.func_193369_a(CraftingHelper.getItemStackBasic(json, context))); + registerI("minecraft:empty", (context, json) -> Ingredient.field_193370_a); + registerI("minecraft:item_nbt", (context, json) -> Ingredient.func_193369_a(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); + } + + 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() + "]"); + try + { + Class cls = Class.forName(clsName); + if (!IIngredientFactory.class.isAssignableFrom(cls)) + throw new JsonSyntaxException("Class '" + clsName + "\' is not a IIngredientFactory!"); + register(key, (IIngredientFactory)cls.newInstance()); + } + catch (ClassNotFoundException e) + { + throw new JsonSyntaxException("Could not find ingredient factory: " + clsName, e); + } + catch (InstantiationException | IllegalAccessException e) + { + throw new JsonSyntaxException("Could not instantiate ingredient factory: " + clsName, e); + } + } + } + + 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() + "]"); + try + { + Class cls = Class.forName(clsName); + if (!IRecipeFactory.class.isAssignableFrom(cls)) + throw new JsonSyntaxException("Class '" + clsName + "\' is not a IRecipeFactory!"); + register(key, (IRecipeFactory)cls.newInstance()); + } + catch (ClassNotFoundException e) + { + throw new JsonSyntaxException("Could not find recipe factory: " + clsName, e); + } + catch (InstantiationException | IllegalAccessException e) + { + throw new JsonSyntaxException("Could not instantiate recipe factory: " + clsName, e); + } + } + } + } + + public static void loadRecipes() + { + //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(); + if (DEBUG_LOAD_MINECRAFT) + { + Iterator itr = GameData.getRecipeRegistry().iterator(); + while(itr.hasNext()) + { + itr.next(); + itr.remove(); + } + } + //ModContainer old = Loader.instance().activeModContainer(); + Loader.instance().setActiveModContainer(null); + Loader.instance().getActiveModList().forEach((mod) -> loadFactories(mod)); + Loader.instance().getActiveModList().forEach((mod) -> loadRecipes(mod)); + Loader.instance().setActiveModContainer(null); + } + + private static void loadFactories(ModContainer mod) + { + FileSystem fs = null; + BufferedReader reader = null; + try + { + JsonContext ctx = new JsonContext(mod.getModId()); + URI source = mod.getSource().toURI(); + Path fPath = null; + if ("jar".equals(source.getScheme())) + { + fs = FileSystems.newFileSystem(source, Maps.newHashMap()); + fPath = fs.getPath("/assets/" + ctx.getModId() + "/recipes/_factories.json"); + } + else if ("file".equals(source.getScheme())) + { + fPath = Paths.get(source).resolve("assets/" + ctx.getModId() + "/recipes/_factories.json"); + } + if (fPath != null && Files.exists(fPath)) + { + reader = Files.newBufferedReader(fPath); + JsonObject json = JsonUtils.func_193839_a(GSON, reader, JsonObject.class); + loadFactories(json, ctx); + } + } + catch (IOException e) + { + e.printStackTrace(); + } + finally + { + IOUtils.closeQuietly(fs); + IOUtils.closeQuietly(reader); + } + } + + private static boolean loadRecipes(ModContainer mod) + { + FileSystem fs = null; + BufferedReader reader = null; + try + { + JsonContext ctx = new JsonContext(mod.getModId()); + URI source = mod.getSource().toURI(); + + if ("minecraft".equals(mod.getModId()) && DEBUG_LOAD_MINECRAFT) + { + try + { + source = CraftingManager.class.getResource("/assets/.mcassetsroot").toURI(); + source = source.resolve(".."); + } + catch (URISyntaxException e) + { + FMLLog.log(Level.ERROR, e, "Error finding Minecraft jar: " + e.toString()); + return false; + } + } + + Path root = null; + if ("jar".equals(source.getScheme())) + { + try + { + fs = FileSystems.newFileSystem(source, Maps.newHashMap()); + root = fs.getPath("/assets/" + ctx.getModId() + "/recipes/"); + } + catch (IOException e) + { + FMLLog.log(Level.ERROR, e, "Error loading FileSystem from jar: " + e.toString()); + return false; + } + } + else if ("file".equals(source.getScheme())) + { + root = Paths.get(source).resolve("assets/" + ctx.getModId() + "/recipes/"); + } + + if (root == null || !Files.exists(root)) + return false; + + Path fPath = root.resolve("_constants.json"); + if (fPath != null && Files.exists(fPath)) + { + try + { + reader = Files.newBufferedReader(fPath); + JsonObject[] json = JsonUtils.func_193839_a(GSON, reader, JsonObject[].class); + ctx.loadConstants(json); + } + catch (IOException e) + { + FMLLog.log(Level.ERROR, e, "Error loading _constants.json: " + e.toString()); + return false; + } + } + + Iterator itr = null; + try + { + itr = Files.walk(root).iterator(); + } + catch (IOException e) + { + FMLLog.log(Level.ERROR, e, "Error iterating recipes for: " + ctx.getModId()); + return false; + } + + Loader.instance().setActiveModContainer(mod); + while (itr != null && itr.hasNext()) + { + Path f = itr.next(); + if (!"json".equals(FilenameUtils.getExtension(f.toString())) || + "_factories.json".equals(FilenameUtils.getName(f.toString())) || + "_constants.json".equals(FilenameUtils.getName(f.toString()))) + continue; + + String name = FilenameUtils.removeExtension(root.relativize(f).toString()).replaceAll("\\\\", "/"); + ResourceLocation key = new ResourceLocation(ctx.getModId(), name); + + IOUtils.closeQuietly(reader); + try + { + reader = Files.newBufferedReader(f); + JsonObject json = JsonUtils.func_193839_a(GSON, reader, JsonObject.class); + IRecipe recipe = CraftingHelper.getRecipe(json, ctx); + ForgeRegistries.RECIPES.register(recipe.setRegistryName(key)); + } + catch (JsonParseException e) + { + FMLLog.log(Level.ERROR, e, "Parsing error loading recipe " + key); + return false; + } + catch (IOException e) + { + FMLLog.log(Level.ERROR, e, "Couldn't read recipe " + key + " from " + f); + return false; + } + } + + return true; + } + finally + { + IOUtils.closeQuietly(fs); + IOUtils.closeQuietly(reader); + } + } +} diff --git a/src/main/java/net/minecraftforge/common/crafting/IConditionFactory.java b/src/main/java/net/minecraftforge/common/crafting/IConditionFactory.java new file mode 100644 index 0000000000..3530dc7449 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/IConditionFactory.java @@ -0,0 +1,27 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * 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.function.BooleanSupplier; + +import com.google.gson.JsonObject; + +public interface IConditionFactory { + public BooleanSupplier parse(JsonContext context, JsonObject json); +} diff --git a/src/main/java/net/minecraftforge/common/crafting/IIngredientFactory.java b/src/main/java/net/minecraftforge/common/crafting/IIngredientFactory.java new file mode 100644 index 0000000000..2c0edc10d6 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/IIngredientFactory.java @@ -0,0 +1,31 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * 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 javax.annotation.Nonnull; + +import com.google.gson.JsonObject; + +import net.minecraft.item.crafting.Ingredient; + +public interface IIngredientFactory +{ + @Nonnull //If you would return null throw JsonSyntaxException to explain why + public Ingredient parse(JsonContext context, JsonObject json); +} diff --git a/src/main/java/net/minecraftforge/common/crafting/IRecipeFactory.java b/src/main/java/net/minecraftforge/common/crafting/IRecipeFactory.java new file mode 100644 index 0000000000..16a3e54d87 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/IRecipeFactory.java @@ -0,0 +1,27 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * 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 { + public IRecipe parse(JsonContext context, JsonObject json); +} diff --git a/src/main/java/net/minecraftforge/common/crafting/JsonContext.java b/src/main/java/net/minecraftforge/common/crafting/JsonContext.java new file mode 100644 index 0000000000..cd415ebb4d --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/JsonContext.java @@ -0,0 +1,72 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * 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/fml/common/FMLCommonHandler.java b/src/main/java/net/minecraftforge/fml/common/FMLCommonHandler.java index f79ac5a8e5..c8dcd44d0b 100644 --- a/src/main/java/net/minecraftforge/fml/common/FMLCommonHandler.java +++ b/src/main/java/net/minecraftforge/fml/common/FMLCommonHandler.java @@ -57,6 +57,7 @@ import net.minecraft.world.storage.WorldInfo; import net.minecraftforge.client.model.animation.Animation; import net.minecraftforge.common.ForgeVersion; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.crafting.CraftingHelper; import net.minecraftforge.common.util.CompoundDataFixer; import net.minecraftforge.fml.common.eventhandler.EventBus; import net.minecraftforge.fml.common.gameevent.InputEvent; diff --git a/src/main/java/net/minecraftforge/fml/common/FMLContainer.java b/src/main/java/net/minecraftforge/fml/common/FMLContainer.java index 75c128d1fe..011ed81359 100644 --- a/src/main/java/net/minecraftforge/fml/common/FMLContainer.java +++ b/src/main/java/net/minecraftforge/fml/common/FMLContainer.java @@ -125,6 +125,9 @@ public final class FMLContainer extends DummyModContainer implements WorldAccess for (Map.Entry e : dataSnapshot.entries.entrySet()) { + if (!e.getValue().saveToDisc) //Skip registries that don't need to persist, Currently Recipes. + continue; + NBTTagCompound data = new NBTTagCompound(); registries.setTag(e.getKey().toString(), data); diff --git a/src/main/java/net/minecraftforge/fml/common/Loader.java b/src/main/java/net/minecraftforge/fml/common/Loader.java index 9905a6af80..6d8ec72b63 100644 --- a/src/main/java/net/minecraftforge/fml/common/Loader.java +++ b/src/main/java/net/minecraftforge/fml/common/Loader.java @@ -37,6 +37,7 @@ import java.util.Set; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.capabilities.CapabilityManager; import net.minecraftforge.common.config.ConfigManager; +import net.minecraftforge.common.crafting.CraftingHelper; import net.minecraftforge.fml.common.LoaderState.ModState; import net.minecraftforge.fml.common.ModContainer.Disableable; import net.minecraftforge.fml.common.ProgressManager.ProgressBar; @@ -824,6 +825,7 @@ public class Loader public void initializeMods() { progressBar.step("Initializing mods Phase 2"); + CraftingHelper.loadRecipes(); // Mod controller should be in the initialization state here modController.distributeStateMessage(LoaderState.INITIALIZATION); progressBar.step("Initializing mods Phase 3"); diff --git a/src/main/java/net/minecraftforge/fml/common/discovery/ASMDataTable.java b/src/main/java/net/minecraftforge/fml/common/discovery/ASMDataTable.java index c027db08e9..50da9f6ccb 100644 --- a/src/main/java/net/minecraftforge/fml/common/discovery/ASMDataTable.java +++ b/src/main/java/net/minecraftforge/fml/common/discovery/ASMDataTable.java @@ -23,8 +23,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import net.minecraftforge.fml.common.ModContainer; -import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData; +import javax.annotation.Nullable; import com.google.common.base.Predicate; import com.google.common.collect.HashMultimap; @@ -35,7 +34,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; -import javax.annotation.Nullable; +import net.minecraftforge.fml.common.ModContainer; public class ASMDataTable { diff --git a/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java b/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java index 200c2f64b5..125923ac03 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java @@ -97,8 +97,13 @@ public class FMLControlledNamespacedRegistry> e private final CreateCallback createCallback; @Nullable private final SubstitutionCallback substitutionCallback; + /** + * If true, this registry will persist in the save folder and be reloaded when the world is loaded. + * This should only be true if the save format includes this registry entries in ID format. + */ + private final boolean saveToDisc; - FMLControlledNamespacedRegistry(@Nullable ResourceLocation defaultKey, int minIdValue, int maxIdValue, Class type, BiMap> registries, @Nullable AddCallback addCallback, @Nullable ClearCallback clearCallback, @Nullable CreateCallback createCallback, @Nullable SubstitutionCallback substitutionCallback) + FMLControlledNamespacedRegistry(@Nullable ResourceLocation defaultKey, int minIdValue, int maxIdValue, Class type, BiMap> registries, @Nullable AddCallback addCallback, @Nullable ClearCallback clearCallback, @Nullable CreateCallback createCallback, @Nullable SubstitutionCallback substitutionCallback, boolean saveToDisc) { super(defaultKey); this.superType = type; @@ -115,6 +120,7 @@ public class FMLControlledNamespacedRegistry> e { createCallback.onCreate(slaves, registries); } + this.saveToDisc = saveToDisc; } void validateContent(ResourceLocation registryName) @@ -684,7 +690,7 @@ public class FMLControlledNamespacedRegistry> e FMLControlledNamespacedRegistry makeShallowCopy(BiMap> registries) { - return new FMLControlledNamespacedRegistry(optionalDefaultKey, minId, maxId, superType, registries, addCallback, clearCallback, createCallback, substitutionCallback); + return new FMLControlledNamespacedRegistry(optionalDefaultKey, minId, maxId, superType, registries, addCallback, clearCallback, createCallback, substitutionCallback, saveToDisc); } void resetSubstitutionDelegates() @@ -935,4 +941,9 @@ public class FMLControlledNamespacedRegistry> e return new RegistryEvent.Register(location, this); } + public boolean shouldSaveToDisc() + { + return this.saveToDisc; + } + } diff --git a/src/main/java/net/minecraftforge/fml/common/registry/ForgeRegistries.java b/src/main/java/net/minecraftforge/fml/common/registry/ForgeRegistries.java index 8645fbe0a6..ac29177c65 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/ForgeRegistries.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/ForgeRegistries.java @@ -21,7 +21,9 @@ package net.minecraftforge.fml.common.registry; import net.minecraft.block.Block; import net.minecraft.enchantment.Enchantment; +import net.minecraft.init.Bootstrap; import net.minecraft.item.Item; +import net.minecraft.item.crafting.IRecipe; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionType; import net.minecraft.util.SoundEvent; @@ -46,6 +48,7 @@ public class ForgeRegistries public static final IForgeRegistry ENCHANTMENTS = GameRegistry.findRegistry(Enchantment.class); public static final IForgeRegistry VILLAGER_PROFESSIONS = GameRegistry.findRegistry(VillagerProfession.class); public static final IForgeRegistry ENTITIES = GameRegistry.findRegistry(EntityEntry.class); + public static final IForgeRegistry RECIPES = GameRegistry.findRegistry(IRecipe.class); /** @@ -55,6 +58,7 @@ public class ForgeRegistries { GameData.getMain(); VillagerRegistry.instance(); + Bootstrap.register(); } } diff --git a/src/main/java/net/minecraftforge/fml/common/registry/GameData.java b/src/main/java/net/minecraftforge/fml/common/registry/GameData.java index d6d9b03b80..c6f36770c5 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/GameData.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/GameData.java @@ -30,6 +30,7 @@ import net.minecraft.enchantment.Enchantment; import net.minecraft.entity.EntityList; import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; +import net.minecraft.item.crafting.IRecipe; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionType; import net.minecraft.tileentity.TileEntity; @@ -39,12 +40,13 @@ import net.minecraft.util.SoundEvent; import net.minecraft.world.biome.Biome; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.Loader; -import net.minecraftforge.fml.common.ModContainer; import com.google.common.collect.BiMap; import net.minecraftforge.fml.relauncher.ReflectionHelper; import org.apache.logging.log4j.Level; +import static net.minecraftforge.fml.common.registry.PersistentRegistryManager.*; + public class GameData { private static final int MIN_BLOCK_ID = 0; @@ -63,6 +65,8 @@ public class GameData private static final int MAX_ENCHANTMENT_ID = Short.MAX_VALUE - 1; // Short - serialized as a short in ItemStack NBTs. private static final int MIN_ENTITY_ID = 0; private static final int MAX_ENTITY_ID = Integer.MAX_VALUE >> 5; // Varint (SPacketSpawnMob) + private static final int MIN_RECIPE_ID = 0; + private static final int MAX_RECIPE_ID = Integer.MAX_VALUE >> 5; // Varint CPacketRecipeInfo/SPacketRecipeBook private static final ResourceLocation BLOCK_TO_ITEM = new ResourceLocation("minecraft:blocktoitemmap"); private static final ResourceLocation BLOCKSTATE_TO_ID = new ResourceLocation("minecraft:blockstatetoid"); @@ -73,15 +77,15 @@ public class GameData public GameData() { - iBlockRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, new ResourceLocation("minecraft:air"), MIN_BLOCK_ID, MAX_BLOCK_ID, true, BlockCallbacks.INSTANCE, BlockCallbacks.INSTANCE, BlockCallbacks.INSTANCE, BlockCallbacks.INSTANCE); - iItemRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, MIN_ITEM_ID, MAX_ITEM_ID, true, ItemCallbacks.INSTANCE, ItemCallbacks.INSTANCE, ItemCallbacks.INSTANCE, ItemCallbacks.INSTANCE); - iPotionRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.POTIONS, Potion.class, null, MIN_POTION_ID, MAX_POTION_ID, false, PotionCallbacks.INSTANCE, PotionCallbacks.INSTANCE, PotionCallbacks.INSTANCE, null); - iBiomeRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.BIOMES, Biome.class, null, MIN_BIOME_ID, MAX_BIOME_ID, false, BiomeCallbacks.INSTANCE, BiomeCallbacks.INSTANCE, BiomeCallbacks.INSTANCE, null); - iSoundEventRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.SOUNDEVENTS, SoundEvent.class, null, MIN_SOUND_ID, MAX_SOUND_ID, false, null, null, null, null); - ResourceLocation WATER = new ResourceLocation("water"); - iPotionTypeRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.POTIONTYPES, PotionType.class, WATER, MIN_POTIONTYPE_ID, MAX_POTIONTYPE_ID, false, null, null, null, null); - iEnchantmentRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.ENCHANTMENTS, Enchantment.class, null, MIN_ENCHANTMENT_ID, MAX_ENCHANTMENT_ID, false, null, null, null, null); - iEntityRegistry = (FMLControlledNamespacedRegistry)new RegistryBuilder().setName(PersistentRegistryManager.ENTITIES).setType(EntityEntry.class).setIDRange(MIN_ENTITY_ID, MAX_ENTITY_ID).addCallback(EntityCallbacks.INSTANCE).create(); + iBlockRegistry = (FMLControlledNamespacedRegistry) makeRegistry(BLOCKS, Block.class, MIN_BLOCK_ID, MAX_BLOCK_ID).addCallback(BlockCallbacks.INSTANCE).enableDelegates().setDefaultKey(new ResourceLocation("air")).create(); + iItemRegistry = (FMLControlledNamespacedRegistry) makeRegistry(ITEMS, Item.class, MIN_ITEM_ID, MAX_ITEM_ID).addCallback(ItemCallbacks.INSTANCE).enableDelegates().create(); + iPotionRegistry = (FMLControlledNamespacedRegistry) makeRegistry(POTIONS, Potion.class, MIN_POTION_ID, MAX_POTION_ID).addCallback(PotionCallbacks.INSTANCE).create(); + iBiomeRegistry = (FMLControlledNamespacedRegistry) makeRegistry(BIOMES, Biome.class, MIN_BIOME_ID, MAX_BIOME_ID).addCallback(BiomeCallbacks.INSTANCE).create(); + iSoundEventRegistry = (FMLControlledNamespacedRegistry) makeRegistry(SOUNDEVENTS, SoundEvent.class, MIN_SOUND_ID, MAX_SOUND_ID).create(); + iPotionTypeRegistry = (FMLControlledNamespacedRegistry) makeRegistry(POTIONTYPES, PotionType.class, MIN_POTIONTYPE_ID, MAX_POTIONTYPE_ID).setDefaultKey(new ResourceLocation("water")).create(); + iEnchantmentRegistry = (FMLControlledNamespacedRegistry)makeRegistry(ENCHANTMENTS, Enchantment.class, MIN_ENCHANTMENT_ID, MAX_ENCHANTMENT_ID).create(); + iEntityRegistry = (FMLControlledNamespacedRegistry)makeRegistry(ENTITIES, EntityEntry.class, MIN_ENTITY_ID, MAX_ENTITY_ID).addCallback(EntityCallbacks.INSTANCE).create(); + iRecipeRegistry = (FMLControlledNamespacedRegistry) makeRegistry(RECIPES, IRecipe.class, MIN_RECIPE_ID, MAX_RECIPE_ID).disableSaving().create(); try { @@ -95,6 +99,11 @@ public class GameData iTileEntityRegistry = new LegacyNamespacedRegistry>(); } + + private > RegistryBuilder makeRegistry(ResourceLocation name, Class type, int min, int max) + { + return new RegistryBuilder().setName(name).setType(type).setIDRange(min, max); + } // internal registry objects private final FMLControlledNamespacedRegistry iBlockRegistry; private final FMLControlledNamespacedRegistry iItemRegistry; @@ -110,6 +119,9 @@ public class GameData //don't follow the same patterns as the other ones. private final LegacyNamespacedRegistry> iTileEntityRegistry; + //TODO: This is not a recipe that is serilized to disc by ID, so we need to skip over any saving/loading from disc + private final FMLControlledNamespacedRegistry iRecipeRegistry; + /** INTERNAL ONLY */ @Deprecated public static FMLControlledNamespacedRegistry getBlockRegistry() @@ -154,17 +166,9 @@ public class GameData @Deprecated public static FMLControlledNamespacedRegistry getEntityRegistry() { return getMain().iEntityRegistry; } + /** INTERNAL ONLY */ @Deprecated - static Item findItem(String modId, String name) - { - return getMain().iItemRegistry.getObject(new ResourceLocation(modId, name)); - } - - @Deprecated - static Block findBlock(String modId, String name) - { - return getMain().iBlockRegistry.getObject(new ResourceLocation(modId, name)); - } + public static FMLControlledNamespacedRegistry getRecipeRegistry() { return getMain().iRecipeRegistry; } protected static GameData getMain() { diff --git a/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistryEntry.java b/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistryEntry.java index 8214618dbf..25a3f288d6 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistryEntry.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistryEntry.java @@ -62,7 +62,7 @@ public interface IForgeRegistryEntry Class getRegistryType(); // Default implementation, modders who make extra items SHOULD extend this instead of Object. - // We have to do this until we get default implementations in Java 8. + // So, all fields in interfaces are forced static, so even with Java8 people must still extend this. @SuppressWarnings({ "serial", "unchecked" }) public static class Impl> implements IForgeRegistryEntry { diff --git a/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java b/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java index 12a4fd6adf..f35a543576 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java @@ -90,6 +90,7 @@ public class PersistentRegistryManager ObjectHolderRegistry.INSTANCE.applyObjectHolders(); // inject any items for (ResourceLocation rl : registryKeys) { if (rl == BLOCKS || rl == ITEMS) continue; + if (rl == RECIPES) continue; //For now disable, we should fire this event in ServerInit or something... fireRegistryEvent(PersistentRegistry.ACTIVE.registries, rl); } ObjectHolderRegistry.INSTANCE.applyObjectHolders(); // inject everything else @@ -129,7 +130,7 @@ public class PersistentRegistryManager return getRegistry(key, regType); } - private > FMLControlledNamespacedRegistry createRegistry(ResourceLocation registryName, Class type, ResourceLocation defaultObjectKey, int minId, int maxId, @Nullable IForgeRegistry.AddCallback addCallback, @Nullable IForgeRegistry.ClearCallback clearCallback, @Nullable IForgeRegistry.CreateCallback createCallback, @Nullable IForgeRegistry.SubstitutionCallback substitutionCallback) + private > FMLControlledNamespacedRegistry createRegistry(ResourceLocation registryName, Class type, ResourceLocation defaultObjectKey, int minId, int maxId, @Nullable IForgeRegistry.AddCallback addCallback, @Nullable IForgeRegistry.ClearCallback clearCallback, @Nullable IForgeRegistry.CreateCallback createCallback, @Nullable IForgeRegistry.SubstitutionCallback substitutionCallback, boolean saveToDisc) { Set> parents = Sets.newHashSet(); findSuperTypes(type, parents); @@ -140,7 +141,7 @@ public class PersistentRegistryManager FMLLog.severe("Found existing registry of type %1s named %2s, you cannot create a new registry (%3s) with type %4s, as %4s has a parent of that type", foundType, registrySuperTypes.get(foundType), registryName, type); throw new IllegalArgumentException("Duplicate registry parent type found - you can only have one registry for a particular super type"); } - FMLControlledNamespacedRegistry fmlControlledNamespacedRegistry = new FMLControlledNamespacedRegistry(defaultObjectKey, minId, maxId, type, registries, addCallback, clearCallback, createCallback, substitutionCallback); + FMLControlledNamespacedRegistry fmlControlledNamespacedRegistry = new FMLControlledNamespacedRegistry(defaultObjectKey, minId, maxId, type, registries, addCallback, clearCallback, createCallback, substitutionCallback, saveToDisc); registries.put(registryName, fmlControlledNamespacedRegistry); registrySuperTypes.put(type, registryName); return getRegistry(registryName, type); @@ -192,30 +193,21 @@ public class PersistentRegistryManager public static final ResourceLocation POTIONTYPES = new ResourceLocation("minecraft:potiontypes"); public static final ResourceLocation ENCHANTMENTS = new ResourceLocation("minecraft:enchantments"); public static final ResourceLocation ENTITIES = new ResourceLocation("minecraft:entities"); + public static final ResourceLocation RECIPES = new ResourceLocation("minecraft:recipes"); static final ResourceLocation SUBSTITUTION_ORIGINALS = new ResourceLocation("fml:suboriginals"); - @Deprecated //Use RegistryBuilder TODO: Remove in 1.11 - public static > FMLControlledNamespacedRegistry createRegistry( - ResourceLocation registryName, Class registryType, ResourceLocation optionalDefaultKey, - int minId, int maxId, boolean hasDelegates, - @Nullable IForgeRegistry.AddCallback addCallback, - @Nullable IForgeRegistry.ClearCallback clearCallback, - @Nullable IForgeRegistry.CreateCallback createCallback) - { - return PersistentRegistry.ACTIVE.createRegistry(registryName, registryType, optionalDefaultKey, minId, maxId, - getLegacyAdd(addCallback), getLegacyClear(clearCallback), getLegacyCreate(createCallback), null); - } @Deprecated //Use RegistryBuilder TODO: Remove in 1.11 {Make package private so only builder can use it} - public static > FMLControlledNamespacedRegistry createRegistry( + static > FMLControlledNamespacedRegistry createRegistry( ResourceLocation registryName, Class registryType, @Nullable ResourceLocation optionalDefaultKey, int minId, int maxId, boolean hasDelegates, @Nullable IForgeRegistry.AddCallback addCallback, @Nullable IForgeRegistry.ClearCallback clearCallback, @Nullable IForgeRegistry.CreateCallback createCallback, - @Nullable IForgeRegistry.SubstitutionCallback substitutionCallback) + @Nullable IForgeRegistry.SubstitutionCallback substitutionCallback, + boolean saveToDisc) { return PersistentRegistry.ACTIVE.createRegistry(registryName, registryType, optionalDefaultKey, minId, maxId, - addCallback, clearCallback, createCallback, substitutionCallback); + addCallback, clearCallback, createCallback, substitutionCallback, saveToDisc); } @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -693,6 +685,7 @@ public class PersistentRegistryManager public final Map aliases; public final Set blocked; public final Set dummied; + public final boolean saveToDisc; public Entry() { @@ -706,6 +699,7 @@ public class PersistentRegistryManager this.aliases = aliases; this.blocked = blocked; this.dummied = dummies; + saveToDisc = false; } public Entry(FMLControlledNamespacedRegistry registry) @@ -721,6 +715,7 @@ public class PersistentRegistryManager registry.serializeAliases(this.aliases); registry.serializeBlockList(this.blocked); registry.serializeDummied(this.dummied); + this.saveToDisc = registry.shouldSaveToDisc(); } } diff --git a/src/main/java/net/minecraftforge/fml/common/registry/RegistryBuilder.java b/src/main/java/net/minecraftforge/fml/common/registry/RegistryBuilder.java index 51470aedf8..fd9f8356ff 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/RegistryBuilder.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/RegistryBuilder.java @@ -37,10 +37,12 @@ public class RegistryBuilder> private ResourceLocation optionalDefaultKey; private int minId; private int maxId; + private boolean hasDelegates = false; private List> addCallback = Lists.newArrayList(); private List> clearCallback = Lists.newArrayList(); private List> createCallback = Lists.newArrayList(); private List> substitutionCallback = Lists.newArrayList(); + private boolean saveToDisc; public RegistryBuilder setName(ResourceLocation name) { @@ -105,11 +107,23 @@ public class RegistryBuilder> return this; } + public RegistryBuilder enableDelegates() + { + this.hasDelegates = true; + return this; + } + + public RegistryBuilder disableSaving() + { + this.saveToDisc = false; + return this; + } + @SuppressWarnings("deprecation") public IForgeRegistry create() { - return PersistentRegistryManager.createRegistry(registryName, registryType, optionalDefaultKey, minId, maxId, false, - getAdd(), getClear(), getCreate(), getSubstitution()); + return PersistentRegistryManager.createRegistry(registryName, registryType, optionalDefaultKey, minId, maxId, hasDelegates, + getAdd(), getClear(), getCreate(), getSubstitution(), saveToDisc); } @Nullable diff --git a/src/main/java/net/minecraftforge/fml/common/registry/VillagerRegistry.java b/src/main/java/net/minecraftforge/fml/common/registry/VillagerRegistry.java index 08b7308f12..5e4231ec61 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/VillagerRegistry.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/VillagerRegistry.java @@ -38,6 +38,7 @@ import net.minecraft.world.gen.structure.StructureVillagePieces; import net.minecraft.world.gen.structure.StructureVillagePieces.PieceWeight; import net.minecraft.world.gen.structure.StructureVillagePieces.Village; import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder; +import net.minecraftforge.fml.common.registry.RegistryBuilder; import javax.annotation.Nullable; @@ -138,7 +139,7 @@ public class VillagerRegistry } private boolean hasInit = false; - private FMLControlledNamespacedRegistry professions = PersistentRegistryManager.createRegistry(PROFESSIONS, VillagerProfession.class, null, 0, 1024, true, null, null, null, null); + private FMLControlledNamespacedRegistry professions = (FMLControlledNamespacedRegistry)new RegistryBuilder().setName(PROFESSIONS).setType(VillagerProfession.class).setIDRange(0, 1024).enableDelegates().create(); public IForgeRegistry getRegistry() { return this.professions; } diff --git a/src/main/java/net/minecraftforge/oredict/ShapedOreRecipe.java b/src/main/java/net/minecraftforge/oredict/ShapedOreRecipe.java index 2997ed1c1a..f62bc69233 100644 --- a/src/main/java/net/minecraftforge/oredict/ShapedOreRecipe.java +++ b/src/main/java/net/minecraftforge/oredict/ShapedOreRecipe.java @@ -19,23 +19,36 @@ package net.minecraftforge.oredict; -import java.util.HashMap; import net.minecraft.block.Block; import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.Ingredient; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.util.JsonUtils; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.crafting.CraftingHelper; +import net.minecraftforge.common.crafting.CraftingHelper.ShapedPrimer; +import net.minecraftforge.common.crafting.JsonContext; +import net.minecraftforge.fml.common.registry.IForgeRegistryEntry; + +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; import javax.annotation.Nonnull; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; -public class ShapedOreRecipe implements IRecipe +public class ShapedOreRecipe extends IForgeRegistryEntry.Impl implements IRecipe { //Added in for future ease of change, but hard coded for now. public static final int MAX_CRAFT_GRID_WIDTH = 3; @@ -51,106 +64,15 @@ public class ShapedOreRecipe implements IRecipe public ShapedOreRecipe(ResourceLocation group, Block result, Object... recipe){ this(group, new ItemStack(result), recipe); } public ShapedOreRecipe(ResourceLocation group, Item result, Object... recipe){ this(group, new ItemStack(result), recipe); } - public ShapedOreRecipe(ResourceLocation group, @Nonnull ItemStack result, Object... recipe) + public ShapedOreRecipe(ResourceLocation group, @Nonnull ItemStack result, Object... recipe) { this(group, result, CraftingHelper.parseShaped(recipe)); } + public ShapedOreRecipe(ResourceLocation group, @Nonnull ItemStack result, ShapedPrimer primer) { this.group = group; output = result.copy(); - - String shape = ""; - int idx = 0; - - if (recipe[idx] instanceof Boolean) - { - 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) - { - width = s.length(); - shape += s; - } - - height = parts.length; - } - else - { - while (recipe[idx] instanceof String) - { - String s = (String)recipe[idx++]; - shape += s; - width = s.length(); - height++; - } - } - - if (width * height != shape.length()) - { - String ret = "Invalid shaped ore recipe: "; - for (Object tmp : recipe) - { - ret += tmp + ", "; - } - ret += output; - throw new RuntimeException(ret); - } - - HashMap itemMap = Maps.newHashMap(); - - for (; idx < recipe.length; idx += 2) - { - Character chr = (Character)recipe[idx]; - Object in = recipe[idx + 1]; - - if (in instanceof ItemStack) - { - itemMap.put(chr, Ingredient.func_193369_a(((ItemStack)in).copy())); - } - else if (in instanceof Item) - { - itemMap.put(chr, Ingredient.func_193367_a((Item)in)); - } - else if (in instanceof Block) - { - itemMap.put(chr, Ingredient.func_193369_a(new ItemStack((Block)in, 1, OreDictionary.WILDCARD_VALUE))); - } - else if (in instanceof String) - { - itemMap.put(chr, new OreIngredient((String)in)); - } - else if (in instanceof Ingredient) - { - itemMap.put(chr, (Ingredient)in); - } - else - { - String ret = "Invalid shaped ore recipe: "; - for (Object tmp : recipe) - { - ret += tmp + ", "; - } - ret += output; - throw new RuntimeException(ret); - } - } - - this.input = NonNullList.withSize(width * height, Ingredient.field_193370_a); - int x = 0; - for (char chr : shape.toCharArray()) - { - input.add(x++, itemMap.get(chr)); - } + this.width = primer.width; + this.height = primer.height; + this.input = primer.input; + this.mirrored = primer.mirrored; } @Override @@ -251,4 +173,66 @@ public class ShapedOreRecipe implements IRecipe { return p_194133_1_ >= this.width && p_194133_2_ >= this.height; } + + public static ShapedOreRecipe factory(JsonContext context, JsonObject 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.field_193370_a); + + JsonArray patternJ = JsonUtils.getJsonArray(json, "pattern"); + + if (patternJ.size() == 0) + throw new JsonSyntaxException("Invalid pattern: empty pattern not allowed"); + + String[] pattern = new String[patternJ.size()]; + for (int x = 0; x < pattern.length; ++x) + { + String line = JsonUtils.getString(patternJ.get(x), "pattern[" + x + "]"); + if (x > 0 && pattern[0].length() != line.length()) + throw new JsonSyntaxException("Invalid pattern: each row must be the same width"); + pattern[x] = line; + } + + ShapedPrimer primer = new ShapedPrimer(); + primer.width = pattern[0].length(); + primer.height = pattern.length; + primer.mirrored = JsonUtils.getBoolean(json, "mirrored", true); + primer.input = NonNullList.withSize(primer.width * primer.height, Ingredient.field_193370_a); + + Set keys = Sets.newHashSet(ingMap.keySet()); + keys.remove(' '); + + int x = 0; + for (String line : pattern) + { + for (char chr : line.toCharArray()) + { + Ingredient ing = ingMap.get(chr); + if (ing == null) + throw new JsonSyntaxException("Pattern references symbol '" + chr + "' but it's not defined in the key"); + primer.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 ShapedOreRecipe(group.isEmpty() ? null : new ResourceLocation(group), result, primer); + } } diff --git a/src/main/java/net/minecraftforge/oredict/ShapelessOreRecipe.java b/src/main/java/net/minecraftforge/oredict/ShapelessOreRecipe.java index 9042a950fc..070372451b 100644 --- a/src/main/java/net/minecraftforge/oredict/ShapelessOreRecipe.java +++ b/src/main/java/net/minecraftforge/oredict/ShapelessOreRecipe.java @@ -20,58 +20,53 @@ package net.minecraftforge.oredict; import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.List; import net.minecraft.block.Block; import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.Ingredient; +import net.minecraft.item.crafting.ShapedRecipes; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraft.util.JsonUtils; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.crafting.CraftingHelper; +import net.minecraftforge.common.crafting.JsonContext; +import net.minecraftforge.fml.common.registry.IForgeRegistryEntry; import javax.annotation.Nonnull; -public class ShapelessOreRecipe implements IRecipe +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +public class ShapelessOreRecipe extends IForgeRegistryEntry.Impl implements IRecipe { @Nonnull protected ItemStack output = ItemStack.EMPTY; - protected NonNullList input = NonNullList.create(); + protected NonNullList input = NonNullList.create(); protected ResourceLocation group; public ShapelessOreRecipe(ResourceLocation group, Block result, Object... recipe){ this(group, new ItemStack(result), recipe); } public ShapelessOreRecipe(ResourceLocation group, Item result, Object... recipe){ this(group, new ItemStack(result), recipe); } - + public ShapelessOreRecipe(ResourceLocation group, NonNullList input, @Nonnull ItemStack result) + { + this.group = group; + output = result.copy(); + this.input = input; + } public ShapelessOreRecipe(ResourceLocation group, @Nonnull ItemStack result, Object... recipe) { this.group = group; output = result.copy(); for (Object in : recipe) { - if (in instanceof ItemStack) + Ingredient ing = CraftingHelper.getIngredient(in); + if (ing != null) { - input.add(Ingredient.func_193369_a(((ItemStack)in).copy())); - } - else if (in instanceof Item) - { - input.add(Ingredient.func_193367_a((Item)in)); - } - else if (in instanceof Block) - { - input.add(Ingredient.func_193369_a(new ItemStack((Block)in, 1, OreDictionary.WILDCARD_VALUE))); - } - else if (in instanceof String) - { - input.add(new OreIngredient((String)in)); - } - else if (in instanceof Ingredient) - { - input.add((Ingredient)in); + input.add(ing); } else { @@ -94,7 +89,6 @@ public class ShapelessOreRecipe implements IRecipe @Nonnull public ItemStack getCraftingResult(@Nonnull InventoryCrafting var1){ return output.copy(); } - @SuppressWarnings("unchecked") @Override public boolean matches(InventoryCrafting var1, World world) { @@ -145,11 +139,26 @@ public class ShapelessOreRecipe implements IRecipe public String func_193358_e() { - return this.group.toString(); + return this.group == null ? "" : this.group.toString(); } public boolean func_194133_a(int p_194133_1_, int p_194133_2_) { return p_194133_1_ * p_194133_2_ >= this.input.size(); } + + public static ShapelessOreRecipe factory(JsonContext context, JsonObject 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"); + + ItemStack itemstack = ShapedRecipes.func_192405_a(JsonUtils.getJsonObject(json, "result"), true); + return new ShapelessOreRecipe(group.isEmpty() ? null : new ResourceLocation(group), ings, itemstack); + } } diff --git a/src/test/java/net/minecraftforge/debug/CraftingSystemTest.java b/src/test/java/net/minecraftforge/debug/CraftingSystemTest.java new file mode 100644 index 0000000000..744f2750fb --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/CraftingSystemTest.java @@ -0,0 +1,46 @@ +package net.minecraftforge.debug; + +import com.google.gson.JsonObject; + +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.Ingredient; +import net.minecraftforge.common.crafting.IIngredientFactory; +import net.minecraftforge.common.crafting.IRecipeFactory; +import net.minecraftforge.common.crafting.JsonContext; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.FMLLog; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +@Mod(modid = CraftingSystemTest.MOD_ID, name = "CraftingTestMod", version = "1.0", acceptableRemoteVersions = "*") +@Mod.EventBusSubscriber +public class CraftingSystemTest +{ + static final String MOD_ID = "crafting_system_test"; + static final boolean ENABLED = false; + + @SubscribeEvent + public static void registerRecipes(RegistryEvent.Register event) + { + FMLLog.info("Registering Test Recipes:"); + } + + public static class IngredientFactory implements IIngredientFactory + { + @Override + public Ingredient parse(JsonContext context, JsonObject json) + { + return null; + } + } + + public static class RecipeFactory implements IRecipeFactory + { + @Override + public IRecipe parse(JsonContext context, JsonObject json) + { + return null; + } + + } +} diff --git a/src/test/java/net/minecraftforge/fml/common/registry/FreezingTests.java b/src/test/java/net/minecraftforge/fml/common/registry/FreezingTests.java index 3628bd93a1..4aaaaae022 100644 --- a/src/test/java/net/minecraftforge/fml/common/registry/FreezingTests.java +++ b/src/test/java/net/minecraftforge/fml/common/registry/FreezingTests.java @@ -42,9 +42,9 @@ public class FreezingTests { Loader.instance(); System.setProperty("fml.queryResult", "confirm"); - registry = PersistentRegistryManager.createRegistry(resloc, RTest.class, null, 0, 255, false, null, null, null, null); - PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, null, 0, 255, false, null, null, null, null); - PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, 0, 255, false, null, null, null, null); + registry = new RegistryBuilder().setName(resloc).setType(RTest.class).setIDRange(0, 255).create(); + new RegistryBuilder().setName(PersistentRegistryManager.BLOCKS).setType(Block.class).setIDRange(0, 255).create(); + new RegistryBuilder().setName(PersistentRegistryManager.ITEMS).setType(Item.class).setIDRange(0, 255).create(); r1 = new RTest("test1"); r2 = new RTest("test2"); r3 = new RTest("test3"); @@ -61,9 +61,9 @@ public class FreezingTests ss.entries.put(resloc, new PersistentRegistryManager.GameDataSnapshot.Entry(PersistentRegistryManager.PersistentRegistry.ACTIVE.getRegistry(RTest.class))); PersistentRegistryManager.PersistentRegistry.ACTIVE.clean(); PersistentRegistryManager.PersistentRegistry.FROZEN.clean(); - registry = PersistentRegistryManager.createRegistry(resloc, RTest.class, null, 0, 255, false, null, null, null, null); - PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, null, 0, 255, false, null, null, null, null); - PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, 0, 255, false, null, null, null, null); + registry = new RegistryBuilder().setName(resloc).setType(RTest.class).setIDRange(0, 255).create(); + new RegistryBuilder().setName(PersistentRegistryManager.BLOCKS).setType(Block.class).setIDRange(0, 255).create(); + new RegistryBuilder().setName(PersistentRegistryManager.ITEMS).setType(Item.class).setIDRange(0, 255).create(); } @Test diff --git a/src/test/resources/assets/crafting_system_test/recipes/_factories.json b/src/test/resources/assets/crafting_system_test/recipes/_factories.json new file mode 100644 index 0000000000..5b299e3369 --- /dev/null +++ b/src/test/resources/assets/crafting_system_test/recipes/_factories.json @@ -0,0 +1,8 @@ +{ + "ingredients": { + "nbt": "net.minecraftforge.debug.CraftingSystemTest$IngredientFactory" + }, + "recipes": { + "free": "net.minecraftforge.debug.CraftingSystemTest$RecipeFactory" + } +} \ No newline at end of file