Files
codexPySnake/CLAUDE.md
Vladyslav Doloman ed5cb14b30 WIP: Add input broadcasting and client-side prediction features
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>
2025-10-19 15:17:16 +03:00

11 KiB
Raw Blame History

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:

  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