# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview This is a real-time multiplayer Snake game using Python 3 (server) and vanilla JavaScript (web client), communicating via WebTransport datagrams (HTTP/3) for low-latency gameplay. The game features continuous persistent gameplay, unique collision mechanics (head-blocks-and-tail-shrinks instead of death), and sophisticated UDP packet handling with compression and partitioning. ## Running the Server ### Dependencies ```bash # Create and activate virtual environment python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate # Install dependencies pip install -r requirements.txt ``` ### Server Modes The server supports multiple transport modes via `MODE` environment variable: ```bash # In-memory mode (testing, no network) MODE=mem python run.py # WebTransport mode (HTTP/3, default for production) MODE=wt QUIC_CERT=cert.pem QUIC_KEY=key.pem python run.py # QUIC datagram mode only MODE=quic QUIC_CERT=cert.pem QUIC_KEY=key.pem python run.py # Combined mode (both WebTransport and QUIC) MODE=net WT_PORT=4433 QUIC_PORT=4443 QUIC_CERT=cert.pem QUIC_KEY=key.pem python run.py ``` ### Environment Variables - `MODE`: Transport mode (mem|wt|quic|net) - `QUIC_CERT` / `QUIC_KEY`: TLS certificate and key paths (required for wt/quic/net) - `WT_CERT` / `WT_KEY`: WebTransport-specific cert/key (falls back to QUIC_* if not set) - `WT_HOST` / `WT_PORT`: WebTransport server host/port (default: 0.0.0.0:4433) - `QUIC_HOST` / `QUIC_PORT`: QUIC server host/port (default: 0.0.0.0:4433, or 4443 in net mode) - `STATIC`: Enable static HTTPS server for client (default: 1 in wt mode) - `STATIC_HOST` / `STATIC_PORT` / `STATIC_ROOT`: Static server settings (default: same as WT host, port 8443, root "client") - `LOG_LEVEL`: Logging level (DEBUG|INFO|WARNING|ERROR) - `RUN_SECONDS`: Optional timeout for server shutdown (testing) ### Help ```bash python run.py --help ``` ## Architecture ### Server Architecture (Python 3 + asyncio) **Core Components:** 1. **server/server.py** (`GameServer`): Authoritative game server - Fixed tick rate simulation (default 10 TPS, configurable 5-30) - Manages player sessions, snakes, apples, collision detection - Handles join/spawn logic, input processing, state broadcasting - Automatically partitions large updates across multiple datagrams (<1200 bytes each to avoid IP fragmentation) 2. **server/model.py**: Game state entities - `GameState`: Field grid, snakes dict, apples list, occupancy map - `Snake`: Deque-based body representation, input buffer (capacity 3), blocked flag - `PlayerSession`: Per-player metadata (id, name, color, peer handle) 3. **server/protocol.py**: Wire protocol implementation - Packet types: JOIN, JOIN_ACK, JOIN_DENY, INPUT, INPUT_BROADCAST, STATE_DELTA, STATE_FULL, PART, CONFIG_UPDATE - TLV body encoding: 2-bit direction streams, RLE compression, chunked variants for oversized snakes - QUIC varint encoding for efficient integer serialization - Sequence number handling with wraparound logic (16-bit) 4. **server/config.py** (`ServerConfig`): Server configuration dataclass with validation 5. **Transport layer** (pluggable): - `server/transport.py`: Abstract `DatagramServerTransport` interface - `server/webtransport_server.py`: WebTransport (HTTP/3) implementation using aioquic - `server/quic_transport.py`: Raw QUIC datagram implementation - `server/multi_transport.py`: Multiplexer for running multiple transports concurrently - `server/static_server.py`: Optional HTTPS static file server for serving client assets ### Client Architecture (Vanilla JavaScript) **Files:** - `client/index.html`: Main HTML page with canvas and UI controls - `client/client.js`: Game client, WebTransport connection, input handling, rendering - `client/protocol.js`: Wire protocol parsing/building (mirrors server protocol) **Key Features:** - WebTransport datagram API for unreliable, unordered messaging - Canvas-based rendering with auto-scaling grid - Input buffering (up to 3 direction changes) with 180° turn filtering - State update handling: STATE_FULL (initial/recovery), STATE_DELTA (per-tick), PART (fragmented) - Packet sequence tracking to discard late/out-of-order updates ### Game Mechanics (Unique Collision System) **No Death on Collision:** - When a snake's head hits an obstacle (wall, self, or another snake), the head stays in place (blocked state) - While blocked, the tail shrinks by 1 cell per tick until the player turns to a free direction - Minimum length is 1 (head only); snake cannot disappear while player is connected **Input Buffer Rules:** - Client-side buffer holds up to 3 upcoming direction changes - Opposite direction to last buffered input replaces the last entry (not appended) - Consecutive duplicates are dropped - Overflow policy: replace last element - Server consumption: At each tick, consume at most one input; skip 180° turns when length > 1 **Apples & Scoring:** - Displayed as current snake length (not separate score) - Eating an apple grows snake by 1 immediately - Target apple count: max(3, min(apples_cap, connected_players × apples_per_snake)) - When 0 players remain, pre-populate field with exactly 3 apples **Spawning:** - Try to allocate a 3-cell straight strip in a free direction - Fallback: spawn length 1 (head only) at any free cell - Deny join if no free cells available ### Protocol Details **Packet Structure:** ``` Header (all packets): ver (u8) | type (u8) | flags (u8) | seq (u16 big-endian) | [tick (u16 optional)] ``` **Sequence Numbers:** - 16-bit wraparound with half-range window comparison: `is_newer(a,b) = ((a-b) & 0xFFFF) < 0x8000` - Clients discard packets with older seq than last applied per sender **State Encoding:** - **STATE_FULL**: Complete snapshot (on join, periodic recovery); includes all snakes + apples - Per-snake: id (u8), len (u16), head (x,y), body TLV (BODY_2BIT or BODY_RLE) - **STATE_DELTA**: Incremental update (per tick); includes only changed snakes + apple diffs - Per-change: snake_id, flags (head_moved|tail_removed|grew|blocked), direction, new_head coords - **PART**: Multi-part fragmentation for large updates (>1200 bytes) - Fields: update_id (u16), part_index, parts_total, inner_type (STATE_FULL or STATE_DELTA), chunk_payload **Body TLV Types:** - `BODY_2BIT (0x00)`: 2-bit direction stream (up=00, right=01, down=10, left=11), packed LSB-first - `BODY_RLE (0x01)`: Run-length encoding (dir u8, count QUIC varint) for straight segments - `BODY_2BIT_CHUNK (0x10)` / `BODY_RLE_CHUNK (0x11)`: Chunked variants for splitting oversized snakes across parts - Chunk header: start_index (u16), dirs_in_chunk (u16) **Input Broadcasting:** - Server relays client inputs immediately to all other clients as INPUT_BROADCAST packets - Enables client-side opponent prediction during late/lost state updates - Fields: player_id, input_seq, base_tick, events (rel_tick_offset + direction), optional apply_at_tick **Partitioning Strategy:** - Soft limit: 1200 bytes per datagram (avoid fragmentation; MTU ~1280-1500) - Whole-snake-first: pack complete snakes greedily; if a single snake exceeds budget, split using chunked TLV - Apples included only in first part; clients merge across parts using update_id ## Key Simulation Details **Tick Order:** 1. Consume at most one valid input per snake (apply 180° rule for length > 1) 2. Compute intended head step; if blocked (wall/occupied), set `blocked=True` and keep head stationary 3. If blocked: shrink tail by 1 (min length 1) 4. If moving into apple: grow by 1 (don't shrink tail that tick) and respawn apple 5. Emit per-snake delta and global apple changes **Collision Detection:** - Occupancy map tracks all snake cells as (coord) -> (snake_id, index) - Allow moving into own tail if it will vacate (i.e., not growing) - Walls block only when `wrap_edges=False`; wrapped coordinates computed by `_step_from()` **180° Turn Handling:** - Checked at consumption time using XOR: `(int(new_dir) ^ int(current_dir)) == 2` - Allowed only when snake length == 1 (head only) ## Development Notes - **Testing**: No automated test suite currently exists. Manual testing via in-memory mode (`MODE=mem`) or live WebTransport server. - **Logging**: Use `LOG_LEVEL=DEBUG` for verbose packet-level debugging. - **Field Size**: Default 60×40; protocol supports 3×3 to 255×255 (negotiated in JOIN_ACK). - **Players**: Max 32 concurrent (ids 0-31); names truncated to 16 UTF-8 bytes. - **Colors**: 32-color palette; deterministic mapping from player_id to color_id (0-31). - **Tick Rate Changes**: Server broadcasts CONFIG_UPDATE every 50 ticks; clients apply at next tick boundary. - **Compression**: Optional global DEFLATE mode (handshake-only; requires server restart). Currently defaults to "none". - **Browser Compatibility**: Requires WebTransport API support (latest Firefox/Chrome). ## Common Patterns **Adding a New Packet Type:** 1. Add enum value to `PacketType` in server/protocol.py and client/protocol.js 2. Implement builder function in protocol (e.g., `build_foo()`) and parser (e.g., `parse_foo()`) 3. Handle in server's `on_datagram()` and client's `readLoop()` switch statements 4. Update header packing if tick field is required (pass tick to `pack_header()`) **Modifying Simulation Logic:** 1. Edit `_simulate_tick()` in server/server.py for per-tick updates 2. Update `SnakeDelta` dataclass if new change types are needed 3. Adjust `build_state_delta_body()` if encoding changes 4. Mirror changes in client rendering logic (client/client.js) **Changing Configuration:** 1. Update `ServerConfig` dataclass in server/config.py 2. Add runtime validation in `validate_runtime()` 3. If live-configurable: broadcast via CONFIG_UPDATE packet; otherwise require server restart 4. Update JOIN_ACK builder/parser if part of handshake ## File Organization ``` G:\Coding\code2\ ├── run.py # Server entrypoint with mode switching ├── requirements.txt # Python dependencies (aioquic, cryptography) ├── PROJECT_PLAN.md # Detailed design spec (authoritative reference) ├── IDEAS.md # Original feature brainstorming ├── server/ │ ├── server.py # GameServer (tick loop, join/spawn, simulation) │ ├── model.py # GameState, Snake, PlayerSession entities │ ├── protocol.py # Wire protocol (packet builders/parsers, TLV encoding) │ ├── config.py # ServerConfig dataclass │ ├── transport.py # Abstract DatagramServerTransport interface │ ├── webtransport_server.py # HTTP/3 WebTransport implementation │ ├── quic_transport.py # QUIC datagram implementation │ ├── multi_transport.py # Multi-transport multiplexer │ ├── static_server.py # Optional HTTPS static file server │ └── utils.py # Utility functions └── client/ ├── index.html # Main HTML page ├── client.js # Game client (WebTransport, input, rendering) └── protocol.js # Wire protocol (mirrors server protocol.py) ``` ## References - **PROJECT_PLAN.md**: Comprehensive design document covering protocol, mechanics, limits, testing strategy, and milestones. - **aioquic**: Python QUIC and HTTP/3 library (https://github.com/aiortc/aioquic) - **WebTransport**: W3C spec for low-latency client-server messaging over QUIC (https://w3c.github.io/webtransport/)