Skip to content

Latest commit

 

History

History
617 lines (480 loc) · 21.2 KB

File metadata and controls

617 lines (480 loc) · 21.2 KB

Multiplayer Networking Analysis for NSnipes

Current MQTT Implementation Analysis

Current Setup

  • Broker: Public HiveMQ (broker.hivemq.com:1883)
  • Position Updates: Every 20ms (throttled), QoS 0 (fire-and-forget)
  • Game State: Published on events (not regular timer)
  • Serialization: JSON
  • Architecture: Host-client (host authoritative for game state, players authoritative for own position/bullets)

Identified Latency Issues

  1. Broker Location: Public broker may be geographically distant from players

    • All messages route through broker (extra hop)
    • Network latency to broker + broker processing + network latency to other players
    • Typical round-trip: 50-200ms depending on location
  2. JSON Serialization Overhead:

    • Text-based format is verbose
    • Parsing overhead on both ends
    • Larger message sizes = more network time
  3. No Client-Side Prediction:

    • Remote players appear to "lag" because updates are delayed
    • No interpolation between position updates
    • Jerky movement when updates arrive
  4. Update Frequency:

    • 20ms position updates = 50 updates/second (good)
    • But with broker latency, effective update rate is lower
    • Game state snapshots are event-based (could miss updates)

Option 1: Improve MQTT Implementation

Improvements That Could Reduce Latency

A. Use Closer/Private Broker

  • Option: Deploy local Mosquitto broker or use regional HiveMQ Cloud
  • Benefit: Reduce network latency to broker
  • Drawback: Requires infrastructure setup
  • Latency Improvement: 20-50ms reduction

B. Binary Protocol (MessagePack/Protobuf)

  • Option: Replace JSON with binary serialization
  • Benefit:
    • Smaller message sizes (30-50% reduction)
    • Faster serialization/deserialization
    • Lower bandwidth usage
  • Drawback: Requires code changes, less human-readable
  • Latency Improvement: 5-15ms reduction per message

C. Client-Side Prediction & Interpolation

  • Option: Predict remote player positions between updates
  • Implementation:
    • Store last 2-3 position updates per player
    • Interpolate position between updates
    • Extrapolate if update is late
    • Snap to authoritative position when received
  • Benefit: Smooth movement even with 50-100ms latency
  • Drawback: Requires interpolation logic, may cause slight "rubber-banding"
  • Latency Improvement: Perceived latency reduced by 50-70%

D. Increase Update Frequency

  • Option: Reduce position update throttle to 10ms (100 updates/sec)
  • Benefit: More frequent updates = smoother movement
  • Drawback: Higher bandwidth, more broker load
  • Latency Improvement: Minimal (still limited by broker latency)

E. Use QoS 0 for All Updates

  • Current: Already using QoS 0 for positions (good)
  • Recommendation: Use QoS 0 for all non-critical updates
  • Benefit: No acknowledgment overhead
  • Latency Improvement: 5-10ms per message

F. Delta Compression

  • Option: Only send changed data, not full state
  • Benefit: Smaller messages, faster transmission
  • Drawback: More complex state management
  • Latency Improvement: 10-20ms for large state updates

Estimated MQTT Improvement Potential

  • Best Case: 50-100ms total latency (with all optimizations)
  • Realistic: 80-150ms total latency
  • Still noticeable for fast-paced gameplay

Option 2: WebSockets Implementation

Architecture Overview

WebSockets provide a direct TCP connection between clients, but still require a server for:

  1. Matchmaking/Signaling: Finding and connecting players
  2. NAT Traversal: Most players are behind routers (NAT)

Implementation Approaches

Approach A: WebSocket Server (Recommended)

┌─────────┐         ┌──────────────┐         ┌─────────┐
│ Player1 │◄───────►│ WebSocket    │◄───────►│ Player2 │
│ (Host)  │         │ Server       │         │ (Client)│
└─────────┘         └──────────────┘         └─────────┘

How It Works:

  1. Host starts game → connects to WebSocket server
  2. Server generates game ID, stores host connection
  3. Client joins → connects to same server with game ID
  4. Server routes messages between players in same game
  5. All game messages go through server (similar to MQTT broker)

