Files
codexPySnake/PROJECT_PLAN.md
Vladyslav Doloman 03969ebd99 Plan: initial multiplayer Snake design (server, client, protocol)
- Add PROJECT_PLAN.md outlining the core design:
  - Server: Python 3 asyncio, authoritative tick loop
  - Client: WebTransport (datagrams), canvas rendering, spectator overlay
  - Mechanics: blocked head + tail shrink, apples, colors, 60x40 field,
    continuous play, length as score, input buffer rules, 180° policy
  - Networking: datagrams, seq numbers + wraparound, delta/full updates,
    1280-byte budget, up to 32 players, names ≤16 bytes
  - Testing strategy and milestones

Rationale: establish a shared baseline for scope and interfaces.
2025-10-07 18:36:46 +03:00

9.7 KiB
Raw Blame History

Multiplayer Snake (WebTransport) — Project Plan

Overview

  • Goal: Build a real-time, networked multiplayer Snake game with a Python 3 authoritative server and a web client using WebTransport datagrams for low-latency updates.
  • Gameplay: Continuous, persistent world (no rounds). Players can join/leave anytime. Display each snakes current length; the longest snake is the momentary “leader”.
  • Field: Default 60×40 grid; protocol supports 3×3 up to 255×255.
  • Networking: Unreliable, unordered datagrams with sequence numbers and wraparound handling. Compression and packet partitioning to fit MTU (~12801500 bytes).

Core Mechanics

  • Collision handling: When the snake head would hit an obstacle (wall, self, other snake), the head stays in place (blocked) and the tail shrinks by 1 per tick until the player turns to a free direction.
  • Self-collision: Temporary; tail shrink can clear the blocking segment.
  • Other-snake collision: Clears when the other snake moves or shrinks away.
  • Minimum length: A snake cannot be shorter than its head; minimum length is 1.
  • Turning rules:
    • Length > 1: 180° turns are invalid and ignored at consumption time.
    • Length = 1 (only head left): 180° turns are valid.
  • Apples and scoring:
    • Replace score display with current snake length.
    • Eating apple grows snake by 1 immediately.
    • When 0 players remain connected, pre-populate field with exactly 3 apples.
  • Colors: Assign a color when a client connects and keep it stable for that session.
  • Spectator join: On initial connection, show current gameplay and overlay “press space to join”.

Input Buffer Rules

  • Maintain a small buffer of up to 3 upcoming direction changes.
  • If the new input is directly opposite to the last buffered direction, replace the last buffered entry instead of appending.
  • Drop repeats: Do not add consecutive duplicates to the buffer.
  • Overflow policy: If buffer is full, replace the last element with the new input.
  • At each tick, before moving:
    • Consume at most one input from the buffer.
    • If length > 1 and it is a 180° turn, ignore it and immediately try the next buffered input; if none valid, keep current direction.

Server Architecture (Python 3)

  • Runtime: Python 3 with asyncio.
  • Transport: WebTransport (HTTP/3 datagrams). Candidate library: aioquic for server-side H3 and datagrams.
  • Model:
    • Authoritative simulation on the server with a fixed tick rate (target 1520 TPS; start with 15 for network headroom).
    • Each tick: process inputs, update snakes, resolve collisions (blocked/shrink), move apples, generate state deltas.
  • Entities:
    • Field: width, height, random seed, apple set.
    • Snake: id, name (≤16 chars), color id (031), deque of cells (head-first), current direction, input buffer, blocked flag, last-move tick, last-known client seq.
    • Player session: transport bindings, last-received input seq per player, anti-spam counters.
  • Spawn:
    • New snake spawns at a random free cell with a random legal starting direction, length 3 by default (configurable).
    • On apple eat: grow by 1; spawn a replacement apple at a free cell.
  • Disconnect: Immediately remove snake and its score/length; apples persist. If all disconnect, ensure field contains 3 apples.
  • Rate limiting: Cap input datagrams per second and buffer growth per client.

Client Architecture (Web)

  • Tech: Browser WebTransport (H3 datagrams), Canvas/WebGL rendering, vanilla or minimal framework for UI overlay.
  • Modes:
    • Spectator: Render world, show overlay “press space to join”.
    • Player: Capture keyboard, build input buffer client-side, send inputs with sequence numbers; predict local movement for smoothness but reconcile to server.
  • Rendering:
    • Gridless pixel or cell-based canvas.
    • Colors: 32-color predefined palette; deterministic mapping from player id to color id.
    • HUD: Current length; leaderboard (top N by length).

Networking & Protocol

Constraints and goals:

  • Use datagrams (unreliable, unordered). Include a packet sequence number for late-packet discard with wraparound handling.
  • Keep payloads ≤1280 bytes to avoid fragmentation; if larger, partition logically.
  • Support up to 32 concurrent players; names limited to 16 bytes UTF8.

Header (all packets):

  • ver (u8): protocol version.
  • type (u8): packet type (join, join_ack, input, state_delta, state_full, part, ping, pong, error).
  • flags (u8): bit flags (compression, is_last_part, etc.).
  • seq (u16): senders monotonically incrementing sequence number (wraps at 65535). Newer-than logic uses half-range window.
  • Optional tick (u16): simulation tick the packet refers to (on server updates).

