Skip to main content

FigNet Protocol Specification

Overview

This document defines the complete protocol specification for FigNet's RUDP (Reliable UDP) networking framework, inspired by ENet and based on best practices from game networking literature. The protocol provides reliable, ordered, and unordered message delivery over UDP with congestion control, packet fragmentation, and multiple communication channels.


Table of Contents

  1. Protocol Fundamentals
  2. Packet Format
  3. Virtual Connections
  4. Reliability System
  5. Ordering Guarantees
  6. Congestion Control
  7. Packet Fragmentation & Reassembly
  8. Serialization
  9. Connection Management
  10. Channel System
  11. State Synchronization
  12. Security Considerations

Protocol Fundamentals

Transport Layer

  • Base Protocol: UDP (User Datagram Protocol)
  • Rationale: UDP provides low-latency, connectionless packet delivery essential for real-time games
  • Why Not TCP: TCP's reliable ordered stream abstraction causes head-of-line blocking, making it unsuitable for time-critical game data

Protocol ID

Every packet begins with a 32-bit protocol identifier to distinguish FigNet packets from other UDP traffic:

[Protocol ID: 32 bits] [Packet Data...]
  • Protocol ID is a unique 32-bit value (e.g., hash of "FigNet" + version)
  • Packets without matching protocol ID are silently discarded
  • Provides basic packet filtering and protocol versioning

Packet Size Constraints

  • Maximum Transmission Unit (MTU): 1200 bytes (conservative for internet)
  • Maximum Packet Size: 64 KB (after fragmentation)
  • Recommended Packet Size: < 576 bytes for best compatibility
  • Header Overhead: Variable (see Packet Format section)

Packet Format

Base Packet Header

[Protocol ID: 32 bits]
[Packet Type: 8 bits]
[Flags: 8 bits]
[Sequence Number: 16 bits]
[ACK Sequence Number: 16 bits]
[ACK Bitfield: 32 bits]
[Channel ID: 8 bits]
[Reserved: 8 bits]
[Payload Length: 16 bits]
[Payload Data: variable]

Packet Types

TypeValueDescription
CONNECT0x01Connection request
CONNECT_ACK0x02Connection acknowledgment
DISCONNECT0x03Graceful disconnect
DISCONNECT_ACK0x04Disconnect acknowledgment
PING0x05Keep-alive ping
PONG0x06Keep-alive pong
RELIABLE0x10Reliable message
UNRELIABLE0x11Unreliable message
RELIABLE_ORDERED0x12Reliable ordered message
FRAGMENT0x20Fragmented packet
FRAGMENT_ACK0x21Fragment acknowledgment

Flags

Bit 0: Has ACK Bitfield (1 = ACK bitfield present)
Bit 1: Has Fragments (1 = contains fragment data)
Bit 2: Compressed (1 = payload is compressed)
Bit 3: Encrypted (1 = payload is encrypted)
Bit 4-7: Reserved

Sequence Numbers

  • Sequence Number: 16-bit wrapping sequence number for outgoing packets
  • ACK Sequence Number: Most recent sequence number received
  • ACK Bitfield: Bitfield indicating which of the last 32 packets were received
    • Bit 0 = sequence (ACK - 31)
    • Bit 1 = sequence (ACK - 30)
    • ...
    • Bit 31 = sequence (ACK - 0)

Virtual Connections

Connection Establishment

A virtual connection is established when two peers exchange packets with matching protocol IDs.

Connection States

  1. DISCONNECTED: No connection established
  2. CONNECTING: Connection request sent, awaiting acknowledgment
  3. CONNECTED: Connection established, packets flowing
  4. DISCONNECTING: Disconnect initiated, awaiting acknowledgment

Connection Handshake

Client → Server:

CONNECT packet {
protocol_id: 0x12345678
client_version: 16 bits
client_nonce: 32 bits
max_channels: 8 bits
mtu: 16 bits
}

Server → Client:

CONNECT_ACK packet {
protocol_id: 0x12345678
server_version: 16 bits
server_nonce: 32 bits
peer_id: 16 bits
max_channels: 8 bits
mtu: 16 bits
server_time: 64 bits
}

