Solved Clearing a region of blocks without dropping items

Discussion in 'Plugin Development' started by skiithaw, Feb 21, 2018.

Thread Status:
Not open for further replies.
  1. Block.breakNaturally() doesn't trigger BlockBreakEvent. If you tell a block to break "naturally" or set its type to air, the blockbreakevent doesn't trigger. I assume because it's supposed to also record the player that broke it as well?

    I'm trying to make a region despawn without dropping anything (just like in world edit). Normal blocks are fine, but the problem is with I guess "non solid" things like torches, carpets, signs, that are dependant on adjacent blocks. So if the loop kills the block its attached to first, it pops off and subsequently drops an item.

    My idea was to add all the blocks to a hashmap as they get destroyed, and then check against that in the block break event. It works fine for manually breaking blocks from in minecraft, just not directly through code.

    I tried calling the blockbreakevent manually but it seems somewhat disconnected (and all the event.functions return useless data).

    Here's the code:
    Code:
    
    //Get size of region on each axis (B1 and B2 are the two corner blocks like in world edit)
    this.x_Size = Math.abs(B1.getX()-B2.getX())+1;
    this.y_Size = Math.abs(B1.getZ()-B2.getZ())+1;
    this.z_Size = Math.abs(B1.getY()-B2.getY())+1;
    
    //Find the 'smallest' corner
    Block BSX; Block BSY; Block BSZ;
    if (B1.getX() < B2.getX()) {BSX = B1;} else {BSX = B2;}
    if (B1.getY() < B2.getY()) {BSY = B1;} else {BSY = B2;}
    if (B1.getZ() < B2.getZ()) {BSZ = B1;} else {BSZ = B2;}
    
    //Get the block to start with
    Block BlockStart = new Location(BSX.getWorld(),BSX.getX(),BSY.getY(), BSZ.getZ()).getBlock();
    
    
    
    
    //Clear entire region
    for (int ix = 0; ix < x_Size; ix++)
          {
          for (int iy = 0; iy < y_Size; iy++)
                 {
                 for (int iz = 0; iz < z_Size; iz++)
                         {
                         Block b = BlockStart.getRelative(ix, iy, iz);
                         //Empty the chest
                         if (b.getType() == Material.CHEST)
                               {
                               Chest chest = (Chest) b.getState();
                               Inventory inv = chest.getInventory();
                               inv.clear();
                               }
                        Global.DespawnedBlocks.add(b);
                        b.breakNaturally();
                        Bukkit.getPluginManager().callEvent(new BlockBreakEvent(b, null));
                        }
                 }
          }

    TLDR: How do I mass delete a region without any dropped items?
     
    Last edited: Feb 22, 2018
  2. Offline

    Zombie_Striker

    @skiithaw
    Events don't do anything on their own. After calling the event, you need to read what changes were done to the event and react accordingly (i.e, if it is canceled, don't break the block.)

    Create the event instance outside of the callEvent line, call the event, and then check if it is canceled, and if not, break the block.
     
  3. Sorry I managed to forget to include the event code as well. That's here:
    Code:
    
    @EventHandler
    public void onBlockBreak(BlockBreakEvent event)
     {
     Block brokenblock = event.getBlock();
     //If this block was broken internally
     if (Global.DespawnedBlocks.contains(brokenblock))
      {
      //Don't drop items
      Global.DespawnedBlocks.remove(brokenblock);
      event.setDropItems(false);
      System.out.print("Drop cancelled");
      }
     }
    
    PS: Sorry, I have no idea what happens to the formatting?! Pasting from eclipse messes so much stuff up.

    I got to here with this:
    Code:
    Event e = new BlockBreakEvent(b, null);
    Bukkit.getPluginManager().callEvent(e);
    if (e.
    
    But I can't access any of the event methods? Are they private?

    Also I'm not trying to cancel the breakblockevent, wouldn't that stop the block being broken at all? I just don't want items to drop from it.

    The objective is to clear a region and have it drop absolutely nothing.
     
  4. Try replacing Event with BlockBreakEvent, or Cancellable. Event is a class that all events extend off of, and not all events are cancellable (only the ones that implement Cancellable). For that reason a vague Event doesn’t show cancel methods.
     
    skiithaw likes this.
  5. Thank you! That works, let me try.

    - Would it be faster to put all the blocks in an arraylist, rather than accessing them with getrelative if I'm iterating through multiple times?

    - Does bukkit spread server tasks across multiple cores / threads, meaning there's a risk of two instances of this code using the hashmap at the same time?
     
    Last edited: Feb 22, 2018
  6. Offline

    timtower Administrator Administrator Moderator

    That would be overkill in this stage. Get everything first, then improve.
     
  7. I understand not biting off more than I can chew (and that premature optimisation is the death of most projects), but please, what are the answers to the questions?

    This plugin is for spawning in/out large regions of blocks on a whim, so if something is even only slightly faster I should use it.

    And if there's a potential for a bug like that, I really need to know lol
     
  8. Offline

    timtower Administrator Administrator Moderator

    1. Not worth the effort.
    2. Bukkit is singlethreaded.
    3. The world changing itself is probably the biggest issue in this case, and sending that to the player.
     
    skiithaw likes this.
  9. How do you mean?

    The plugin I am trying to make is this.

    ----------------------------------
    Anyway, I have actually managed to fix this by just looping through twice. First looping through and removing doors, torches, carpets, saplings, anything that's going to be problematic. Then loop through again but with the rest of the blocks.


    So I store the block with this:
    DataList.add(block.getData());
    TypeIDList.add(block.getTypeId());

    And can then restore the block back again with this.
    block.setTypeIdAndData(TypeIDList.get(i), DataList.get(i), false);

    Next, I just have to make it so it can retain sign text and chests.
     
    Last edited: Feb 22, 2018
  10. Offline

    timtower Administrator Administrator Moderator

    @skiithaw You should be editting th actual world for that though
     
  11. Offline

    timtower Administrator Administrator Moderator

    That was a typo.
    You should not be editting the actual world for that plugin.
    Then you change it for all players, and not just the ones far away.
    Using ProtocolLib to hide / modify packets is a better way, then you don't need to restore the world on shutdown.
     
    skiithaw likes this.
  12. Ah yes. But does that involve packet inspection and altering before compression and all that? And does bukkit really send world updates for players that are the other side of the map? D:
     
  13. Offline

    timtower Administrator Administrator Moderator

    That is why I suggested ProtocolLib, does a lot for you already.
    And no, but you might want to hide something for somebody that is 10 blocks further and is in the update radius.
     
    skiithaw likes this.
  14. I think I'm going to do it the other way around. Bases are spawned out by default and only spawn in if a surface block is broken, or someone teleports directly inside.

    OPs will be able to type a command that restores all bases if they wish to remove the plugin.
     
Thread Status:
Not open for further replies.

Share This Page