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>
This commit is contained in:
Vladyslav Doloman
2025-10-04 21:21:49 +03:00
parent ce492b0dc2
commit 4dbbf44638
7 changed files with 129 additions and 9 deletions

View File

@@ -21,6 +21,7 @@ class MessageType(Enum):
GAME_STARTED = "GAME_STARTED"
GAME_OVER = "GAME_OVER"
ERROR = "ERROR"
PLAYER_INPUT = "PLAYER_INPUT" # Broadcast player input for prediction
class Message:
@@ -114,3 +115,18 @@ def create_game_over_message(winner_id: str = None) -> Message:
def create_error_message(error: str) -> Message:
"""Create an ERROR message."""
return Message(MessageType.ERROR, {"error": error})
def create_player_input_message(player_id: str, direction: tuple, input_buffer: list) -> Message:
"""Create a PLAYER_INPUT message for client-side prediction.
Args:
player_id: ID of the player who sent the input
direction: Current direction tuple
input_buffer: List of buffered direction tuples (max 3)
"""
return Message(MessageType.PLAYER_INPUT, {
"player_id": player_id,
"direction": direction,
"input_buffer": input_buffer
})