R-Type network protocol¶
This document specifies the protocol spoken between the R-Type server and its clients,
built on the Aegis2D net module. It describes every byte on the wire, so that a new
client or server can be written from it alone.
Status
Draft of protocol version 1. Sections are added as the protocol is implemented; the ones not written yet are listed at the end of this page.
1. Conventions¶
1.1 Requirements language¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (RFC 2119, RFC 8174) when, and only when, they appear in all capitals.
1.2 Terminology¶
| Term | Meaning |
|---|---|
| Server | The authoritative process: it runs the simulation and decides everything. |
| Client | A player's process: it sends inputs and displays what the server sends. |
| Peer | The other end of a communication, client or server. |
| Session | A player's stay on the server, from login to disconnection, identified by a session token. |
| Datagram | One UDP payload, header included. |
| Packet | The content of a datagram: the header followed by the payload of its type. |
1.3 Transport¶
The server listens on one TCP port and one UDP port, both configurable.
- TCP carries the session. A client opens a TCP connection to log in and receives a session token. The connection stays open for the whole session, and its closure ends the session. TCP MUST NOT carry gameplay data.
- TCP messages are framed. TCP delivers a byte stream, so each message travels behind
its length: a
u16(section 1.4) giving the number of bytes that follow, then those bytes. A message MUST NOT exceed 4096 bytes, prefix excluded; a receiver MUST close the connection when a prefix announces more, before reading further. The string"hi"as a message is sent as02 00 68 69. - UDP carries the game. Every in-game message travels in UDP datagrams, in both directions. Each datagram MUST start with the packet header (section 2).
- Datagram size. A datagram, header included, MUST NOT exceed 1200 bytes, so that IP never fragments it. A receiver MUST drop larger datagrams.
1.4 Encoding¶
- Multi-byte numbers are little-endian: the least significant byte comes first.
- Fields follow each other in the order given, with no padding or alignment.
- The types below are the only ones used:
| Type | Size (bytes) | Encoding |
|---|---|---|
u8, u16, u32, u64 |
1, 2, 4, 8 | Unsigned integer, little-endian |
i8, i16, i32, i64 |
1, 2, 4, 8 | Signed integer, two's complement, little-endian |
f32 |
4 | IEEE 754 binary32, its bits sent as a u32 |
f64 |
8 | IEEE 754 binary64, its bits sent as a u64 |
bool |
1 | 0 for false, 1 for true; any other value makes the packet invalid |
string |
2 + n | Length n as a u16, then n bytes of UTF-8, without terminator |
For example, the u32 value 0x11223344 is sent as the bytes 44 33 22 11, and the
string "hi" as 02 00 68 69.
Packed payloads. The Snapshot payload (4.3) is packed bit
by bit instead (net::BitWriter): bit n of the payload is bit n % 8 (from the least
significant) of byte n / 8, and each field is written from its least significant bit.
Its types:
| Type | Size (bits) | Encoding |
|---|---|---|
bN |
N | Unsigned integer of N bits |
sN |
N | Signed integer of N bits, two's complement |
bit |
1 | 1 for true |
varint |
8 to 40 | An unsigned integer of up to 32 bits in groups of 7 bits, the lowest first, each followed by a bit set when another group follows. A value has a single encoding: a last group of 0 (other than the lone group of the value 0), a sixth group or a value above 32 bits makes the packet invalid |
The last byte is completed with 0 bits; any other padding makes the packet invalid.
1.5 Validation¶
Every byte received is untrusted. A receiver MUST check that a field fits in the bytes remaining before reading it, and MUST drop a packet that ends before its last field or that contains an invalid value. A dropped packet MUST NOT change any state. Every check, the size limits and what is logged are gathered in section 6.
The Aegis2D implementation is net::BinaryWriter and net::BinaryReader
(aegis2d/net/binary.hpp): every read is bounds-checked, and the first failure rejects
the rest of the packet. TCP framing is net::TcpConnection
(aegis2d/net/tcp_connection.hpp).
Sections¶
| Section | Content | Status |
|---|---|---|
| 1 | Conventions, transport and encoding (this page) | Written |
| 2 | UDP packet header | Written |
| 3 | Session: TCP login and session token, binding the UDP endpoint | Written |
| 4 | Gameplay messages: inputs, snapshots, events | Written |
| 5 | Reliability: acknowledgements, resending events, heartbeat and timeouts | Written |
| 6 | Malformed and unknown packets: size limits, rejection rules, logging | Written |