Events
Every event Server, Client, and Proxy emit, with their arguments.
Server, Client, and Proxy extend Node’s EventEmitter. Server re-emits most Client events with the client prepended as the first argument, so you can listen in one place instead of subscribing per connection.
Server
| Event | Arguments | When |
|---|---|---|
Server:Ready |
– | Listening socket is open. |
Server:Error |
error |
The listening socket failed (port in use, and so on). |
Server:Close |
– | The listening socket closed. |
connection |
client |
A TCP connection was accepted. Nothing has been parsed yet. |
packet |
client, packet |
A packet was parsed and its automatic initialization responses completed, if enabled. Not emitted for failed initialization or handled automatic keep-alive replies. packet is { id, name, data }. |
<PacketName> |
client, data, packet |
Every packet with this name across all states, for example server.on("KeepAlive", …). |
<State>/<PacketName> |
client, data, packet |
The same packet filtered by the state in which it was decoded, for example server.on("Config/KeepAlive", …). State names are Handshake, Status, Login, Config, and Play. |
login |
client, session |
The client sent LoginAcknowledged; state is now config. |
playerJoin |
client, session |
The client sent FinishConfiguration; state is now play. Fires once per connection. |
playerLeave |
client |
A client that had joined disconnected. Fires before disconnect. |
disconnect |
client |
Any client disconnected, joined or not. |
stateChange |
client, { direction, from, to } |
Either direction changed state. |
versionRejected |
client, protocol |
The handshake asked for a protocol not in server.versions with a login or transfer intent. A LoginDisconnect was sent and the socket ended. |
keepAlive |
client, { keepAliveId, latency } |
A KeepAlive reply matched an outstanding ping. latency is in milliseconds. Only with autoKeepAlive. |
Client:Error |
client, error |
A per-client failure: packet parse error, autoInit throwing, unexpected or timed-out keep-alive. The client is destroyed on keep-alive failures. |
server.clients is a Set<Client> of live connections.
Client
Packets
| Event | Arguments | When |
|---|---|---|
packet |
packet |
A packet was parsed. packet is { id, name, data }. |
<PacketName> |
data, packet |
Every packet with this name across all states, for example client.on("KeepAlive", …). |
<State>/<PacketName> |
data, packet |
The same packet filtered by the state in which it was decoded, for example client.on("Config/KeepAlive", …). State names are Handshake, Status, Login, Config, and Play. |
packetSent |
{ name, data } |
After sendPacket wrote to the socket. |
Packet:Error |
error |
Incoming bytes could not be parsed. The connection stays open. |
Lifecycle
| Event | Arguments | When |
|---|---|---|
login |
session |
LoginAcknowledged passed through; state is now config. |
playerJoin |
session |
Entered play for the first time. |
stateChange |
{ direction, from, to } |
direction is "incoming" or "outgoing". |
compression |
threshold |
LoginCompression set the threshold. null disables. |
version |
protocol |
The packet tables switched to another built-in version after Intension. |
versionRejected |
protocol |
Server side: the handshake protocol is not accepted and the connection was refused. |
Socket
| Event | Arguments | When |
|---|---|---|
Socket:Ready |
– | Connected (client side) or attached (server side). |
Socket:Data |
buffer |
Raw bytes arrived, before parsing. |
Socket:End |
– | The peer half-closed. |
Socket:Close |
hadError |
The socket is gone. |
Socket:Error |
error |
Underlying net.Socket error. |
Async listeners
sendPacket is async and event listeners are called synchronously, so a listener that awaits can reject after the emitter has moved on. Attach a .catch:
server.on("Play/PlayerAction", (client, data, packet) => {
handle(client, data, packet).catch((error) => {
console.error(error);
client.destroy();
});
});
Packets are dispatched in order, but a listener that awaits does not block the next packet from being parsed. If ordering between two handlers matters, serialize them yourself.
Proxy
Proxy emits its own lifecycle events. Packet interception uses proxy.Client.on(name, handler) and proxy.Server.on(name, handler) instead; those are not listed here. See Building a proxy.
| Event | Arguments | When |
|---|---|---|
Proxy:Ready |
– | The player-facing listener is open. |
Proxy:Error |
error |
The player-facing listener failed. |
Proxy:Close |
– | The player-facing listener closed. |
connection |
player, upstream |
A player connected and an upstream Client was created. The upstream socket may not be ready yet. |
disconnect |
player, upstream |
The player socket closed. The upstream connection is destroyed. |
Upstream:Error |
player, error |
The upstream socket or packet parse failed. A socket error also destroys the player. |
Client:Error |
player, error |
The player connection failed to parse a packet. |
proxy.connections is a Map<Client, Client> from player to upstream.
session
login and playerJoin hand you client.session. Every field is optional until the matching packet has passed through:
interface ClientSession {
protocol?: number; // from Intension
serverAddress?: string; // from Intension
serverPort?: number; // from Intension
username?: string; // from Hello, then LoginFinished
uuid?: string; // from Hello, then LoginFinished
profile?: unknown; // gameProfile from LoginFinished
}