Skip to main content

R-Type Network Protocol Specification

Version: 5.0.0 Last Updated: 17th December 2025

Table of Contents

  1. Overview
    1. Strategies
    2. Data Types
    3. Data Definitions
    4. OpCode Categorization
  2. Error Handling Strategy
    1. Malformed Packets (UDP & TCP)
    2. Logical Errors (TCP)
    3. Desynchronization (UDP)
  3. Packet Structure
    1. Common Header
  4. TCP Commands (Session Management)
  5. UDP Commands (Real-time Gameplay)
  6. Security Considerations

1. Overview

This document defines the binary communication protocol used between the R-Type Client and Server. The architecture follows a Server Authoritative model with Snapshot Interpolation.

1.1 Strategies

  • Snapshot State: The server sends the full state of visible entities frequently. The client infers Spawn (new ID appears) and Death (ID disappears) from this list.
  • Client Prediction: Visual effects (charging, movement initiation) start immediately on client input, but logic validation happens on server state reception.

1.2 Data Types

  • u8, u16, u32: Unsigned integers (1, 2, 4 bytes).
  • f32: 32-bit Float (IEEE 754).
  • Endianness: Little Endian.

1.3 Data Definitions

To ensure strict binary compatibility, the following field types and sizes are defined for the entire protocol.

Field NameTypeSize (Bytes)RangeDescription
OpCodeu810..255Command ID
PayloadSizeu1620..65535Size of following data
TickIdu3240..4294967295Frame counter
PacketIndexu810..255Fragment index
PacketCountu810..255Total fragments for Tick
Reserved-Varies-Padding bytes, MUST be set to 0
PlayerIdu810..255Session ID
EntityIdu3240..4294967295Unique Object ID (ECS)
Statusu810..255Error/Success Codes (0=OK)
Inputsu810..255Bitmask (Keys held down)
Scoreu3240..4294967295Game Score
Livesu810..255Player Life Count
Angleu1620..360Degrees
Position (X/Y)u1620..65535Normalized Coordinate
Typeu810..255Sprite/Prefab ID
Usernamechar320..255Fixed-size String (Null-term)
ServerNamechar320..255Fixed-size String (Null-term)
ServerDescriptionchar640..255Fixed-size String (Null-term)
PlayerCountu810..255Number of connected players

Alignment Note: Structures and Packets are padded to align on 4-byte boundaries where possible to prevent compiler-specific packing issues.

1.4 OpCode Categorization

Command IDs (OpCodes) are grouped by protocol and direction to facilitate debugging and parsing.

RangeProtocolDirectionCategory
0x00 - 0x3FTCPBi-directionalSession Management (Login, Lobby, Game State).
0x40 - 0x7FUDPClient -> ServerClient Inputs (Inputs, Actions).
0x80 - 0xBFUDPServer -> ClientServer Snapshots (World State, Stats, Events).

2. Error Handling Strategy

2.1 Malformed Packets (UDP & TCP)

If a packet has an invalid Header, a PayloadSize mismatch, or an unknown OpCode:

  • Server Behavior: Silent Discard. The server MUST NOT reply to avoid bandwidth flooding or amplification attacks.
  • Client Behavior: Ignored.

2.2 Logical Errors (TCP)

Connection logic errors are handled explicitly in the CONNECT_ACK packet via the Status field.

2.3 Desynchronization (UDP)

If the Client and Server disagree on a position (e.g., due to lag or cheating), the Server's WORLD_SNAPSHOT is the absolute authority. The Client must override its local state with the received snapshot data.


3. Packet Structure

3.1 Common Header

Crucial: This header is prepended to EVERY packet sent over the network (TCP and UDP).

Total Header Size: 12 bytes.

FieldTypeSizeDescription
OpCodeu81Unique command identifier.
PayloadSizeu162Size of the data following this header.
PacketIndexu81Index of this packet within the Tick (0, 1, 2...).
TickIdu324The game frame number. Used for UDP reassembly. Set to 0 for TCP.
PacketCountu81Total number of packets expected for this Tick.
Reservedu8[3]3Padding to align Header to 12 bytes.

TickId Usage

  • The TickId is primarily used for UDP packets to manage out-of-order delivery and reassembly.
  • TickId of the client and server are not made to be synchronized, they are only identifiers for packet grouping.
  • It is used for polish movements for the client, and used as a SequenceId for the server.

Fragmentation Logic

  • The server may send large snapshots split across multiple UDP packets.
  • Clients must reassemble them based on TickId, PacketIndex, and PacketCount.
  • A "Game Frame" is considered complete only when the client has received PacketCount unique packets for the same TickId.
  • If a newer TickId is received fully, older partial Ticks are discarded.
  • If the packet is sent into only one piece, PacketIndex and PacketCount are always 0 and 1 respectively.