Handshake (join → join_ack):

  • Client sends: desired name (≤16), optional preferred color id.
  • Server replies: assigned player id (u8), color id (u8), field size (width u8, height u8), tick rate (u8), random seed (u32), palette, and initial full snapshot.

Inputs (client → server):

  • Packet input includes: last-acknowledged server seq (u16), local input_seq (u16), one or more direction events with timestamps or relative tick offsets. Client pre-filters per buffer rules.

State updates (server → client):

  • Types:
    • state_full: rare (on join or periodic recovery); includes all snakes and apples.
    • state_delta: frequent; contains only changed snakes (head move, tail shrink/grow) and apple changes since last acked tick.
  • Per-snake encoding (compressed):
    • id (u8), len (u16), head (x,y) (u8,u8), direction (2 bits), and a compact body representation:
      • Option A: Run-length of axis-aligned segments (dir, run u16) starting at head.
      • Option B: Delta-coded cells with RLE for straight runs.

Compression:

  • Primary: domain-specific packing (bits + RLE for segments and apples), then optional DEFLATE flag (flags.compressed=1) if still large.
  • Client decompresses if set; otherwise reads packed structs directly.

Packet partitioning (if >1280 bytes):

  • Split state_full/state_delta by snakes into multiple independent sub-updates.
  • Each part has: update_id (u16), part_index (u8), parts_total (u8). Clients apply parts as they arrive; missing parts simply leave some snakes stale until the next update.
  • Extremely long single-snake updates: split that snakes body into balanced segments by RLE chunks.

Sequence wrap & ordering:

  • Define is_newer(a, b) using signed 16-bit difference: ((a - b) & 0xFFFF) < 0x8000.
  • Clients ignore updates older-than last applied per-sender.

Simulation Details

  • Tick order per tick:
    1. Incorporate at most one valid buffered input per snake (with 180° rule).
    2. Compute intended head step; if blocked, set blocked=true and keep head stationary.
    3. If blocked=true: shrink tail by 1 (to min length 1). If the blocking cell becomes free (due to own shrink or others moving), the next ticks step in current direction proceeds.
    4. If moving into an apple: grow by 1 (do not shrink tail that tick) and respawn an apple.
    5. Emit per-snake delta (move, grow, or shrink) and global apple changes.
  • Blocking detection: walls (0..width-1, 0..height-1), any occupied snake cell (including heads and tails that are not about to vacate this tick).
  • 180° turn allowance when length==1 only.

Data Model & Structures

  • Field cells: Represent coordinates as (x:u8, y:u8).
  • Snakes: Deque of cells or segment list; store length as deque.size() and maintain head index.
  • Apples: Hash set of cells for O(1) lookup; spawn on empty cells only.
  • Occupancy: Sparse set/map from cell→(snake_id, index) for O(1) collision checks.

Limits & Config

  • Players: max 32 (ids 0..31); deny joins beyond capacity.
  • Names: UTF8, truncate to 16 bytes; filter control chars.
  • Field: default 60×40; min 3×3; max 255×255; provided by server in join_ack.
  • Tick rate: default 15 TPS; configurable 1030 TPS.

Error Handling & Resilience

  • Invalid packets: drop and optionally send error with code.
  • Flooding: server-side rate limits and strike-based disconnects.
  • Out-of-order: discard using is_newer check.
  • Desync recovery: server sends state_full periodically or when client reports large gaps.

Testing Strategy

  • Unit tests: input buffer rules, 180° logic, blocking/shrink, collision resolution, sequence wrap comparison.
  • Property tests: snake movement invariants (no duplicates in body except expected at head on block), apple placement safety.
  • Soak tests: bot clients sending random but rate-limited inputs; measure packet size and latency.

Milestones

  1. Protocol draft + wire structs (this plan).
  2. Server skeleton: tick loop, entities, occupancy, apples, basic WebTransport datagrams.
  3. Input handling: buffer logic and per-tick consumption; collision/blocking/shrink rules.
  4. State encoding: deltas, compression, and partitioning; client renderer (spectator).
  5. Player flow: join overlay, name/color, spawn, HUD and leaderboard.
  6. Performance pass: packet size tuning, RLE, optional DEFLATE, rate limits.
  7. Polishing: error messages, reconnect, telemetry/logging, Docker/dev scripts.

Open Questions

  • Exact tick rate target and interpolation strategy on client.
  • Initial snake length on spawn (3 is suggested) and spawn retry rules in crowded fields.
  • Apple count baseline when players are present (fixed count vs proportional to players vs area).
  • Whether to allow wrap-around at field edges (currently: walls are blocking).
  • Compression choice tradeoffs: custom bitpacking only vs optional DEFLATE.
  • Minimum viable browser targets (WebTransport support varies).

Next Steps

  • Validate the protocol choices and mechanics with stakeholders.
  • Decide on tick rate and initial lengths.
  • Confirm library choices (aioquic server; client-side WebTransport API usage).
  • Begin Milestone 2: implement server skeleton and simulation core.