Connection Detection

  • Connection is considered active when packets are received within timeout window
  • Timeout: 10 seconds (configurable)
  • Each received packet resets the timeout timer
  • If timeout expires, connection transitions to DISCONNECTED

Disconnection

Graceful Disconnect:

DISCONNECT packet {
reason: 8 bits
data: variable (optional disconnect message)
}

Immediate Disconnect:

  • Send DISCONNECT packet
  • Wait for DISCONNECT_ACK (with timeout)
  • If timeout, disconnect anyway

Reliability System

Acknowledgment Mechanism

The reliability system uses a combination of:

  1. ACK Sequence Number: Most recent packet received
  2. ACK Bitfield: Bitfield for last 32 packets
  3. Selective Acknowledgments: Explicit ACKs for critical packets

Reliable Packet Transmission

Sender Side:

  1. Assign sequence number to packet
  2. Store packet in send queue
  3. Send packet
  4. Start retransmission timer
  5. On ACK received, remove from queue
  6. On timeout, retransmit packet

Receiver Side:

  1. Receive packet with sequence number
  2. Check if already received (duplicate detection)
  3. Update ACK sequence and bitfield
  4. Deliver to application
  5. Send ACK in next outgoing packet

Retransmission Strategy

  • Initial Retransmission Timeout (RTO): 100ms
  • RTO Calculation: Based on round-trip time (RTT) measurement
  • Exponential Backoff: RTO *= 1.5 on each retransmission
  • Maximum Retransmissions: 8 attempts
  • After Max Retries: Consider connection lost

Round-Trip Time (RTT) Measurement

  • Send PING packet with timestamp
  • Receive PONG packet with echoed timestamp
  • Calculate RTT = current_time - ping_time
  • Use exponentially weighted moving average (EWMA):
    RTT = α * RTT_old + (1 - α) * RTT_sample
    α = 0.875 (typical value)

Ordering Guarantees

Ordering Modes

  1. Unreliable Unordered (UNRELIABLE)

    • No guarantees
    • Lowest latency
    • Use for: position updates, visual effects
  2. Unreliable Ordered (UNRELIABLE_ORDERED)

    • Packets may be lost
    • Received packets are in order
    • Use for: voice chat, particle effects
  3. Reliable Unordered (RELIABLE)

    • All packets arrive
    • Order not guaranteed
    • Use for: chat messages, inventory updates
  4. Reliable Ordered (RELIABLE_ORDERED)

    • All packets arrive
    • Packets arrive in order
    • Use for: commands, state changes

Ordered Packet Handling

Sender:

  • Assign sequence number per channel
  • Sequence numbers are channel-specific

Receiver:

  • Maintain receive window per channel
  • Deliver packets in sequence order
  • Buffer out-of-order packets
  • Deliver when gap is filled

Receive Window

  • Window Size: 1024 packets per channel
  • Out-of-Order Buffer: Up to 64 packets
  • If Buffer Full: Drop oldest or disconnect

Congestion Control

Goals

  1. Avoid network congestion
  2. Maximize throughput
  3. Minimize latency
  4. Fair bandwidth sharing

Bandwidth Management

Initial Settings:

  • Bandwidth Limit: 256 KB/s (configurable)
  • Packet Rate: 30 packets/second
  • Packet Size: Adaptive based on MTU

Adaptive Rate Control

Packet Rate Adjustment:

  • Start with conservative rate (10 pps)
  • Increase rate if no packet loss detected
  • Decrease rate on packet loss
  • Target: 1-2% packet loss

Algorithm:

if (packet_loss_rate < 0.01):
packet_rate = min(packet_rate * 1.1, max_packet_rate)
else if (packet_loss_rate > 0.02):
packet_rate = max(packet_rate * 0.9, min_packet_rate)

Bandwidth Estimation

  • Measure bytes sent per second
  • Track packet loss rate
  • Adjust send rate accordingly
  • Respect configured bandwidth limits

Flow Control

  • Send Window: Limit unacknowledged packets
  • Window Size: Based on RTT and bandwidth
  • Formula: window_size = bandwidth * RTT / packet_size
  • Maximum Window: 64 packets

