Changes include: - Client: INPUT_BROADCAST packet handling and opponent prediction rendering - Client: Protocol parsing for INPUT_BROADCAST packets - Server: Input broadcasting to all clients except sender 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
11 KiB
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
# 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:
# 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
python run.py --help
Architecture
Server Architecture (Python 3 + asyncio)
Core Components:
-
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)
-
server/model.py: Game state entities
GameState: Field grid, snakes dict, apples list, occupancy mapSnake: Deque-based body representation, input buffer (capacity 3), blocked flagPlayerSession: Per-player metadata (id, name, color, peer handle)
-
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)
-
server/config.py (
ServerConfig): Server configuration dataclass with validation -
Transport layer (pluggable):
server/transport.py: AbstractDatagramServerTransportinterfaceserver/webtransport_server.py: WebTransport (HTTP/3) implementation using aioquicserver/quic_transport.py: Raw QUIC datagram implementationserver/multi_transport.py: Multiplexer for running multiple transports concurrentlyserver/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 controlsclient/client.js: Game client, WebTransport connection, input handling, renderingclient/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-firstBODY_RLE (0x01): Run-length encoding (dir u8, count QUIC varint) for straight segmentsBODY_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:
- Consume at most one valid input per snake (apply 180° rule for length > 1)
- Compute intended head step; if blocked (wall/occupied), set
blocked=Trueand keep head stationary - If blocked: shrink tail by 1 (min length 1)
- If moving into apple: grow by 1 (don't shrink tail that tick) and respawn apple
- 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=DEBUGfor 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:
- Add enum value to
PacketTypein server/protocol.py and client/protocol.js - Implement builder function in protocol (e.g.,
build_foo()) and parser (e.g.,parse_foo()) - Handle in server's
on_datagram()and client'sreadLoop()switch statements - Update header packing if tick field is required (pass tick to
pack_header())
Modifying Simulation Logic:
- Edit
_simulate_tick()in server/server.py for per-tick updates - Update
SnakeDeltadataclass if new change types are needed - Adjust
build_state_delta_body()if encoding changes - Mirror changes in client rendering logic (client/client.js)
Changing Configuration:
- Update
ServerConfigdataclass in server/config.py - Add runtime validation in
validate_runtime() - If live-configurable: broadcast via CONFIG_UPDATE packet; otherwise require server restart
- 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/)