4. TCP Commands (Session Management)

Protocol: TCP (Reliable, Ordered). Usage: Lobby, Connection, Game State transitions.

0x01 - CONNECT_REQ

Direction: Client -> Server Description: First packet sent by a client to join the server.

Payload: 32 bytes

FieldTypeSizeDescription
Usernamechar[32]32The desired username of the player (ASCII, null-terminated).
  • When to send: When the user clicks "Connect" on the main menu.
  • Server Behavior: Checks if lobby is full. If not, assigns a PlayerId.

0x02 - CONNECT_ACK

Direction: Server -> Client Description: Response to the connection request.

Payload: 8 bytes

FieldTypeSizeDescription
PlayerIdu81The unique ID assigned to this client (e.g., 1, 2, 3, 4).
Statusu81Status Codes:
0 = OK (Success)
1 = Server Full
2 = Bad Username
3 = Game in Progress
ConnectedPlayersu81Number of currently connected players in the lobby.
ReadyPlayersu81Number of players marked as "Ready" in the lobby.
MaxPlayersu81Maximum players allowed on the server.
MinPlayersu81Minimum players required to start the game.
Reservedu8[2]2Padding to align with 4 bytes.
  • Client Behavior: If Status is 0, proceed to Lobby. Otherwise, show error message.

0x03 - DISCONNECT_REQ

Direction: Client -> Server Description: Notifies the server that the client is closing the game gracefully.

Payload: 0 bytes

  • When to send: When user clicks "Quit" or closes the window.

0x04 - NOTIFY_DISCONNECT

Direction: Server -> Client Description: Broadcasted to all connected clients when someone leaves.

Payload: 4 bytes

FieldTypeSizeDescription
PlayerIdu81The ID of the player who disconnected.
Reservedu8[3]3Padding to align with 4 bytes.
  • Client Behavior: Remove the corresponding player sprite/UI element from the lobby or game.

0x05 - GAME_START

Direction: Server -> Client Description: Sent when the game is starting. Upon sending/receiving this command, the TickId counter MUST be reset to 0 for the new match.

Payload: 4 bytes

FieldTypeSizeDescription
ControlledEntityIdu324The EntityID of the spaceship assigned to this player. The client should track this ID for camera/HUD.

0x06 - GAME_END

Direction: Server -> Client Description: Sent when the game has ended.

Payload: 4 bytes

FieldTypeSizeDescription
WinningPlayerIdu81ID of the winning player (or 0 for draw).
Reservedu8[3]3Padding to align with 4 bytes.

0x07 - READY_STATUS

Direction: Client -> Server Description: Sent by the client to indicate readiness in the lobby.

Payload: 1 byte

FieldTypeSizeDescription
IsReadyu810 = Not Ready, 1 = Ready.
Reservedu8[3]3Padding to align with 4 bytes.
  • When to send: When the user toggles the "Ready" button in the lobby.

When all the players in the lobby have sent IsReady = 1, the server automatically starts the game by sending GAME_START to all clients.

0x08 - NOTIFY_CONNECT

Direction: Server -> Client Description: Notify clients of a new player's connection. Payload: 36 bytes

FieldTypeSizeDescription
PlayerIdu81The unique ID assigned to the new player.
Reservedu8[3]3Padding to align with 4 bytes.
Usernamechar[32]32The username of the newly connected player (ASCII, null-terminated).
  • Client Behavior: Add the new player to the lobby UI.
  • When to send: Immediately after processing a successful CONNECT_REQ from a new client.

0x09 - NOTIFY_READY

Direction: Server -> Client Description: Broadcasted to all clients when a player changes their ready status. Payload: 4 bytes

FieldTypeSizeDescription
PlayerIdu81The ID of the player who changed status.
IsReadyu810 = Not Ready, 1 = Ready.
Reservedu8[2]2Padding to align with 4 bytes.
  • Client Behavior: Update the lobby UI to reflect the player's new ready status.
  • When to send: Whenever a player sends a READY_STATUS packet.

0x0A - SERVER_INFO_REQ

Direction: Client -> Server Description: Request server information such as current player count and max capacity. Payload: 0 bytes

  • When to send: When the client wants to display server info (e.g., in server browser).
  • Server Behavior: Respond with SERVER_INFO_RES containing the requested information.

0x0B - SERVER_INFO_RES

Direction: Server -> Client Description: Response to SERVER_INFO_REQ with server details. Payload: 100 bytes

FieldTypeSizeDescription
CurrentPlayersu81Number of currently connected players.
MaxPlayersu81Maximum player capacity of the server.
Statusu810 = Lobby Open, 1 = Full, 2 = In Game.
Reservedu8[1]1Padding to align with 4 bytes.
ServerNamechar[32]32Name of the server (ASCII, null-terminated).
ServerDescriptionchar[64]64Description of the server (ASCII, null-terminated).
  • Client Behavior: Display the server information in the server browser UI.
  • When to send: In response to a SERVER_INFO_REQ packet.

