Rutentoy Protocol

This is a work-in-progress net protocol spec for a yet-unimplemented video game.

The Rutentoy protocol uses either the SCTP or TCP transport protocol, on port 29778 by default. Other transport protocols such as UDP or WebSocket may also be used, though their encodings for reliable transport and packet ordering are out-of-scope for this specification.

The opcode numbers before the packet names are hexadecimal.

References

QOI Format (36KB application/pdf)

xxHash (14KB text/markdown)

Types

All integers are two's-compliment when signed and are in little-endian byte order.

Text strings are encoded as UTF-8 without a BOM.

Object positions are encoded as signed 24-8 fixed point.

Object rotations are encoded as unsigned 0-16 fixed point, representing fractions of 1 turn. 0-65536 should be scaled to 0-2π.

Structure fields are not aligned and do not contain padding. They are not intended to be read directly into program memory.

Structure fields named 'cookie' refer to ephemeral message-specific identifiers, not permanent client data as the term is used for the Web.

Server Info Sequence

Login Sequence

Client-to-Server Texture Exchange Sequence

If the server has the player's texture cached:

If the server doesn't have the player's texture cached:

Server-to-Client Texture Exchange Sequence

If the client already has the requested texture:

If the client doesn't have the requested texture:

00 Disconnect

Sent by either the client or the server to announce the network socket is about to be closed. Usually prefixed with the "LEAVE ..." Client IPC message to describe why the disconnect happened. The server may optionally announce a client disconnecting by sending a 'player has left' message to the System channel.

struct 00_disconnect {
    opcode: u8 = 0x00,
}

sizeof(00_disconnect) = 1

01 Ping

Sent by the server to check for both the network latency and responsiveness of a client. The client should echo this packet back to the server when recieved.

struct 01_ping {
    opcode: u8 = 0x01,
    cookie: u32,
}

sizeof(01_ping) = 5

02 Object Create

Sent by the server to create a movable object on the client. How the server determines the value of the 'oid' field is implementation-defined.

The following list is a list of model IDs that a client must support rendering:

struct 02_create {
    opcode: u8 = 0x03,
    oid: u32,          // unique object id
    model: u16,        // model id
    model_meta: u16,   // extra metadata for the model
}

sizeof(02_create) = 9

03 Object Move

Sent by the client every 40 milliseconds with the entity field set to zero.

The server sends this to a client with the entity field set to zero to teleport them.

The server sends this to a client with the entity field set to a nonzero value to update the position and rotation of another object model.

struct 03_move {
    opcode: u8 = 0x02,
    oid: u32,          // unique object id, see 02 Object Create
    x: i32,            // signed 24-8 position fixed-point
    y: i32,
    z: i32,
    yaw: u16,          // unsigned 0-16 angle fixed-point
    pitch: u16,
}

sizeof(03_move) = 21

04 Object Delete

The server sends this to a client to remove an object model.

If sent with the entity field set to zero, the client displays an intermission screen until it is sent another 02 Object Create packet with the 'oid' field set to zero.

When a client receives this packet with the 'oid' field set to zero, world data and objects are cleared to avoid incongruencies with possible future data.

struct 04_delete {
    opcode: u8 = 0x04,
    oid: u32,          // unique object id
}

sizeof(04_delete) = 5

04 Object Texture Chunk

Sent by the client to give the server a custom player-specified model texture. The server or client may optionally cache textures on disk. The server may implement a minimum time limit between each texture sent by the client.

The 'data_len' field being set to less than 1024 indicates the packet is the final chunk for the texture being sent.

The server or client must not abuse this packet to create animated textures.

The textures are sent ad-hoc to avoid relying on a centralised texture server. The maximum texture size is 128x128. Textures must be have power-of-two dimensions. The image data is QOI encoded, which also includes a header containing the image dimensions.

struct 04_texture {
    opcode: u8 = 0x05,
    oid: u32,          // unique object id
    cookie: u32,       // unique identifier for this specific texture
    data_len: u16,     // how many bytes in the 1024-byte chunk is used
    data: [1024]u8,    // a 1024-byte chunk of QOI data
}

sizeof(04_texture) = 1035

05 Object Metadata

Sent by the server to update an object model's details.

Required Supported 'meta_type' Values

Animation IDs

Some animation IDs will only work with certain models. Animations with (Overlay) next to their name should be layered on top of the current (Stance) animation.

Model Equipment Slot IDs

struct 06_object_meta {
    opcode: u8 = 0x06,
    oid: u32,          // unique object id
    meta_type: u8,     // what detail should be changed
    meta_data: u32,    // 32 arbitrary bits of data, depends on meta_type
}

