BlazeBans API/Reacting to events
Enforcement and exemptions
Two things the API is genuinely good at: buying yourself a few seconds before a banned player is removed, and letting something through while a player is muted.
Delaying enforcement
BlazeBansPunishmentCreatedEvent lets you ask BlazeBans to wait before it removes the player.
@EventHandler
public void onPunishmentCreated(BlazeBansPunishmentCreatedEvent event) {
if (event.punishment().type() != PunishmentType.BAN) {
return;
}
startMyBanEffect(event.punishment().targetUuid());
event.enforcementDelay(Duration.ofSeconds(2));
}This is how the ban effects addon works, using nothing private.
The rules
Five seconds is the ceiling. Any longer is clamped down to five. A negative or null duration becomes zero.
Kicks are immediate. A kick has no delay path; the player goes straight away.
The punishment is already stored. The delay only postpones removing the player. If they quit during it, the ban still applies at their next login.
Do not assume you are alone. Several listeners can ask for a delay. If yours should not shorten someone else's, compare first:
@EventHandler
public void onPunishmentCreated(BlazeBansPunishmentCreatedEvent event) {
Duration mine = Duration.ofSeconds(2);
if (event.enforcementDelay().compareTo(mine) < 0) {
event.enforcementDelay(mine);
}
}Using the time well
The player is still online during the delay, and still able to act. If you are playing something cinematic, restrict them for the duration, or they will spend those seconds breaking blocks.
The event may fire off the main thread, so schedule before you touch anything:
@EventHandler
public void onPunishmentCreated(BlazeBansPunishmentCreatedEvent event) {
if (event.punishment().type() != PunishmentType.BAN) {
return;
}
UUID uuid = event.punishment().targetUuid();
if (uuid == null) {
return;
}
Player player = Bukkit.getPlayer(uuid);
if (player == null) {
return;
}
event.enforcementDelay(Duration.ofSeconds(3));
player.getScheduler().run(this.plugin, task -> {
player.setGameMode(GameMode.SPECTATOR);
player.showTitle(Title.title(
Component.text("Banned", NamedTextColor.RED),
Component.text(event.punishment().reason())
));
}, null);
}Set the delay from the event thread, because the event has to carry the value back. Do the in-game work on the scheduled task.
Whether to
A delay is five seconds of a banned player still being on your server. For a griefing ban that is five more seconds of griefing. Narrow it to the punishments where the effect is the point, using type and reason, rather than applying it to everything.
Skipping one enforcement
BlazeBansPunishmentEnforceEvent fires before each individual action, and cancelling skips that one.
@EventHandler
public void onEnforcement(BlazeBansPunishmentEnforceEvent event) {
if (event.enforcementType() != BlazeBansEnforcementType.BAN_LOGIN) {
return;
}
if (isDuringMaintenanceWindow()) {
event.setCancelled(true);
}
}This does not revoke anything. The punishment stays active and the next attempt is enforced normally, so this is a way to make an exception, not a way to lift a ban. To actually lift it, call revokePunishment.
Letting a command through while muted
The muted events invert the usual meaning of cancellation: cancelling tells BlazeBans not to block.
@EventHandler
public void onMutedCommand(BlazeBansMutedCommandEvent event) {
String command = event.command().toLowerCase(Locale.ROOT);
if (command.startsWith("/appeal") || command.startsWith("/support")) {
event.setCancelled(true);
}
}An appeal command is the obvious case. A player muted for something they intend to appeal cannot appeal if the command is blocked.
Doing this in code rather than by permission lets you decide per command and per situation, instead of granting blazebans.mute.commands.bypass, which exempts every blocked command at once.
Letting a message through while muted
@EventHandler
public void onMutedChat(BlazeBansMutedChatEvent event) {
if (isStaffOnlyChannel(event.playerUuid())) {
event.setCancelled(true);
}
}Useful when your chat plugin has channels a mute should not cover, such as a private ticket channel.
Be conservative here. Every exemption is a way around a mute, and the point of the mute is that there is not one.
Reacting instead of intervening
If you only want to know, not change anything, ignoreCancelled keeps your listener out of the decision:
@EventHandler(ignoreCancelled = true)
public void onEnforcement(BlazeBansPunishmentEnforceEvent event) {
metrics.record(event.enforcementType(), event.punishment().type());
}That will not fire for actions another plugin already exempted, which is usually what you want from a metric.

