Solved Unable to modify Shulker Box Inventory contents?

Discussion in 'Spigot Plugin Development' started by Qruet, May 25, 2018.

  1. I'm fairly certain the answer is staring right at me, however I'm having rather a difficult time figuring out why my potential solutions to the issue aren't working. I believe the problem is that I'm passing a copy of the player instance or at least passing a copy of the contents (Itemstack instances), therefore the player's contents won't directly modify the original items when changed, however can be used to read what items the player may within their inventory. I of course thought I could resolve this by directly retrieving the player from using the static getPlayer(UUID id) method that the Bukkit class contains by using the id from the passed player. Perhaps someone can give me an idea why I'm having trouble modifying items from within a player's inventory/shulker box?

    Version Tested: Spigot - 1.12.2
    Stack trace: N/A


    Code (Text):
        public static boolean withdraw(Player p, double amount, boolean b) {

            Player player = Bukkit.getPlayer(p.getUniqueId()); //making sure I have the original player object (most likely not any different from the passed 'p' Player object)

            int blocks = getCurrencyAmount(player, ItemType.Block);
            int ingots = getCurrencyAmount(player, ItemType.Ingot);
            int nuggets = getCurrencyAmount(player, ItemType.Nugget);

            Bukkit.broadcastMessage("Blocks: " + blocks + " Ingots: " + ingots + " Nuggets: " + nuggets); //prints accurately

            double cBlocks = CurrencyItem.getCurrency(ItemType.Block);
            double cIngots = CurrencyItem.getCurrency(ItemType.Ingot);
            double cNuggets = CurrencyItem.getCurrency(ItemType.Nugget);

            double InvAmount = (blocks * cBlocks) + (ingots * cIngots) + (nuggets * cNuggets);

            if (InvAmount <= amount) {
                Bukkit.broadcastMessage("Clearing!");
                for (ItemStack item : player.getInventory().getContents()) {
                    if (CurrencyItem.isCurrency(item)) {
                        item.setType(Material.AIR); //doesn't modify ingame
                        item.setAmount(0);
                    } else if (isShulker(item)) {
                        BlockStateMeta meta = (BlockStateMeta) item.getItemMeta();
                        ShulkerBox box = (ShulkerBox) meta.getBlockState();
                        for (ItemStack i : box.getInventory().getContents()) {
                            if (CurrencyItem.isCurrency(i)) {
                                Bukkit.broadcastMessage("Removing: " + i); //this prints successfully
                                i.setAmount(0); //doesn't modify ingame
                                i.setType(Material.AIR);
                            }
                        }
                    }
                }
                if (PlayerInventory.getCursorData(player) != null) {
                    PlayerInventory.getCursorData(player).setCursor(null);
                }
                return false;
            }
            Bukkit.broadcastMessage("1");
    [...]

    EDIT:
     
    #1 Qruet, May 25, 2018
    Last edited: May 25, 2018
  2. getContents() returns an array, not a reference to the players inventory. You're modifying the array, I think you'd have to use setContents() after you've modified it.
     
  3. getContents() is a method from the inventory class. You're correct that it returns an array of items, but by modifying those instances directly which should be stored in the array, they should modify the item ingame as well unless those itemstacks are just clones of the original itemstack instance. I'm using the same exact method of retrieving the contents from the inventory and modifying the itemstack, and it works perfectly fine, however for some reason, it isnt performing the same within the withdraw method.
     
  4. Made a slight change to the code. This version only allows me to remove items from within the player's actual inventory but still unable to alter the contents within the shulkerbox.
    Code (Text):
            if (InvAmount <= amount) {
                Bukkit.broadcastMessage("Clearing!");
                for (ItemStack item : player.getInventory().getContents()) {
                    if (CurrencyItem.isCurrency(item)) {
                        item.setAmount(0); //removes item successfully
                    } else if (isShulker(item)) {
                        BlockStateMeta meta = (BlockStateMeta) item.getItemMeta();
                        ShulkerBox box = (ShulkerBox) meta.getBlockState();
                        for (ItemStack i : box.getInventory().getContents()) {
                            if (CurrencyItem.isCurrency(i)) {
                                Bukkit.broadcastMessage("Removing: " + i); // this prints successfully
                                i.setAmount(0); // doesn't modify ingame
                            }
                        }
                    }
                }
                if (PlayerInventory.getCursorData(player) != null) {
                    PlayerInventory.getCursorData(player).setCursor(null);
                }
                return false;
            }
     
     
  5. maybe just edit items in the block entity data?
     
  6. I've found this on the Spigot docs
    Code (Text):
    Inventory getInventory()
    Gets the inventory of the block represented by this block state.
    If the block was changed to a different type in the meantime, the returned inventory might no longer be valid.

    If this block state is not placed this will return the captured inventory snapshot instead.
    Specifically the last part.
    Which if I'm not mistaken means it IS returning a copy of the Shulker box inventory.
     
  7. So that would mean, unless I have the shulkerbox placed down as a block instead of being stored in the inventory as an item, it won't return the instance of the itemstacks within its inventory? So I guess the only way of resolving this is figuring out what items to remove, create a content list of my own, then set the box's inventory to that created list of contents since modifying those items won't update the original instance?
    Seems setting the box's inventory contents to everything it had before but without the items doesn't appear to work either. Perhaps its blockstate is a copy of the original instance as well, which would probably mean I would have to completely remove the shulker box from the player's inventory and add a new one with predefined contents already in the shulker box? It just seems a bit over the top. I would think bukkit/spigot would've come up with a simpler approach to this then simply totally removing and adding in the shulker box back into the player's inventory just to alter some contents. :unsure: I would like to know if there's a better approach before I conclude this as the only solution.
     
    #7 Qruet, May 25, 2018
    Last edited: May 25, 2018
  8. Aha, Just got it working. So, you have to set both the blockstate, and then the itemmeta. Weird workaround, but it works.
    You seem to understand just fine and this is mostly your code so we won't call this one spoonfeeding. This is what I've got- it works for me. Replace my quick methods with your proper ones.
    Sorry that it's a mess <3
    Code (Text):

    Bukkit.broadcastMessage("Clearing!");
    for (ItemStack item : player.getInventory().getContents()) {
        if(item != null && item.getType() != null) { //was running into NPE, oof
            if(item.getType() == Material.BLACK_SHULKER_BOX){//assuming your "isShulker" is just this
                BlockStateMeta meta = (BlockStateMeta) item.getItemMeta();
                ShulkerBox box = (ShulkerBox) meta.getBlockState();
           
                for (ItemStack i : box.getInventory().getContents()) {
                    if(i != null) {
                        if(i.getType() == Material.DIRT){
                            Bukkit.broadcastMessage("Removed: " + i);
                            i.setAmount(0);
                            Bukkit.broadcastMessage("It's air now!: " + i);
                        }                
                    }
                }
               
                meta.setBlockState(box);
                item.setItemMeta(meta);
            }
        }
    }
     
     
    • Winner Winner x 2
  9. Also left out that after updating the BlockMeta that the item's ItemMeta should be updated as well. I forgot that I was still using the Item's item meta so it makes sense why the item wasn't updating.