Menu

WebSocket Gateway

Csaba Skrabák

WebSocket Gateway

WebSocket server that accepts game client connections and publishes player actions to Kafka.

Architecture

Browser Client (test-client.html)
        ↓ WebSocket
WebSocket Gateway (port 8090)
        ↓ Kafka Producer
Kafka Topic: player.movement
        ↓ Kafka Consumer
Player Movement Consumer → Redis

Message Protocol

Client → Server

1. AUTH - Authenticate Player

{
  "type": "AUTH",
  "realm": "EU",
  "name": "Alice",
  "arenaId": "arena_5"
}

Response:

{
  "type": "AUTH_SUCCESS",
  "playerId": "EU:Alice",
  "roomX": 0,
  "roomY": 0
}

2. MOVE - Request Movement

{
  "type": "MOVE",
  "toRoomX": 1,
  "toRoomY": 0,
  "timestamp": 1702500000000
}

Response:

{
  "type": "MOVE_ACK",
  "fromRoomX": 0,
  "fromRoomY": 0,
  "toRoomX": 1,
  "toRoomY": 0
}

Server → Client

ERROR

{
  "type": "ERROR",
  "message": "Not authenticated. Send AUTH message first."
}

How It Works

1. Client Connects

const ws = new WebSocket('ws://localhost:8090/ws/game');

2. Client Authenticates

ws.send(JSON.stringify({
  type: 'AUTH',
  realm: 'EU',
  name: 'Alice',
  arenaId: 'arena_5'
}));

Server behavior:
- Checks if player exists in Redis
- If new player: Initializes at (0,0)
- Returns current position to client

3. Client Sends Move Request

ws.send(JSON.stringify({
  type: 'MOVE',
  toRoomX: 1,
  toRoomY: 0
}));

Server behavior:
- Gets current position from Redis
- Creates full movement event with fromRoom/toRoom
- Publishes to Kafka topic player.movement
- Sends ACK back to client

4. Consumer Validates & Updates Redis

The PlayerMovementRedisConsumer you already built:
- Validates adjacency
- Checks room locks
- Updates Redis if valid
- Denies if invalid

Note: Client receives ACK immediately (optimistic), but actual movement may be denied by consumer. In production, you'd need a feedback mechanism.

Setup & Running

1. Create New Maven Project

# In Eclipse: File → New → Maven Project
Group ID: kepzeletmuhely.hu
Artifact ID: heist-websocket-gateway

Add these files:
- src/main/java/kepzeletmuhely/hu/WebSocketGateway.java
- src/main/java/kepzeletmuhely/hu/WebSocketServer.java
- pom.xml
- test-client.html (anywhere accessible)

2. Build the Project

mvn clean package

Creates: target/heist-websocket-gateway-1.0.0.jar

3. Start Required Services

Make sure these are running:
- Kafka (localhost:9092, localhost:9093)
- Redis (localhost:6379)
- PlayerMovementRedisConsumer

4. Start WebSocket Gateway

In Eclipse:

Right-click WebSocketServer.java → Run As → Java Application

Via command line:

java -jar target/heist-websocket-gateway-1.0.0.jar

Output:

=== WebSocket Gateway Server ===
Configuration:
  WebSocket Port: 8090
  Kafka: localhost:9092,localhost:9093
  Redis: localhost:6379 (DB 1)

WebSocket Gateway initialized:
  Kafka: localhost:9092,localhost:9093
  Redis: localhost:6379:6379 (DB 1)
✅ WebSocket server started on ws://localhost:8090/ws/game
Press Ctrl+C to stop...

5. Open Test Client

Open test-client.html in your browser (just double-click it).

You should see a beautiful UI with:
- Authentication form
- 5x5 grid for movement
- Real-time log