5. UDP Commands (Real-time Gameplay)

Protocol: UDP (Fast, Unreliable). Usage: Inputs, Positions, Physics.

0x40 - PLAYER_INPUT

Direction: Client -> Server Freq: Every client tick (e.g., 60Hz). Description: Contains the current state of the controller/keyboard.

Payload: 4 bytes

FieldTypeSizeDescription
Inputsu81Bitmask of HELD keys.
Reservedu8[3]3Padding to align with 4 bytes.

Bitmask Values:

Combine these values using bitwise OR (|) to set multiple keys (e.g., UP + SHOOT).

HexDecimalKey
0x011UP
0x022DOWN
0x044LEFT
0x088RIGHT
0x1016SHOOT

Charge Shot Logic (State vs Event): The protocol does not send "Press" or "Release" events. It sends the "Hold" state. The server detects the shot logic by comparing the current tick's state with the previous one.

Example Timeline (Processing Logic):

TickClient ActionBitmask sentPacket StatusServer Logic & Action
100Nothing0 (00000)Received0 -> 0. No change.
101Press Shoot16 (10000)Received0 -> 16. Start Charge Timer.
102Hold Shoot16 (10000)LOST(Server assumes state persists or waits).
103Hold Shoot16 (10000)Received16 -> 16. Continue Charge.
...............
120Release Shoot0 (00000)Received16 -> 0. FIRE TRIGGERED!

Note: Even if packet 102 was lost, the server recovers the state at tick 103. The shot is triggered only when the bit returns to 0.

0x80 - WORLD_SNAPSHOT

Direction: Server -> Client Freq: 60Hz. Description: The "Source of Truth". Contains the state of every active entity.

Payload: 4 bytes (Header) + N * 12 bytes

FieldTypeSizeDescription
EntityCountu162Number of entities in this specific packet.
Reservedu8[2]2Padding to align with 4 bytes.
EntitiesArrayVarList of EntityState structures.

Client Logic:

  1. Reassemble: Collect all parts for TickId.
    • If complete: Process Snapshot. If any older incomplete ticks are pending, delete them.
    • If incomplete: Wait for missing parts (or discard if too old).
  2. Compare: Compare received Entity IDs with local Entity IDs.
    • New ID: Spawn Entity.
    • Missing ID: Destroy Entity.
    • Existing ID: Interpolate position.

Structure: EntityState (Repeating block)

Size: 12 bytes per entity.

FieldTypeSizeDescription
EntityIdu324Unique ID.
Typeu81Sprite Type (1=P1, 2=Enemy...).
Reservedu81Padding to align with 4 bytes.
PosXu162X Position (Normalized). 0 = Left, 65535 = Right.
PosYu162Y Position (Normalized). 0 = Top, 65535 = Bottom.
Angleu162Rotation in degrees (0-360).

Note on Coordinates: The game world is defined on a fixed coordinate system of 65535 x 38864 units (approximately 16:9 aspect ratio). The client is responsible for mapping these coordinates to its local screen resolution using the rule of three.

Example for a client with a 1920x1080 screen:

  • ScreenX = (NetworkX * 1920) / 65535
  • ScreenY = (NetworkY * 1080) / 38864

0x81 - PLAYER_STATS

Direction: Server -> Client Freq: On change. Description: Updates HUD information.

Payload: 8 bytes

FieldTypeSizeDescription
PlayerIdu81ID of the player to update.
Livesu81Remaining lives.
Reservedu8[2]2Padding to align with 4 bytes.
Scoreu324Current score.

6. Security Considerations

6.1 Plaintext Communication

This protocol does not implement encryption (TLS/SSL). All data, including usernames and game states, is transmitted in cleartext.

  • Risk: The protocol is susceptible to Packet Sniffing and Man-in-the-Middle (MITM) attacks.
  • Requirement: Do not send sensitive data (real passwords, personal info) over this protocol.

6.2 Input Validation (Anti-Cheat)

The Server is the authoritative source of truth and MUST validate all incoming packets.

  • Trust No One: The server must not blindly accept game logic from the client (e.g., fire rate, movement speed, cooldowns). The server simulates the input and validates the result.
  • Sanitization: The Username field in CONNECT_REQ must be sanitized to prevent buffer overflows, display exploits, or injection attacks in the server logs/UI.

6.3 Denial of Service (DoS) Mitigation

The Silent Discard policy defined in the Error Handling section is a security measure.

  • By not replying to malformed packets, the server prevents Amplification Attacks.
  • It reduces the CPU load when under a flooding attack, as the server stops processing the packet immediately after the header check fails.