[SPIGOT-4376] Add some way to get the drops caused by a broken block Created: 15/Sep/18 Updated: 31/Oct/18 Resolved: 31/Oct/18 |
|
| Status: | Resolved |
| Project: | Spigot |
| Component/s: | None |
| Affects Version/s: | None |
| Fix Version/s: | None |
| Type: | New Feature | Priority: | Minor |
| Reporter: | Anda Block | Assignee: | Unassigned |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | BlockBreakEvent, Items | ||
| Attachments: |
|
||||||||
| Issue Links: |
|
||||||||
| Version: | 1.13.1 | ||||||||
| Guidelines Read: | Yes | ||||||||
| Description |
|
Hello, I need a way to get all drops caused by a block break, for example if a torch is placed on top of a stone block, I need both drops, the stone drop and the torch drop. This also needs to respect if the block was mined with silktouch, fortune, bare hand or something else. The BlockBreakEvent can't give away this information, because it is called before the breaking calculation. If my understanding of the Code is correct, the thing I need is already in the PlayerInteractManager, (Decompiled Lines 315 to 326 in Method public boolean breakBlock(BlockPosition)) this.world.captureDrops = new ArrayList(); boolean flag = this.c(blockposition); if (event.isDropItems()) { Iterator var21 = this.world.captureDrops.iterator(); while(var21.hasNext()) { EntityItem item = (EntityItem)var21.next(); this.world.addEntity(item); } } this.world.captureDrops = null; this.world.captureDrops seems to store all drops caused by the block to break, so they can be prevented dropping, if isDropItems was set to false. It would be awesome if some sort of event could be added on this place, something like BlockBreakDropItemsEvent(Player, Block, List<Items>), to access the excact drops caused by the breaking of this block.
Best regards |
| Comments |
| Comment by md_5 [ 08/Oct/18 ] |
|
You’re welcome to try yourself |
| Comment by Anda Block [ 08/Oct/18 ] |
|
It has been a while, has anyone decided yet if this has a chance of going into the api?
Best Regards |
| Comment by Ugleh [ 28/Sep/18 ] |
|
I'll look into that when I have time. |
| Comment by Anda Block [ 28/Sep/18 ] |
|
The one from the event only returns what the block COULD drop if it was borken with an appropriate tool, it does not return the items the will actually drop, it does not account for silk touch or fortune.
Best Regards |
| Comment by Ugleh [ 28/Sep/18 ] |
|
To get the drop of the some wouldn't you just use getDrops from the event and add it yourself? Prevents is from having to modify a ton of nms |
| Comment by Anda Block [ 28/Sep/18 ] |
|
I did some more digging on this, downloaded build tools and tested a bit, this seems to work: // CraftBukkit start world.captureDrops = new ArrayList<>(); //start capturing all drops happening in the world //remember the data of the block to break for the event later on BlockData blockData = this.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()).getBlockData(); boolean flag = this.c(blockposition); //call the physic update that causes all drops except the ones of the block itself //if (event.isDropItems()) { // for (EntityItem item : world.captureDrops) { // world.addEntity(item); //don't drop them here // } //} //world.captureDrops = null; //keep capturing drops // CraftBukkit end if (!this.isCreative()) { //let all the calls run to break the block itself while still capturing drops ItemStack itemstack1 = this.player.getItemInMainHand(); boolean flag1 = this.player.hasBlock(iblockdata); itemstack1.a(this.world, iblockdata, blockposition, this.player); // CraftBukkit start - Check if block should drop items if (flag && flag1 && event.isDropItems()) { ItemStack itemstack2 = itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack(); iblockdata.getBlock().a(this.world, this.player, blockposition, iblockdata, tileentity, itemstack2); } // CraftBukkit end } //now captureDrops also has all the drops of the broken block in it if (event.isDropItems()) { for (EntityItem item : world.captureDrops) { //call some sort of event for every? drop. since the broken block does not exist anymore on this point //i used the remembered BlockData from above and the location as an option to identify the block //that break if you have a listener for this event in a plugin. also contains the player and the drop itself BlockBreakDropItemEvent blockBreakDropItemEvent = new BlockBreakDropItemEvent( (org.bukkit.entity.Item) item.getBukkitEntity(), this.player.getBukkitEntity(), blockData, new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()) ); this.world.getServer().getPluginManager().callEvent(blockBreakDropItemEvent); if(!blockBreakDropItemEvent.isCancelled()) { //if the event did not canceled, drop the item world.addEntity(item); } } } world.captureDrops = null; //stop capturing the drops This worked with the stuff I tested: - breaking the carpet contraption from above - breaking redstone ore and coal ore with: - an unanchanted diamond pickaxe - a silk touch diamond pickaxe - a fortune diamond pickaxe
I hope my understanding of the code is correct, if not, please tell me.
I don't have an irc account, so i can't do requests.
Best Regards |
| Comment by Senmori [ 27/Sep/18 ] |
|
I don't see how this could be fixed without rewriting how Minecraft drops items. |
| Comment by Anda Block [ 16/Sep/18 ] |
|
I tested it, and can confirm, that the stone block is not in the List. Maybe they could move the whole captureDrops part below this if (!this.isCreative()) { ItemStack itemstack1 = this.player.getItemInMainHand(); boolean flag1 = this.player.hasBlock(iblockdata); itemstack1.a(this.world, iblockdata, blockposition, this.player); if (flag && flag1 && event.isDropItems()) { ItemStack itemstack2 = itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack(); iblockdata.getBlock().a(this.world, this.player, blockposition, iblockdata, tileentity, itemstack2); } } so it could capture the drops of the block itself, too?
If no new event would be added, this would be a nice workaround, but I am aware off that nms uses are not supported.
Best regards |
| Comment by Ugleh [ 16/Sep/18 ] |
|
I think the solution for developers is to add captureDrops to the API, and then you can grab it during ItemSpawnEvent. That or a new event called BlockBreakSpawnItemEvent like you said. For example, this returns a list of torches and yellow carpets, however not the stone block.
boolean hasRan = false; @EventHandler public void itemSpawnEvent(ItemSpawnEvent e) { List<EntityItem> entityList = ((CraftWorld)e.getEntity().getWorld()).getHandle().captureDrops; if(entityList == null) return; if(hasRan) return; hasRan = true; for(EntityItem i : entityList) { Bukkit.broadcastMessage(i.getBukkitEntity().getName()); } }
|
| Comment by Anda Block [ 15/Sep/18 ] |
|
I'm pretty sure, that those lines contain the torch. If I set isDropItems to false in a BlockBreakEvent, and I break the stone of a construct like this:
nothing will drop. This commit added, that every Item dropped after a block breaks gets stored in the list and only dropped if isDropItems is true.
Best regards |
| Comment by Black Hole [ 15/Sep/18 ] |
|
The drops in those lines won't contain the torch. After the block is broken, neighbor blocks gets notified and decide if they'll break, too. |