[SPIGOT-7408] Cancelling sign block break event does not preserve sign text on client Created: 25/Jun/23  Updated: 25/Dec/24

Status: Open
Project: Spigot
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Minor
Reporter: blablubbabc Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: BlockBreakEvent, sign

Version: This server is running CraftBukkit version dev-Spigot-64b565e-c1279f7 (MC: 1.20.1) (Implementing API version 1.20.1-R0.1-SNAPSHOT)
Guidelines Read: Yes

 Description   

On Spigot 1.20.1, when a plugin cancels the BlockBreakEvent for a sign block, the sign block immediately reappears, but it loses its sign content. Once the player rejoins the server, the sign content reappears.

Tested in both survival and creative mode.

Also tested to manually send a sign update after the BlockBreakEvent: Sending the sign update works if there is at least a delay of 2 ticks. 1 tick seems to longer be sufficient.

Also, maybe related to this, I noticed that when the player keeps hitting the block in survival mode (without letting go of the left mouse button), the block can end up being displayed in its "damaged" state, which only resets once the player interacts with another block.

Test plugin code:

package de.blablubbabc.test.block.sign;

import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;

import de.blablubbabc.test.Test;

public class SignBlockBreakTest implements Listener {

	public SignBlockBreakTest() {
		Bukkit.getPluginManager().registerEvents(this, Test.INSTANCE);
	}

	public static boolean isSign(Material material) {
		if (material == null) return false;
		return material.data == org.bukkit.block.data.type.Sign.class
				|| material.data == org.bukkit.block.data.type.WallSign.class;
	}

	@EventHandler
	public void onBlockBreak(BlockBreakEvent event) {
		Player player = event.getPlayer();
		if (!player.isSneaking()) return;

		player.sendMessage("Cancelling block break");
		event.setCancelled(true);

		Block block = event.getBlock();
		if (isSign(block.getType()) && Math.random() > 0.5) {
			String[] lines = ((Sign) block.getState()).getSide(Side.FRONT).getLines();
			player.sendMessage("Sending sign update: " + String.join(", ", lines));
			Bukkit.getScheduler().runTaskLater(Test.INSTANCE, () -> {
				player.sendSignChange(block.getLocation(), lines);
			}, 2L); // Delay of 0 or 1 tick is not sufficient
		}
	}

	@EventHandler
	public void onPlayerInteract(PlayerInteractEvent event) {
		if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
		if (event.getHand() != EquipmentSlot.HAND) return;

		Player player = event.getPlayer();
		if (!player.isSneaking()) return;

		player.sendMessage("Cancelling interact");
		event.setCancelled(true);

		Block block = event.getClickedBlock();
		if (isSign(block.getType())) {
			player.sendMessage("Sending sign update.");
			player.sendSignChange(block.getLocation(), new String[] { "Bla", "", "", "" });
		}
	}
}
 


 Comments   
Comment by md_5 [ 26/Jun/23 ]

If 1 tick is not sufficient doesn't that suggest a client issue?

Generated at Tue Apr 22 05:33:01 UTC 2025 using Jira 10.3.5#10030005-sha1:190c783f2bd6c69cd5accdb70f97e48812a78d14.