This page assume you already have knowledge of network programming.
GbxRemote is Nadeo’s derivation of the XML-RPC protocol exposed by the Trackmania servers that allows remote control of the game flow, settings and administration.
It works almost identical to the normal XML-RPC protocol but with a few extra features like simple request/response validation by introducing a handler value.
When you first connect to a GbxRemote server, it will send you a magic header value, identifying itself as GbxRemote server.
| Ref. Name | Data type | Position | Size | Description |
|---|---|---|---|---|
hSize |
uint | 0x0 |
32-bit | The size of the header packet |
header |
bytes | 0x4 |
hSize bytes |
The magic header value. |
The header value should equal GBXRemote 2 on the latest GbxRemote implementation.
Note: uint values at these packets are encoded Litte Endian.
The handler value is a 32-bit Unsigned Integer that is used for validating responses against requests in a multi-threaded configuration or to detect errors.
When a client first connects to a GbxRemote server, the handler is reset to the value 0x80000000.
For each request the client makes, this value is increased by one and sent along with the data packet:
If the handler value reaches the maximum value, that is
0xFFFFFFFF, it should wrap around back to0x80000000.
| Ref. Name | Data type | Position | Size | Description |
|---|---|---|---|---|
pSize |
uint | 0x0 |
32-bit | The size of the packet. |
handler |
uint | 0x4 |
32-bit | Handler value. |
methodCall |
bytes | 0x8 |
pSize bytes |
Method call in XML. |
The format of this is the same as in the normal XML-RPC protocol.
The method call response can be identified by having the method name called in the response. Whereas a callback doesn’t.
| Ref. Name | Data type | Position | Size | Description |
|---|---|---|---|---|
size |
uint | 0x0 |
32-bit | Size of the response data. |
handler |
uint | 0x4 |
32-bit | Handler value. |
data |
bytes | 0x8 |
size bytes |
XML response. |
The response is, again the same as the normal XML-RPC protocol.
The first step to a successful connection to a GbxRemote server is to authenticate the client using a username and a password.
The username and password is defined in the dedicated config file, under the
<level>tags.
The startup flow for connecting to GbxRemote with authentication is as following:
Rather than having the client call methods on the server, the server can also call methods on the client and recieve responses from it. This is called a callback.
To enable callbacks, the client must send the call EnableCallbacks(true).
In order to recieve callbacks, the client should have an asynchronous recieve loop to look out for callbacks. For some callback methods, the client may be required to send back a response. It is then important to keep track of the handler value to ensure correct communication with the server.
The packet format is the same as the method call.
A fault is the XML-RPC way of handling errors. If an error occured, for example a bad method call, the server will respond with a XML response that explains the error. For GbxRemote, it is the same as in the normal XML-RPC protocol.
This is a low level step-by-step code showing how you can connect and authenticate with the GbxRemote server. For better and more high-level examples, check out How to connect.
Example:
import socket
HOST = 'localhost'
PORT = 5000
# connect
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((socket.gethostbyname(HOST), PORT))
# recieve and validate header the gbx header
## header length, 4 bytes int, little endian
data = s.recv(4)
headerLength = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24)
## header data, bytes of length n=headerLength
data = s.recv(headerLength)
header = data.decode() # decode bytes to string
## the header should equal "GBXRemote 2"
if header != "GBXRemote 2":
print('Invalid header.')
exit(0)
print("Valid header!")
# we need to authenticate
print('Authenticating ...')
handler = 0x80000000 # handler starts at this value
## build the packet
### pack handler, 4 bytes int, little endian
handlerBytes = bytes([
handler & 0xFF,
(handler >> 8) & 0xFF,
(handler >> 16) & 0xFF,
(handler >> 24) & 0xFF])
### pack method call, xml structure
data = b'<?xml version="1.0"?>'
data += b'<methodCall>'
data += b'<methodName>Authenticate</methodName>' # call the Authenticate method
data += b'<params>'
data += b'<param><value>SuperAdmin</value></param>' # username: SuperAdmin
data += b'<param><value>SuperAdmin</value></param>' # password: SuperAdmin
data += b'</params>'
data += b'</methodCall>'
### compile packet
packet = bytes()
#### packet length
packetLen = len(data)
packet += bytes([
packetLen & 0xFF,
(packetLen >> 8) & 0xFF,
(packetLen >> 16) & 0xFF,
(packetLen >> 24) & 0xFF
])
#### handler
packet += handlerBytes
#### data
packet += data
### Send the authentication call
print('sending packet: ' + str(packet))
s.send(packet)
## recieve authentication response
### recieve response header, 8 bytes
header = s.recv(8)
#### unpack response size, 4 bytes int, little endian
size = header[0] | (header[1] << 8) | (header[2] << 16) | (header[3] << 24)
#### unpack handler, 4 bytes int, big endian
responseHandler = header[4] | (header[5] << 8) | (header[6] << 16) | (header[7] << 24)
##### the response must have the same handler value
if responseHandler != handler:
print('Response handler does not match!')
exit(0)
#### recieve response data
response = s.recv(size)
print('got response: ' + str(response))