sizeof(06_object_meta) = 10

07 Interact

Sent by the client to indicate the player is interacting with a block, item, or object.

Interaction Types

struct 07_interact {
    opcode: u8 = 0x07,
    mode: u8,          // F0 attack/use, 0F interaction type
    oid: u32,          // targeted object, 0 if none
    block_x: i32,      // targeted block position
    block_y: i32,
    block_z: i32,
    item: u16,         // used item ID
}

sizeof(07_interact) = 20

08 GUI Open

Sent by the server to open a GUI screen on the client. Sent by the client to notify a GUI screen being closed.

Modes

struct 08_gui_open {
    opcode: u8 = 0x08,
    mode: u16,         // which gui to open
    cookie: u16,       // a client-unique identifier for the opened gui,
                       // set to 0 when using mode 1
}

sizeof(08_gui_open) = 12

09 GUI Interact

Sent by the client to move a container's items between slots or other containers.

Modes

struct 09_gui_interact {
    opcode: u8 = 0x09,
    mode: u8,
    send_cookie: u16,
    recv_cookie: u16,
    send_slot: u8,
    recv_slot: u8,
}

sizeof(09_gui_interact) = 8

0A GUI Items

TODO

struct 0a_gui_items {
    opcode: u8 = 0x0a,
}

sizeof(0a_gui_items) = ??

0B Message

A text message sent to the server, or a text message sent from the server to display on the client. The only ASCII control characters allowed are 00 NUL and 0A LF. Implementations must not assume that non-UTF-8 data will be transferred losslessly.

Channels

The channel field describes where the message should be displayed.

The following is a list of defined values, with names suggesting use cases:

struct 0b_message {
    opcode: u8 = 0x0a,
    cookie: u32,       // a unique identifier for the whole message
                       // so multiple message chunks can be sent in parallel
    channel: u8,       // 8th bit final chunk marker, 7 bits of msg channel
    msg: [64]u8,       // a chunk of utf-8 text, padded with
                       // 00 bytes if the chunk is < 64 bytes long
}

sizeof(0b_message) = 66

10 World Chunk

Sends the block data for a 8³ world chunk. The data field contains an uncompressed block ID array and metadata array.

The lower metadata nibble contains a lightmap value for artificial lights. The upper metadata nibble contains the third nibble of the block ID.

The server can send this to replace an already loaded chunk rather than sending multiple 11 Set Block packets.

TODO: should this packet be compressed? it gets sent a lot and 1031 bytes aren't nothing

struct 10_chunk {
    opcode: u8 = 0x10,
    x: i21, // chunk coordinate, these three fields are packed into 8 bytes
    y: i19,
    z: i21,
    blocks: [512]u8,
    meta: [512]u8,
}

sizeof(10_chunk) = 1031

11 Set Block

Sent by the client or the server to update a single block in a chunk.

The client should only directly send this packet if the server allows it with the 'Set Blocks Instantly' permission in the 20 Gamemode Settings packet.

struct 11_block {
    opcode: u8 = 0x11,
    chunk_x: i21,      // these three fields are packed into 8 bytes
    chunk_y: i19,
    chunk_z: i21,
    block_x: u4,       // these four fields are packed into two bytes
    block_y: u4,
    block_z: u4,
    block_nibble: u4,  // the upper 4 bits of the block ID
    block: u8,
}

sizeof(11_block) = 12

12 World Effect

Sent by the server to create a special effect. Supporting this packet and its effects is optional. Clients not implementing this packet should still read this packet and ignore its contents.

Modes

Weather Effects

struct 12_world_effect {
    opcode: u8 = 0x12,
    mode: u8,
    data1: u32, // may be signed or unsigned, depending on the value of 'mode'
    data2: u32,
    data3: u32,
    data4: u32,
}

sizeof(12_world_effect) = 18

20 Gamemode Settings

Sent by the server to allow various cheats or gamemode tweaks on the client. Servers should not assume the client will cooperate with these settings.

Flags

struct 20_gamemode {
    opcode: u8 = 0x20,
    permissions: u16,
    walk_speed: u16,  // unsigned 8.8 fixed point
    jump_height: u16, // ditto
}

sizeof(20_gamemode) = 7

Block IDs

A list of defined block IDs and their names. The IDs are written in hexadecimal. IDs not present on this list should be rendered on the client with a texture that makes it obvious the ID is unused.

This particular encoding for block IDs allows for better compression by storing two most significant nibbles of the block ID in one byte array, and the third least significant nibble and a lightmap value in a second byte array.

Item IDs

A list of defined item IDs and their names. The ID is written in hexadecimal. Items that use the meta field will describe how they use it.