Skip to content

Overview

The Brass Monkey protocol allows playing games on one device (the host) and controlling them using a mobile phone (the controller).

In BM, every participant is called a Device. A Device is identified by three properties: a deviceType (i32), a deviceId (UTF string), and a deviceName (UTF string). See Device Types for more information on device types.

Registry Identity

To register with the server, every device must have a BMRegistryInfo object. This wraps the Device identity with networking and session information:

  • Device (nested object): Contains deviceType, deviceId, and deviceName.
  • BMAddress (nested object): Contains the device's private IP address, reliable (TCP) port, and unreliable (UDP) port.
  • appId (UTF string): A hardcoded application identifier.
  • slotId (i16): The slot the device occupies on the registry. Only relevant for games (hosts), where the slot ID is greater than 0.
  • currentPlayers (i16): The number of controllers currently connected to the game. Only set by games (hosts).
  • maxPlayers (i16): The maximum number of controllers that can connect to the game. Only set by games (hosts).

Roles

The protocol defines three roles, distinguished by deviceType:

  • Server (deviceType=7): The central broker for all other devices that connect to it via TCP. It maintains a list of active games, sends the games list to the controllers, relays packets between devices. The original Brass Monkey servers are no longer operational.

  • Game Host (deviceType=1, 3 or 5): A game running the BM SDK. It registers itself with the server, then waits for the deviceConnectRequested packet to start communicating with controllers. It can update the registry server with information about current players.

  • Controller (deviceType=2, 4 or 6): A phone app. It connects to the registry server to discover available games, then requests a connection to a specific game host and communicates with it directly.

See Device Types for additional information about roles.

Transport

Devices can communicate over two transports: TCP and UDP. Each device that accepts direct connections listens on two separate ports, both specified in its BMAddress object upon registration with the registry server:

  • Reliable port (TCP): Used for all data that must arrive in order, such as BMInvoke RPC calls, control scheme delivery via BMByteChunk, connection setup, and optionally sensor/touch data.
  • Unreliable port (UDP): Used for high-frequency data where low latency might matter more than guaranteed delivery, such as sensor and touch data.

The BMReliability enum determines which transport is used for each channel. See Reliability Modes for more information.

Framing

TCP is a byte stream with no inherent message boundaries. To delimit messages, every TCP packet is prefixed with a 4-byte little-endian length field indicating the size of the payload that follows.

UDP datagrams have inherent boundaries, so no length prefix is used. The datagram itself is the message.

Flash Limitation

Flash games can only use TCP (the Socket class does not support UDP). Flash hosts ignore the unreliable port entirely and force all traffic to be reliable via setReliabilityForTouch(1,1). As a result, all communication with a Flash host, including high-frequency touch and sensor data, happens over TCP.

Version Handshake

The first packet between any two BM devices. Both sides send a 12-byte reliable TCP message:

  • size (i32): Always 8, indicating that 8 bytes follow.
  • currentVersion (i32): The sender's protocol version.
  • minVersion (i32): The minimum protocol version the sender will accept.

Versions are encoded as (major << 24) | (minor << 16) | build. For example, the latest BM SDK reports currentVersion as 1.7.0 and minVersion as 0.9.0.

Each side checks whether its own version satisfies the other's minimum requirement. If not, the connection is closed.

Registry Connection

All devices connect to the registry server over a single TCP connection. This connection is used exclusively for BMInvoke RPC communication (registration, game list request, relay). The registry server never uses UDP.

Serialization

All data is encoded in little-endian byte order. BM uses a custom binary serialization format.

Object envelope

Every serializable object is wrapped in a 5-byte envelope:

Offset Type Value Description
0 UTF "@" Object marker (i16 length 1 + byte 0x40)
3 i16 varies Class ID

The class ID identifies the object and dictates how the remaining bytes are deserialized. See Class ID Registry for the complete list.

Strings

Strings are encoded as length-prefixed UTF-8:

Offset Type Description
0 i16 Byte length of the UTF-8 data
2 bytes UTF-8 encoded string

Packet Structure

Almost all communication is wrapped in BMPacket (class ID 0 or 256). After the object envelope, a BMPacket contains the following fields in order:

Field Type Description
channel i32 Channel identifier. See Channel Types.
sequence i32 Per-channel sequence number
timestamp f64 Sender timestamp in milliseconds
rtt f64 Round-trip time estimate
packetType i32 See Packet Types.
deviceType i32 Sender's device type
deviceId UTF Sender's device ID
deviceName UTF Sender's device name
hasMessage bool (u8) Whether a nested message object follows
message object Nested payload (only if hasMessage is true)

The message field, when present, is itself a serialized object with its own envelope and class ID.

Communication Model

BM uses two patterns for communication:

  • RPC via BMInvoke: Structured method calls with pre-determined named methods and typed parameters. Used for registry operations, game session setup, sensor configuration, and button input. Always sent on the Message channel (3) with reliable transport.

  • Typed input objects: Sensor and positional data (touch, acceleration, gyroscope, orientation, d-pad) are sent as dedicated object types on their own channels. Some of these can use unreliable transport depending on the configuration set by the host device. See Channel Types and Reliability Modes for more information.

Connection Flow

At a high level, a typical session follows these steps:

  1. The game host connects to the registry server via TCP and calls registry.register with its BMRegistryInfo.
  2. A controller connects to the registry server via TCP, registers and calls registry.list to discover available games.
  3. The controller sends a registry.relay containing a deviceConnectRequested invoke targeting the game host.
  4. The registry server forwards the relay to the game host.
  5. The game host sends an AckPacket directly to the controller establishing the direct connection and calls registry.update with the server to update currentPlayers.
  6. The game host delivers the BMApplicationScheme XML to the controller via BMByteChunk chunks.
  7. The controller collects the chunks and renders the control scheme, and begins sending input data.

See Connection Flows for a more detailed explanation of the connection flow.