Packet Fragmentation & Reassembly

Fragmentation Strategy

When a message exceeds MTU, it must be fragmented:

Fragment Header:

[Fragment ID: 16 bits]
[Fragment Index: 16 bits]
[Total Fragments: 16 bits]
[Fragment Size: 16 bits]
[Fragment Data: variable]

Fragmentation Process

Sender:

  1. Split message into MTU-sized fragments
  2. Assign unique fragment ID
  3. Send fragments with sequence numbers
  4. Wait for fragment ACKs
  5. Retransmit lost fragments

Receiver:

  1. Receive fragments
  2. Store fragments by fragment ID
  3. When all fragments received, reassemble
  4. Deliver complete message
  5. Send fragment ACK

Fragment Reassembly

  • Fragment Buffer: Per fragment ID
  • Timeout: 5 seconds per fragment set
  • Maximum Fragments: 64 per message
  • If Timeout: Discard all fragments, notify sender

Large Block Transfer

For very large blocks (e.g., initial world state):

Optimized Transfer:

  1. Use dedicated reliable channel
  2. Increase packet size (up to MTU)
  3. Send fragments in parallel (multiple in-flight)
  4. Use selective ACK for fragments
  5. Prioritize fragment retransmission

Block Transfer Header:

[Block ID: 32 bits]
[Block Size: 32 bits]
[Block Type: 8 bits]
[Compression: 8 bits]
[Fragment Data...]

Serialization

Bit Packing

For maximum bandwidth efficiency, use bit-level packing:

Basic Types:

  • Boolean: 1 bit
  • Integer: Variable bits (specify range)
  • Float: 16-bit quantized or 32-bit full precision
  • String: Length-prefixed UTF-8

Serialization Format

Write Operations:

void WriteBits(uint32_t value, int bits);
void WriteInteger(int32_t value, int32_t min, int32_t max);
void WriteFloat(float value, float min, float max, int bits);
void WriteString(const char* str, int max_length);

Read Operations:

uint32_t ReadBits(int bits);
int32_t ReadInteger(int32_t min, int32_t max);
float ReadFloat(float min, float max, int bits);
const char* ReadString(int max_length);

Safety Checks

Automatic Validation:

  • Check remaining bits before read
  • Validate ranges on read
  • Detect buffer overflows
  • Fail-safe on invalid data

Example:

class BitStream {
bool ReadBits(uint32_t& value, int bits) {
if (bits_remaining < bits) {
error = BUFFER_OVERFLOW;
return false;
}
// ... read bits
return true;
}
};

Delta Compression

For state synchronization:

  • Send only changed fields
  • Use bitfield to indicate changed fields
  • Quantize floats to reduce precision
  • Use run-length encoding for sparse data

Connection Management

Peer Management

Peer States:

  • DISCONNECTED: Not connected
  • CONNECTING: Connection in progress
  • ACKNOWLEDGING_CONNECT: Waiting for CONNECT_ACK
  • CONNECTION_PENDING: Server-side, waiting for CONNECT
  • CONNECTION_SUCCEEDED: Connected
  • CONNECTION_FAILED: Connection failed
  • CONNECTED: Fully connected
  • DISCONNECT_LATER: Queued for disconnect
  • DISCONNECTING: Disconnect in progress
  • ACKNOWLEDGING_DISCONNECT: Waiting for DISCONNECT_ACK
  • ZOMBIE: Disconnected but not cleaned up

Connection Limits

  • Max Peers: 64 (configurable)
  • Max Channels per Peer: 255
  • Max Pending Connections: 8

Keep-Alive

  • PING Interval: 5 seconds
  • PING Timeout: 10 seconds
  • PING Packet: Contains timestamp
  • PONG Response: Echoes timestamp

Channel System

Channel Types

Channels provide independent ordering and reliability:

Channel Configuration:

Channel {
channel_id: 8 bits
type: RELIABLE_ORDERED | RELIABLE | UNRELIABLE_ORDERED | UNRELIABLE
sequence: 16 bits (for ordering)
send_queue: Packet[]
receive_window: Packet[]
}

Channel Usage