User Experience:

  • ✅ Same as MQTT: Host selects "Start Game", others "Join Game" with ID
  • ✅ No IP addresses needed
  • ✅ Works across internet (server handles NAT)
  • ✅ Lower latency than MQTT (direct TCP, no MQTT protocol overhead)

Latency: 30-80ms (depending on server location)

Implementation Requirements:

  • WebSocket server (can be same machine as game, or cloud-hosted)
  • Simple message routing logic
  • Connection management

Approach B: Direct Peer-to-Peer (WebRTC)

┌─────────┐         ┌──────────────┐         ┌─────────┐
│ Player1 │◄────────│ Signaling    │────────►│ Player2 │
│ (Host)  │         │ Server       │         │ (Client)│
└─────────┘         └──────────────┘         └─────────┘
         │                                        │
         └──────── Direct P2P ──────────────────┘

How It Works:

  1. Host and clients connect to signaling server (for matchmaking only)
  2. Signaling server exchanges connection info (ICE candidates)
  3. Players establish direct P2P connection (bypassing server)
  4. Game messages sent directly between players

User Experience:

  • ✅ Same: Host "Start Game", others "Join Game" with ID
  • ✅ No IP addresses needed (signaling server handles discovery)
  • ✅ Lowest latency (direct connection, no server hop)
  • ⚠️ May not work if both players behind strict NATs

Latency: 20-60ms (direct connection)

Implementation Requirements:

  • Signaling server (lightweight, just for matchmaking)
  • WebRTC library (complex, but handles NAT traversal)
  • Fallback to server relay if P2P fails

Approach C: Hybrid (WebSocket + Direct Connection)

  • Use WebSocket server for matchmaking and initial connection
  • Attempt direct TCP connection between players
  • Fall back to server relay if direct connection fails
  • Best of both worlds: low latency when possible, reliable when not

WebSockets vs MQTT Comparison

Aspect MQTT (Current) WebSockets
Latency 80-200ms 30-80ms
Protocol Overhead Higher (MQTT protocol) Lower (raw TCP)
Message Format JSON (can be binary) JSON or binary
Connection Model Pub/Sub (broker) Direct (server relay)
NAT Traversal Handled by broker Requires server
User Experience Game ID join Game ID join (same)
Infrastructure Public broker (free) Need to host server
Scalability Excellent (broker handles) Good (server handles)
Complexity Medium Medium-High

WebSocket Implementation Details

