Files
claudePySnake/web/protocol.js
Vladyslav Doloman 4dbbf44638 Implement client-side prediction with input broadcasting
Reduces perceived lag over internet by broadcasting player inputs immediately
and predicting next positions on all clients before server update arrives.

Protocol changes:
- Added PLAYER_INPUT message type for broadcasting inputs
- Server broadcasts player inputs to all clients on every MOVE message
- Includes player_id, current direction, and full input_buffer (max 3)

Desktop client (Python):
- Tracks input buffers and predicted head positions for all players
- On PLAYER_INPUT: predicts next head position using buffered input
- On STATE_UPDATE: clears predictions, uses authoritative state
- Renderer draws predicted positions with darker color (60% brightness)

Web client (JavaScript):
- Same prediction logic as desktop client
- Added darkenColor() helper for visual differentiation
- Predicted heads shown at 60% brightness

Benefits:
- Instant visual feedback for own movements (no round-trip wait)
- See other players' inputs before server tick (better collision avoidance)
- Smooth experience bridging input-to-update gap
- Low bandwidth (only direction tuples, not full state)
- Backward compatible (server authoritative, old clients work)

All 39 tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-04 21:21:49 +03:00

67 lines
1.5 KiB
JavaScript

/**
* Network protocol for client-server communication
* Matches the Python protocol implementation
*/
const MessageType = {
// Client -> Server
JOIN: 'JOIN',
MOVE: 'MOVE',
START_GAME: 'START_GAME',
LEAVE: 'LEAVE',
// Server -> Client
WELCOME: 'WELCOME',
STATE_UPDATE: 'STATE_UPDATE',
PLAYER_JOINED: 'PLAYER_JOINED',
PLAYER_LEFT: 'PLAYER_LEFT',
GAME_STARTED: 'GAME_STARTED',
GAME_OVER: 'GAME_OVER',
ERROR: 'ERROR',
PLAYER_INPUT: 'PLAYER_INPUT' // Broadcast player input for prediction
};
class Message {
constructor(type, data = {}) {
this.type = type;
this.data = data;
}
toJSON() {
return JSON.stringify({
type: this.type,
data: this.data
});
}
static fromJSON(jsonStr) {
const obj = JSON.parse(jsonStr);
return new Message(obj.type, obj.data || {});
}
}
// Helper functions for creating messages
function createJoinMessage(playerName) {
return new Message(MessageType.JOIN, { player_name: playerName });
}
function createMoveMessage(direction) {
return new Message(MessageType.MOVE, { direction: direction });
}
function createStartGameMessage() {
return new Message(MessageType.START_GAME);
}
function createLeaveMessage() {
return new Message(MessageType.LEAVE);
}
// Direction constants (matching Python)
const Direction = {
UP: [0, -1],
DOWN: [0, 1],
LEFT: [-1, 0],
RIGHT: [1, 0]
};