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.
All integer values are sent through the network connection in Big 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, big 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, big 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, big 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))