Networking & Protocol
Protocol States
Void implements the Minecraft protocol state machine with six states:
pub enum State {
Handshake = 0x0,
Status = 0x1,
Login = 0x2,
Transfer = 0x3,
Configuration = 0x4,
Play = 0x5,
}
The active state determines which packets are valid. Each client entity tracks its state via the ConnectionState component.
Connection Lifecycle
Full Join Flow
Client Server
| |
|--- TCP Connect ------------------->| Server::accept()
| | Spawn Client task
| |
|--- Handshake (next_state=2) ------>| Store ProtocolVersion
| | Set state -> Login
| |
|--- LoginStart (name, uuid) ------->| Store PlayerName, PlayerUuid
|<-- LoginSuccess -------------------|
| |
|--- LoginAcknowledged ------------->| Set state -> Configuration
|<-- KnownPacks ---------------------|
| |
|--- ClientInformation ------------->| Store ClientSettings
|--- PluginMessage (brand) --------->| (logged)
|--- KnownPacks -------------------->| Send all RegistryData
|<-- RegistryData (xN) -------------|
|<-- FinishConfiguration ------------|
| |
|--- FinishConfigurationAck -------->| Set state -> Play
| | Allocate MinecraftEntityId
| | Insert all player components
|<-- Play Login ---------------------| (game settings, dimensions)
|<-- Commands -----------------------| (command tree for tab-completion)
|<-- GameEvent(StartWaiting) --------|
|<-- SetCenterChunk -----------------|
|<-- ChunkDataAndLight (xN) --------| (initial_chunk_radius)
|<-- SynchronizePlayerPosition ------| (teleport to spawn)
| | Trigger PlayerJoinEvent
| |
|--- ConfirmTeleportation ---------->| Clear TeleportState.pending_id
|--- PlayerLoaded ------------------>| Insert PlayerReady
| | Trigger PlayerReadyEvent
| | Broadcast spawn to other players
| |
|<== Keep-alive (periodic) =========|
|=== Keep-alive response ==========>|
| |
|<== Chunk streaming ===============| (as player moves)
|<== Position broadcasts ===========| (from other players)
Server List Ping
When a client connects with next_state = 1 (Status):
Client Server
|--- Handshake (next_state=1) ------>|
|--- StatusRequest ----------------->|
|<-- StatusResponse -----------------| (MOTD, version, player count)
|--- PingRequest (timestamp) ------->|
|<-- PingResponse (timestamp) ------|
Packet Handling Pipeline
On every tick during PreUpdate:
- Drain all packets from the incoming channel
- For each packet:
- Look up or create the client entity in
ClientToEntityMap
- Read the entity's
ConnectionState
- Decode the raw packet bytes using the protocol crate
- Call the appropriate
handle_{state}_packet() function
- Drain the disconnect channel and despawn disconnected entities
fn dispatch_packet(world, client_id, entity, packet) {
match state {
Handshake => handlers::handshake::handle_handshake_packet(..),
Status => handlers::status::handle_status_packet(..),
Login => handlers::login::handle_login_packet(..),
Configuration => handlers::configuration::handle_configuration_packet(..),
Play => handlers::play::handle_play_packet(..),
}
}
Keep-Alive System
The server sends periodic keep-alive packets to detect dead connections:
- Interval: 200 ticks (10 seconds at 20 TPS)
- Mechanism: Each tick,
KeepAliveTicker increments. When the interval is reached, a KeepAlive packet with a timestamp ID is sent to all ready players.
- Tracking:
KeepAliveState on each player tracks the last sent ID and whether a response is pending.
- Timeout: If a player hasn't responded when the next keep-alive is due, a warning is logged and the keep-alive is skipped for that player.
Status Response
When a client pings the server list, the response includes:
| Field |
Source |
| Version name |
"Void Server" |
| Protocol version |
Client's declared version (echoed back) |
| Max players |
ServerConfig::max_players |
| Online players |
0 (hardcoded) |
| Description (MOTD) |
ServerConfig::motd |
Current Limitations
- No encryption: The server does not implement Mojang authentication or encrypted connections. All traffic is plaintext.
- No compression: Packet compression is not implemented. All packets are sent at full size.
- No Transfer state: The Transfer protocol state is defined but not handled.