aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Game Server

Game Loop Tick Rate: Designing the Server Loop

A single thing beats at the heart of every game server: the loop. The game loop tick rate decides how many times per second the server updates the world — stepping physics, applying movement and sending state to clients. Get this number, and the way the loop is built, wrong and players experience teleporting characters, inconsistent collisions and gameplay that falls apart as things speed up. In this article I explain what a tick is, why you need a fixed timestep and how to build a stable server loop in C++.

What is a tick, and what does tick rate mean?

A tick is a single step the simulation advances. On each tick the server moves the game state forward by a fixed amount of time: it reads inputs, moves characters, resolves collisions and computes the result. Tick rate is how many of those happen per second, usually expressed in Hz. 20 ticks/s means the world updates 20 times a second (once every 50 ms).

Common values vary by genre:

  • 10–20 Hz — MMOs and non-shooter RPGs where reaction time isn't critical.
  • 30 Hz — a reasonable balance for many action and survival games.
  • 60–128 Hz — competitive FPS titles, where aiming and hit registration need high precision.

Raising the tick rate makes the game more responsive, but it grows CPU load and bandwidth proportionally. 64 Hz means twice the work — and usually twice the packets — of 32 Hz. The right number is the balance between the precision your game demands and the load your server can carry.

Why a fixed timestep?

A naive loop is often written as: "measure the elapsed time, advance everything by that much." That's a variable timestep, and it's trouble. When delta changes every frame the physics is not deterministic: the same input can produce two different outcomes, fast objects can skip through collisions (tunneling), and the server and client never agree on the same number.

A fixed timestep solves this. You always advance the simulation by the same dt (for example 1/30 of a second). Even if real time flows faster or slower, the logic moves the same amount every step. That determinism is essential for replays, cheat detection and a server-authoritative architecture.

The logic runs on an accumulator: you add the elapsed real time to a bucket, and as long as the bucket exceeds one dt you run fixed steps. If the CPU falls behind by a frame, the loop runs several ticks back to back to drain the accumulator ("catch-up"), so simulation time never lags behind real time.

A fixed-step server loop

The C++ example below shows the classic accumulator-based loop, using a high-resolution clock via std::chrono:

#include <chrono>
#include <thread>

using clock_t = std::chrono::steady_clock;
using namespace std::chrono;

const int   TICK_RATE = 30;
const double DT = 1.0 / TICK_RATE;       // fixed step, in seconds

void run_server() {
    auto previous = clock_t::now();
    double accumulator = 0.0;

    while (server_running) {
        auto now = clock_t::now();
        double frame = duration<double>(now - previous).count();
        previous = now;

        // One slow frame must not trigger the spiral of death
        if (frame > 0.25) frame = 0.25;
        accumulator += frame;

        while (accumulator >= DT) {
            process_input();        // apply client packets
            update_world(DT);       // physics + game logic, always the same DT
            accumulator -= DT;
        }

        broadcast_state();          // send current state to clients

        // Sleep until the next tick so the CPU doesn't spin
        auto next = previous + duration_cast<clock_t::duration>(duration<double>(DT));
        std::this_thread::sleep_until(next);
    }
}

Three critical points here: DT is always constant (not the variable elapsed time from the clock), the frame time is capped (otherwise, if the server stalls, the accumulator balloons and the loop locks up in never-ending ticks — the "spiral of death"), and sleep_until waits for the next tick so the CPU doesn't burn 100% spinning.

Sleep accuracy and timing drift

Using sleep_until instead of sleep_for matters. sleep_for(33ms) says "sleep 33 ms" every time, but wake-up latency accumulates each round and slowly drifts the ticks. sleep_until(next) aims at an absolute target time; if one tick wakes late, the next wakes earlier and keeps the average on track.

Operating-system sleep resolution isn't perfect: a few hundred microseconds on Linux, and milliseconds on Windows with the default timer. If you target a high tick rate (64+ Hz), a common technique is a short busy-wait very close to the target time to sharpen the clock — but it keeps a CPU core busy, so use it sparingly where it counts.

Tick rate, network send rate and the client side

The server's simulation rate and its rate of sending state over the network do not have to be the same. A server can simulate at 60 Hz but broadcast state only at 20 Hz, which lowers bandwidth while keeping simulation quality. The client fills the gap with interpolation (smoothing between two past states) and prediction for its own input.

A practical checklist:

  • Pick the simulation rate from the precision your game needs; think about the network rate separately.
  • Keep the server authoritative: the client sends input, the server computes the result.
  • Put a tick number in every state packet so the client knows which step it is seeing.
  • Measure tick duration; if a tick starts exceeding DT, either lower the tick rate or optimize the work.

Common mistakes

  • Physics on a variable delta: breaks determinism; hit registration and replays become unreliable.
  • An uncapped accumulator: once the server stalls, the spiral of death locks it up completely.
  • Blocking I/O inside the tick: a disk or synchronous database call stalls the whole simulation; move those to a separate thread or a queue.
  • Blindly raising the tick rate: CPU and bandwidth grow linearly; measure the bottleneck first.

Frequently Asked Questions

What should the tick rate be?

It depends on the genre. 10–20 Hz is enough for a slow-paced MMO; 30 Hz is a balanced starting point for action games; competitive shooters target 60–128 Hz. First decide the reaction time your game needs, then measure whether the server can carry it.

What's the difference between a fixed and a variable timestep?

With a fixed step the simulation always advances by the same dt and is deterministic; with a variable step each frame uses the real elapsed time, which makes physics inconsistent and unrepeatable. Server-authoritative games almost always use a fixed step.

If I raise the tick rate, will latency drop?

Somewhat: a higher tick rate shortens the wait between the server processing inputs. But it doesn't change the network round-trip time (ping). The felt latency depends largely on the network and on client-side interpolation/prediction design.

Is your server loop unstable, or are you hunting for the right tick rate? Let's review your game server architecture together and build a stable, deterministic game loop — get in touch with me.

Bu kategorideki tüm yazılar →

Devamı için