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>
67 lines
1.5 KiB
JavaScript
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]
|
|
};
|