Solved Dispenser Inventory Problems

Discussion in 'Spigot Plugin Development' started by abilnfMC, May 5, 2017.

  1. Hello!
    I tried to make it, that dispensers can place blocks, and it worked (relativile) well, but now i have a problem.
    The blocks are being placed, and the 1 item removes, but if only 1 item is in the dispenser, nothing happenes.

    Here the (I think) most important code parts:
    Code (Text):

    //Test if it's a block
            if (e.getItem().getType().getId() < 256)
            {
                //Get dispenser
                Block b = e.getBlock();
                Dispenser disp = (Dispenser) b.getState().getData();
                BlockFace face = (disp.getFacing());
                //Get placed-block location
                Location loc = b.getLocation();
                loc.add(face.getModX(), face.getModY(), face.getModZ());
    Code (Text):
    //Cancel event & get inventory

    e.setCancelled(true);
                    Block block = e.getBlock();
                    org.bukkit.block.Dispenser dispenser = (org.bukkit.block.Dispenser) block.getState();;
                    Inventory inv = dispenser.getInventory();
                    ItemStack[] items = inv.getContents();
    Code (Text):
    //Get random slot

    //Test if the inventory is empty
    if (items == null || (items[0] == null && items[1] == null && items[2] == null && items[3] == null  && items[4] == null  && items[5] == null  && items[6] == null  && items[7] == null  && items[8] == null)) return;

    //get slot
                    int slot = (int) (Math.random()*9);
                    while (items[slot] == null)
                    {
                        slot = (int) (Math.random()*9);
                    }
    Code (Text):
    //Placing Block

    loc.getWorld().getBlockAt(loc).setType(stack.getType()); loc.getWorld().getBlockAt(loc).setData((byte) stack.getDurability());

    //UpdatingBlock
                    Material bak = loc.getWorld().getBlockAt(loc.add(0, 1, 0)).getType();
                    byte bakb = loc.getWorld().getBlockAt(loc).getData();
                    loc.getWorld().getBlockAt(loc).setType(Material.AIR);
                    loc.getWorld().getBlockAt(loc).setType(Material.GRASS);
                    loc.getWorld().getBlockAt(loc).setType(bak);
                    loc.getWorld().getBlockAt(loc).setData(bakb);
    Code (Text):
    //Remove 1 item

    if (stack.getAmount() != 1)
                    {
                        stack.setAmount(stack.getAmount() - 1);
                        dispenser.getInventory().setItem(slot, stack);
                    }
                    else
                    {
                        //Tried different things here: This, set Slot to air, set slot to null
                        stack.setAmount(stack.getAmount() - 1);
                        dispenser.getInventory().setItem(slot, stack);
                    }
    package blockplacer.main;

    import org.bukkit.Bukkit;
    import org.bukkit.Location;
    import org.bukkit.Material;
    import org.bukkit.block.Block;
    import org.bukkit.block.BlockFace;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.EventPriority;
    import org.bukkit.event.Listener;
    import org.bukkit.event.block.BlockDispenseEvent;
    import org.bukkit.inventory.Inventory;
    import org.bukkit.inventory.ItemStack;
    import org.bukkit.material.Dispenser;
    import org.bukkit.plugin.java.JavaPlugin;

    public class BlockPlacer extends JavaPlugin implements Listener
    {
    @Override
    public void onEnable()
    {
    Bukkit.getPluginManager().registerEvents(this, this);
    }

    @SuppressWarnings("deprecation")
    @EventHandler(priority = EventPriority.HIGHEST)
    public void BlockDispense(BlockDispenseEvent e)
    {
    if (e.getItem().getType().getId() < 256 || e.getItem().getType() == Material.POTATO_ITEM || e.getItem().getType() == Material.CARROT_ITEM || e.getItem().getType() == Material.SEEDS || e.getItem().getType() == Material.PUMPKIN_SEEDS || e.getItem().getType() == Material.MELON_SEEDS || e.getItem().getType() == Material.BEETROOT_SEEDS || e.getItem().getType() == Material.SAPLING)
    {
    Block b = e.getBlock();
    Dispenser disp = (Dispenser) b.getState().getData();
    BlockFace face = (disp.getFacing());
    Location loc = b.getLocation();
    loc.add(face.getModX(), face.getModY(), face.getModZ());

    if (!loc.getWorld().getBlockAt(loc).getType().isSolid() && !(loc.getWorld().getBlockAt(loc).getType() == Material.RAILS) && !(loc.getWorld().getBlockAt(loc).getType() == Material.POWERED_RAIL) && !(loc.getWorld().getBlockAt(loc).getType() == Material.DETECTOR_RAIL) && !(loc.getWorld().getBlockAt(loc).getType() == Material.ACTIVATOR_RAIL) && !(loc.getWorld().getBlockAt(loc).getType() == Material.POTATO) && !(loc.getWorld().getBlockAt(loc).getType() == Material.CARROT) && !(loc.getWorld().getBlockAt(loc).getType() == Material.PUMPKIN_STEM) && !(loc.getWorld().getBlockAt(loc).getType() == Material.MELON_STEM) && !(loc.getWorld().getBlockAt(loc).getType() == Material.BEETROOT_BLOCK) && !(loc.getWorld().getBlockAt(loc).getType() == Material.CROPS) && !(loc.getWorld().getBlockAt(loc).getType() == Material.SAPLING))
    {
    e.setCancelled(true);
    Block block = e.getBlock();
    org.bukkit.block.Dispenser dispenser = (org.bukkit.block.Dispenser) block.getState();;
    Inventory inv = dispenser.getInventory();
    ItemStack[] items = inv.getContents();
    /*if (items == null || (items[0] == null && items[1] == null && items[2] == null && items[3] == null && items[4] == null && items[5] == null && items[6] == null && items[7] == null && items[8] == null))
    {
    if (e.getItem() != null)
    {
    items[0] = e.getItem();
    }
    }*/
    if (items == null || (items[0] == null && items[1] == null && items[2] == null && items[3] == null && items[4] == null && items[5] == null && items[6] == null && items[7] == null && items[8] == null)) return;
    int slot = (int) (Math.random()*9);
    while (items[slot] == null)
    {
    slot = (int) (Math.random()*9);
    }
    ItemStack stack = items[slot];
    if (stack.getType() == Material.POTATO_ITEM)
    {
    loc.getWorld().getBlockAt(loc).setType(Material.POTATO);
    loc.getWorld().getBlockAt(loc).setData((byte) stack.getDurability());
    }
    else if (stack.getType() == Material.CARROT_ITEM)
    {
    loc.getWorld().getBlockAt(loc).setType(Material.CARROT);
    loc.getWorld().getBlockAt(loc).setData((byte) stack.getDurability());
    }
    else if (stack.getType() == Material.SEEDS)
    {
    loc.getWorld().getBlockAt(loc).setType(Material.CROPS);
    loc.getWorld().getBlockAt(loc).setData((byte) stack.getDurability());
    }
    else if (stack.getType() == Material.PUMPKIN_SEEDS)
    {
    loc.getWorld().getBlockAt(loc).setType(Material.PUMPKIN_STEM);
    loc.getWorld().getBlockAt(loc).setData((byte) stack.getDurability());
    }
    else if (stack.getType() == Material.MELON_SEEDS)
    {
    loc.getWorld().getBlockAt(loc).setType(Material.MELON_STEM);
    loc.getWorld().getBlockAt(loc).setData((byte) stack.getDurability());
    }
    else if (stack.getType() == Material.BEETROOT_SEEDS)
    {
    loc.getWorld().getBlockAt(loc).setType(Material.BEETROOT_BLOCK);
    loc.getWorld().getBlockAt(loc).setData((byte) stack.getDurability());
    }
    else if (stack.getType() == Material.SAPLING)
    {
    loc.getWorld().getBlockAt(loc).setType(Material.SAPLING);
    loc.getWorld().getBlockAt(loc).setData((byte) stack.getDurability());
    }
    else
    {
    loc.getWorld().getBlockAt(loc).setType(stack.getType());
    loc.getWorld().getBlockAt(loc).setData((byte) stack.getDurability());
    }
    Material bak = loc.getWorld().getBlockAt(loc.add(0, 1, 0)).getType();
    byte bakb = loc.getWorld().getBlockAt(loc).getData();
    loc.getWorld().getBlockAt(loc).setType(Material.AIR);
    loc.getWorld().getBlockAt(loc).setType(Material.GRASS);
    loc.getWorld().getBlockAt(loc).setType(bak);
    loc.getWorld().getBlockAt(loc).setData(bakb);
    if (stack.getAmount() != 1)
    {
    stack.setAmount(stack.getAmount() - 1);
    dispenser.getInventory().setItem(slot, stack);
    }
    else
    {
    stack.setAmount(stack.getAmount() - 1);
    dispenser.getInventory().setItem(slot, stack);
    }
    }
    }
    }
    }

    I have an Idea why it could not work: If I put 4 items in the dispenser, activate it and make at the start of the event System.out.print(stack.getAmount()), it says 3. And if make a sysout for the slot that I generated randomly, it says it right. But if I have only 1 item, it always says: "Slot:0, amount:1".
    Just for the case that can help you

    I hope someone can help me!
    Thanks!
     
    #1 abilnfMC, May 5, 2017
    Last edited: May 5, 2017
  2. I've had the same problem with my plugin when handling with dispenser and tnt. You need to clear the inventory (If and ONLY if one item remain inside the dispenser inventory) with a 1tick delay, since during the event when is cancelled, the inventory is not updated and does not see the item change
     
  3. Ok, thanks.

    That's how I did it:
    Code (Text):
    //the variables items, itemcount, slot & stack are already defined

                    if (stack.getAmount() != 1)
                    {
                        stack.setAmount(stack.getAmount() - 1);
                        dispenser.getInventory().setItem(slot, stack);
                    }
                    else
                    {
                            Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable()
                            {
                             
                                @Override
                                public void run()
                                {
                                    itemcount = 0;
                                    for (int i = 0; i < 9; i++)
                                    {
                                        if (items[i] != null)
                                        {
                                            itemcount+=items[i].getAmount();
                                        }
                                    }
                                    if (itemcount == 1)
                                    {
                                        dispenser.getInventory().clear();
                                    }
                                    else
                                    {
                                        stack.setAmount(stack.getAmount() - 1);
                                        dispenser.getInventory().setItem(slot, stack);
                                    }
                                }
                            }, 1);
                    }
    That worked, exeptly with 1 issue:
    The removing now works, except if there are 2 items, that are not stacked.
    I have an idea why: Minecraft thinks, that the item is dispensed and removes it from the dispenser item list.
    If the items are stacked, it's no problem, because then it just removes one.
    But if the items are not stacked, it thinks that, because one item isn't anymore in list, there is only one item in the dispenser and clears the inventory.
    Now i made a little fix:

    Code (Text):
    boolean isMissing = true;
                    int freeslot = 0;
                    for (int i = 0; i < 9; i++)
                    {
                           ItemStack is = items[i];
                           if (is != null)
                           {
                            if (is.getType().equals(e.getItem().getType()))
                            {
                                if (is.getDurability() == e.getItem().getDurability())
                                {
                                       isMissing = false;
                                   }
                               }
                           }
                           else
                           {
                               freeslot = i;
                           }
                       }
                    if (isMissing) items[freeslot] = e.getItem();
    The fix tests goes through all items in the inv, and
    1. checks if there not null
    if so, it checks
    2. if the item is the same type as that one that would normally dispensed and
    3. checks for the same durability
    if that is the case, then i know that the item is in the inventory.
    And if not, I add the Item to inventory list (not the dispenser inventory, just to the list that the inv won't be cleared)

    And with this fix EVERYTHING except 1 little thing works:
    If there are 2 items of the same type but not stacked, because it thinks that it is already inside.

    So, any idea how I can fix this, or another way?
    I would have an idea: Just make a method that stacks the items. But, if it is possible, i would be happy for a better solution.

    Thanks for your help & I hope you have an Idea!
     
    #3 abilnfMC, May 5, 2017
    Last edited: May 5, 2017
  4. The way i did that, instead take the item that is being dispensed, take the first item in the inventory that fits your needs (Using a for loop and the getItem(Integer) method). If the inventory was empty, then i was moved to the item being dispensed and check if it's the one i need. This was the only i could fix it
     
  5. Ok, thanks.
    I tried it but it didn't really Work.
    Can you give me a little example?
     
  6. So, here the code i use for check which item use (cItem is my item manager, so the method you use is the one i use to fetch the name in the config)
    Code (Text):
            for(slot=0;slot<i.getSize();slot++)
            {
                if(i.getItem(slot)!=null)
                {
                    empty=false;
                    if(cItem.getByItem(i.getItem(slot))!=null)
                    {
                        item=i.getItem(slot);
                        break;
                    }
                }
            }
               
            if(item==null)
            {
                if(cItem.getByItem(e.getItem())==null && empty)
                {
                    return;
                }
                item=e.getItem();
            }

    And here the code to how i manage to remove the item or clear the inventory
    Code (Text):
            if(empty)
            {
                if(use!=1)
                {
                    return;
                }
                   
                new BukkitRunnable()
                {
                    @Override
                    public void run()
                    {
                        d.getInventory().clear();
                    }
                }.runTaskLater(pl, 1);
            }
            else
            {
                ItemStack tItem = i.getItem(slot);
                if(tItem.getAmount()+1<use)
                {
                    return;
                }
                tItem.setAmount(tItem.getAmount()-use);
                i.setItem(slot, tItem);
            }
     
  7. Thanks :)
    I can't Test ist now, because I'm not at Home, but it in can i will write if it worked!
     
  8. Yeaaah, it works!
    Thank you so much!
     
  9. Glad to been able to help :D
     
    • Winner Winner x 1