Network Protocol Specification
Overview
The R-Type network protocol is a binary protocol designed for real-time multiplayer game communication over UDP. It implements an ECS (Entity Component System) architecture with component-based snapshots for efficient network synchronization.
Protocol Version: 1.0
Transport Layer: UDP (User Datagram Protocol)
Protocol Identifier: 0x5254 ('RT')
Packet Structure
Every packet consists of two main parts:
+-------------------------------------------+
| Header (12 bytes) |
+-------------------------------------------+
| Payload (variable, 0-1188 bytes) |
+-------------------------------------------+
Packet Header
The packet header is a fixed 12-byte structure present in all protocol messages:
struct PacketHeader {
uint16_t magic; // Protocol identifier: 0x5254 ('RT')
uint8_t packet_type; // Packet type identifier
uint8_t flags; // Control flags
uint32_t sequence_number; // Monotonically increasing sequence
uint32_t timestamp; // Milliseconds since connection
};
| Field | Size | Description |
|---|---|---|
magic |
2 bytes | Protocol magic number (0x5254). Packets with invalid magic MUST be discarded. |
packet_type |
1 byte | Identifies the packet type. Valid range: 0x01-0x7F. |
flags |
1 byte | Bitfield for packet control flags. |
sequence_number |
4 bytes | Monotonically increasing packet sequence number for ordering and acknowledgment. |
timestamp |
4 bytes | Timestamp in milliseconds since connection establishment. |
Packet Flags
enum class PacketFlags : uint8_t {
FLAG_RELIABLE = 0x01, // Requires acknowledgment
FLAG_COMPRESSED = 0x02, // Payload is compressed
FLAG_ENCRYPTED = 0x04, // Payload is encrypted
FLAG_FRAGMENTED = 0x08, // Part of fragmented message
FLAG_PRIORITY = 0x10, // High priority processing
// Bits 5-7 reserved for future use
};
Packet Types
Packet types are organized into functional categories:
| Range | Category | Description |
|---|---|---|
| 0x01-0x0F | Connection | Session establishment and management |
| 0x10-0x1F | Input | Player input and control messages |
| 0x20-0x3F | World State | Entity lifecycle and ECS component snapshots |
| 0x40-0x5F | Game Events | Gameplay events and actions |
| 0x60-0x6F | Game Control | Game lifecycle and state transitions |
| 0x70-0x7F | Protocol Control | Reliability and connection maintenance |
Connection Management (0x01-0x0F)
CLIENT_CONNECT (0x01)
Sent by the client to initiate a connection.
struct ClientConnect {
PacketHeader header; // type = 0x01
uint8_t protocol_version; // Version = 1
char player_name[32]; // Player name (null-terminated)
uint32_t client_id; // Unique client identifier
};
// Total size: 49 bytes
Usage:
1. Client generates unique client_id
2. Client sends CLIENT_CONNECT to server
3. Server responds with SERVER_ACCEPT or SERVER_REJECT
SERVER_ACCEPT (0x02)
Sent by the server to accept a client connection.
struct ServerAccept {
PacketHeader header; // type = 0x02
uint32_t assigned_player_id; // Unique player ID assigned by server
uint8_t max_players; // Maximum players allowed
uint32_t game_instance_id; // Unique game instance ID
uint16_t server_tickrate; // Server update rate (Hz)
};
// Total size: 23 bytes
SERVER_REJECT (0x03)
Sent by the server to reject a connection attempt.
struct ServerReject {
PacketHeader header; // type = 0x03
uint8_t reject_code; // Reason code
char reason_message[64]; // Human-readable reason
};
// Total size: 77 bytes
Reject Codes:
enum class RejectCodes : uint8_t {
REJECT_SERVER_FULL = 0x00,
REJECT_INCOMPATIBLE_PROTOCOL_VERSION = 0x01,
REJECT_INVALID_PLAYER_NAME = 0x02,
REJECT_BANNED_CLIENT = 0x03,
REJECT_GENERIC_ERROR = 0xFF
};
CLIENT_DISCONNECT (0x04)
Sent by either client or server to terminate the connection.
struct ClientDisconnect {
PacketHeader header; // type = 0x04, FLAG_RELIABLE
uint32_t client_id; // Client identifier
uint8_t reason; // Disconnect reason
};
// Total size: 17 bytes
Disconnect Reasons:
enum class DisconnectReasons : uint8_t {
REASON_NORMAL_DISCONNECT = 0x00,
REASON_TIMEOUT = 0x01,
REASON_KICKED_BY_SERVER = 0x02,
REASON_CLIENT_ERROR = 0x03,
REASON_GENERIC_ERROR = 0xFF
};
Note
This packet MUST have the FLAG_RELIABLE flag set.
HEARTBEAT (0x05)
Sent periodically to maintain the connection.
struct HeartBeat {
PacketHeader header; // type = 0x05
uint32_t player_id; // Player identifier
};
// Total size: 16 bytes
Usage: - Client SHOULD send every 1-2 seconds - Server SHOULD disconnect clients that don't send heartbeat within 10 seconds
Player Input (0x10-0x1F)
PLAYER_INPUT (0x10)
Contains player input state. Sent frequently (typically every frame or when input changes).
struct PlayerInput {
PacketHeader header; // type = 0x10
uint32_t player_id; // Player identifier
uint16_t input_state; // Bitfield of input states
uint16_t aim_direction_x; // Aim direction X (normalized)
uint16_t aim_direction_y; // Aim direction Y (normalized)
};
// Total size: 24 bytes
Input Flags:
enum class InputFlags : uint16_t {
INPUT_MOVE_UP = 0x0001,
INPUT_MOVE_DOWN = 0x0002,
INPUT_MOVE_LEFT = 0x0004,
INPUT_MOVE_RIGHT = 0x0008,
INPUT_FIRE_PRIMARY = 0x0010,
INPUT_FIRE_SECONDARY = 0x0020,
INPUT_ACTION_SPECIAL = 0x0040,
// Bits 7-15 reserved for future actions
};
Entity Management (0x20-0x23)
Entity Types
enum class EntityTypes : uint8_t {
ENTITY_TYPE_PLAYER = 0x01,
ENTITY_TYPE_ENEMY = 0x02,
ENTITY_TYPE_ENEMY_BOSS = 0x03,
ENTITY_TYPE_PROJECTILE_PLAYER = 0x04,
ENTITY_TYPE_PROJECTILE_ENEMY = 0x05,
ENTITY_TYPE_POWERUP = 0x06,
ENTITY_TYPE_OBSTACLE = 0x07,
ENTITY_TYPE_BG_ELEMENT = 0x08
};
ENTITY_SPAWN (0x21)
Notifies clients of a new entity entering the game world.
struct EntitySpawn {
PacketHeader header; // type = 0x21, FLAG_RELIABLE
uint32_t entity_id; // Unique entity identifier
uint8_t entity_type; // Entity type
uint16_t position_x; // Initial X position
uint16_t position_y; // Initial Y position
uint8_t mob_variant; // Sub-type for mobs
uint8_t initial_health; // Initial health
uint16_t initial_velocity_x; // Initial X velocity
uint16_t initial_velocity_y; // Initial Y velocity
uint8_t is_playable; // 0=NPC, 1=playable entity
};
// Total size: 28 bytes
ENTITY_DESTROY (0x22)
Notifies clients that an entity has been destroyed or removed.
struct EntityDestroy {
PacketHeader header; // type = 0x22, FLAG_RELIABLE
uint32_t entity_id; // Entity identifier
uint8_t destroy_reason; // Reason for destruction
uint16_t final_position_x; // Final X position (for animations)
uint16_t final_position_y; // Final Y position (for animations)
};
// Total size: 21 bytes
Destroy Reasons:
enum class EntityDestroyReasons : uint8_t {
DESTROY_KILLED_BY_PLAYER = 0x00,
DESTROY_KILLED_BY_ENEMY = 0x01,
DESTROY_OUT_OF_BOUNDS = 0x02,
DESTROY_TIMEOUT_DESPAWN = 0x03,
DESTROY_LEVEL_TRANSITION = 0x04
};
ECS Component System
The protocol uses a pure ECS architecture where game state is synchronized through component snapshots.
Component Types
enum class ComponentType : uint8_t {
COMPONENT_TRANSFORM = 0x01, // Position, rotation, scale
COMPONENT_VELOCITY = 0x02, // Linear velocity
COMPONENT_HEALTH = 0x03, // Health/shield
COMPONENT_WEAPON = 0x04, // Weapon state
COMPONENT_AI = 0x05, // AI behavior
COMPONENT_FORCE = 0x06, // R-Type Force attachment
COMPONENT_COLLISION = 0x07, // Collision bounds
COMPONENT_SPRITE = 0x08, // Visual representation
COMPONENT_ANIMATION = 0x09, // Animation state
COMPONENT_POWERUP = 0x0A, // Powerup effect
COMPONENT_SCORE = 0x0B, // Score tracking
COMPONENT_INPUT = 0x0C, // Input state
COMPONENT_PHYSICS = 0x0D, // Physics properties
COMPONENT_LIFETIME = 0x0E, // Auto-destroy after time
COMPONENT_PARENT = 0x0F, // Parent-child relationship
};
Component Data Structures
Transform Component
struct ComponentTransform {
int16_t pos_x;
int16_t pos_y;
uint16_t rotation; // 0-65535 maps to 0-360 degrees
uint16_t scale; // Fixed point: 1000 = 1.0x scale
};
// Size: 8 bytes
Velocity Component
struct ComponentVelocity {
int16_t vel_x;
int16_t vel_y;
int16_t acceleration_x;
int16_t acceleration_y;
};
// Size: 8 bytes
Health Component
struct ComponentHealth {
uint8_t current_health;
uint8_t max_health;
uint8_t current_shield;
uint8_t max_shield;
};
// Size: 4 bytes
Weapon Component
struct ComponentWeapon {
uint8_t weapon_type;
uint8_t ammo_count;
uint16_t cooldown_remaining; // Milliseconds
uint8_t power_level; // 1-5 for R-Type
};
// Size: 5 bytes
AI Component
struct ComponentAI {
uint8_t ai_state;
uint8_t behavior_type;
uint32_t target_entity_id; // 0 = no target
uint16_t state_timer; // Time in current state (ms)
};
// Size: 8 bytes
Force Component (R-Type Specific)
struct ComponentForce {
uint32_t parent_ship_id; // Ship this Force is attached to
uint8_t attachment_point; // 0=front, 1=back, 2=detached
uint8_t force_level; // Power level
uint8_t charge_state; // Charge beam state
};
// Size: 7 bytes
Component Snapshots (0x24-0x2F)
ECS Architecture
This approach is optimal for ECS because:
- Each component type has its own packet
- Server iterates over one component type at a time (cache-friendly)
- Allows different update frequencies per component
- Optimal compression (homogeneous data)
- Aligned with pure ECS architecture (one system = one component type)
Update Frequencies
| Component | Frequency | Reason |
|---|---|---|
| Transform | 60 Hz | Critical: smooth movement |
| Velocity | 30 Hz | Medium: prediction possible |
| Health | 20 Hz | Low: changes are rare |
| Weapon | 10 Hz | Low: changes are rare |
| AI | 5 Hz | Very low: predictable |
| Animation | 15 Hz | Medium: visible but interpolable |
TRANSFORM_SNAPSHOT (0x24)
Sent frequently (30-60 Hz) for smooth movement.
struct TransformSnapshot {
PacketHeader header; // type = 0x24
uint32_t world_tick; // Server tick number
uint16_t entity_count; // Number of entities
// Followed by entity_count entries of:
// [uint32_t entity_id][ComponentTransform data (8 bytes)]
};
// Base size: 18 bytes + (entity_count × 12) bytes
// Example: 20 entities = 18 + 240 = 258 bytes
VELOCITY_SNAPSHOT (0x25)
Sent moderately (20-30 Hz).
struct VelocitySnapshot {
PacketHeader header; // type = 0x25
uint32_t world_tick;
uint16_t entity_count;
// Followed by: [uint32_t entity_id][ComponentVelocity data (8 bytes)]
};
// Base size: 18 bytes + (entity_count × 12) bytes
HEALTH_SNAPSHOT (0x26)
Sent less frequently (10-20 Hz) as it's less critical for display.
struct HealthSnapshot {
PacketHeader header; // type = 0x26
uint32_t world_tick;
uint16_t entity_count;
// Followed by: [uint32_t entity_id][ComponentHealth data (4 bytes)]
};
// Base size: 18 bytes + (entity_count × 8) bytes
// More compact! 20 entities = 18 + 160 = 178 bytes
WEAPON_SNAPSHOT (0x27)
Sent rarely (5-10 Hz) except on changes.
struct WeaponSnapshot {
PacketHeader header; // type = 0x27
uint32_t world_tick;
uint16_t entity_count;
// Followed by: [uint32_t entity_id][ComponentWeapon data (5 bytes)]
};
// Base size: 18 bytes + (entity_count × 9) bytes
AI_SNAPSHOT (0x28)
Sent rarely (2-5 Hz) as AI is predictable.
struct AISnapshot {
PacketHeader header; // type = 0x28
uint32_t world_tick;
uint16_t entity_count;
// Followed by: [uint32_t entity_id][ComponentAI data (8 bytes)]
};
// Base size: 18 bytes + (entity_count × 12) bytes
ANIMATION_SNAPSHOT (0x29)
Sent moderately (10-20 Hz).
struct AnimationSnapshot {
PacketHeader header; // type = 0x29
uint32_t world_tick;
uint16_t entity_count;
// Followed by: [uint32_t entity_id][ComponentAnimation data (7 bytes)]
};
// Base size: 18 bytes + (entity_count × 11) bytes
Component Lifecycle
COMPONENT_ADD (0x2A)
Used when an entity gains a new component.
struct ComponentAdd {
PacketHeader header; // type = 0x2A, FLAG_RELIABLE
uint32_t entity_id; // Target entity
uint8_t component_type; // ComponentType enum
uint8_t data_size; // Size of component data
// Followed by component data (variable size)
};
// Base size: 18 bytes + component_data_size
Example: Player picks up powerup → adds ComponentPowerup
COMPONENT_REMOVE (0x2B)
Used when a component is removed from an entity.
struct ComponentRemove {
PacketHeader header; // type = 0x2B, FLAG_RELIABLE
uint32_t entity_id; // Target entity
uint8_t component_type; // ComponentType to remove
};
// Size: 17 bytes
Example: Powerup expires → removes ComponentPowerup
Delta Snapshots (Advanced Optimization)
Delta snapshots send only the changes since the last snapshot, providing massive bandwidth savings.
TRANSFORM_SNAPSHOT_DELTA (0x2C)
struct TransformSnapshotDelta {
PacketHeader header; // type = 0x2C
uint32_t world_tick;
uint32_t base_tick; // Reference tick
uint16_t entity_count; // Only modified entities
// Followed by: [uint32_t entity_id][ComponentTransform data]
};
Bandwidth Savings Example: - Full snapshot (40 entities): 18 + 40×12 = 498 bytes - Delta snapshot (5 changed): 18 + 5×12 = 78 bytes - 84% bandwidth reduction!
HEALTH_SNAPSHOT_DELTA (0x2D)
struct HealthSnapshotDelta {
PacketHeader header; // type = 0x2D
uint32_t world_tick;
uint32_t base_tick;
uint16_t entity_count;
// Followed by: [uint32_t entity_id][ComponentHealth data]
};
Full Entity State
ENTITY_FULL_STATE (0x2E)
Used to synchronize multiple components of a specific entity (e.g., when a player reconnects).
struct EntityFullState {
PacketHeader header; // type = 0x2E, FLAG_RELIABLE
uint32_t entity_id;
uint8_t entity_type; // EntityTypes enum
uint8_t component_count; // Number of components
// Followed by component_count entries of:
// [uint8_t component_type][uint8_t size][component_data]
};
Game Events (0x40-0x5F)
PLAYER_HIT (0x40)
struct PlayerHit {
PacketHeader header; // type = 0x40, FLAG_RELIABLE
uint32_t player_id; // Player who was hit
uint32_t attacker_id; // Entity that caused damage
uint8_t damage; // Damage amount
uint8_t remaining_health; // Health after damage
uint8_t remaining_shield; // Shield after damage
int16_t hit_pos_x; // Hit location X
int16_t hit_pos_y; // Hit location Y
};
// Total size: 27 bytes
PLAYER_DEATH (0x41)
struct PlayerDeath {
PacketHeader header; // type = 0x41, FLAG_RELIABLE
uint32_t player_id; // Player who died
uint32_t killer_id; // Entity that killed player
uint32_t score_before_death; // Player's score
int16_t death_pos_x; // Death location X
int16_t death_pos_y; // Death location Y
};
// Total size: 28 bytes
SCORE_UPDATE (0x42)
struct ScoreUpdate {
PacketHeader header; // type = 0x42
uint32_t player_id; // Player whose score changed
uint32_t new_score; // Updated total score
int16_t score_delta; // Change in score (can be negative)
uint8_t reason; // Reason for score change
};
// Total size: 23 bytes
Score Change Reasons:
enum class ScoreChangeReasons : uint8_t {
SCORE_ENEMY_KILL = 0x00,
SCORE_BOSS_KILL = 0x01,
SCORE_POWERUP_COLLECTED = 0x02,
SCORE_LEVEL_COMPLETED = 0x03,
SCORE_BONUS_SURVIVAL = 0x04,
SCORE_TOOK_DAMAGE = 0x05
};
POWERUP_PICKUP (0x43)
struct PowerupPickup {
PacketHeader header; // type = 0x43, FLAG_RELIABLE
uint32_t player_id; // Player who picked up powerup
uint32_t powerup_id; // Powerup entity ID
uint8_t powerup_type; // Type of powerup
uint8_t duration; // Effect duration (seconds, 0=permanent)
};
// Total size: 22 bytes
Powerup Types:
enum class PowerupTypes : uint8_t {
POWERUP_SPEED_BOOST = 0x00,
POWERUP_WEAPON_UPGRADE = 0x01,
POWERUP_FORCE = 0x02, // R-Type signature
POWERUP_SHIELD = 0x03,
POWERUP_EXTRA_LIFE = 0x04,
POWERUP_INVINCIBILITY = 0x05
};
WEAPON_FIRE (0x44)
struct WeaponFire {
PacketHeader header; // type = 0x44
uint32_t shooter_id; // Entity that fired
uint32_t projectile_id; // New projectile entity ID
int16_t origin_x; // Fire origin X
int16_t origin_y; // Fire origin Y
int16_t direction_x; // Direction vector X (normalized×1000)
int16_t direction_y; // Direction vector Y (normalized×1000)
uint8_t weapon_type; // Weapon type fired
};
// Total size: 29 bytes
Game Control (0x60-0x6F)
GAME_START (0x60)
struct GameStart {
PacketHeader header; // type = 0x60, FLAG_RELIABLE
uint32_t game_instance_id; // Game instance identifier
uint8_t player_count; // Number of players
uint32_t player_ids[4]; // Player IDs (up to 4)
uint8_t level_id; // Starting level
uint8_t difficulty; // Difficulty setting
};
// Total size: 35 bytes
GAME_END (0x61)
struct GameEnd {
PacketHeader header; // type = 0x61, FLAG_RELIABLE
uint8_t end_reason; // Reason game ended
uint32_t final_scores[4]; // Final scores for all players
uint8_t winner_id; // Winning player (if applicable)
uint32_t play_time; // Total game time (seconds)
};
// Total size: 34 bytes
LEVEL_COMPLETE (0x62)
struct LevelComplete {
PacketHeader header; // type = 0x62, FLAG_RELIABLE
uint8_t completed_level; // Level that was completed
uint8_t next_level; // Next level to load (0xFF=game end)
uint32_t bonus_score; // Completion bonus
uint16_t completion_time; // Time taken (seconds)
};
// Total size: 20 bytes
LEVEL_START (0x63)
struct LevelStart {
PacketHeader header; // type = 0x63, FLAG_RELIABLE
uint8_t level_id; // Level identifier
char level_name[32]; // Level name
uint16_t estimated_duration; // Estimated time (seconds)
};
// Total size: 47 bytes
Protocol Control (0x70-0x7F)
ACKNOWLEDGMENT (0x70)
struct Acknowledgment {
PacketHeader header; // type = 0x70
uint32_t acked_sequence; // Sequence number being ACKed
uint32_t received_timestamp; // When packet was received
uint32_t client_id; // Client identifier
};
// Total size: 24 bytes
PING (0x71)
struct Ping {
PacketHeader header; // type = 0x71
uint32_t client_timestamp; // Client's current timestamp
uint32_t client_id; // Client identifier
};
// Total size: 20 bytes
PONG (0x72)
struct Pong {
PacketHeader header; // type = 0x72
uint32_t client_timestamp; // Original client timestamp from PING
uint32_t server_timestamp; // Server's timestamp when received
};
// Total size: 20 bytes
Effects System (0x50-0x5F)
VISUAL_EFFECT (0x50)
struct VisualEffect {
PacketHeader header; // type = 0x50
uint8_t effect_type;
int16_t pos_x;
int16_t pos_y;
uint16_t duration_ms; // Effect duration
uint8_t scale; // Scale multiplier (100 = 1.0x)
uint8_t color_tint_r; // Color modulation
uint8_t color_tint_g;
uint8_t color_tint_b;
};
// Size: 23 bytes
AUDIO_EFFECT (0x51)
struct AudioEffect {
PacketHeader header; // type = 0x51
uint8_t effect_type;
int16_t pos_x; // 3D audio positioning
int16_t pos_y;
uint8_t volume; // 0-255
uint8_t pitch; // Pitch modifier (100 = normal)
};
// Size: 19 bytes
PARTICLE_SPAWN (0x52)
struct ParticleSpawn {
PacketHeader header; // type = 0x52
uint16_t particle_system_id; // Particle system type
int16_t pos_x;
int16_t pos_y;
int16_t velocity_x; // Initial velocity
int16_t velocity_y;
uint16_t particle_count; // Number of particles
uint16_t lifetime_ms; // Particle lifetime
uint8_t color_start_r; // Start color
uint8_t color_start_g;
uint8_t color_start_b;
uint8_t color_end_r; // End color (for fade)
uint8_t color_end_g;
uint8_t color_end_b;
};
// Size: 32 bytes
R-Type Specific Systems (0x64-0x6F)
FORCE_STATE (0x64)
Updates the state of the Force (R-Type's signature weapon).
struct ForceState {
PacketHeader header; // type = 0x64
uint32_t force_entity_id; // The Force entity
uint32_t parent_ship_id; // Ship it's attached to (0=detached)
uint8_t attachment_point; // Where it's attached
uint8_t power_level; // 1-5
uint8_t charge_percentage; // 0-100 for charge beam
uint8_t is_firing; // Boolean flag
};
// Size: 24 bytes
Attachment Points:
enum class ForceAttachmentPoint : uint8_t {
FORCE_DETACHED = 0x00,
FORCE_FRONT = 0x01,
FORCE_BACK = 0x02,
FORCE_ORBITING = 0x03 // For special modes
};
AI_STATE (0x65)
struct AIState {
PacketHeader header; // type = 0x65
uint32_t entity_id; // AI entity
uint8_t current_state; // Current AI state
uint8_t behavior_type; // Behavior pattern
uint32_t target_entity_id; // Current target (0=no target)
int16_t waypoint_x; // Next waypoint or target pos
int16_t waypoint_y;
uint16_t state_timer; // Time remaining in current state
};
// Size: 30 bytes
AI Behavior Types:
enum class AIBehaviorType : uint8_t {
AI_IDLE = 0x00,
AI_PATROL = 0x01,
AI_CHASE = 0x02,
AI_FLEE = 0x03,
AI_ATTACK_PATTERN_1 = 0x04, // Straight line
AI_ATTACK_PATTERN_2 = 0x05, // Sine wave
AI_ATTACK_PATTERN_3 = 0x06, // Circle
AI_BOSS_PHASE_1 = 0x07,
AI_BOSS_PHASE_2 = 0x08,
AI_BOSS_PHASE_3 = 0x09,
AI_KAMIKAZE = 0x0A
};
Implementation Notes
Code References (source of truth)
- Packet enum and structs: src/common/include/common/protocol/Protocol.hpp
- Validation/serialization handlers: src/common/include/common/protocol/PacketManager.hpp and src/common/src/protocol/PacketManager.cpp
- Runtime dispatch (client side): src/game/src/coordinator/Coordinator.cpp
- Legacy minimalist enum (not used by the handler table): src/common/include/common/protocol/PacketTypes.hpp
All packet types listed in this document are present in the handler table (see handlers array) of PacketManager and the Coordinator switch; no documented packet type is missing from the codebase as of this branch.
Serialization Format
All multi-byte integers are transmitted in network byte order (big-endian).
Deserialization
When receiving component snapshots, the variable-length arrays are deserialized into std::vector<std::pair<uint32_t, ComponentXXX>> for easier processing:
// Network format: header + world_tick + entity_count + [entity_id, ComponentTransform][]
// After deserialization: vector<pair<entity_id, ComponentTransform>>
Reliability Mechanism
Packets marked with FLAG_RELIABLE MUST be acknowledged:
- Receiver sends
ACKNOWLEDGMENTpacket - Sender retransmits if no ACK received within timeout
- Timeout period: typically 100-500ms
Packet Priorities
| Priority | Packet Types | Handling |
|---|---|---|
| High | Connection, Game Control, Reliable packets | Process immediately |
| Medium | Player Input, Game Events | Process in tick order |
| Low | Component Snapshots | Can drop old packets |
Best Practices
Client Implementation
- Interpolation: Use Transform snapshots for smooth rendering between updates
- Prediction: Predict movement based on Velocity components
- Reconciliation: Correct predictions when authoritative state arrives
- Prioritization: Process reliable packets first
Server Implementation
- Tick Rate: Run simulation at fixed rate (typically 60 Hz)
- Snapshot Frequency: Adjust per component type (see table above)
- Delta Encoding: Use delta snapshots when >70% entities unchanged
- Culling: Only send snapshots for entities visible to player
Bandwidth Optimization
| Technique | Savings | Complexity |
|---|---|---|
| Delta snapshots | 70-90% | Medium |
| Interest management | 50-80% | High |
| Component frequency tuning | 30-50% | Low |
| Quantization | 10-20% | Low |
Troubleshooting
Common Issues
High Latency - Check for packet loss - Verify server tick rate - Consider reducing snapshot frequencies
Desync Issues - Ensure reliable packets are acknowledged - Check for dropped delta snapshots - Implement full state resync mechanism
Bandwidth Problems - Enable delta snapshots - Reduce non-critical component update rates - Implement interest management