Typical Channel Assignments:

  • Channel 0: Reliable Ordered (commands, state changes)
  • Channel 1: Unreliable (position updates, visual effects)
  • Channel 2: Reliable Unordered (chat, inventory)
  • Channel 3: Unreliable Ordered (voice, particles)

Channel Priority

  • HIGH: Critical game state (0-63)
  • NORMAL: Standard messages (64-191)
  • LOW: Background data (192-255)

Priority affects:

  • Send queue ordering
  • Bandwidth allocation
  • Retransmission priority

State Synchronization

Snapshot System

For synchronizing game world state:

Snapshot Packet:

[Snapshot Sequence: 16 bits]
[Timestamp: 64 bits]
[Object Count: 16 bits]
[Object Data...]

Object Data:

[Object ID: 32 bits]
[Object Type: 8 bits]
[Changed Fields Bitfield: variable]
[Field Data...]

Snapshot Compression

Techniques:

  1. Delta Compression: Send only changed fields
  2. Quantization: Reduce float precision
  3. Huffman Encoding: Compress common values
  4. Run-Length Encoding: Compress sparse data
  5. Object Culling: Don't send invisible objects

Snapshot Interpolation

Client Side:

  1. Buffer snapshots in interpolation buffer
  2. Delay rendering by 150-350ms (based on send rate)
  3. Interpolate between snapshots
  4. Use Hermite interpolation for smooth motion

Interpolation Buffer:

  • Hold 2-4 snapshots
  • Delay = 3x packet send interval + jitter buffer
  • Example: 10 pps → 300ms delay

Snapshot Rate

  • Minimum: 10 snapshots/second
  • Recommended: 20-30 snapshots/second
  • Maximum: 60 snapshots/second (high bandwidth)

Rate selection based on:

  • Available bandwidth
  • Network conditions
  • Game requirements

Security Considerations

Packet Validation

Input Validation:

  • Validate all packet fields
  • Check sequence numbers are reasonable
  • Verify packet size matches header
  • Reject malformed packets

Rate Limiting

Per-Peer Limits:

  • Max packets per second: 60
  • Max bytes per second: 256 KB
  • Max connection attempts: 3 per minute

Enforcement:

  • Track per-peer statistics
  • Drop packets exceeding limits
  • Disconnect abusive peers

Protocol ID Protection

  • Use cryptographically random protocol ID
  • Don't rely solely on protocol ID for security
  • Validate all packet fields

Encryption (Optional)

AEAD Encryption:

  • Use ChaCha20-Poly1305 or AES-GCM
  • Encrypt payload only (not headers)
  • Key exchange via control plane (WSS)
  • Per-connection encryption keys

Encryption Header:

[Nonce: 96 bits]
[Encrypted Payload...]
[Authentication Tag: 128 bits]

Performance Considerations

Packet Batching

Batching Strategy:

  • Collect multiple messages per frame
  • Send in single UDP packet when possible
  • Reduces UDP overhead
  • Improves bandwidth efficiency

Batch Header:

[Message Count: 8 bits]
[Message 1...]
[Message 2...]
...

Memory Management

Pre-Allocated Pools:

  • Packet buffers: Pool of 256 buffers
  • Fragment buffers: Pool per peer
  • ACK bitfields: Reuse bitfield storage

Garbage Collection:

  • Clean up old packets from queues
  • Release fragment buffers on timeout
  • Recycle sequence numbers after wrap

CPU Optimization

Optimizations:

  • Use SIMD for bit packing/unpacking
  • Cache-friendly data structures
  • Minimize memory allocations
  • Profile hot paths

Configuration Parameters

Network Settings

