Minecraft Community Edition implements a custom network protocol for client-server communication, with platform-specific P2P implementations for console multiplayer.
Network Stack Overview
┌─────────────────────────────────────────┐
│ Application Layer │
│ (PacketListener implementations) │
├─────────────────────────────────────────┤
│ Packet Layer │
│ (Packet serialization/deserialization) │
├─────────────────────────────────────────┤
│ Connection Layer │
│ (Buffering, threading, flow control) │
├─────────────────────────────────────────┤
│ Transport Layer │
│ (Socket abstraction) │
├─────────────────────────────────────────┤
│ Platform Network Implementation │
│ (TCP, P2P, Platform SDKs) │
└─────────────────────────────────────────┘
Connection Class
The Connection class (Connection.h:20) provides the core networking functionality:
class Connection
{
private:
static const int SEND_BUFFER_SIZE = 1024 * 5 ;
static const int MAX_TICKS_WITHOUT_INPUT = 20 * 60 ; // 1 minute
Socket * socket;
DataInputStream * dis;
DataOutputStream * bufferedDos;
// Packet queues
queue < shared_ptr < Packet >> incoming;
queue < shared_ptr < Packet >> outgoing;
queue < shared_ptr < Packet >> outgoing_slow; // Low-priority packets
// Threading
C4JThread * readThread;
C4JThread * writeThread;
C4JThread ::Event * m_hWakeReadThread;
C4JThread ::Event * m_hWakeWriteThread;
PacketListener * packetListener;
bool running;
public:
Connection ( Socket * socket , const wstring & id , PacketListener * listener );
void send ( shared_ptr < Packet > packet ); // Immediate send
void queueSend ( shared_ptr < Packet > packet ); // Queue for later
void tick ();
void close ( DisconnectPacket :: eDisconnectReason reason , ...);
};
The Connection class uses separate read and write threads to prevent blocking and ensure smooth gameplay even with network latency.
Read Thread
int Connection :: runRead ( void* lpParam ) {
Connection * conn = (Connection * )lpParam;
while ( conn -> running ) {
// Wait for data or wake event
WaitForSingleObject ( conn -> m_hWakeReadThread , 100 );
// Read and process incoming packets
if ( ! conn -> readTick ()) {
break ; // Connection closed
}
}
return 0 ;
}
bool Connection :: readTick () {
try {
// Read packet ID
int packetId = dis -> read ();
// Create packet instance
shared_ptr < Packet > packet = Packet :: newPacket (packetId);
// Deserialize packet data
packet -> read (dis);
// Queue for processing on main thread
EnterCriticalSection ( & incoming_cs);
incoming . push (packet);
LeaveCriticalSection ( & incoming_cs);
return true ;
} catch (...) {
return false ;
}
}
Write Thread
int Connection :: runWrite ( void* lpParam ) {
Connection * conn = (Connection * )lpParam;
while ( conn -> running ) {
// Wait for packets or wake event
WaitForSingleObject ( conn -> m_hWakeWriteThread , 100 );
// Write queued packets
if ( ! conn -> writeTick ()) {
break ;
}
}
return 0 ;
}
bool Connection :: writeTick () {
EnterCriticalSection ( & writeLock);
// Send high-priority packets first
while ( ! outgoing . empty ()) {
shared_ptr < Packet > packet = outgoing . front ();
outgoing . pop ();
// Write packet ID
bufferedDos -> write ( packet -> getId ());
// Serialize packet data
packet -> write (bufferedDos);
}
// Send low-priority packets (rate-limited)
if (slowWriteDelay <= 0 && ! outgoing_slow . empty ()) {
shared_ptr < Packet > packet = outgoing_slow . front ();
outgoing_slow . pop ();
bufferedDos -> write ( packet -> getId ());
packet -> write (bufferedDos);
slowWriteDelay = MINECRAFT_SERVER_SLOW_QUEUE_DELAY;
}
bufferedDos -> flush ();
LeaveCriticalSection ( & writeLock);
return true ;
}
Packet System
Packets are defined in net.minecraft.network.packet.h with a base class:
class Packet
{
public:
static shared_ptr < Packet > newPacket ( int id );
virtual int getId () = 0 ;
virtual void read ( DataInputStream * input ) = 0 ;
virtual void write ( DataOutputStream * output ) = 0 ;
virtual void handle ( PacketListener * listener ) = 0 ;
virtual int getEstimatedSize () = 0 ;
};
Common Packet Types
PreLoginPacket: Initial handshake (UGC settings, texture pack info)
LoginPacket: Player authentication and spawn data
DisconnectPacket: Connection termination with reason
KeepAlivePacket: Connection health check (every 15 seconds)
MovePlayerPacket: Player position and rotation
TeleportEntityPacket: Instant position change
MoveEntityPacket: Entity position delta
RotateHeadPacket: Entity head rotation
ChunkVisibilityPacket: Send/remove chunk data
ChunkVisibilityAreaPacket: Set render distance
TileUpdatePacket: Single block change
BlockRegionUpdatePacket: Multiple block changes
AddPlayerPacket: Spawn player entity
AddMobPacket: Spawn mob entity
AddEntityPacket: Spawn generic entity
RemoveEntitiesPacket: Despawn entities
SetEntityDataPacket: Entity metadata update
PlayerActionPacket: Mining, interact, use item
UseItemPacket: Right-click/place block
AnimatePacket: Swing arm, take damage, etc.
InteractPacket: Entity interaction
Packet Example: MovePlayerPacket
class MovePlayerPacket : public Packet
{
public:
double x, y, z; // Position
double stance; // Eye height
float yRot, xRot; // Rotation
bool onGround;
virtual int getId () { return 13 ; }
virtual void read ( DataInputStream * input ) {
x = input -> readDouble ();
y = input -> readDouble ();
stance = input -> readDouble ();
z = input -> readDouble ();
yRot = input -> readFloat ();
xRot = input -> readFloat ();
onGround = input -> readBoolean ();
}
virtual void write ( DataOutputStream * output ) {
output -> writeDouble (x);
output -> writeDouble (y);
output -> writeDouble (stance);
output -> writeDouble (z);
output -> writeFloat (yRot);
output -> writeFloat (xRot);
output -> writeBoolean (onGround);
}
virtual void handle ( PacketListener * listener ) {
listener -> handleMovePlayer ( shared_from_this ());
}
};
Socket Abstraction
class Socket
{
public:
virtual bool connect ( const wstring & host , int port ) = 0 ;
virtual int read ( byte * buffer , int length ) = 0 ;
virtual int write ( const byte * buffer , int length ) = 0 ;
virtual void close () = 0 ;
virtual bool isConnected () = 0 ;
class SocketInputStream ;
class SocketOutputStream ;
};
P2P Connection Manager
Each platform has its own P2P implementation:
// Common/Network/P2PConnectionManager.h
class P2PConnectionManager
{
public:
virtual bool initialize () = 0 ;
virtual Socket * createP2PSocket ( PlayerUID remotePlayer ) = 0 ;
virtual bool acceptIncoming () = 0 ;
virtual void tick () = 0 ;
};
// Windows64/Network/P2PConnectionManagerWin.h
class P2PConnectionManagerWin : public P2PConnectionManager
{
// Uses Windows networking APIs
};
// Xbox implementations use Xbox Live services
// PlayStation implementations use PSN services
P2P connections require NAT traversal and may fail on restrictive networks. Always implement fallback to relay servers for production use.
Multiplayer Synchronization
Entity Synchronization
The server is authoritative for all entity state:
Client Server
│ │
├─── MovePlayerPacket ─────────>│
│ (input/desired position) │
│ ├─ Validate movement
│ ├─ Update entity
│<──── MovePlayerPacket ────────┤
│ (authoritative position) │
│ │
├─ Update local position │
│ (smooth interpolation) │
Client-Side Prediction
For responsive gameplay, clients predict their own movement:
void MultiplayerLocalPlayer :: move ( float xa , float za ) {
// 1. Apply movement locally (prediction)
Player :: move (xa, za);
// 2. Send to server for validation
shared_ptr < MovePlayerPacket > packet = make_shared < MovePlayerPacket >();
packet -> x = x;
packet -> y = y;
packet -> z = z;
packet -> yRot = yRot;
packet -> xRot = xRot;
packet -> onGround = onGround;
connection -> send (packet);
}
When the server sends back the authoritative position, the client smoothly interpolates if there’s a mismatch, avoiding jarring teleportation.
Chunk Loading
Chunks are streamed based on player position:
// Server sends view distance on login
ChunkVisibilityAreaPacket:
int viewDistance = 8 ; // chunks
// Server continuously sends chunks as player moves
for ( int x = playerChunkX - viewDistance; x <= playerChunkX + viewDistance; x ++ ) {
for ( int z = playerChunkZ - viewDistance; z <= playerChunkZ + viewDistance; z ++ ) {
if ( ! player -> hasChunk (x, z)) {
ChunkVisibilityPacket packet;
packet . x = x;
packet . z = z;
packet . chunkData = level -> getChunk (x, z)-> serialize ();
connection -> send (packet);
}
}
}
Block Updates
Block changes are broadcast to nearby players:
void ServerLevel :: setTile ( int x , int y , int z , int tile ) {
// Update server-side
Level :: setTile (x, y, z, tile);
// Notify players in range
TileUpdatePacket packet;
packet . x = x;
packet . y = y;
packet . z = z;
packet . tile = tile;
packet . data = getData (x, y, z);
for (ServerPlayer * player : getPlayersInRange (x, z, VIEW_DISTANCE)) {
player -> connection -> send (packet);
}
}
Network Optimization
Slow Queue System
Low-priority packets use a rate-limited queue to prevent bandwidth saturation:
// MinecraftServer.h:223
static int s_slowQueuePlayerIndex;
static int s_slowQueueLastTime;
static bool canSendOnSlowQueue ( INetworkPlayer * player ) {
int now = currentTimeMillis ();
if (now - s_slowQueueLastTime < MINECRAFT_SERVER_SLOW_QUEUE_DELAY) {
return false ; // Too soon
}
// Round-robin between players
if ( player -> getIndex () != s_slowQueuePlayerIndex) {
return false ;
}
return true ;
}
Slow queue is used for:
Non-critical entity updates
Distant chunk data
Particle effects
Sound events
Packet Batching
Multiple small updates can be batched:
class BlockRegionUpdatePacket : public Packet
{
int x0, y0, z0, x1, y1, z1; // Region bounds
vector < BlockUpdate > updates; // Multiple changes
};
// Instead of sending 100 TileUpdatePackets,
// send 1 BlockRegionUpdatePacket
Compression
Large data (chunks, textures) uses compression:
#include "compression.h" // zlib wrapper
void ChunkVisibilityPacket :: write ( DataOutputStream * output ) {
// Serialize chunk data
byte * rawData = serializeChunk ();
int rawSize = getChunkDataSize ();
// Compress
byte * compressed = new byte [rawSize];
int compressedSize = compress (rawData, rawSize, compressed);
// Write compressed data
output -> writeInt (compressedSize);
output -> write (compressed, compressedSize);
}
Texture Synchronization
Custom skins and texture packs are synchronized:
class TexturePacket : public Packet
{
wstring textureName;
int width, height;
byteArray imageData; // PNG or compressed format
};
// Server workflow:
// 1. Player requests texture
// 2. Server checks if it has the texture
// 3. Server sends TexturePacket or requests from uploader
// 4. All players receive the texture for rendering
Texture packets use the slow queue to avoid overwhelming the network during multiplayer sessions.
Error Handling
Disconnect Reasons
namespace DisconnectPacket {
enum eDisconnectReason {
GENERIC ,
KICKED ,
OVERFLOW , // Too many packets
TIMEOUT , // No data received
PROTOCOL_ERROR , // Invalid packet
LOST_CONNECTION ,
SIGNED_OUT , // User signed out of account
PRIVILEGE_CHANGE // Lost multiplayer privilege
};
}
Connection Timeout
void Connection :: tick () {
// Process incoming packets
while ( ! incoming . empty ()) {
shared_ptr < Packet > packet = incoming . front ();
incoming . pop ();
packet -> handle (packetListener);
noInputTicks = 0 ; // Reset timeout
}
// Check for timeout
noInputTicks ++ ;
if (noInputTicks > MAX_TICKS_WITHOUT_INPUT) {
close ( DisconnectPacket ::TIMEOUT);
}
}
Best Practices
Minimize Packet Size Use compact data types (shorts instead of ints where possible) and avoid sending redundant data.
Batch Updates Group multiple changes into region update packets instead of individual updates.
Use Slow Queue Put non-critical updates in the slow queue to prioritize gameplay packets.
Validate Server-Side Never trust client data. Always validate positions, inventory changes, etc.
Client-Server Learn about the client-server architecture
Overview High-level architecture overview