6. Test the Flow

  1. Fill in credentials:
   - Realm: EU
   - Name: TestPlayer
   - Arena: arena_test
  1. Click "Connect"
   - Watch WebSocket server logs
   - Should see "Player authenticated"
  1. Click adjacent cells to move
   - Click cell (1,0) to move right
   - Watch logs in browser
   - Watch WebSocket server logs
   - Watch Consumer logs
  1. Check Redis:
   docker exec -it redis-cache redis-cli
   > SELECT 1
   > SMEMBERS room:arena_test:1,0:players
   ["EU:TestPlayer"]

Complete Test Flow

Terminal 1: Start Consumer

# Run PlayerMovementRedisConsumer

Output:

Player Movement Consumer started...
✅ Processed: Player EU:TestPlayer moved from (0,0) to (1,0)

Terminal 2: Start WebSocket Gateway

# Run WebSocketServer

Output:

✅ WebSocket server started on ws://localhost:8090/ws/game
New WebSocket connection: session-abc123
Player authenticated: EU:TestPlayer at (0,0)
Published: EU:TestPlayer move from (0,0) to (1,0) [partition=1, offset=5]

Browser: Test Client

  • Connect
  • Move around the grid
  • See real-time updates

Terminal 3: Check Redis

docker exec -it redis-cache redis-cli
> SELECT 1
> SMEMBERS room:arena_test:1,0:players
> HGETALL player:arena_test:EU:TestPlayer

Environment Variables

Configure via environment variables:

# Kafka
export KAFKA_BOOTSTRAP_SERVERS="localhost:9092,localhost:9093"

# Redis
export REDIS_HOST="localhost"
export REDIS_PORT="6379"
export REDIS_DB="1"

# WebSocket
export WS_PORT="8090"

Features

✅ Authentication Flow

  • Client sends realm, name, arenaId
  • Server checks/initializes player in Redis
  • Returns current position

✅ Movement Flow

  • Client sends target room
  • Server looks up current position from Redis
  • Creates complete movement event
  • Publishes to Kafka
  • Consumer validates and updates Redis

✅ State Management

  • Server is stateless (reads from Redis)
  • Can scale horizontally
  • Multiple WebSocket instances can run

✅ Optimistic Response

  • Client gets ACK immediately
  • Actual validation happens async in consumer
  • Good for low-latency feel

Extending the Gateway

Add Item Pickup

case "GAIN_ITEM":
    handleGainItem(msg, session);
    break;

Publish to player.items topic.

Add WebRTC Signaling

case "WEBRTC_OFFER":
    handleWebRTCOffer(msg, session);
    break;

Publish to player.signaling topic.

Add Encounter Detection

When consumer detects multiple players in same room:
- Publish encounter event to Kafka
- Gateway consumes and notifies affected clients
- Clients initiate WebRTC

Production Considerations

1. Add Authentication

Current code has no real auth. Add:
- JWT tokens
- Session validation
- Rate limiting

2. Add Feedback Loop

Consumer should publish validation results:
- If movement denied → notify client
- Client can rollback optimistic update

Consumer detects teleport
    ↓
Publish to player.movement.rejected topic
    ↓
Gateway consumes and notifies client
    ↓
Client resets position

3. Connection Management

Track connected players:
- Heartbeat/ping
- Disconnect handling
- Reconnection logic

4. Load Balancing

Multiple gateway instances:
- Use session affinity (sticky sessions)
- Or use Redis pub/sub for inter-gateway communication

Troubleshooting

WebSocket won't connect:
- Check port 8090 is not in use
- Check firewall settings
- Try telnet localhost 8090

Authentication fails:
- Check Redis is running
- Check Redis DB 1 is accessible
- Check consumer is running

Moves not processing:
- Check Kafka is running
- Check topic player.movement exists
- Check consumer is subscribed

Invalid moves not denied:
- Check consumer validation logic
- Check consumer is running
- Check Redis has room lock data

Next Steps

  1. Add more message types (GAIN_ITEM, LOSS_ITEM, etc.)
  2. Add WebRTC signaling for encounters
  3. Add feedback loop for validation results
  4. Add real authentication
  5. Deploy to production with proper security

This gateway is the bridge between your game clients and the Kafka-based backend! 🎮