18 KiB
Minecraft Server: Login Flow & World Synchronization
This document outlines the detailed protocol sequences, state transitions, and network synchronization mechanisms used in the Minecraft server, based on the codebase in serversSrc.
1. Connection Handshake & Protocol Transition
When a client initiates a connection to a Minecraft server, it starts in the Handshake protocol. This is handled by ServerHandshakePacketListenerImpl.
sequenceDiagram
autonumber
participant Client
participant Server (Handshake)
participant Server (Login)
participant Mojang Session Service
participant Server (Config)
Client->>Server (Handshake): ClientIntentionPacket (Intention=LOGIN, protocolVersion)
Note over Server (Handshake): Validates protocol version
Server (Handshake)->>Server (Login): Instantiate ServerLoginPacketListenerImpl
Client->>Server (Login): ServerboundHelloPacket (name)
alt Offline Mode
Server (Login)->>Server (Login): startClientVerification (Offline UUID)
else Online Mode
Server (Login)-->>Client: ClientboundHelloPacket (ServerID, public key, challenge)
Client->>Server (Login): ServerboundKeyPacket (encrypted shared secret, encrypted challenge)
Note over Server (Login): Decrypts secret & sets up AES encryption
Server (Login)->>Mojang Session Service: hasJoinedServer(username, digest, IP)
Mojang Session Service-->>Server (Login): GameProfile (UUID, textures)
end
Note over Server (Login): verifyLoginAndFinishConnectionSetup
Server (Login)-->>Client: ClientboundLoginCompressionPacket (optional)
Server (Login)-->>Client: ClientboundLoginFinishedPacket
Client->>Server (Login): ServerboundLoginAcknowledgedPacket
Server (Login)->>Server (Config): Switch protocol, instantiate ServerConfigurationPacketListenerImpl
Protocol Steps:
- Client Intention: The client sends a
ClientIntentionPacketindicating its target state:STATUS: The client is pinging the server for info (MOTD, online players).LOGIN/TRANSFER: The client wants to connect to the game server.
- Version Verification: The server verifies that the client's protocol version matches the server's current version:
- If mismatched, the server sends a
ClientboundLoginDisconnectPacketand closes the socket. - If matching, the server transitions the connection to the
Loginprotocol state and spawns a ServerLoginPacketListenerImpl.
- If mismatched, the server sends a
2. Login Protocol Phase
The login protocol handles authentication, encryption setup, and duplicate connection handling.
Step-by-Step Flow:
- Hello: The client sends its username inside a
ServerboundHelloPacket. - Authentication Determination:
- Offline Mode: The server bypasses encryption/auth, creates an offline UUID profile, and starts verification.
- Online Mode: The server transitions to the
KEYstate and sends aClientboundHelloPacketcontaining a random challenge token, the server's public key, and server ID.
- Encryption Setup:
- The client generates a shared secret symmetric key (AES), encrypts it and the challenge token using the server's RSA public key, and sends it back in a
ServerboundKeyPacket. - The server decrypts the shared secret and challenge token using its private key. It verifies the challenge matches.
- Symmetric AES encryption is initialized on the network socket (
connection.setEncryptionKey(...)).
- The client generates a shared secret symmetric key (AES), encrypts it and the challenge token using the server's RSA public key, and sends it back in a
- Session Verification:
- The server computes a SHA-1 hash (server ID + shared secret + server public key) and sends a request to Mojang's session servers to verify if the client has successfully authenticated their session (
hasJoinedServer). - If verified, the server receives the client's official
GameProfile(UUID, username, skin textures, etc.).
- The server computes a SHA-1 hash (server ID + shared secret + server public key) and sends a request to Mojang's session servers to verify if the client has successfully authenticated their session (
- Verifying and Compression:
- The server verifies if the player is allowed to connect (checks
UserBanList,IpBanList,UserWhiteList, and server full limitations viaPlayerList.canPlayerLogin). - If a compression threshold is configured in
server.properties, the server sends aClientboundLoginCompressionPacketand turns on network compression. - Duplicate Connection Check: The server disconnects any existing players with the same UUID.
- The server verifies if the player is allowed to connect (checks
- Finished Protocol Transition:
- The server sends a
ClientboundLoginFinishedPacketto notify the client that the login phase is complete. - The client acknowledges this by responding with
ServerboundLoginAcknowledgedPacket. - The server switches the connection to the Configuration protocol and creates a ServerConfigurationPacketListenerImpl.
- The server sends a
3. Configuration Phase
The server configuration phase is a task-based queue that sets up registry entries, client/server resource settings, and initial spawn calculations before the player actually joins the world.
sequenceDiagram
autonumber
participant Client
participant Server (Config)
Note over Server (Config): startConfiguration()
Server (Config)-->>Client: Brand / Server Links / Update Features Packets
Server (Config)-->>Client: Registry/Pack select request (SynchronizeRegistriesTask)
Client->>Server (Config): ServerboundSelectKnownPacks
Note over Server (Config): PrepareSpawnTask (Loads player data & chunks)
Note over Server (Config): JoinWorldTask (Sends finish config packet)
Server (Config)-->>Client: ClientboundFinishConfigurationPacket
Client->>Server (Config): ServerboundFinishConfigurationPacket
Note over Server (Config): Transition connection to Play state
Configuration Task Queue:
- Server Identity: The server sends initial information like brand name (
ClientboundCustomPayloadPacketwithBrandPayload) and links (ClientboundServerLinksPacket). - SynchronizeRegistriesTask: Sends the server's known resource packs and waits for the client to acknowledge with
ServerboundSelectKnownPacks. The server then replies withClientboundRegistryDataPacketandClientboundUpdateTagsPacket. - Optional Tasks:
ServerCodeOfConductConfigurationTask: Prompts client to accept terms.ServerResourcePackConfigurationTask: Sends resource pack download prompts.
- PrepareSpawnTask:
- Loads player data (position, rotation, dimension).
- Asynchronously requests spawn chunk loading (radius of 3 chunks around player spawn position).
- Holds configuration tick execution until the client's immediate spawn area is fully loaded and ready.
- JoinWorldTask: Sends
ClientboundFinishConfigurationPacketto trigger play state transition. - Transition:
- The client responds with
ServerboundFinishConfigurationPacket. - The server updates the network handler to the Play protocol template (
GameProtocols.CLIENTBOUND_TEMPLATE.bind(...)). - The server spawns the player into the level and hands control to ServerGamePacketListenerImpl.
- The client responds with
4. Play State Transition (Initial World Sync)
When the connection transitions to the Play state, PlayerList.placeNewPlayer() sends a dense stream of packets to synchronize the player's HUD, environment, inventory, and initial chunks.
[ Client ] [ Server (Play) ]
| |
| <----------- ClientboundLoginPacket -------------| (EID, difficulty, dimensions...)
| <------- ClientboundChangeDifficultyPacket ------| (Current difficulty state)
| <------ ClientboundPlayerAbilitiesPacket -------| (Flying/creative capabilities)
| <-------- ClientboundSetHeldSlotPacket ----------| (Active hotbar slot index)
| <------- ClientboundUpdateRecipesPacket ---------| (Synchronize recipes)
| <--------- Send Commands Tree Packet ------------| (Command syntax helper)
| <------ ClientboundPlayerInfoUpdatePacket -------| (Initialize online tab list)
| <--------- Teleport Packet to Spawn -------------| (Pos/Rot snap location)
| |
| (Send World Info) |
| <----- ClientboundInitializeBorderPacket --------| (World border settings)
| <------------- Synchronize Clock Packet ---------| (World time / day-night tick)
| <---- ClientboundSetDefaultSpawnPositionPacket --| (World spawn coordinate)
| <--------- ClientboundGameEventPacket -----------| (Weather updates: Rain/Thunder)
| <-------- LEVEL_CHUNKS_LOAD_START Event ---------| (Trigger client chunk loading)
| |
| (Active Entities & Chunks) |
| <------- ClientboundUpdateMobEffectPacket -------| (Apply ongoing status effects)
| <--------- Initialize Inventory Packet ----------| (Fill inventory UI slots)
- ClientboundLoginPacket: Sets up core game parameters (Entity ID, hardcore mode, view distance, simulation distance).
- ClientboundChangeDifficultyPacket & ClientboundPlayerAbilitiesPacket: Syncs world difficulty and character flight/speed settings.
- ClientboundSetHeldSlotPacket: Syncs the player's currently selected hotbar slot.
- ClientboundUpdateRecipesPacket: Syncs stonecutter and generic crafting recipes.
- Commands & Permissions: Sends operator status and command syntax mappings.
- Scoreboard & Teams: Sends objective lists and color styling data (
updateEntireScoreboard). - Player List Info: Sends
ClientboundPlayerInfoUpdatePacketto add the joining player and all current players to the tab list. - Position Teleport: Teleports the player's local camera to the spawn position.
- Environment/Weather: Syncs the world border, time of day, default spawn points, and weather conditions (e.g.,
START_RAINING,RAIN_LEVEL_CHANGE). - Start Level Sync: Sends a game event of type
LEVEL_CHUNKS_LOAD_STARTto indicate to the client that it should prepare to receive chunk data.
5. Live World Synchronization Mechanisms
Once a player is in the world, the server continuously updates the player's client via three loops: Chunk Sync, Entity Sync, and World State Sync.
sequenceDiagram
autonumber
participant Client
participant Server (Play)
Note over Server (Play): PlayerList.placeNewPlayer()
Server (Play)-->>Client: ClientboundLoginPacket (Entity ID, Dimension keys, View distance...)
Server (Play)-->>Client: ChangeDifficulty / PlayerAbilities / SetHeldSlot / UpdateRecipes Packets
Server (Play)-->>Client: Teleport (initial position)
Server (Play)-->>Client: InitializeBorder / Sync Time / DefaultSpawnPosition Packets
Server (Play)-->>Client: LEVEL_CHUNKS_LOAD_START Game Event
rect rgb(200, 220, 240)
Note over Server (Play), Client: Chunk Synchronization Loop
Server (Play)-->>Client: ClientboundChunkBatchStartPacket
Server (Play)-->>Client: ClientboundLevelChunkWithLightPacket (multiple chunks)
Server (Play)-->>Client: ClientboundChunkBatchFinishedPacket
end
rect rgb(220, 240, 200)
Note over Server (Play), Client: Entity Tracking Loop
Server (Play)-->>Client: ClientboundBundlePacket (Spawn Entity, Metadata, Attributes, Equipment)
Server (Play)-->>Client: ClientboundMoveEntityPacket / EntityPositionSyncPacket / RotateHeadPacket
Server (Play)-->>Client: ClientboundRemoveEntitiesPacket (when entity out of range)
end
rect rgb(240, 200, 220)
Note over Server (Play), Client: Dynamic World State Loop
Server (Play)-->>Client: ClientboundBlockUpdatePacket (single block)
Server (Play)-->>Client: ClientboundSectionBlocksUpdatePacket (multiple blocks in section)
Server (Play)-->>Client: ClientboundLightUpdatePacket (lighting recalculations)
end
A. Chunk Synchronization
Minecraft manages which chunks are loaded on the client through the player's view distance and ChunkTrackingView.
- Movement Tracking: As a player walks, the difference between their previous
ChunkTrackingViewand currentChunkTrackingViewis computed:- New Chunks: Scheduled for sending via
markChunkPendingToSend(player, chunk). - Old Chunks: Cleared from the client via
ClientboundForgetLevelChunkPacket.
- New Chunks: Scheduled for sending via
- Chunk Batching: To prevent network congestion, the server uses PlayerChunkSender to batch chunks:
- Sends a
ClientboundChunkBatchStartPacket. - Sends individual chunk data using
ClientboundLevelChunkWithLightPacket(containing blocks, state mappings, tile entities, and light values). - Sends
ClientboundChunkBatchFinishedPacketconfirming the batch size. - Waits for the client to acknowledge before sending the next batch (the batch rate dynamically throttles based on the client's processing feedback).
- Sends a
B. Entity Tracking & Synchronization
The server tracks close entities (players, items, projectiles, mobs) on a per-player basis using ChunkMap.TrackedEntity.
1. Spawn Sync (Adding a Pairing)
When an entity enters a player's tracking range:
- The server calls
addPairing(player), collecting all initialization packets. - It packs them inside a
ClientboundBundlePacketto guarantee atomic client rendering:getAddEntityPacket(...): Spawns the visual representation of the entity.ClientboundSetEntityDataPacket: Syncs metadata values (e.g., if a creeper is ignited, if a wolf is sitting).ClientboundUpdateAttributesPacket: Syncs movement speed, health limits, etc.ClientboundSetEquipmentPacket: Syncs armor, shield, and hand items.ClientboundSetPassengersPacket: Syncs riding links.
2. Position & State Sync (Incremental Updates)
Every tick, the server runs sendChanges() inside ServerEntity:
- Relative Movement: If the movement delta since the last packet is small, the server encodes it using a
VecDeltaCodec(fitting into ashortrepresentation) and sends:ClientboundMoveEntityPacket.Pos(position only)ClientboundMoveEntityPacket.Rot(rotation only)ClientboundMoveEntityPacket.PosRot(both position and rotation)
- Hard Synced Teleportation: If the displacement exceeds the short delta limit, the riding/grounded state changes, or 400 ticks (
FORCED_TELEPORT_PERIOD) have passed, the server sends aClientboundEntityPositionSyncPacketto force-snap the position. - Head Rotation: Head yaw changes are tracked separately and sent via
ClientboundRotateHeadPacket. - Velocity: Real-time motion forces (like knockback) are sent using
ClientboundSetEntityMotionPacket.
3. Despawn Sync (Removing a Pairing)
When an entity is destroyed or moves out of the player's tracking range, removePairing(player) is executed, sending a ClientboundRemoveEntitiesPacket to free memory on the client.
C. Dynamic World State Updates
Real-time edits to the environment (player building, chest placements, water flows) are pushed block-by-block using ChunkHolder:
- Block Change Recording: When a block updates,
blockChanged(pos)records the position relative to its 16x16x16 section. - Broadcasting Updates:
- Single Block Change: The server sends a
ClientboundBlockUpdatePacketcontaining the coordinate and the new block state. - Multiple Block Changes: If multiple blocks update in the same section within a single tick, they are consolidated and sent as a
ClientboundSectionBlocksUpdatePacketto save bandwidth. - Tile Entities: If the updated block holds a block entity (like a sign, container, or banner), the server fetches and broadcasts its NBT tag via
ClientboundBlockEntityDataPacket. - Light Recalculation: If block updates affect ambient brightness, a
ClientboundLightUpdatePacketis broadcasted.
- Single Block Change: The server sends a
Packet sniffer proxy (development)
MITM proxy: the Java client connects to a local minecraft-protocol server; the sniffer opens a second authenticated client to config.server and relays decrypted packets both ways while logging to JSONL.
npm run sniffer
- Listens on
config.sniffer.port(default 25567); upstream target isconfig.server. - Connect the Java client to the sniffer (not 25566). One client at a time.
- Logs:
logs/sniffer/session-<timestamp>.jsonlwith"type":"packet"entries (dir,state,name, payload or summary). sniffer.upstreamAuth:"microsoft"(default) or"offline"for the upstream leg.sniffer.onlineMode:false(default) lets the Java client join the sniffer without Mojang checking the sniffer itself; upstream still usesupstreamAuth.- Server list ping (
nextState: 1) uses a raw TCP pass-through; Join (nextState: 2) runs the MITM path (login,registry_data,map_chunk, …). registry_data/ chunk packets are relayed withwriteRawwhere needed so NBT stays byte-identical.
For the main FlayerProxy handoff path (25566), captured upstream config is still replayed with writeRaw so registry NBT stays byte-identical.