ChangeBlockEvent cannot be cancelled

Hello,

I try to cancel the ChangeBlockEvent when a player break block in the State.WAIT and State.FINISH. I try also to cancel event when glass are break. But the event cannot be cancelled i don’t know why.

	@Listener
public void onBreak(ChangeBlockEvent.Break e) {
	if(main.isState(State.BUILD) || main.isState(State.FIGHT)) {
		if(e.getSource() instanceof Player) {
			Player p = (Player) e.getSource();
			p.sendMessage(Text.of("§aTu as cassé un block!"));
		}
		
		for (Transaction<BlockSnapshot> transaction : e.getTransactions()) {
			if(transaction.getOriginal() == BlockTypes.GLASS) {
				e.setCancelled(true);
				Player p = (Player) e.getSource();
				p.sendMessage(Text.of("§cLa vitre n'est pas à casser."));
			}
		   
		}
		
	}
	if(main.isState(State.WAIT) || main.isState(State.FINISH))
		e.setCancelled(true);
}

Thank’s.

I don’t know where your main.isState(State.BUILD) etc. is coming from, but cancelling the block event works as it should, maybe try debugging whatever is providing main.isState()?

It’s possible that you’re not actually cancelling it - before assuming that a method call doesn’t work, add a log message right before it to ensure that the code is actually being run.
That said, there are two giant problems I see in this code.
First of all, PLEASE do not use § in your text. We have a text API, use it.

Text.of(TextColors.GREEN, "Tu as cassé un block!")

Second, you’re casting .getSource() to a Player inside the for loop regardless of whether or not they actually are a player. There is no guarantee that the source is a player even if the actual cause is a player. e.getCause().getFirst(Player.class) will be better.

Avoid thinking the first player is the direct cause though.

Then how do you get the direct cause?

Root, just like the javadocs and entire structure is set up.

I thought you told me recently that root could no longer be trusted to be the root.

If an explosion was spawned by a Player, the player would be pushed to the cause stack first, then the explosion. Block changes from said explosion (think of it like tnt going off), will have the explosion as the direct cause, and the Player second. Using @First will iterate the cause for the desired type, regardless whether the type object is actually the root of the cause or not. Which is why I’ve always said to exercise using @Root for guaranteeing you are working based on the player directly causing events whereas indirectly causing events would be used for @First. It even explains that bit in Cause’s Javadocs. In no way does that mean that the first found object is the direct cause.

This is further going to be exemplified with the implementation providing better causes and contexts with the cause stack frames being merged to be pushed and posed in synchronosity with IPhaseStates.

To that effect, I have never been the proponent to use @First as it’s a wildcard and sometimes leads to confusion as to why certain indirect interactions are being triggered by the player.

Thank’s for your reply, but i don’t anderstand, why the code

for (Transaction<BlockSnapshot> transaction : e.getTransactions()) {
		if(transaction.getOriginal() == BlockTypes.GLASS) {
			e.setCancelled(true);
			Player p = (Player) e.getSource();
			p.sendMessage(Text.of(TextColors.RED, "La vitre n'est pas à casser."));
		}
	   
	}

doesn’t work? It’s correct no ?

transaction.getOriginal() returns a BlockSnapshot object, not a BlockType. You need to replace it with transaction.getOriginal().getState().getType().

In fact your IDE should be showing you a warning about attempting to use the == operator on objects of different types.