Skip to main content
Minecraft Community Edition uses a client-server architecture even for single-player games. This design enables seamless transitions between local and multiplayer gameplay.

Architecture Diagram

┌─────────────────────────────────────┐
│         Minecraft (Client)          │
│  ┌───────────────────────────────┐  │
│  │   MultiplayerLocalPlayer(s)   │  │
│  └───────────────────────────────┘  │
│              │                       │
│              ▼                       │
│  ┌───────────────────────────────┐  │
│  │    ClientConnection(s)        │  │
│  └───────────────────────────────┘  │
└──────────────┬──────────────────────┘
               │ Connection (Socket)

┌─────────────────────────────────────┐
│      MinecraftServer (Server)       │
│  ┌───────────────────────────────┐  │
│  │    ServerConnection           │  │
│  │  ┌─────────────────────────┐  │  │
│  │  │  PlayerConnection(s)    │  │  │
│  │  └─────────────────────────┘  │  │
│  └───────────────────────────────┘  │
│              │                       │
│              ▼                       │
│  ┌───────────────────────────────┐  │
│  │     ServerPlayer(s)           │  │
│  └───────────────────────────────┘  │
│              │                       │
│              ▼                       │
│  ┌───────────────────────────────┐  │
│  │      ServerLevel(s)           │  │
│  └───────────────────────────────┘  │
└─────────────────────────────────────┘

Client Architecture

Minecraft Class

The main client class manages the game state and rendering (Minecraft.h:47):
class Minecraft
{
public:
    // Level and rendering
    MultiPlayerLevel *level;
    LevelRenderer *levelRenderer;
    GameRenderer *gameRenderer;
    
    // Players (split-screen support)
    shared_ptr<MultiplayerLocalPlayer> player;
    shared_ptr<MultiplayerLocalPlayer> localplayers[XUSER_MAX_COUNT];
    int localPlayerIdx;
    
    // Network connections (one per local player)
    ClientConnection *m_pendingLocalConnections[XUSER_MAX_COUNT];
    bool m_connectionFailed[XUSER_MAX_COUNT];
    
    // Game loop
    void tick(bool bFirst, bool bUpdateTextures);
    void run_middle();
};
Even in single-player mode, the client maintains a ClientConnection to a local MinecraftServer instance running in the same process.

MultiplayerLocalPlayer

Represents a local player on the client:
class MultiplayerLocalPlayer : public Player
{
    ClientConnection *connection;
    int userIndex;  // For split-screen
    
    // Input handling
    virtual void move(float xa, float za);
    virtual void attack(Entity *entity);
    virtual void useItem();
};

Server Architecture

MinecraftServer Class

The server manages world simulation and player connections (MinecraftServer.h:60):
class MinecraftServer : public ConsoleInputSource
{
public:
    ServerConnection *connection;
    Settings *settings;
    ServerLevelArray levels;  // Multiple dimensions
    
    bool running;
    int tickCount;
    
    // Server settings
    bool onlineMode;
    bool animals;
    bool npcs;
    bool pvp;
    bool allowFlight;
    
    void run(__int64 seed, void *lpParameter);
    void tick();
    void stopServer();
};

Server Tick System

The server runs at a fixed 20 ticks per second:
private:
    static const int MS_PER_TICK = 1000 / SharedConstants::TICKS_PER_SECOND; // 50ms
    
public:
    void tick() {
        // 1. Handle console input
        handleConsoleInputs();
        
        // 2. Tick all levels (dimensions)
        for (ServerLevel *level : levels) {
            level->tick();
        }
        
        // 3. Tick all player connections
        connection->tick();
        
        // 4. Save chunks periodically
        if (tickCount % 900 == 0) {  // Every 45 seconds
            saveAllChunks();
        }
        
        tickCount++;
    }
The server must maintain 20 TPS (ticks per second) for proper gameplay. Performance issues causing tick lag affect all connected players.

Connection Management

ClientConnection

Manages the client-side network connection (ClientConnection.h:9):
class ClientConnection : public PacketListener
{
private:
    enum eClientConnectionConnectingState {
        eCCPreLoginSent = 0,
        eCCPreLoginReceived,
        eCCLoginSent,
        eCCLoginReceived,
        eCCConnected
    };
    
    Connection *connection;  // Low-level socket
    Minecraft *minecraft;
    MultiPlayerLevel *level;
    bool started;
    bool done;
    
public:
    ClientConnection(Minecraft *mc, const wstring& ip, int port);
    ClientConnection(Minecraft *mc, Socket *socket, int userIndex);
    
    void tick();
    void send(shared_ptr<Packet> packet);
    void close();
    
