This document will go over the Fultiples network specification. The purpose of this specification is to make it so clients and servers designed for Fultiples can connect and talk to each other no matter what programming language or design they use.
This number is always a positive integer.
Version: 0
Fultiples (multiplayer over a network) functions on a client and server model. The clients connect to a server and send messages such as where they made their move, and the server sends back messages telling the client various things like whether or not a move succeeded or failed, or if it is their turn or not.
First off, there are the global specifications like the data types, and then there are the specific client and server specifications.
The way you read the client/server specifications is that they will have a list of messages in a table that contains the name, opcode, and carried data (if any).
The simplest way I can explain how this table would translate to code is as a tagged union. The name is the tag, and the carried data is the union data. Don’t worry about the opcode in this explanation, since it is for serialization and deserialization over the network (since the network only expects bytes, not multi-byte data structures).
Message opcodes are 1 byte in size.
Opcodes are written in hexadecimal notation.
Number types are either a u
or an i
followed by their size in bits. The u
is for unsigned, and
the i
is for signed. So for example, a byte is:
u8
In the layout for each type, the parameters and their type are
written like this:
<PARAMETER NAME>:<PARAMETER TYPE>
When serializing to individual bytes to be sent over the network, integers are serialized in little-endian format.
Name | Layout | Description |
---|---|---|
Vec2 | x:u16 y:u16 |
A type that stores 2 integer parameters big enough for sizes or positions. |
ClientConnectInfo | spec_version:u32 |
A data structure that stores information about the connection coming from the client to the server. |
Serialization and deserialization in this specification are used for turning messages which at the end of the day are represented as multi-byte structures into raw bytes which can be sent over the network, and then recovering the original message from those bytes once received on the receiver end.
Messages are serialized by creating a new buffer (in modern programming languages, this would be a vector) for the bytes, and then pushing the opcode corresponding to the message to that said buffer. If that message has carried data associated with it, the serialized bytes of the carried data are then appended to the end of the buffer.
Carried data is serialized by creating a buffer of bytes (for the carried data bytes), taking the layout (see network datatypes specification section), and appending the serialized bytes from the layout types/data to the buffer. This is a recursive operation since the layout can contain types which in themselves contain a multi-byte layout.
Messages are deserialized by reading a byte from the stream, and interpreting that as an 8-bit opcode.
If the decoded message is meant to carry data with it, the data is deserialized from the stream. If you are familiar with recursive decent parsers, this process will sound familiar. If you were to think of this as a recursive decent parser (which it is), the token stream is a stream of bytes from the network.
If this explanation is still a bit confusing to you, keep in mind that any algorithms implemented for serialization are essentially just the deserialization algorithms implemented in reverse, since deserializing is the act of reversing a serialization process.
Message | Opcode | Carried Data | Description |
---|---|---|---|
Connect | 0x0 | connect_info:ClientConnectInfo |
Message sent when the client is connecting to a server. |
Disconnect | 0x1 | Message sent when the client is disconnecting from the server. | |
Shade | 0x2 | cell_position:Vec2 |
Message sent when the client is making a move and choosing a cell on the board to shade. |
GetSize | 0x3 | Message sent when the client wants to know the board size. | |
GetCell | 0x4 | cell_position:Vec2 |
Message sent when the client wants to know the value of a given cell on the board. |
Message | Opcode | Carried Data | Description |
---|---|---|---|
Kicked | 0x0 | Message sent to a client when it is kicked from the server. | |
ConnectionAccepted | 0x1 | Message sent to clients with valid connection requests. | |
ConnectionRejected | 0x2 | Message sent to clients with invalid connection requests. | |
Winner | 0x3 | Message sent to the winner’s client. | |
Loser | 0x4 | Message sent to the loser’s client. | |
ClientTurn | 0x5 | Message sent to the current turn’s client once it is their turn. | |
NotClientTurn | 0x6 | Message sent to the other clients who don’t currently have the turn (this is just to notify them in case they had the previous turn or something like that) | |
TurnSuccess | 0x7 | Message sent to a client when their turn succeeded. | |
TurnFail | 0x8 | Message sent to a client when their turn failed. | |
BoardSize | 0x9 | size:Vec2 |
Message sent to a client telling them what the board size is. |
BoardCell | 0xA | cell_position:Vec2 value:i32 |
Message sent to a client telling them what the value of a given cell is. |
TurnTimeLimit | 0xB | seconds:u16 |
Message sent to a client when there are turn time limits (in seconds). If this message is never sent, there are no turn time limits. |