Solved Itemstack help

Discussion in 'Plugin Development' started by TekxWolf, Jul 12, 2015.

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

    TekxWolf

    I am currently stuck badly. So My plugin gets a material from the config and using that it assigns it to a slot in a system Inventory and to do so I use ItemStack and ItemMeta. The error occurs when i try to initiate the inventory making.

    My PoV In-Game (open)

    Code:
    [23:26:29] [Client thread/INFO]: [CHAT] TekxWolf joined the game
    [23:26:41] [Client thread/INFO]: [CHAT] [EGui] Version [v0.01]
    [23:26:50] [Client thread/INFO]: [CHAT] ---------- Welcome to EGui ----------
    [23:26:50] [Client thread/INFO]: [CHAT] /EGui  - Shows plugin Version
    [23:26:50] [Client thread/INFO]: [CHAT] /EGui info - Shows plugin information
    [23:26:50] [Client thread/INFO]: [CHAT] /EGui help  -  Shows this help page
    [23:26:50] [Client thread/INFO]: [CHAT] /EGui test  - Initiates the gui
    [23:26:50] [Client thread/INFO]: [CHAT] ----------------------------------
    [23:26:58] [Client thread/INFO]: [CHAT] ---------- EGui Plugin information ----------
    [23:26:58] [Client thread/INFO]: [CHAT] Plugin Name: EasyGui
    [23:26:58] [Client thread/INFO]: [CHAT] Plugin Version: v0.01
    [23:26:58] [Client thread/INFO]: [CHAT] Plugin page: [Insert URL here]
    [23:26:58] [Client thread/INFO]: [CHAT] Like this plugin? Want to help me make more?
    [23:26:58] [Client thread/INFO]: [CHAT] Donate to me here: [Insert URL Here]
    [23:26:58] [Client thread/INFO]: [CHAT] ----------------------------------------
    [23:27:06] [Client thread/INFO]: [CHAT] An internal error occurred while attempting to perform this command

    PoV from Console (open)
    Code:
    [23:26:41 INFO]: TekxWolf issued server command: /egui
    [23:26:50 INFO]: TekxWolf issued server command: /egui help
    [23:26:58 INFO]: TekxWolf issued server command: /egui info
    [23:27:06 INFO]: TekxWolf issued server command: /egui test
    [23:27:06 ERROR]: null
    org.bukkit.command.CommandException: Unhandled exception executing command 'egui' in plugin EasyGui v0.01
       at org.bukkit.command.PluginCommand.execute(PluginCommand.java:46) ~[spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:141) ~[spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at org.bukkit.craftbukkit.v1_8_R3.CraftServer.dispatchCommand(CraftServer.java:642) ~[spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at net.minecraft.server.v1_8_R3.PlayerConnection.handleCommand(PlayerConnection.java:1135) [spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at net.minecraft.server.v1_8_R3.PlayerConnection.a(PlayerConnection.java:970) [spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at net.minecraft.server.v1_8_R3.PacketPlayInChat.a(PacketPlayInChat.java:45) [spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at net.minecraft.server.v1_8_R3.PacketPlayInChat.a(PacketPlayInChat.java:1) [spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at net.minecraft.server.v1_8_R3.PlayerConnectionUtils$1.run(SourceFile:13) [spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_45]
       at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_45]
       at net.minecraft.server.v1_8_R3.SystemUtils.a(SystemUtils.java:19) [spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at net.minecraft.server.v1_8_R3.MinecraftServer.B(MinecraftServer.java:718) [spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at net.minecraft.server.v1_8_R3.DedicatedServer.B(DedicatedServer.java:367) [spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at net.minecraft.server.v1_8_R3.MinecraftServer.A(MinecraftServer.java:657) [spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:560) [spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at java.lang.Thread.run(Thread.java:745) [?:1.8.0_45]
    Caused by: java.lang.NullPointerException
       at org.bukkit.inventory.ItemStack.<init>(ItemStack.java:68) ~[spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       at io.github.TekxWolf.EasyGui.EasyGui.onCommand(EasyGui.java:89) ~[?:?]
       at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[spigot-1.8.7.jar:git-Spigot-f928e7a-994b2aa]
       ... 15 more
    

    Main Class (open)

    Code:
    if (args[0].equalsIgnoreCase(Command)){
                        for(int a= 1; a <= 54; a++){
                            String NamePath = String.format("Item%d.Name", a);
                            String Name = this.getConfig().getString(NamePath);
                            // Format the path to accordance with the integer 'a
                            // then get the string associated with the path
                            String AmountPath = String.format("Item%d.Amount", a);
                            int Amount = this.getConfig().getInt(AmountPath);
                            // Format the path to accordance with the integer 'a
                            // then get the string associated with the path
                            String LorePath = String.format("Item%d.Lore", a);
                            List<String> Lore = this.getConfig().getStringList(LorePath);
                            // Format the path to accordance with the integer 'a
                            // then get the string associated with the path'
                            String MaterialPath = String.format("Item%d.Lore", a);
                            Material material = Material.getMaterial(getConfig().getString(MaterialPath));
                            ItemStack item = new ItemStack(material, Amount);
                            ItemMeta itemMeta = item.getItemMeta();
                            itemMeta.setDisplayName(Name);
                            itemMeta.setLore(Lore);
                            item.setItemMeta(itemMeta);
                            Inv.setItem(a, item);
                            // This is setting each item in its slot in accordance to the Config.yml
                        }
                        p.openInventory(Inv);
                        return true;
                    }

    Config.yml (open)

    Code:
    # This is the command to use the plugin
    # Important that the command is not used by any other plugin
    # Otherwise it will override this one
    # Example:
    #   Command: "warps"
    Command: "test"
    # This is the basic Setup
    # For doing items
    #
    # For Item:
    # Use this page or the item ID's http://kiwike.se/wiki/index.php/Item_Names
    # Use the Bukkit Block Name
    # Give options 'null' if you have nothing you want to out there
    # Example:
    #    Lore: null
    Item1:
      Name: "Item One"
      Lore:
        - "This"
        - "Is"
        - "Item One"
      Item: "DIAMOND"
      Amount: 1


    The error on line 89 is:
    Code:
    ItemStack item = new ItemStack(material, Amount);
    I found these two threads:
    https://bukkit.org/threads/setting-display-name-lores-of-itemstacks.147292/
    https://bukkit.org/threads/how-to-get-a-material-type-from-a-yml-config-file.163834/

    To my knowledge all the syntax and the methods are correct but I just don't understand what's causing this error
    Any help would be greatly appreciated

    @MOMOTHEREAL @Konato_K
    You two helped me last time to solve my stupid bug, I don't suppose you are knowledable on ItemStack and ItemMeta methods and classes?

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 12, 2016
  2. Offline

    Totom3

    @TekxWolf The variable material is null, which causes an NPE in ItemStack's constructor. Material.getMaterial() failed because you passed a null value: the expression getConfig().getString(MaterialPath) evaluates to null because once a reaches 2, Bukkit no longer finds a value in the config associated with the path "Item2.Lore" (you made a mistake there, should be "Material" instead of "Lore"). Your main mistake was to assume there is a defined item stack for each slot. In your case, only the item at slot 1 was defined.

    In order to fix this, you will need to check for each value of a if an item was defined. For that you will need to use getConfigurationSection("Item"+a) and get the return value. If said return value is null, then no item was defined for the current value of a, stop there. If not, proceed. Also since you're extracting the data from the config, which server owners may have edited, it would be safer to add checks in order to make sure the said data is valid. For example, I could change the amount to -1, or even worse, add an invalid material name.
     
  3. Offline

    TekxWolf

    @Totom3 I kind of understand this but could you either dumb it down for me or show me an example? Thanks atleast for replying

    Oh after reading it over a lot of times I think I finally understood most of it. I see now where the path configuration got messed up.
    So would I redo the config.yml and store item# under a "master" part like so
    Code:
    Items:
      Item1:
        Name: "Item One"
        Lore:
          - "This"
          - "Is"
          - "Item One"
        Item: "DIAMOND"
        Amount: 1
      Item2:
        Name: "Item Two"
        Lore:
          - "This"
          - "Is"
          - "Item Two"
        Item: "DIAMOND"
        Amount: 1
    and then use
    Code:
    int ItemSlots = getConfig().getConfigurationSection("Items").getKeys(false);
    Is that what you mean?
    Well since you are offline I guess I'll just test it and see what happens :p

    Okay so I got the NPE error fixed and fixed up my loop
    Code:
    if (args[0].equalsIgnoreCase(Command)){
       for(int a= 1; a <= 64; a++){
           String Checker = String.format("Item&d",a);
           if(getConfig().contains(Checker)){
             String NamePath = String.format("Item%d.Name", a);
             String Name = this.getConfig().getString(NamePath);
             // Format the path to accordance with the integer 'a
             // then get the string associated with the path
             String AmountPath = String.format("Item%d.Amount", a);
             int Amount = this.getConfig().getInt(AmountPath);
             // Format the path to accordance with the integer 'a
             // then get the string associated with the path
             String LorePath = String.format("Item%d.Lore", a);
             List<String> Lore = this.getConfig().getStringList(LorePath);
             // Format the path to accordance with the integer 'a
             // then get the string associated with the path'
             String MaterialPath = String.format("Item%d.Item", a);
             Material material = Material.getMaterial(getConfig().getString(MaterialPath));
             ItemStack item = new ItemStack(material, Amount);
             ItemMeta itemMeta = item.getItemMeta();
             itemMeta.setDisplayName(Name);
             itemMeta.setLore(Lore);
             item.setItemMeta(itemMeta);
             Inv.setItem(a, item);
             // This is setting each item in its slot in accordance to the Config.yml
           } else {}
         }
         p.openInventory(Inv);
         return true;
       }
    But now i am faced with the issue that the items do not show in the Inv inventory

    After running a test with commenting out the for loop and just having
    Code:
    ItemStack item = new ItemStack(Material.APPLE, 1);
    p.openInventory(Inv);
    return true;
    I discovered that the problem wasnt actually the code inside the for loop but literally the for loop. It seems to not be functioning properly, either that or the String.format isnt working how i think it should

    Update: I learned that this code bit " getConfig().contains(Checker) " Is actually returning a value of 0. This I am not entirely sure why but could anyone help me with it?
    Or should i just make 54 possible item slots in the Config.yml ?

    Okay so I redid the entire code method
    This code works:
    Code:
    if (args[0].equalsIgnoreCase(Command)){
                        for(String s : getConfig().getKeys(false)){
                            String Name = this.getConfig().getString(s+".Name");
                            int Amount = this.getConfig().getInt(s+".Amount");
                            List<String> Lore = this.getConfig().getStringList(s+".Lore");
                            //String MaterialItem = getConfig().getString(s+".Item");
                            //Material material = Material.getMaterial(MaterialItem);
                            ItemStack item = new ItemStack(Material.ANVIL, Amount);
                            ItemMeta itemMeta = item.getItemMeta();
                            itemMeta.setDisplayName(Name);
                            itemMeta.setLore(Lore);
                            item.setItemMeta(itemMeta);
                            Inv.addItem(item);
                        }
                        p.openInventory(Inv);
                        return true;
                    }
    But this one does not:
    Code:
    if (args[0].equalsIgnoreCase(Command)){
                        for(String s : getConfig().getKeys(false)){
                            String Name = this.getConfig().getString(s+".Name");
                            int Amount = this.getConfig().getInt(s+".Amount");
                            List<String> Lore = this.getConfig().getStringList(s+".Lore");
                            String MaterialItem = getConfig().getString(s+".Item");
                            Material material = Material.getMaterial(MaterialItem);
                            ItemStack item = new ItemStack(material, Amount);
                            ItemMeta itemMeta = item.getItemMeta();
                            itemMeta.setDisplayName(Name);
                            itemMeta.setLore(Lore);
                            item.setItemMeta(itemMeta);
                            Inv.addItem(item);
                        }
                        p.openInventory(Inv);
                        return true;
                    }
    This one also throws a NPE
    Could Anyone Explain why this is?
    Also here's a Picture of the first code block in action:
    [​IMG]

    EDIT: Sorry about all the posts, I got carried away with trying to fix it and idk i kept trying to tell myself through the thread or something xD

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 12, 2016
  4. public ItemStack createItem(Material mat, int amount) {
    ItemStack hi = new ItemStack(mat, amount);
    return item;
     
  5. Offline

    Totom3

    @TekxWolf Why not just use config.getConfigurationSection("Item"+a) ? It will give you the section of item#, or null if there isn't any. What I said earlier is that you should make checks each time you fetch data from a config. I wrote an example for you (feel free to remove comments & adapt the code to your needs):
    Example (open)
    Code:
    YamlConfiguration config = null;
    for (int a = __; a < __; ++a) {
        // will return section 'Item1', 'Item2', 'Item3', etc...
        ConfigurationSection section = config.getConfigurationSection("Item"+a);
        // if no item was defined for the current value of a, stop there
        if (section == null) {
            continue;
        }
      
        // notice how I use section.getString() instead of config.getString()
        // this is the equivalent of calling config.getString("Item"+a+".Name")
        String itemName = section.getString("Name");
      
        // same here, using section instead of config.
        // it is important however to make sure a valid itemstack amount was entered
        int amount = section.getInt("Amount");
        if (amount <= 0) {  // typically itemstacks can't have an amount of 0 or less
            // you have 3 options here:
            //    1. throw an exception (if you're not familiar with these, just forget this part)
            //    2. set a default value
            //    3. skip the current item
            // in any case I strongly recommend you log something like "An invalid item amount was entered for item "+a
            // so that server owners know
    
            getLogger().warning("An invalid item amount was entered for item "+a+" ( "+amount+" )");
    
            // 2. setting a default value:
            amount = 1;
            // 3. skipping the current item
            continue;
         }
    
        // this is safe: lore will never be null because getStringList() will return an empty list if nothing was defined
        List<String> lore = section.getStringList("Lore");
    
        // now the tricky part comes: getting the material
        // we need to make sure materialName represents an existing material
        String materialName = section.getString("Item");
    
        // that is however very easy. If materialName is not a valid material, the following expression will be null
        Material material = Material.getMaterial(materialName);
        if (material == null) {
             // you again have 3 options: throw an exception, set a default value, skip the current item (and log this)
             // here's an example of logging + skipping
             getLogger().warning("An invalid item material '"+materialName+"' was entered for item "+a+"; skipping");
             continue;
        }
      
        // only thing left now is to create your item stack & set it in your inventory
    }

    This code will not compile until you make a few changes such as the min/max value for a, and remove the examples I wrote when facing an invalid amount.

    By the way, I think inventory slots start at 0, not 1.

    Please show us your config.yml.
     
  6. Offline

    TekxWolf

    It does, thats why I changed the "setItem(item)" method to "addItem(item);"

    Heres the current setup for the method:
    Code:
    if (args[0].equalsIgnoreCase(Command)){
       for(String s : getConfig().getKeys(false)){
         String Name = getConfig().getString(s+".Name");
         int Amount = getConfig().getInt(s+".Quantity");
         if(Amount <= 0) {
           Amount = 1;
           continue;
         }
         if(getConfig().getString(s+".Item") == null){
           p.sendMessage(PluginPrefix + RED + " Error! Material provided is not valid! Please Check your Config.yml");
           p.sendMessage(PluginPrefix + RED + " Setting Default Value to DIAMOND");
           getLogger().warning(" Error! Material provided is not valid! Please Check your Config.yml");
           getLogger().warning(" Setting Default Value to DIAMOND");
           List<String> Lore = this.getConfig().getStringList(s+".Lore");
           ItemStack item = new ItemStack(Material.DIAMOND, Amount);
           ItemMeta itemMeta = item.getItemMeta();
           itemMeta.setDisplayName(Name);
           itemMeta.setLore(Lore);
           item.setItemMeta(itemMeta);
           Inv.addItem(item);
           continue;
         } else {
           List<String> Lore = this.getConfig().getStringList(s+".Lore");
           ItemStack item = new ItemStack(Material.valueOf(getConfig().getString(s+".Item")), Amount);
           ItemMeta itemMeta = item.getItemMeta();
           itemMeta.setDisplayName(Name);
           itemMeta.setLore(Lore);
           item.setItemMeta(itemMeta);
           Inv.addItem(item);
         }
       }
       p.openInventory(Inv);
       return true;
    }
    
    Like we did for the empty Item slot and Lore, I want to make a safety measure to check that the material in the config is infact a valid Material ID. I am not entirely sure how to do this so perhaps you can show me?

    This line " getConfig().getString(s+".Item") == null " Just checks that theres infact a path but no value put. I want to check whether or not the value assigned to 'Item' is a valid Material ID

    Also @Totom3 I tried using the method you put but it threw up an NPE

    Update:
    Everything works, still no valid material checker but i can live with that I suppose :p
    Right now I'm trying to work out "binding" commands to an Item
    Heres is the code I am trying to use:
    Code:
        @EventHandler
        public void onInventoryClick(InventoryClickEvent event) {
            Player player = (Player) event.getWhoClicked();
            if (event.getInventory().getTitle().equals("EGui")) {
                event.setCancelled(true);
                player.updateInventory();
            }
        }
    I want to know: How do I bind a Command to a specific Item?
    I saw this method: " Bukkit.getServer().dispatchCommand(arg0, arg1) "
    But I no Idea of how to use it and after an hour of digging through the Bukkit Forums and the Spigot API reference I still have no clue

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 12, 2016
  7. Offline

    Totom3

    @TekxWolf
    The method you mentioned will make a CommandSender execute a certain command. The first argument stands for the CommandSender (typically a player), and the second, the command to execute. An example would be:
    Code:
    Bukkit.dispatchCommand(player, "/say Hello!");
    Now I can see a few errors in the code you posted:

    • You must use setItem, not addItem. Why? Take the following scenario: an item is defined for slots 1 and 5. Code gets to slot 1, parses the item, adds it to the inventory. No item for slot 2: skip. Same for slots 3 and 4. It gets to slot 5, parses the item, adds it to inventory, but since you're using addItem, which adds the item to the first empty slot, item is added to slot 2 instead of 5.
    • At the line 7, you set a default value for the amount, and at line 8, you skip the current item. There's no need to skip if you're setting a default value and there's no need to set a default value if you skip. Choose one of the two. You also might want to add a warning as you did for the material.
    • Speaking of the material part, there is an unnecessary continue statement at line 22.
    • What you did at line 10 will make sure a material was entered, but it will not make sure it's valid. The latter is very easy to do:
    The method you are using (Material.valueOf()) will throw IllegalArgumentException if the entered material is not valid. You need to surround the call with a try-catch statement:
    Code:
    Material material;
    try {
         material = Material.valueOf(....);
    } catch (IllegalArgumentException ex) {
         // set a default value OR skip
         // and preferably log
    }
    If you are not familiar with this syntax, do NOT copy/paste it without understanding it. Lookup "java try-catch".

    Finally, it's no help to say you got a NPE if you don't post along with it the stack-trace.
     
  8. Offline

    TekxWolf

    I am familiar with it, I use it to "paste" in the default config on first load if there isnt an existing one present.
    Code:
      @Override
       public void onEnable() {
         getServer().getPluginManager().registerEvents(this, this);
         File file = new File(getDataFolder(), "config.yml");
         if (!file.exists()) {
           try {
             getConfig().options().copyDefaults(true);
             saveConfig();
           } catch (Exception e) {
             e.printStackTrace();
           }
         }
       }
    Also For the Bukkit DispatchCommand I used " player.performCommand(arg0); "
    It requires the user to have the permission to use the command so I think thats good?
    This is what I mean:
    InventoryClickEvent (open)

    Code:
    @EventHandler
        public void onInventoryClick(InventoryClickEvent event) {
            Player player = (Player) event.getWhoClicked();
            if (event.getInventory().getTitle().equals("EGui")) {
                event.setCancelled(true);
                player.updateInventory();
                int value = event.getRawSlot();
                switch (value) {
                case 0:
                    player.closeInventory();
                    player.performCommand(getConfig().getString("Item1.Command"));
                    break;
                case 1:
                    player.closeInventory();
                    player.performCommand(getConfig().getString("Item2.Command"));
                    break;
                case 2:
                    player.closeInventory();
                    player.performCommand(getConfig().getString("Item3.Command"));
                    break;
                case 3:
                    player.closeInventory();
                    player.performCommand(getConfig().getString("Item4.Command"));
                    break;
                case 4:
                    player.closeInventory();
                    player.performCommand(getConfig().getString("Item5.Command"));
                    break;
                case 5:
                    player.closeInventory();
                    player.performCommand(getConfig().getString("Item6.Command"));
                    break;
                case 6:
                    player.closeInventory();
                    player.performCommand(getConfig().getString("Item7.Command"));
                    break;
                case 7:
                    player.closeInventory();
                    player.performCommand(getConfig().getString("Item8.Command"));
                    break;
                case 8:
                    player.closeInventory();
                    player.performCommand(getConfig().getString("Item9.Command"));
                    break;
                }
            }
        }

    Everything works and I'm adding in that code bit you said I should try
    @Totom3
    I changed it a little to suit me how i think it should like so:
    Code:
    try {
                                        List<String> Lore = this.getConfig().getStringList(s + ".Lore");
                                        ItemStack item = new ItemStack(Material.valueOf(getConfig().getString(s + ".Item")),
                                                Amount);
                                        ItemMeta itemMeta = item.getItemMeta();
                                        itemMeta.setDisplayName(Name);
                                        itemMeta.setLore(Lore);
                                        item.setItemMeta(itemMeta);
                                        Inv.addItem(item);
                                    } catch (IllegalArgumentException ex) {
                                        p.sendMessage(PluginPrefix + " Error, Value Input is not Valid. Changing to default (Diamond)");
                                        List<String> Lore = this.getConfig().getStringList(s + ".Lore");
                                        ItemStack item = new ItemStack(Material.DIAMOND, Amount);
                                        ItemMeta itemMeta = item.getItemMeta();
                                        itemMeta.setDisplayName(Name);
                                        itemMeta.setLore(Lore);
                                        item.setItemMeta(itemMeta);
                                        Inv.addItem(item);
                                    }
    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 12, 2016
Thread Status:
Not open for further replies.

Share This Page