Reference this skill when handling events in a Waterfall (BungeeCord) proxy plugin. Waterfall's event system resembles Bukkit's @EventHandler pattern but uses BungeeCord-specific event classes.
When to Use This Skill
Responding to player login, server switch, or disconnect events at the proxy level
Intercepting chat at the proxy
Implementing ban checks or authentication before players join the network
API Quick Reference
Class / Method
Purpose
Notes
Listener
Marker interface for BungeeCord event listeners
net.md_5.bungee.api.plugin.Listener
@EventHandler
Marks event handler method
net.md_5.bungee.event.EventHandler
EventPriority
Controls order: LOWEST → LOW → NORMAL → HIGH → HIGHEST
net.md_5.bungee.event.EventPriority
PluginManager#registerListener(Plugin, Listener)
Register all handlers in a listener
PostLoginEvent
Player fully connected to proxy
net.md_5.bungee.api.event.PostLoginEvent
LoginEvent
Before player is authenticated
Cancellable; set reason to kick
PlayerDisconnectEvent
Player left the proxy
ServerConnectedEvent
Player connected to a backend
ServerSwitchEvent
Player switched to a different backend
ChatEvent
Player sent a chat message
Cancellable
PluginMessageEvent
Plugin message received at proxy
ProxiedPlayer
BungeeCord player handle
net.md_5.bungee.api.connection.ProxiedPlayer
Code Pattern
package com.yourorg.waterfallplugin.listeners;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ChatEvent;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import java.util.logging.Logger;
public class ConnectionListener implements Listener {
private final Plugin plugin;
private final Logger logger;
public ConnectionListener(Plugin plugin) {
this.plugin = plugin;
this.logger = plugin.getLogger();
}
// Fires before login — kick players early
@EventHandler(priority = EventPriority.NORMAL)
public void onLogin(LoginEvent event) {
// event.getConnection() gives InboundConnection (no player yet)
String name = event.getConnection().getName();
if (isNetworkBanned(name)) {
// BungeeCord uses legacy chat components
event.setCancelled(true);
event.setCancelReason(new TextComponent("§cYou are banned from this network."));
}
}
// Player fully connected to the proxy
@EventHandler(priority = EventPriority.NORMAL)
public void onPostLogin(PostLoginEvent event) {
ProxiedPlayer player = event.getPlayer();
logger.info(player.getName() + " (" + player.getUniqueId() + ") joined the network.");
// Broadcast in BungeeCord style (legacy chat)
ProxyServer.getInstance().broadcast(
new TextComponent("§a» " + player.getName() + " joined the network.")
);
}
// Player connected to a backend server
@EventHandler
public void onServerConnected(ServerConnectedEvent event) {
ProxiedPlayer player = event.getPlayer();
String serverName = event.getServer().getInfo().getName();
logger.info(player.getName() + " connected to " + serverName);
}
// Player disconnected from the proxy
@EventHandler
public void onDisconnect(PlayerDisconnectEvent event) {
ProxiedPlayer player = event.getPlayer();
logger.info(player.getName() + " disconnected.");
}
// Chat event (fires at proxy level for all messages)
@EventHandler(priority = EventPriority.NORMAL)
public void onChat(ChatEvent event) {
if (event.isCommand()) return; // Skip commands
String message = event.getMessage();
if (containsProfanity(message)) {
event.setCancelled(true);
if (event.getSender() instanceof ProxiedPlayer player) {
player.sendMessage(new TextComponent("§cYour message was blocked."));
}
}
}
private boolean isNetworkBanned(String name) { return false; }
private boolean containsProfanity(String msg) { return false; }
}
Register listener in main class:
@Override
public void onEnable() {
getProxy().getPluginManager().registerListener(this, new ConnectionListener(this));
}
Common Pitfalls
Using Bukkit @EventHandler annotation: The BungeeCord annotation is net.md_5.bungee.event.EventHandler, NOT org.bukkit.event.EventHandler. Using the wrong import means handlers are silently ignored.
Using Adventure components on Waterfall without the adapter: Waterfall doesn't natively support Adventure. Use legacy TextComponent and ChatColor OR add the adventure-platform-bungeecord adapter to your shaded JAR.
Cancelling PlayerDisconnectEvent: This event is NOT cancellable. You cannot prevent a disconnect at this stage.
Using ServerConnectedEvent vs ServerSwitchEvent: ServerConnectedEvent fires when a player joins a backend (initial or switch). ServerSwitchEvent fires after the switch is complete and provides the from server. For most use cases, ServerConnectedEvent is preferred.
Version Notes
Waterfall 1.21: Event API is unchanged from older BungeeCord versions. All events in net.md_5.bungee.api.event.* are available.
LoginEvent is async in BungeeCord — blocking in its handler delays login for that player.
1---2name: events-43description: Events Skill — Waterfall4---5# Events Skill — Waterfall67## Purpose8Reference this skill when handling events in a Waterfall (BungeeCord) proxy plugin. Waterfall's event system resembles Bukkit's `@EventHandler` pattern but uses BungeeCord-specific event classes.910## When to Use This Skill11- Responding to player login, server switch, or disconnect events at the proxy level12- Intercepting chat at the proxy13- Implementing ban checks or authentication before players join the network1415## API Quick Reference1617| Class / Method | Purpose | Notes |18|---------------|---------|-------|19| `Listener` | Marker interface for BungeeCord event listeners | `net.md_5.bungee.api.plugin.Listener` |20| `@EventHandler` | Marks event handler method | `net.md_5.bungee.event.EventHandler` |21| `EventPriority` | Controls order: `LOWEST` → `LOW` → `NORMAL` → `HIGH` → `HIGHEST` | `net.md_5.bungee.event.EventPriority` |22| `PluginManager#registerListener(Plugin, Listener)` | Register all handlers in a listener | |23| `PostLoginEvent` | Player fully connected to proxy | `net.md_5.bungee.api.event.PostLoginEvent` |24| `LoginEvent` | Before player is authenticated | Cancellable; set reason to kick |25| `PlayerDisconnectEvent` | Player left the proxy | |26| `ServerConnectedEvent` | Player connected to a backend | |27| `ServerSwitchEvent` | Player switched to a different backend | |28| `ChatEvent` | Player sent a chat message | Cancellable |29| `PluginMessageEvent` | Plugin message received at proxy | |30| `ProxiedPlayer` | BungeeCord player handle | `net.md_5.bungee.api.connection.ProxiedPlayer` |3132## Code Pattern3334```java35package com.yourorg.waterfallplugin.listeners;3637import net.md_5.bungee.api.ProxyServer;38import net.md_5.bungee.api.chat.TextComponent;39import net.md_5.bungee.api.connection.ProxiedPlayer;40import net.md_5.bungee.api.event.ChatEvent;41import net.md_5.bungee.api.event.LoginEvent;42import net.md_5.bungee.api.event.PlayerDisconnectEvent;43import net.md_5.bungee.api.event.PostLoginEvent;44import net.md_5.bungee.api.event.ServerConnectedEvent;45import net.md_5.bungee.api.plugin.Listener;46import net.md_5.bungee.api.plugin.Plugin;47import net.md_5.bungee.event.EventHandler;48import net.md_5.bungee.event.EventPriority;4950import java.util.logging.Logger;5152public class ConnectionListener implements Listener {5354 private final Plugin plugin;55 private final Logger logger;5657 public ConnectionListener(Plugin plugin) {58 this.plugin = plugin;59 this.logger = plugin.getLogger();60 }6162 // Fires before login — kick players early63 @EventHandler(priority = EventPriority.NORMAL)64 public void onLogin(LoginEvent event) {65 // event.getConnection() gives InboundConnection (no player yet)66 String name = event.getConnection().getName();6768 if (isNetworkBanned(name)) {69 // BungeeCord uses legacy chat components70 event.setCancelled(true);71 event.setCancelReason(new TextComponent("§cYou are banned from this network."));72 }73 }7475 // Player fully connected to the proxy76 @EventHandler(priority = EventPriority.NORMAL)77 public void onPostLogin(PostLoginEvent event) {78 ProxiedPlayer player = event.getPlayer();79 logger.info(player.getName() + " (" + player.getUniqueId() + ") joined the network.");8081 // Broadcast in BungeeCord style (legacy chat)82 ProxyServer.getInstance().broadcast(83 new TextComponent("§a» " + player.getName() + " joined the network.")84 );85 }8687 // Player connected to a backend server88 @EventHandler89 public void onServerConnected(ServerConnectedEvent event) {90 ProxiedPlayer player = event.getPlayer();91 String serverName = event.getServer().getInfo().getName();92 logger.info(player.getName() + " connected to " + serverName);93 }9495 // Player disconnected from the proxy96 @EventHandler97 public void onDisconnect(PlayerDisconnectEvent event) {98 ProxiedPlayer player = event.getPlayer();99 logger.info(player.getName() + " disconnected.");100 }101102 // Chat event (fires at proxy level for all messages)103 @EventHandler(priority = EventPriority.NORMAL)104 public void onChat(ChatEvent event) {105 if (event.isCommand()) return; // Skip commands106107 String message = event.getMessage();108 if (containsProfanity(message)) {109 event.setCancelled(true);110 if (event.getSender() instanceof ProxiedPlayer player) {111 player.sendMessage(new TextComponent("§cYour message was blocked."));112 }113 }114 }115116 private boolean isNetworkBanned(String name) { return false; }117 private boolean containsProfanity(String msg) { return false; }118}119```120121**Register listener in main class:**122```java123@Override124public void onEnable() {125 getProxy().getPluginManager().registerListener(this, new ConnectionListener(this));126}127```128129## Common Pitfalls130131- **Using Bukkit `@EventHandler` annotation**: The BungeeCord annotation is `net.md_5.bungee.event.EventHandler`, NOT `org.bukkit.event.EventHandler`. Using the wrong import means handlers are silently ignored.132133- **Using Adventure components on Waterfall without the adapter**: Waterfall doesn't natively support Adventure. Use legacy `TextComponent` and `ChatColor` OR add the `adventure-platform-bungeecord` adapter to your shaded JAR.134135- **Cancelling `PlayerDisconnectEvent`**: This event is NOT cancellable. You cannot prevent a disconnect at this stage.136137- **Using `ServerConnectedEvent` vs `ServerSwitchEvent`**: `ServerConnectedEvent` fires when a player joins a backend (initial or switch). `ServerSwitchEvent` fires after the switch is complete and provides the `from` server. For most use cases, `ServerConnectedEvent` is preferred.138139## Version Notes140141- **Waterfall 1.21**: Event API is unchanged from older BungeeCord versions. All events in `net.md_5.bungee.api.event.*` are available.142- `LoginEvent` is async in BungeeCord — blocking in its handler delays login for that player.143144## Related Skills145146- [../OVERVIEW.md](../OVERVIEW.md) — Waterfall platform setup147- [../messaging/SKILL.md](../messaging/SKILL.md) — Plugin messaging148- [../../velocity/events/SKILL.md](../../velocity/events/SKILL.md) — Equivalent Velocity events
Run npx skillmds@latest add mrpippi/events-4 in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Events Skill — Waterfall It is listed under Coding & Dev Tools on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
MrPippi (@mrpippi) published this skill. Their other Agent Skills are listed on their SkillMD profile.