Server Requirements:

  • Simple WebSocket server (Node.js, C#, Python, etc.)
  • Game room management (track active games)
  • Message routing (forward messages to all players in game)
  • Can be hosted on:
    • Same machine as game (for LAN)
    • Cloud service (AWS, Azure, DigitalOcean)
    • Free tier services (Heroku, Railway, Fly.io)

Message Protocol (similar to MQTT):

{
  "type": "position",
  "gameId": "ABC123",
  "playerId": "player1",
  "data": { "x": 150, "y": 200, "sequence": 123 }
}

Code Changes Required:

  • Replace MqttGameClient with WebSocketGameClient
  • Replace broker connection with WebSocket server connection
  • Similar message routing logic
  • Same DTOs can be reused

Option 3: Other Networking Approaches

A. Direct UDP (Lowest Latency, Most Complex)

How It Works:

  • Direct UDP sockets between players
  • Requires port forwarding or UPnP
  • No server needed (for LAN play)

Pros:

  • Lowest latency (10-30ms)
  • No server costs
  • Full control

Cons:

  • ❌ Requires IP addresses (or LAN discovery)
  • ❌ NAT traversal issues (won't work across internet without setup)
  • ❌ No reliability (UDP is unreliable)
  • ❌ More complex implementation

User Experience:

  • ⚠️ Players need to know host IP address
  • ⚠️ May require port forwarding
  • ⚠️ Only works on LAN or with network configuration

Not Recommended for internet play.

B. Dedicated Game Server (Photon, Mirror, etc.)

How It Works:

  • Use existing game networking library
  • Hosted service or self-hosted server

Pros:

  • ✅ Battle-tested, optimized
  • ✅ Built-in features (lag compensation, prediction)
  • ✅ Good documentation

Cons:

  • ❌ Additional dependency
  • ❌ May have licensing costs
  • ❌ Less control over implementation
  • ❌ May be overkill for this project

Examples:

  • Photon (Unity-focused, but has .NET SDK)
  • Mirror Networking
  • Lidgren Network

C. WebRTC (Peer-to-Peer)

Already covered in Option 2B - see WebRTC section above.


Recommendations

Short-Term: Improve MQTT (Easiest)

Quick Wins (1-2 days work):

  1. ✅ Add client-side interpolation for remote players
  2. ✅ Switch to MessagePack for binary serialization
  3. ✅ Implement delta compression for game state
  4. ✅ Increase position update frequency to 10ms

Expected Result: 50-70% perceived latency reduction, still noticeable but playable

Medium-Term: WebSocket Server (Best Balance)

Implementation (3-5 days work):

  1. Create simple WebSocket server (C# or Node.js)
  2. Replace MQTT client with WebSocket client
  3. Implement message routing on server
  4. Deploy server (cloud or local)

Expected Result: 60-80% latency reduction, smooth gameplay

Server Options:

  • Local: Run on same machine (LAN only)
  • Cloud Free Tier: Railway, Fly.io, Render (free tier available)
  • Self-Hosted: VPS ($5-10/month)

Long-Term: WebRTC P2P (Lowest Latency)

Implementation (1-2 weeks work):

  1. Implement WebRTC signaling server
  2. Integrate WebRTC library
  3. Handle NAT traversal and fallbacks

Expected Result: 80-90% latency reduction, near-instant response


Decision Matrix

Criteria MQTT (Improved) WebSocket Server WebRTC P2P
Latency Medium (80-150ms) Low (30-80ms) Very Low (20-60ms)
Implementation Time 1-2 days 3-5 days 1-2 weeks
Infrastructure Cost Free (public broker) Free-$10/mo Free-$5/mo
User Experience Same (Game ID) Same (Game ID) Same (Game ID)
Reliability High High Medium (NAT issues)
Complexity Low Medium High
Scalability Excellent Good Excellent

My Recommendation

Start with WebSocket Server approach:

  1. Best latency improvement for effort invested
  2. Same user experience (no IP addresses needed)
  3. Manageable complexity (similar to MQTT)
  4. Flexible deployment (local for testing, cloud for production)
  5. Can be implemented incrementally (keep MQTT as fallback initially)

Implementation Plan:

  1. Create simple WebSocket server (C# console app or ASP.NET Core)
  2. Implement game room management
  3. Replace MQTT client with WebSocket client in game
  4. Test locally first
  5. Deploy to cloud for internet play

Server Code Structure (pseudo-code):

// WebSocket server
- GameRoom class (tracks players, routes messages)
- WebSocket handler (accepts connections, manages rooms)
- Message router (forwards messages to all players in room)

Client Code Changes:

  • Replace MqttGameClient with WebSocketGameClient
  • Same message types (DTOs can be reused)
  • Similar connection flow (connect → join game → play)

Would you like me to proceed with implementing the WebSocket server approach, or would you prefer to try improving MQTT first?


Option 4: gRPC Implementation

What is gRPC?

gRPC is a high-performance RPC (Remote Procedure Call) framework that:

  • Uses HTTP/2 as transport (multiplexed, efficient)
  • Uses Protocol Buffers (protobuf) for serialization (binary, compact)
  • Supports bidirectional streaming (similar to WebSockets)
  • Provides strong typing and code generation
  • Lower latency than REST/JSON APIs

How gRPC Could Help

A. gRPC as WebSocket Alternative (Recommended)

Architecture:

┌─────────┐         ┌──────────────┐         ┌─────────┐
│ Player1 │◄───────►│ gRPC Server  │◄───────►│ Player2 │
│ (Host)  │         │ (Streaming)  │         │ (Client)│
└─────────┘         └──────────────┘         └─────────┘

How It Works:

  1. Host starts game → connects to gRPC server via bidirectional stream
  2. Server generates game ID, stores host stream
  3. Client joins → connects to same server with game ID
  4. Server routes messages between players using streaming
  5. All game messages go through server (similar to WebSocket server)

User Experience:

  • ✅ Same as WebSocket: Host "Start Game", others "Join Game" with ID
  • ✅ No IP addresses needed
  • ✅ Works across internet (server handles NAT)
  • Lower latency than WebSockets (HTTP/2 multiplexing, binary protobuf)

Latency: 25-70ms (slightly better than WebSockets due to HTTP/2 efficiency)

Key Advantages Over WebSockets:

  1. HTTP/2 Multiplexing: Multiple streams over single connection (less overhead)
  2. Binary Protobuf: More efficient than JSON (30-50% smaller messages)
  3. Built-in Code Generation: Type-safe messages, less manual serialization
  4. Better Compression: HTTP/2 header compression
  5. Streaming Built-in: Native bidirectional streaming support
  6. Strong Typing: Compile-time type checking

Implementation Requirements:

  • gRPC server (ASP.NET Core gRPC, or standalone)
  • Protocol buffer definitions (.proto files)
  • Code generation for client/server

B. gRPC vs WebSockets Comparison

Aspect WebSockets gRPC
Transport TCP (raw) HTTP/2
Serialization JSON (or binary) Protobuf (binary)
Message Size Larger (JSON) Smaller (protobuf)
Latency 30-80ms 25-70ms
Type Safety Manual (DTOs) Generated (protobuf)
Multiplexing No (one stream) Yes (HTTP/2)
Compression Manual Built-in (HTTP/2)
Code Generation No Yes (.proto → C#)
Browser Support Excellent Limited (needs gRPC-Web)
Complexity Medium Medium-High
.NET Support Good Excellent (native)

C. gRPC Implementation Details

Protocol Buffer Definition (example):

syntax = "proto3";

package nsnipes;

// Game service for multiplayer
service GameService {
  // Bidirectional streaming for game messages
  rpc GameStream(stream GameMessage) returns (stream GameMessage);
  
  // Join game request
  rpc JoinGame(JoinRequest) returns (JoinResponse);
  
  // Get game state
  rpc GetGameState(GameStateRequest) returns (GameStateSnapshot);
}

message GameMessage {
  string gameId = 1;
  string playerId = 2;
  oneof message {
    PlayerPositionUpdate position = 10;
    BulletUpdate bullet = 11;
    GameStateSnapshot state = 12;
    // ... other message types
  }
}

message PlayerPositionUpdate {
  int32 x = 1;
  int32 y = 2;
  int64 sequence = 3;
  int64 timestamp = 4;
}

message BulletUpdate {
  string bulletId = 1;
  double x = 2;
  double y = 3;
  double velocityX = 4;
  double velocityY = 5;
  string action = 6; // "fired", "updated", "hit", "expired"
}

Server Code (C# ASP.NET Core):

public class GameService : GameServiceBase
{
    private readonly GameRoomManager _roomManager;
    
    public override async Task GameStream(
        IAsyncStreamReader<GameMessage> requestStream,
        IServerStreamWriter<GameMessage> responseStream,
        ServerCallContext context)
    {
        // Handle bidirectional streaming
        // Similar to WebSocket message handling
    }
}

Client Code (C#):

var channel = GrpcChannel.ForAddress("https://game-server.example.com");
var client = new GameService.GameServiceClient(channel);

// Bidirectional streaming
using var call = client.GameStream();
// Send/receive messages

Code Changes Required:

  • Define .proto files for all message types
  • Generate C# code from protobuf
  • Replace MqttGameClient with GrpcGameClient
  • Implement gRPC server (ASP.NET Core or standalone)
  • Similar message routing logic (can reuse business logic)

D. gRPC for MQTT Improvement?

Not Recommended - gRPC doesn't fit MQTT's pub/sub model:

  • MQTT is publish/subscribe (many-to-many)
  • gRPC is request/response or streaming (point-to-point)
  • Would require significant architecture change
  • Better to replace MQTT entirely with gRPC than try to combine

E. gRPC vs Other Options

gRPC vs WebSockets:

  • gRPC wins on: Efficiency, type safety, code generation
  • WebSockets win on: Simplicity, browser support, wider adoption
  • Verdict: gRPC is better for .NET-to-.NET communication, WebSockets better for web compatibility

gRPC vs Direct UDP:

  • gRPC wins on: Reliability, NAT traversal, ease of use
  • UDP wins on: Raw latency (but requires manual implementation)
  • Verdict: gRPC is much better for internet play

gRPC vs WebRTC:

  • gRPC wins on: Simplicity, server-based (no NAT issues)
  • WebRTC wins on: Direct P2P latency (when it works)
  • Verdict: gRPC is more reliable, WebRTC is lower latency when P2P works

gRPC Implementation Recommendation

Best Use Case: Replace WebSocket server with gRPC server

Why:

  1. Better Performance: HTTP/2 + protobuf = lower latency than WebSockets
  2. Type Safety: Generated code reduces bugs
  3. Native .NET: Excellent C# support (ASP.NET Core gRPC)
  4. Same User Experience: Game ID join, no IP addresses needed
  5. Future-Proof: Modern, actively developed

Implementation Plan:

  1. Define .proto files for all game messages
  2. Generate C# code from protobuf
  3. Create gRPC server (ASP.NET Core)
  4. Implement game room management
  5. Replace MQTT client with gRPC client
  6. Deploy server (same options as WebSocket server)

Estimated Latency: 25-70ms (5-10ms better than WebSockets)

Complexity: Medium-High (requires learning protobuf, but well-documented)


Updated Decision Matrix (Including gRPC)

Criteria MQTT (Improved) WebSocket Server gRPC Server WebRTC P2P
Latency Medium (80-150ms) Low (30-80ms) Very Low (25-70ms) Very Low (20-60ms)
Implementation Time 1-2 days 3-5 days 4-6 days 1-2 weeks
Infrastructure Cost Free (public broker) Free-$10/mo Free-$10/mo Free-$5/mo
User Experience Same (Game ID) Same (Game ID) Same (Game ID) Same (Game ID)
Reliability High High High Medium (NAT issues)
Complexity Low Medium Medium-High High
Type Safety Manual Manual Generated Manual
Message Efficiency JSON JSON/Binary Protobuf (best) Binary
.NET Support Good (MQTTnet) Good Excellent (native) Limited

Revised Recommendation

Option A: gRPC Server (Best Performance)

If you want the best latency with good .NET support:

  • ✅ Lowest latency of server-based solutions (25-70ms)
  • ✅ Best message efficiency (protobuf)
  • ✅ Type-safe generated code
  • ✅ Native .NET support (ASP.NET Core gRPC)
  • ⚠️ Requires learning protobuf (but well-documented)
  • ⚠️ Slightly more complex than WebSockets

Best for: Production-ready solution with best performance

Option B: WebSocket Server (Best Simplicity)

If you want good performance with simpler implementation:

  • ✅ Good latency (30-80ms)
  • ✅ Simpler than gRPC
  • ✅ Easy to understand and debug
  • ✅ Wide compatibility
  • ⚠️ Slightly higher latency than gRPC
  • ⚠️ Manual serialization

Best for: Quick implementation with good results

Option C: Improve MQTT (Quickest)

If you want minimal changes:

  • ✅ Fastest to implement (1-2 days)
  • ✅ No infrastructure changes
  • ⚠️ Still noticeable latency (80-150ms)
  • ⚠️ Limited improvement potential

Best for: Quick fix while planning better solution


Final Recommendation

For NSnipes, I recommend gRPC Server:

  1. Best latency of server-based solutions (25-70ms)
  2. Excellent .NET support (native ASP.NET Core)
  3. Type safety reduces bugs
  4. Future-proof technology
  5. Same user experience (Game ID join)
  6. Manageable complexity (4-6 days implementation)

The slight additional complexity over WebSockets is worth it for the performance gains and type safety benefits, especially since you're already using C# and .NET.

Would you like me to:

  1. Implement gRPC server approach (recommended)
  2. Implement WebSocket server approach (simpler alternative)
  3. Improve MQTT first (quick fix)