[Util] Class for comparing Recipes properly - example with permissions restricted recipes

Discussion in 'Resources' started by Digi, Apr 29, 2013.

Thread Status:
Not open for further replies.
  1. I've seen a few people trying to check if the crafted recipe is their custom recipe and fail because it's complicated and it's not implemented in the Bukkit API, and since I've got enough experience with recipes I just figured I'd provide an utility class for it.... so here it is :p

    The class:
    https://github.com/THDigi/RecipeUtil/blob/master/ro/thehunters/digi/recipeUtil/RecipeUtil.java

    Example usage: https://github.com/THDigi/RecipeUtil/blob/master/ro/thehunters/digi/recipeUtil/testing/Testing.java

    Don't blindly rely on it, test if the results are what you expect when using it, if you find issues please post details here or on github.
     
  2. Offline

    Thej0y

    Working flawless so far! :D Thanks a lot man that will ease the work for a lot of people

    Got 72 more custome recipe to test. Will keep you informed!

    Thanks again!
     
  3. Offline

    xMinecraft

    Digi this've problem on line 189 and 190
     
  4. xMinecraft
    Oh wow :confused: github screwed my code up =)

    Fixed.
     
  5. Offline

    xMinecraft

    Digi in my plugin don't work D:

    main.java

    Code:java
    1. package net.xminecraft.items;
    2.  
    3. import java.util.logging.Logger;
    4.  
    5. import org.bukkit.Bukkit;
    6. import org.bukkit.Material;
    7. import org.bukkit.Server;
    8. import org.bukkit.enchantments.Enchantment;
    9. import org.bukkit.entity.HumanEntity;
    10. import org.bukkit.entity.Player;
    11. import org.bukkit.event.EventHandler;
    12. import org.bukkit.event.inventory.CraftItemEvent;
    13. import org.bukkit.event.inventory.PrepareItemCraftEvent;
    14. import org.bukkit.inventory.ItemStack;
    15. import org.bukkit.inventory.ShapedRecipe;
    16. import org.bukkit.inventory.meta.ItemMeta;
    17. import org.bukkit.plugin.java.JavaPlugin;
    18.  
    19. public final class main extends JavaPlugin {
    20. public ItemStack bastondeclerigo;
    21. public final Logger logger = Logger.getLogger("Minecraft");
    22. private ShapedRecipe newitem;
    23.  
    24. @Override
    25. public void onEnable() {
    26. ItemStack is;
    27. ItemMeta meta;
    28. Server server = this.getServer();
    29.  
    30. is = new ItemStack(Material.DIAMOND_HOE, 1);
    31. is.addUnsafeEnchantment(Enchantment.FIRE_ASPECT , 2);
    32. is.setDurability((short) 1);
    33. meta = is.getItemMeta();
    34. meta.setDisplayName("Baston de clerigo");
    35. is.setItemMeta(meta);
    36. bastondeclerigo = is;
    37.  
    38. newitem = new ShapedRecipe(bastondeclerigo).shape(new String[] { "dn ", " b ", " b " }).setIngredient('d', Material.DIAMOND).setIngredient('n', Material.NETHER_STAR).setIngredient('b', Material.BLAZE_ROD);
    39.  
    40. server.addRecipe(newitem);
    41. }
    42. @EventHandler
    43. public void craft(CraftItemEvent event) // event is triggered when clicking result slot
    44. {
    45. // the event has some issues with getRecipe(), it still returns the last recipe when clicking the empty result slot!
    46. ItemStack result = event.getCurrentItem();
    47. if(result == null || result.getTypeId() == 0)
    48. {
    49. return;
    50. }
    51.  
    52. // this is the part where it's checking if the current recipe is equal to the custom recipe.
    53. if(RecipeUtil.areEqual(event.getRecipe(), newitem))
    54. {
    55. // And send a message to all players as an example.
    56. Bukkit.broadcastMessage(event.getWhoClicked().getName() + " crafted a custom recipe making " + event.getCurrentItem() + " !");
    57. }
    58. }
    59. // Practical example (checking permissions)
    60. @EventHandler
    61. public void preCraft(PrepareItemCraftEvent event) // event is triggered when a recipe is found after placing ingredients
    62. {
    63. // Storing the equality result because it's not cheap to compare recipes.
    64. // I'm doing this because I'm using it twice, if you're using it only once you don't need to store it.
    65. boolean equal = RecipeUtil.areEqual(event.getRecipe(), newitem);
    66.  
    67. // confirmation message of event triggering and recipe equality
    68. System.out.print("(debug) recipes equal = " + equal);
    69.  
    70. if(equal)
    71. {
    72. HumanEntity human = event.getView().getPlayer();
    73.  
    74. // check if crafter has permission...
    75. if(!human.hasPermission("craft.bastondeclerigo"))
    76. {
    77. // basically cancels the event
    78. event.getInventory().setResult(null);
    79.  
    80. // need to check because it could be NPCs or something
    81. if(human instanceof Player)
    82. {
    83. Player player = (Player)human;
    84. player.sendMessage("Need permission to craft this!");
    85. }
    86. }
    87. }
    88. }
    89. @Override
    90. public void onDisable() {
    91. this.logger.info("Items xMinecraft desactivado.");
    92. }
    93.  
    94. }


    RecipeUtil.java

    Code:java
    1. package net.xminecraft.items;
    2.  
    3. import java.util.Arrays;
    4. import java.util.List;
    5. import java.util.Map;
    6.  
    7. import org.bukkit.inventory.FurnaceRecipe;
    8. import org.bukkit.inventory.ItemStack;
    9. import org.bukkit.inventory.Recipe;
    10. import org.bukkit.inventory.ShapedRecipe;
    11. import org.bukkit.inventory.ShapelessRecipe;
    12.  
    13. /**
    14.  * Utility class to compare Bukkit recipes.<br>
    15.  * Useful for identifying your recipes in events, where recipes are re-generated in a diferent manner.
    16.  *
    17.  * @version R1.3
    18.  * @author Digi
    19.  */
    20. public class RecipeUtil
    21. {
    22. /**
    23.   * The wildcard data value for ingredients.<br>
    24.   * If this is used as data value on an ingredient it will accept any data value.
    25.   */
    26. public static final short DATA_WILDCARD = Short.MAX_VALUE;
    27.  
    28. /**
    29.   * Checks if both recipes are equal.<br>
    30.   * Compares both ingredients and results.<br>
    31.   * <br>
    32.   * NOTE: If both arguments are null it returns true.
    33.   *
    34.   * @param recipe1
    35.   * @param recipe2
    36.   * @return true if ingredients and results match, false otherwise.
    37.   * @throws IllegalArgumentException
    38.   * if recipe is other than ShapedRecipe, ShapelessRecipe or FurnaceRecipe.
    39.   */
    40. public static boolean areEqual(Recipe recipe1, Recipe recipe2)
    41. {
    42. if(recipe1 == recipe2)
    43. {
    44. return true; // if they're the same instance (or both null) then they're equal.
    45. }
    46.  
    47. if(recipe1 == null || recipe2 == null)
    48. {
    49. return false; // if only one of them is null then they're surely not equal.
    50. }
    51.  
    52. if(!recipe1.getResult().equals(recipe2.getResult()))
    53. {
    54. return false; // if results don't match then they're not equal.
    55. }
    56.  
    57. return match(recipe1, recipe2); // now check if ingredients match
    58. }
    59.  
    60. /**
    61.   * Checks if recipes are similar.<br>
    62.   * Only checks ingredients, not results.<br>
    63.   * <br>
    64.   * NOTE: If both arguments are null it returns true. <br>
    65.   *
    66.   * @param recipe1
    67.   * @param recipe2
    68.   * @return true if ingredients match, false otherwise.
    69.   * @throws IllegalArgumentException
    70.   * if recipe is other than ShapedRecipe, ShapelessRecipe or FurnaceRecipe.
    71.   */
    72. public static boolean areSimilar(Recipe recipe1, Recipe recipe2)
    73. {
    74. if(recipe1 == recipe2)
    75. {
    76. return true; // if they're the same instance (or both null) then they're equal.
    77. }
    78.  
    79. if(recipe1 == null || recipe2 == null)
    80. {
    81. return false; // if only one of them is null then they're surely not equal.
    82. }
    83.  
    84. return match(recipe1, recipe2); // now check if ingredients match
    85. }
    86.  
    87. private static boolean match(Recipe recipe1, Recipe recipe2)
    88. {
    89. if(recipe1 instanceof ShapedRecipe)
    90. {
    91. if(recipe2 instanceof ShapedRecipe == false)
    92. {
    93. return false; // if other recipe is not the same type then they're not equal.
    94. }
    95.  
    96. ShapedRecipe r1 = (ShapedRecipe)recipe1;
    97. ShapedRecipe r2 = (ShapedRecipe)recipe2;
    98.  
    99. // convert both shapes and ingredient maps to common ItemStack array.
    100. ItemStack[] matrix1 = shapeToMatrix(r1.getShape(), r1.getIngredientMap());
    101. ItemStack[] matrix2 = shapeToMatrix(r2.getShape(), r2.getIngredientMap());
    102.  
    103. if(!Arrays.equals(matrix1, matrix2)) // compare arrays and if they don't match run another check with one shape mirrored.
    104. {
    105. mirrorMatrix(matrix1);
    106.  
    107. return Arrays.equals(matrix1, matrix2);
    108. }
    109.  
    110. return true; // ingredients match.
    111. }
    112. else if(recipe1 instanceof ShapelessRecipe)
    113. {
    114. if(recipe2 instanceof ShapelessRecipe == false)
    115. {
    116. return false; // if other recipe is not the same type then they're not equal.
    117. }
    118.  
    119. ShapelessRecipe r1 = (ShapelessRecipe)recipe1;
    120. ShapelessRecipe r2 = (ShapelessRecipe)recipe2;
    121.  
    122. // get copies of the ingredient lists
    123. List<ItemStack> find = r1.getIngredientList();
    124. List<ItemStack> compare = r2.getIngredientList();
    125.  
    126. if(find.size() != compare.size())
    127. {
    128. return false; // if they don't have the same amount of ingredients they're not equal.
    129. }
    130.  
    131. for(ItemStack item : compare)
    132. {
    133. if(!find.remove(item))
    134. {
    135. return false; // if ingredient wasn't removed (not found) then they're not equal.
    136. }
    137. }
    138.  
    139. return find.isEmpty(); // if there are any ingredients not removed then they're not equal.
    140. }
    141. else if(recipe1 instanceof FurnaceRecipe)
    142. {
    143. if(recipe2 instanceof FurnaceRecipe == false)
    144. {
    145. return false; // if other recipe is not the same type then they're not equal.
    146. }
    147.  
    148. FurnaceRecipe r1 = (FurnaceRecipe)recipe1;
    149. FurnaceRecipe r2 = (FurnaceRecipe)recipe2;
    150.  
    151. //return (r1.getInput().equals(r2.getInput())); // TODO use this when furnace data PR is pulled
    152. return r1.getInput().getTypeId() == r2.getInput().getTypeId();
    153. }
    154. else
    155. {
    156. throw new IllegalArgumentException("Unsupported recipe type: '" + recipe1 + "', update this class!");
    157. }
    158. }
    159.  
    160. private static ItemStack[] shapeToMatrix(String[] shape, Map<Character, ItemStack> map)
    161. {
    162. ItemStack[] matrix = new ItemStack[9];
    163. int slot = 0;
    164.  
    165. for(int r = 0; r < shape.length; r++)
    166. {
    167. for(char col : shape[r].toCharArray())
    168. {
    169. matrix[slot] = map.get(col);
    170. slot++;
    171. }
    172.  
    173. slot = ((r + 1) * 3);
    174. }
    175.  
    176. return matrix;
    177. }
    178.  
    179. private static void mirrorMatrix(ItemStack[] matrix)
    180. {
    181. ItemStack tmp;
    182.  
    183. for(int r = 0; r < 3; r++)
    184. {
    185. tmp = matrix[(r * 3)];
    186. matrix[(r * 3)] = matrix[(r * 3) + 2];
    187. matrix[(r * 3) + 2] = tmp;
    188. }
    189. }
    190. }


    plugin.yml

    Code:
    name: ItemsxMinecraft
    main: net.xminecraft.items.main
    author: Lopezito
    version: 1.0
    description: Creador de Items
    website: http://xminecraft.net
    permissions:
      craft.bastondeclerigo:
        default: op
    permissions.yml(PEX)

    Code:
    groups:
      default:
        default: true
        permissions:
        - modifyworld.*
    users:
      lopezito: {}
    
    I can crafting the newitem without op and without permissions
     
  6. Offline

    MineCrypt

    Thank you for this! You suggested it to me on my thread, and I think it will work perfectly!
     
Thread Status:
Not open for further replies.

Share This Page