Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lockstep backend server

Deterministic lockstep server.

The server runs one authoritative game world. Clients receive snapshots and command frames, then run the same fixed-tick simulation locally for prediction and checksum validation.

Goals

  • Low network overhead through semantic commands.
  • Deterministic simulation with replay support.
  • Predictable latency through fixed command delay.

Architecture

The repo holds a library and a worked example of using it.

  • rts_engine/the library, the only installable package. Owns the protocol: command ordering, frame assembly, history windows, resync payloads, desync detection. Owns no game and imports nothing from one.
  • example_server/ — a game built on it: esper world, components, physics, MOVE/JUMP. Copy this as a starting template.
  • example_client/ — browser canvas client for that game, no build step.
  • docs/protocol.md — the contract the library owns; read it to write your own game or a client in another language.
  • docs/client-guide.md — the example's game rules: components, physics, world layout.

Attach a game by implementing WorldProtocol — six methods and a command union. That type is the entire seam; everything game-specific hangs off it.

One process runs one match: the example keeps state in a process-global ECS (esper). Scale by running more processes.

Determinism Rules

  • Simulation uses integer coordinates and velocities.
  • Every tick applies commands in canonical (tick, player_id, sequence, command) order.
  • Entity iteration is sorted by stable unit id.
  • Networking never mutates simulation state directly.
  • Async is allowed around I/O, not inside the simulation update.

Quick Start

Install for development:

! REQUIRES python3.14

python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest

Run the example server (tokens are pre-shared secrets issued to each client by the launcher):

python -m example_server --host 127.0.0.1 --port 8766 --player-tokens alice-token bob-token

Omit --player-tokens to connections become spectators and cannot issue commands.

State Sync

New clients receive a state_sync message:

  • snapshot: deterministic world snapshot at snapshot_tick.
  • command_frames: authoritative command history after the snapshot.
  • current_tick: tick to replay up to before live frames continue.

The server keeps this snapshot as a bootstrap cache. Late-joining clients reconstruct state by loading the latest snapshot and replaying only the command tail after it.

Protocol

Runtime transport uses websocket frames carrying MessagePack payloads. Full contract, including the checksum algorithm and its golden vector, is in docs/protocol.md.

Auth handshake

Every connection must send an auth message as its first frame before any other message is accepted. The server closes the connection silently on an unknown token or a 10-second timeout.

# client → server (first message)
{"kind": "auth", "token": "<pre-shared-token>"}

# server → client (on success)
{"kind": "state_sync", "player_id": 6, "snapshot": ..., "command_frames": [...], ...}

player_id is the server-assigned entity ID that owns the client's units.

Commands

Command frames contain intentions, not replicated unit state. The server assigns the issuer from the authenticated connection — the wire field is ignored.

# client → server
{"kind": "command", "command": {"type": "MOVE", "sequence": 1, "targets": [7, 8], "x": 1}}

# server → client
{"kind": "command_accepted", "sequence": 1, "assigned_tick": 105}

# server → all clients (each tick)
{"kind": "command_frame", "tick": 105, "commands": [...]}

Checksums

Clients periodically send deterministic state checksums. The server validates only ticks within the window [snapshot_tick, current_tick + checksum_interval].

# client → server
{"kind": "checksum", "tick": 200, "checksum": "7d87f1ab"}

Server compares client checksums for the same tick against its own authoritative value and other clients. If values differ, it broadcasts a desync_report with checksum groups by participant.

Known Issues:

  • No backpressure for broadcast
  • No Rate-Limit on commands

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages