Util Creating recipes with specific ingredients/itemstacks

Discussion in 'Resources' started by Zombie_Striker, Aug 3, 2016.

Thread Status:
Not open for further replies.
  1. Offline


    This util allows plugins to require specific itemstacks with certain names or lores. This is useful for plugins that require the "crafter" to use a specific, named item while crafting.

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map.Entry;
    import org.bukkit.Bukkit;
    import org.bukkit.Material;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.Listener;
    import org.bukkit.event.inventory.PrepareItemCraftEvent;
    import org.bukkit.inventory.CraftingInventory;
    import org.bukkit.inventory.ItemStack;
    import org.bukkit.inventory.Recipe;
    import org.bukkit.inventory.ShapedRecipe;
    import org.bukkit.plugin.Plugin;
    public class CraftingUtil implements Listener {
       private static CraftingUtil p;
       protected static Plugin plugin;
       private static List<CraftingRecipe> recipies = new ArrayList<CraftingRecipe>();
        * Call this in your onEnable. This allows for the events to work.
        * @param javaPlugin
        *  : Your main class.
       public static void initialize(Plugin javaPlugin) {
         p = new CraftingUtil();
         Bukkit.getPluginManager().registerEvents(p, javaPlugin);
         plugin = javaPlugin;
        * This adds a special recipe to the list. Make sure you register your
        * recipe before you add it to the list.
        * @param sr
        *  your ShapedRecipe
        * @return the CraftingRecipe instance.
       public static CraftingRecipe addRecipe(Recipe sr) {
         CraftingRecipe cr = p.new CraftingRecipe(sr);
         return cr;
        * Removes a special recipe from the list.
        * @param the
        *  Crafting Recipe instace.
       public static void removeRecipe(CraftingRecipe cr) {
       private void onCraft(PrepareItemCraftEvent event) {
         for (CraftingRecipe recipe : recipies) {
           if (event.getRecipe().getResult()
               && isShape(event.getInventory(),
                   (ShapedRecipe) recipe.getRecipe())) {
             for (int slot = 0; slot < recipe.getIngredients().length; slot++) {
               if (recipe.getIngredients()[slot] != null) {
                 if (!itemstacksSim(
                     recipe.getIngredients()[slot])) {
                       new ItemStack(Material.AIR));
       private boolean itemstacksSim(ItemStack i1, ItemStack i2) {
         if (i1.getType() == i2.getType())
           if (i1.getDurability() == i2.getDurability())
             if (i1.hasItemMeta() && i2.hasItemMeta()) {
               if ((!i1.getItemMeta().hasDisplayName() && !i2
                   || (i1.getItemMeta().hasDisplayName()
                       && i2.getItemMeta().hasDisplayName() && i1
                 if ((!i1.getItemMeta().hasLore() && !i2.getItemMeta()
                     || (i1.getItemMeta().hasLore()
                         && i2.getItemMeta().hasLore() && i1
                   return true;
             } else if (!(i1.hasItemMeta()) && !(i2.hasItemMeta())) {
               return true;
         return false;
       private boolean isShape(CraftingInventory inv, ShapedRecipe sr) {
         ItemStack[] mat = inv.getMatrix();
         ItemStack[] mat2 = new ItemStack[9];
         String[] str = sr.getShape();
         for (int i = 0; i < 9; i++) {
           for (Entry<Character, ItemStack> e : sr.getIngredientMap()
               .entrySet()) {
             if (mat[i] == null)
               mat[i] = new ItemStack(Material.AIR);
             if (str[i / 3].charAt(i % 3) == ' ') {
               mat2[i] = new ItemStack(Material.AIR);
             } else if (str[i / 3].charAt(i % 3) == e.getKey()) {
               mat2[i] = e.getValue();
             if (mat[i].getType() != mat2[i].getType())
               return false;
         return true;
       class CraftingRecipe {
         private Recipe sr;
         private ItemStack[] ing = new ItemStack[9];
         public CraftingRecipe(Recipe sr) {
           this.sr = sr;
          * This is how you specify if an object
          * @param slot
          * @param is
          * @return
         public CraftingRecipe setItemstack(int slot, ItemStack is) {
           ing[slot] = is;
           return this;
         public CraftingRecipe setItemstack(int row, int col, ItemStack is) {
           ing[(row * 3) + col] = is;
           return this;
         public ItemStack[] getIngredients() {
           return ing;
         public Recipe getRecipe() {
           return sr;
       public void onEnable() {
    //Create and register the base recipie
         ShapedRecipe sr = new ShapedRecipe(new ItemStack(Material.GOLD_AXE)).shape("=  ","  ","  ").setIngredient('=', Material.GOLD_AXE);
    //Initialize the util
    //Create your special itemstack
         ItemStack is = new ItemStack(Material.GOLD_AXE);
         ItemMeta im = is.getItemMeta();
         im.setDisplayName("special axe");
    //Add the special itemstack.
    8/3/2016: Init post
    -fixed displaynames and lores
    -added support for multiple recipes with same result.
    Last edited: Aug 3, 2016
  2. Offline


    Nice! As always, good work.
  3. Offline

    I Al Istannen

    I may be too tired to judge, but whatever.

    will throw a NPE if
    is null.
    Your if above doesn't account for this case, as this if can be true if any lore is not null (i1 or i2):
    "i1.getItemMeta().getLore() == null && i2.getItemMeta().getLore() == null".

    And there is "hasLore()" and "hasDisplayName()", which might look cleaner.

    And can't the displayname be null too?

    And what if you have two recipes becoming the same base item, but with different name/lore? In this case, only one will ever be checked.
    Like, if you have two recipes for a diamond. One with 3 coal, named "3", resulting in a diamond with the name "3" and one with 3 coal, but named "4", resulting in a diamond with the name "4".

    3 is added before 4.

    The loop will go through the custom recipes. It finds one whose output is a diamond and therefore matches the event ones.
    It is the "3" diamond. But 4 coals were inserted. Now the ingredients don't match up and the recipe will be cancelled. But it is a valid recipe, just for the "4" diamond.

    It is quite likely my line of thought is screwed up, so just say it.
  4. Offline



    This is something I considered throughout the creation of the util, but I could not figure out a way to compare them. I could not compare the Shapes from the crafting bench, so using the shape and char-key was out as well as grabbing the shape from the crafting table. In short, there is no way around this.

    [edit] Well, I was mistaken. I just remembered the Shapes and char-keys are saved for the ShapedRecipe. Will update soon.

    @I Al Istannen
    Fixed problem #2.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
    Last edited by a moderator: Aug 3, 2016
    I Al Istannen likes this.
  5. Offline


    Is this util no linkback?
    and will it work on furnace / shapeless recipes too?
    Last edited: Jan 7, 2017
  6. Offline


    @Zombie_Striker You've actually got to set the class CraftingRecipe to public in order for the Main class of the plugin to access it. So:

    public class CraftingRecipe {
Thread Status:
Not open for further replies.

Share This Page