---
title: Cancellable Events
type: docs
weight: 3
---

Some events implement the `ICancellable` interface, allowing you to prevent the default game behavior.

## The ICancellable Interface

```java
public interface ICancellable {
    boolean isCancelled();
    void setCancelled(boolean cancelled);
}
```

## ECS Cancellable Events

ECS events use a similar interface `ICancellableEcsEvent`:

```java
public interface ICancellableEcsEvent {
    boolean isCancelled();
    void setCancelled(boolean cancelled);
}
```

## Cancelling Events

```java
getEventRegistry().register(BreakBlockEvent.class, event -> {
    Vector3i position = event.getTargetBlock();

    if (isProtectedArea(position)) {
        event.setCancelled(true);
        // Note: BreakBlockEvent doesn't have direct player access
        // Use ECS components if you need to message the player
    }
});
```

## Checking Cancellation Status

```java
getEventRegistry().register(EventPriority.LATE, BreakBlockEvent.class, event -> {
    if (event.isCancelled()) {
        // Another handler cancelled this event
        return;
    }

    // Process the event
    recordBlockBreak(event.getTargetBlock(), event.getBlockType());
});
```

## Common Cancellable Events

### Player Events

| Event | Cancel Effect |
|-------|---------------|
| `PlayerSetupConnectEvent` | Prevents player from connecting |
| `PlayerChatEvent` | Prevents message from being sent |
| `PlayerMouseButtonEvent` | Prevents mouse button action |
| `PlayerInteractEvent` | Prevents interaction (deprecated) |

### ECS Block Events

| Event | Cancel Effect |
|-------|---------------|
| `BreakBlockEvent` | Prevents block from being broken |
| `PlaceBlockEvent` | Prevents block from being placed |
| `DamageBlockEvent` | Prevents damage to block |
| `UseBlockEvent.Pre` | Prevents block use action |

### Other ECS Events

| Event | Cancel Effect |
|-------|---------------|
| `DropItemEvent` | Prevents item from being dropped |
| `CraftRecipeEvent` | Prevents crafting |

## Best Practices

### Check Before Acting

Always check `isCancelled()` before performing actions:

```java
getEventRegistry().register(BreakBlockEvent.class, event -> {
    // Check if already cancelled
    if (event.isCancelled()) {
        return;
    }

    // Your logic here
    if (shouldPrevent(event.getTargetBlock())) {
        event.setCancelled(true);
    }
});
```

### Use Appropriate Priority

```java
// Protection plugins should use EARLY
getEventRegistry().register(EventPriority.EARLY, BreakBlockEvent.class, event -> {
    if (isProtected(event.getTargetBlock())) {
        event.setCancelled(true);
    }
});

// Feature plugins should respect cancellation
getEventRegistry().register(EventPriority.NORMAL, BreakBlockEvent.class, event -> {
    if (!event.isCancelled()) {
        giveBlockReward(event.getTargetBlock());
    }
});

// Logging should use LATE
getEventRegistry().register(EventPriority.LATE, BreakBlockEvent.class, event -> {
    logBlockBreakAttempt(event.getTargetBlock(), event.isCancelled());
});
```

### Provide Feedback with Chat Events

When cancelling chat events, you can notify the player:

```java
// Note: PlayerChatEvent has String key, use registerAsyncGlobal()
getEventRegistry().registerAsyncGlobal(PlayerChatEvent.class, future -> {
    return future.thenApply(event -> {
        if (containsBadWord(event.getContent())) {
            event.setCancelled(true);

            // Send message via PlayerRef (PlayerRef.sendMessage() works)
            event.getSender().sendMessage(Message.raw("Please don't use that word!"));
        }
        return event;
    });
});
```

## Uncancelling Events

You can also uncancel an event that was previously cancelled:

```java
getEventRegistry().register(EventPriority.LATE, BreakBlockEvent.class, event -> {
    // Override cancellation for certain block types
    if (event.isCancelled() && event.getBlockType().getId().equals("temporary_block")) {
        event.setCancelled(false);
    }
});
```

{{< callout type="warning" >}}
Uncancelling events can cause conflicts with other plugins. Use sparingly and document this behavior.
{{< /callout >}}

## Example: Connection Whitelist

```java
public class WhitelistPlugin extends JavaPlugin {

    private final Set<UUID> whitelist = new HashSet<>();

    @Override
    public void start() {
        // Cancel connection for non-whitelisted players
        getEventRegistry().register(PlayerSetupConnectEvent.class, event -> {
            if (!whitelist.contains(event.getUuid())) {
                event.setCancelled(true);
                event.setReason("You are not whitelisted on this server!");
            }
        });
    }

    public void addToWhitelist(UUID uuid) {
        whitelist.add(uuid);
    }

    public void removeFromWhitelist(UUID uuid) {
        whitelist.remove(uuid);
    }
}
```

## Example: Chat Filter

```java
public class ChatFilterPlugin extends JavaPlugin {

    private final Set<String> bannedWords = new HashSet<>();

    @Override
    public void start() {
        loadBannedWords();

        // Note: PlayerChatEvent has String key, use registerAsyncGlobal()
        getEventRegistry().registerAsyncGlobal(PlayerChatEvent.class, future -> {
            return future.thenApply(event -> {
                String message = event.getContent().toLowerCase();

                for (String banned : bannedWords) {
                    if (message.contains(banned)) {
                        event.setCancelled(true);
                        // Send message via PlayerRef
                        event.getSender().sendMessage(Message.raw("Your message was blocked!"));
                        break;
                    }
                }
                return event;
            });
        });
    }
}
```

## Example: Block Protection

```java
public class ProtectionPlugin extends JavaPlugin {

    private final Set<Vector3i> protectedBlocks = new HashSet<>();

    @Override
    public void start() {
        // Register at EARLY to cancel before other plugins process
        getEventRegistry().register(EventPriority.EARLY, BreakBlockEvent.class, event -> {
            if (protectedBlocks.contains(event.getTargetBlock())) {
                event.setCancelled(true);
            }
        });

        getEventRegistry().register(EventPriority.EARLY, PlaceBlockEvent.class, event -> {
            if (isProtectedArea(event.getTargetBlock())) {
                event.setCancelled(true);
            }
        });
    }

    private boolean isProtectedArea(Vector3i pos) {
        // Check if position is in a protected region
        return false;
    }
}
```
