Block Break Event

Hey i don’t understand how Sponge Events work.

I want to decide wether a player can break a block or not and wether it will drop the block.
Also is it then possible that you can break blocks that are normally unbreakable like bedrock?

To answer your first question… Statement.

Sponge events work like bukkit events in the fact they fire to whatever wants it. However sponge has the added benefit of forge. Therefore sponge will have events that dont seem to offer a lot in data, such as what player broke a block (as a plugin, mod, etc may break a block).

This is where sponge causes come in. Essentially sponge tracks what happens prior to a event firing and put that in a list for the event to handle. Therefore if a player broke a block, that player will be in the cause.

If you want your event to fire only when there is a player in the cause, then you can do this.

public void onBlockBreak(BreakBlockEvent event, @Root Player player) {

What this is, is if the root cause of the event firing is a player then it will fire the event, if not then it wont fire. You can then use that @Root Player player as a normal parameter

Alright got it. But i still don’t know how to get the information i want from the event

What other information other then player are you trying to get?

I mean what block it is so i can check if this block can be broken or not and if it will drop an item after it got broken

So in the

ChangeBlockEvent.Break

event. There is a method called

  getTransactions()

This is all the blocks that were effected by the break.

So if i broke a block behind a attached sign, the block that i broke would be in there as well as the sign that popped off as both were broken in the same transaction.

So you can use a for loop or a stream to do a foreach after that you wiil get a copy of the snapshot of the block. From there you can just do

BlockSnapshot.getType()

To get the type.

Does that make sense?

1 Like

Yes could i cancel it then too?
And what about the drops?

Cancelling is easy.

event.setCancelled(true);

Changing drops i personally have not looked into.
I think it can only be done in the ItemDropEvent

So

event.getTransactions()

returns

List<Transaction<BlockType.>>

I did this:

for(Transaction blockSnapshotTransaction : event.getTransactions())

how can i compare it with something like BlockTypes.STONE?

It returns

List<Transaction<BlockSnapshot>>

(A blocksnapshot is all the data of a block without that block being placed. This is more then a BlockState as a snapshot has location and tile entity data)

So what you can do is

event.getTransactions().stream()
//filters the list to all snapshots that are STONE
.filter(t -> t.getFinal().getType().equals (BlockTypes.STONE)
//runs the code below for each blocksnapshot inside the list
.forEach(t -> {
    BlockSnapshot snapshot = t.getFinal();
    //do what ever you want here. This is all the blocksnapshots in the event that relate to BlockTypes.STONE
});

cannot resolve method getType()

Sorry. Its

getState().getType()

Why is the API so complicated?! Like 2 years ago i coded some plugin for sponge and it wasn’t that hard. Now i try to cancel a Blockbreak event and change the drops and i dont even understand your code. I would say that i have good experience in Java.

I would really apreciate if you (or someone else) could explain this to me maybe I just think too complicated.

What part of my code are you not understanding?

And the main reason why sponge is “complex” is because of its connection with Forge. In terms of Bukkit, that knew what it needed to deal with. Sponge doesnt have that option.

As for comparing it to older api, thats more of a case of developer wants and needs as well as how minecraft internal have changed. Sponge adapts its code based on minecraft and forge.

I would be more then happy to explain this or any part of sponge that i could.

Edit:

So here is the whole code in Java 8+ form (Sponge runs on Java 8 so this is the way you should do it)

@Listener
public void onPlayerBlockBreak(BreakBlockEvent event, @Root Player player){
  event.getTransactions().stream().filter(t -> t.getFinal().getState().getType().equals(BlockTypes.STONE)).forEach(t ->{
  //DO WHATEVER
});
}

and this is the way in Java 7 and lower

@Listener
public void onPlayerBlockBreak(BreakBlockEvent event, @Root Player player){
  for(Transaction<BlockSnapshot> transaction : event.getTransactions()){
    BlockSnapshot snapshot = transaction.getFinal();
    BlockType type = snapshot.getState().getType();
    if(!type.equals(BlockTypes.STONE)){
      continue;
    }
    //DO WHATEVER
  }
}

Ok so now you have seen the code in both Java 1-7 and 8+ ways. Its now time to actually explain whats going on. So I will start with the Java 7 version first and then tell you how the Java 8 version differs.

@Listener 
public void onPlayerBreakBlock(BreakBlockEvent event, @Root Player player){

Ok, so this is the same both Java 8+ and Java 7-. So the @Listener states that this method is a event listener, required for all events.
You then state what event you want to listen to in the parameters, and it is a full object when running to treat it as one.
The next bit is what throws everyone off. The @Root. As stated before, Sponge runs on a cause system. This runs for everything, meaning you will be able to trace back any block to the cause of why its spawned in the first place … if plugins are written with the cause system in mind. The events are what mainly use the cause system and the @Root annotation says find the root cause of this event and due to the fact the annotation is attached to the parameter that wants a player, if the root cause is a player then the event will fire with the player parameter being filled with the Root cause. If the root cause is not a player, then the event will not fire, meaning you don’t need to check if a player caused the event or not.
In Bukkit the BlockBreakEvent is only fired when a Player breaks a block so i didnt need a cause system, however due to the fact Sponge like forge mods, Sponge needed a event that would fire on any block break. Not only that but it makes the api size much smaller due to the fact other mobs can break blocks such as Enderman, but anyway.

for(Transaction<BlockSnapshot> transaction : event.getTransactions())

So as mentioned in one of my previous posts above. The BreakBlockEvent stores each and every transaction that relates to a block being broken. So if a player break a block that has a sign attached, the block that has been broken and the sign that broke off would both be in the same event.

The transactions are stored in a Transaction object. So the Transaction object is there so you can modify the state of the block in other BlockEvents such as ChangeBlockEvent, not that helpful with block break but its still a BlockEvent therefore it inherits it.

The BlockSnapshot is a … well snapshot … errr … a Block that potentially has broken/hasn’t been placed/etc. It is a BlockSnapshot in this event because of it being a BlockEvent, once again useful in other BlockEvents but this one… less so. But you can still grab all the details of the block in this event without harming the original block.

BlockType type = snapshot.getState().getType();

Here we are grabbing the BlockType. Not sure what to talk about here … a BlockState is all the block details that dont rely on a location, meaning if you wanted to create a large structure of Black Wool, you could create just a single BlockState of black wool and then use that on every location and they would all be the same.

if(!type.equals(BlockType.STONE)){
  continue;
}

Again not sure what to talk about here, all you are doing is comparing if the BlockType found by the snapshot is not equal to BlockType of STONE. if its not then it is continuing the for statement.

stream().filter(

Now onto Java 8. Due to the fact Java 8 released the Stream object (which is faster then a for loop) we use a stream.

So first of all we are using the Stream filter option, which is where you provide a boolean statement and if it returns false, it chucks it out the returning stream. so the character before the -> is the variable name, this can be a name but i prefer to use characters on streams.

After that its like the Java 7 way, until the forEach, the foreach works in the same way as the filter only your not filtering, your applying whatever code you want to whats left in the stream list. You may have seen that i used {} on the forEach and didnt on the filter, the {} just means that you can go to another line and use variables

If you want me to explain anything else, or go deeper into anything just say.

finally, due to the fact you just want to cancel the event based on what blocks they where, I would do it like this.

@Listener
public void onPlayerBlockBreak(BreakBlockEvent event, @Root Player player){
  if (event.getTransactions().stream().anyMatch(t -> t.getFinal().getState().getType().equals(BlockTypes.STONE))){
    event.setCancelled(true);
  }
}

Edit 2:
Here are some links that maybe useful.
All about sponge events: Events — Sponge 7.2.0 documentation

All about blocks: Blocks — Sponge 7.2.0 documentation

1 Like

Makes sense to me thanks.

1 Like

Glad i could help