    // Packet handlers
    virtual void handleLogin(shared_ptr<LoginPacket> packet);
    virtual void handleChunkVisibility(shared_ptr<ChunkVisibilityPacket> packet);
    virtual void handleAddPlayer(shared_ptr<AddPlayerPacket> packet);
    virtual void handleMovePlayer(shared_ptr<MovePlayerPacket> packet);
};

Connection Handshake

The connection process follows this sequence:

ServerConnection

Manages all incoming connections on the server (ServerConnection.h:10):
class ServerConnection
{
private:
    vector<shared_ptr<PendingConnection>> pending;
    vector<shared_ptr<PlayerConnection>> players;
    MinecraftServer *server;
    
public:
    void NewIncomingSocket(Socket *socket);
    void tick();
    void stop();
};

PlayerConnection

Manages a single player’s connection on the server (PlayerConnection.h:12):
class PlayerConnection : public PacketListener
{
public:
    Connection *connection;
    shared_ptr<ServerPlayer> player;
    MinecraftServer *server;
    bool done;
    
    void tick();
    void send(shared_ptr<Packet> packet);
    void disconnect(DisconnectPacket::eDisconnectReason reason);
    
    // Packet handlers
    virtual void handlePlayerAction(shared_ptr<PlayerActionPacket> packet);
    virtual void handleMovePlayer(shared_ptr<MovePlayerPacket> packet);
    virtual void handleChat(shared_ptr<ChatPacket> packet);
};

Player Management

PlayerList

The server maintains a centralized player list:
class PlayerList
{
public:
    vector<shared_ptr<ServerPlayer>> players;
    MinecraftServer *server;
    
    void addPlayer(shared_ptr<ServerPlayer> player);
    void removePlayer(shared_ptr<ServerPlayer> player);
    void broadcastPacket(shared_ptr<Packet> packet);
    void sendLevelData(ServerPlayer *player);
};

Player Lifecycle

  1. Connection: PlayerConnection created when socket connects
  2. Login: Player entity created after successful authentication
  3. Spawn: Player spawned in world, visible to other players
  4. Gameplay: Continuous packet exchange (movement, actions, chat)
  5. Disconnect: Player removed, resources cleaned up

Split-Screen Local Play

Minecraft CE supports up to 4 local players simultaneously, each with their own connection to the local server.

Adding Local Players

// Minecraft.h:119
bool Minecraft::addLocalPlayer(int idx) {
    // Create local player
    localplayers[idx] = createExtraLocalPlayer(idx, name, pad, dimension);
    
    // Create client connection to local server
    Socket *socket = createLocalSocket();
    ClientConnection *connection = new ClientConnection(this, socket, idx);
    m_pendingLocalConnections[idx] = connection;
    
    // Update viewport assignments
    updatePlayerViewportAssignments();
    
    return true;
}
Each local player:
  • Has their own ClientConnection to the server
  • Maintains separate inventory and game state
  • Renders to their own viewport
  • Can have different privileges and permissions

Server Lifecycle

Server Initialization

// MinecraftServer.h:124
bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, 
                                  DWORD initSettings, bool findSeed) {
    // 1. Load or generate level
    loadLevel(storageSource, name, seed, levelType, initData);
    
    // 2. Initialize dimensions (Overworld, Nether, End)
    for (int dim : {0, -1, 1}) {
        levels[dim] = createServerLevel(dim);
    }
    
    // 3. Start server connection
    connection = new ServerConnection(this);
    
    // 4. Begin ticking
    running = true;
    return true;
}

Server Main Loop

void MinecraftServer::run(__int64 seed, void *lpParameter) {
    initServer(seed, initData, settings, findSeed);
    
    __int64 lastTick = currentTimeMillis();
    
    while (running) {
        __int64 now = currentTimeMillis();
        __int64 delta = now - lastTick;
        
        if (delta > MS_PER_TICK) {
            tick();
            lastTick = now;
        } else {
            // Sleep to avoid busy-waiting
            Sleep(1);
        }
    }
    
    stopServer();
}

Server Shutdown

void MinecraftServer::stopServer() {
    running = false;
    
    // Save all chunks
    saveAllChunks();
    
    // Disconnect all players
    connection->stop();
    
    // Clean up levels
    for (ServerLevel *level : levels) {
        delete level;
    }
}

Best Practices

Always use critical sections when accessing shared state:
EnterCriticalSection(&m_setLevelCS);
level = newLevel;
LeaveCriticalSection(&m_setLevelCS);
Check connection state before sending packets:
if (connection && connection->isStarted() && !connection->isClosed()) {
    connection->send(packet);
}
Avoid expensive operations in the main tick loop. Use background threads for:
  • Chunk generation
  • Chunk compression
  • File I/O operations

Networking

Deep dive into the network protocol and packet system

Overview

High-level architecture and component overview

Build docs developers (and LLMs) love