Skip to main content

Overview

Minecraft Community Edition uses a fixed timestep game loop running at 20 ticks per second (TPS) for logic and targeting 60 frames per second (FPS) for rendering. The loop is split between the main thread handling rendering and input, with optional threading for level updates.

Main Game Loop

The primary game loop is implemented in Minecraft::run_middle() in Minecraft.cpp:1252.

Loop Structure

void Minecraft::run_middle()
{
    static __int64 lastTime = 0;
    static int frames = 0;
    
    if (running) {
        AABB::resetPool();
        Vec3::resetPool();
        
        // Process autosave timer
        // Handle ban list checks
        // Update player viewports
        
        // Store button presses for all players
        for (int i = 0; i < XUSER_MAX_COUNT; i++) {
            if (localplayers[i]) {
                // Capture input for processing
            }
        }
    }
}
Location: Minecraft.cpp:1252

Timer System

The game uses a Timer class initialized with SharedConstants::TICKS_PER_SECOND (20 TPS).
timer = new Timer(SharedConstants::TICKS_PER_SECOND);
Key Constants:
  • SharedConstants::TICKS_PER_SECOND = 20 - Fixed tick rate
  • MS_PER_TICK = 50 - Milliseconds per tick (1000ms / 20 ticks)
Location: Minecraft.cpp:127

Tick Processing

The server processes game logic in fixed 50ms intervals:
while (unprocessedTime > MS_PER_TICK) {
    unprocessedTime -= MS_PER_TICK;
    tick();
}
Location: MinecraftServer.cpp:1116

Tick Components

  1. Connection Ticking - Process network packets
  2. Level Ticking - Update world state, entities, tile entities
  3. Player Ticking - Process player actions and movement
  4. Weather Updates - Rain and thunder state changes
  5. Time Updates - Day/night cycle progression

Update/Render Separation

The architecture separates simulation from rendering:

Update Thread

  • Runs at 20 TPS fixed timestep
  • Processes game logic deterministically
  • Handles entity updates, physics, AI
  • Independent of frame rate

Render Thread

  • Targets 60 FPS
  • Interpolates between tick states for smooth visuals
  • Uses timer->a for interpolation alpha
  • Can run faster or slower than update rate
// Rendering uses interpolation
gameRenderer->render(timer->a);
Location: Minecraft.cpp:764

Level Tick Thread

Optionally, level ticking can be offloaded to a separate thread:
#ifndef DISABLE_LEVELTICK_THREAD
levelTickEventQueue = new C4JThread::EventQueue(
    levelTickUpdateFunc, 
    levelTickThreadInitFunc, 
    "LevelTick_EventQueuePoll"
);
levelTickEventQueue->setProcessor(3);
levelTickEventQueue->setPriority(THREAD_PRIORITY_NORMAL);
#endif
Location: Minecraft.cpp:230-234

Benefits

  • Offloads expensive world updates from main thread
  • Runs at 20 Hz instead of 60 Hz
  • Reduces frame drops during heavy world processing
  • Uses CPU core 3 for parallel execution

Platform-Specific Threading (C4JThread)

The game uses a custom threading system C4JThread that wraps platform-specific thread APIs:

Thread Creation

C4JThread* thread = new C4JThread(
    runFunction,      // Thread function
    parameter,        // User data
    "ThreadName",     // Debug name
    stackSize         // Stack size in bytes
);

thread->SetProcessor(cpuCore);     // Pin to CPU core
thread->SetPriority(priority);     // Set thread priority
thread->Run();                     // Start execution

Thread Types

Event Queue Thread:
EventQueue* queue = new EventQueue(
    updateFunc,
    initFunc,
    "QueueName"
);
Used for:
  • Level tick offloading
  • Post-processing during world generation
  • Background chunk operations
Location: Minecraft.cpp:230

Pause System

The game supports pausing simulation while keeping rendering active:
if (pause && level != NULL) {
    float lastA = timer->a;
    timer->advanceTime();
    timer->a = lastA;  // Preserve interpolation
}
Server-Side Pausing:
if (!m_isServerPaused) {
    while (unprocessedTime > MS_PER_TICK) {
        tick();
    }
} else {
    // Keep connections alive
    connection->tick();
}
Location: MinecraftServer.cpp:1104-1154

Frame Timing

The game tracks frame and tick durations for debugging:
static __int64 frameTimes[512];
static __int64 tickTimes[512];
static int frameTimePos = 0;
Location: Minecraft.cpp:90-92

Performance Monitoring

  • Frame times stored in ring buffer
  • Displayed in debug overlay (F3)
  • Shows tick duration graph
  • Helps identify performance bottlenecks

Memory Management per Frame

Each frame resets temporary object pools:
AABB::resetPool();   // Reset bounding box pool
Vec3::resetPool();   // Reset vector pool
Purpose:
  • Reduces memory allocations
  • Improves cache coherency
  • Prevents memory fragmentation
  • Faster than individual allocations
Location: Minecraft.cpp:1276-1277

Split-Screen Multiplayer

The game loop processes up to 4 local players:
for (int i = 0; i < XUSER_MAX_COUNT; i++) {
    if (setLocalPlayerIdx(i)) {
        // Process player i
        // Each player gets their viewport
    }
}

Player Context Switching

bool Minecraft::setLocalPlayerIdx(int idx) {
    localPlayerIdx = idx;
    gameMode = localgameModes[idx];
    player = localplayers[idx];
    level = getLevel(localplayers[idx]->dimension);
    return true;
}
Location: Minecraft.cpp:861-876

Key Takeaways

  1. Fixed Timestep: Game logic always runs at 20 TPS for determinism
  2. Render Interpolation: Smooth 60 FPS visuals via alpha blending
  3. Thread Safety: Critical sections protect shared data structures
  4. Split-Screen: Each player processes sequentially with context switching
  5. Pause Support: Can pause simulation while maintaining connections

Build docs developers (and LLMs) love