struct NetworkConfig {
// Connection
uint32_t protocol_id;
uint16_t max_peers;
uint8_t max_channels;
uint16_t mtu;

// Timeouts
uint32_t connection_timeout_ms;
uint32_t ping_interval_ms;
uint32_t disconnect_timeout_ms;

// Bandwidth
uint32_t bandwidth_limit_bytes_per_sec;
uint32_t packet_rate_per_sec;

// Reliability
uint32_t initial_rto_ms;
uint32_t max_rto_ms;
uint8_t max_retransmissions;

// Fragmentation
uint16_t max_fragment_size;
uint8_t max_fragments_per_message;
uint32_t fragment_timeout_ms;
};
const NetworkConfig DEFAULT_CONFIG = {
.protocol_id = 0x12345678,
.max_peers = 64,
.max_channels = 4,
.mtu = 1200,
.connection_timeout_ms = 5000,
.ping_interval_ms = 5000,
.disconnect_timeout_ms = 10000,
.bandwidth_limit_bytes_per_sec = 256 * 1024,
.packet_rate_per_sec = 30,
.initial_rto_ms = 100,
.max_rto_ms = 5000,
.max_retransmissions = 8,
.max_fragment_size = 1200,
.max_fragments_per_message = 64,
.fragment_timeout_ms = 5000
};

Error Handling

Error Codes

CodeNameDescription
0x00SUCCESSOperation succeeded
0x01TIMEOUTOperation timed out
0x02CONNECTION_FAILEDConnection failed
0x03INVALID_PACKETInvalid packet received
0x04BUFFER_OVERFLOWBuffer overflow
0x05PROTOCOL_MISMATCHProtocol version mismatch
0x06PEER_LIMIT_REACHEDMaximum peers reached
0x07CHANNEL_LIMIT_REACHEDMaximum channels reached
0x08FRAGMENT_ERRORFragment error
0x09RATE_LIMIT_EXCEEDEDRate limit exceeded

Error Reporting

  • Log errors with context
  • Return error codes from API calls
  • Provide error callbacks
  • Don't crash on network errors

Testing & Validation

Test Scenarios

  1. Connection Establishment

    • Normal connection
    • Connection timeout
    • Protocol mismatch
    • Peer limit reached
  2. Reliability

    • Packet loss (1%, 5%, 10%)
    • Packet reordering
    • Duplicate packets
    • Maximum retransmissions
  3. Ordering

    • Out-of-order delivery
    • Channel isolation
    • Sequence number wrap
  4. Fragmentation

    • Large message transfer
    • Fragment loss
    • Fragment timeout
    • Reassembly
  5. Congestion Control

    • Bandwidth limiting
    • Rate adaptation
    • Flow control
  6. Stress Testing

    • Maximum peers
    • Maximum channels
    • High packet rate
    • Long-running connections

References

This specification is based on principles from:

  • Gaffer On Games - Glenn Fiedler's networking articles

    • UDP vs. TCP
    • Virtual Connection over UDP
    • Reliability and Congestion Avoidance over UDP
    • Reliable Ordered Messages
    • Packet Fragmentation and Reassembly
    • State Synchronization
    • Snapshot Compression & Interpolation
  • ENet - Reliable UDP networking library

  • LiteNetLib - Lightweight networking library

  • RFC 793 - Transmission Control Protocol

  • RFC 768 - User Datagram Protocol


Appendix: Packet Format Examples

Example: Reliable Ordered Packet

[Protocol ID: 0x12345678] (32 bits)
[Packet Type: RELIABLE_ORDERED] (8 bits = 0x12)
[Flags: 0x01] (8 bits = has ACK bitfield)
[Sequence: 42] (16 bits)
[ACK Sequence: 38] (16 bits)
[ACK Bitfield: 0xFFFFFFFF] (32 bits)
[Channel ID: 0] (8 bits)
[Reserved: 0] (8 bits)
[Payload Length: 64] (16 bits)
[Payload Data: 64 bytes]

Example: Fragment Packet

[Protocol ID: 0x12345678] (32 bits)
[Packet Type: FRAGMENT] (8 bits = 0x20)
[Flags: 0x02] (8 bits = has fragments)
[Sequence: 100] (16 bits)
[ACK Sequence: 95] (16 bits)
[ACK Bitfield: 0xFFFFFFFF] (32 bits)
[Channel ID: 0] (8 bits)
[Reserved: 0] (8 bits)
[Fragment ID: 1234] (16 bits)
[Fragment Index: 2] (16 bits)
[Total Fragments: 5] (16 bits)
[Fragment Size: 1200] (16 bits)
[Fragment Data: 1200 bytes]