The semi-official server list can be browsed on build & shoot.
The data can be parsed in the JSON
format from http://services.buildandshoot.com/serverlist.json.
It does not directly contain IP addresses. They have to be decoded by following the algorithm first.
AoS uses urls like "aos://1124169524:43887" to encode the ip and port of a server. This is a guide on how to parse them.
- strip protocol prefix (e.g. "aos://" -> "1124169524:43887")
- search for colon (e.g. ":" -> "1124169524" ":" "43887")
- if none found
- port is default port (32887)
- if found
- split at colon (e.g. ":" -> "1124169524" ":" "43887")
- port is second part
- continue with first part
- search for dot (e.g. "." -> none found in "1124169524")
- if at least one (or better, exactly four) found (e.g. "192.168.1.1")
- URL is IP
- if none found
- URL is an integer address, parse it as explained here
The integer address is a 32-bit integer that represents the IP address. The IP address is encoded in big-endian. It can be decoded by using the following algorithm:
- to get the four IP parts:
- apply bitwise AND with 255 (e.g., "1124169524" & 255 -> 52)
- right-shift the URL by 8 bits and then apply bitwise AND with 255 (e.g., ("1124169524" >> 8) & 255 -> 119)
- right-shift the URL by 16 bits and then apply bitwise AND with 255 (e.g., ("1124169524" >> 16) & 255 -> 1)
- right-shift the URL by 24 bits and apply bitwise AND with 255 (e.g., ("1124169524" >> 24) & 255 -> 67)
- the four IP parts are 52, 119, 1, 67
- join the parts with dots (e.g. "52.119.1.67")
Example JS code:
let ip = ${url & 255}.${(url >> 8) & 255}.${(url >> 16) & 255}.${(url >> 24) & 255}
The Ace of Spades (AoS) protocol documentation.
This page is based on protocol documentation from piqueserver and heavily modified. The modifications are made to reflect the protocol supported by rspades. In general, rspades aims to support the same protocol as OpenSpades does.
The protocol documented here supports:
- AoS version 0.75, the last fully released version of Ace of Spades Classic
- AoS version 0.76, the last publically available version of Ace of Spades Classic
- Extra packets (EP): Extra packets that were added by the community and are supported by OpenSpades and BetterSpades
The 0.76 protocol is based on 0.75, but with some changes. The main features of 0.76 are:
- Flexible player count
- Map caching
Additionally, we provide documentation for the following protocols, which are currently not supported by rspades:
- powerthirst.md Powerthirst Edition protocol, which is used by the Powerthirst Edition mod for AoS 0.75
- extension.md: The extension negotiation protocol, that allows for custom packets to be sent between the server and the client
The AoS protocol is based on UDP and builds on top of the ENet library for networking.
Generally, all fields in the Protocol are Low Endian if not specified.
The following shorthands for data types are used in this document:
Shorthand | details |
SByte | 8 bits of arbitrary data. Usually accompanied by a note |
UByte | Unisgned 8 bit number |
SInt | 32bit singed integer (little endian) |
UInt | 32bit unsigned integer (little endian) |
Float | 32bit IEEE float (little endian) |
CP437 String | String encoded with CP437. Usually fixed-length. |
When you connect, you must send a version number as the initial data.
Following that a client needs to send an Existing Player data packet to send its own name, team, etc.
If the client does not send an Existing Player packet first, but any other packet, then the server closes the connection and seems to temporarily ban the player.
Number | AoS version |
3 | 0.75 |
4 | 0.76 |
Send this magic number as part of the enet_host_connect(ENetHost, ENetAddress, channels, int)
function
Whenever the connection is closed by the server, there is a reason supplied to the client in the event's data (event.data
).
Number | Reason |
1 | Banned |
2 | IP connection limit exceded? |
3 | Wrong protocol version |
4 | Server full |
10 | Kicked |
In AoS the up-down axis is Z and it is inverted. This means 63 is the water level and 0 is the highest point on a map.
All packets start with an unsigned SByte to specify their type, followed by the data for that type of packet. The size given for each packet below includes this SByte.
Client <-> Server
This packet is used to set the player's position.
| |
Packet ID: | 0 |
Total Size: | 13 Bytes |
Field Name | Field Type | Example | Notes |
x | Float | 0 | |
y | Float | 0 | |
z | Float | 0 | |
This packet is used to set the player's orientation.
| |
Packet ID | 1 |
Total Size: | 13 Bytes |
Field Name | Field Type | Example | Notes |
x | Float | 0 | |
y | Float | 0 | |
z | Float | 0 | |
Updates position and orientation of all players. Always sends data for 32 players, with empty slots being all 0 (position: [0,0,0], orientation: [0,0,0]).
| |
Packet ID | 2 |
Total Size: | 13 Bytes |
Field Name | Field Type | Example | Notes |
players positions and orientations | Array[32] of Player Position Data | | See below table for data |
Field Name | Field Type | Example | Notes |
x position | Float | 0 | 0 for non-players |
y position | Float | 0 | 0 for non-players |
z position | Float | 0 | 0 for non-players |
x orientation | Float | 0 | 0 for non-players |
y orientation | Float | 0 | 0 for non-players |
z orientation | Float | 0 | 0 for non-players |
Updates position and orientation of all players. Unlike 0.75, this only sends information to the necessary players.
| |
Packet ID | 2 |
Total Size: | 1 + 25n Bytes |
Field Name | Field Type | Example | Notes |
players positions and orientations | Array[] of Player Position Data, variable size | | See below table for data |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
x position | Float | 0 | |
y position | Float | 0 | |
z position | Float | 0 | |
x orientation | Float | 0 | |
y orientation | Float | 0 | |
z orientation | Float | 0 | |
Contains the key states of a player, packed into a SByte.
| |
Packet ID | 3 |
Total Size: | 3 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
key states | UByte | 0 | Each bit in the SByte represents a key, as defined in the table below. |
Placement | Key |
1 | up |
2 | down |
3 | left |
4 | right |
5 | jump |
6 | crouch |
7 | sneak |
8 | sprint |
Contains the weapon input state.
| |
Packet ID | 4 |
Total Size: | 3 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
weapon input | UByte | 0 | The lowest bit represents the primary fire, the second lowest represents the secondary fire. |
Sent by the client when a hit is registered. The server should verify that this is possible to prevent abuse (such as hitting without shooting, facing the wrong way, etc).
| |
Packet ID | 5 |
Total Size: | 3 Bytes |
Field Name | Field Type | Example | Notes |
player ID hit | UByte | 0 | |
hit type | UByte | 0 | See values in table below |
Value | Type |
0 | torso |
1 | head |
2 | arms |
3 | legs |
4 | melee |
Sent to the client when hurt.
| |
Packet ID | 5 |
Total Size: | 15 Bytes |
Field Name | Field Type | Example | Notes |
HP | UByte | 0 | |
type | UByte | 0 | 0 = fall, 1 = weapon |
source x position | Float | 0 | |
source y position | Float | 0 | |
source z position | Float | 0 | |
Spawns a grenade with the given information.
| |
Packet ID | 6 |
Total Size: | 30 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
fuse length | Float | 0 | |
x position | Float | 0 | |
y position | Float | 0 | |
z position | Float | 0 | |
x velocity | Float | 0 | |
y velocity | Float | 0 | |
z velocity | Float | 0 | |
Sets a player's currently equipped tool/weapon.
| |
Packet ID | 7 |
Total Size: | 3 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
tool | UByte | 0 | Tool values are listed below |
Value | Type |
0 | spade |
1 | block |
2 | gun |
3 | grenade |
Set the color of a player's held block.
| |
Packet ID | 8 |
Total Size: | 5 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
blue | UByte | 0 | |
green | UByte | 0 | |
red | UByte | 0 | |
Set player's team, weapon, etc.
| |
Packet ID | 9 |
Total Size: | depends |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
team | SByte | 0 | |
weapon | UByte | 0 | |
held item | UByte | 0 | |
kills | UInt | 0 | |
blue | UByte | 0 | |
green | UByte | 0 | |
red | UByte | 0 | |
name | CP437 String | Deuce | |
Like the Existing Player packet, but with less information.
| |
Packet ID | 10 |
Total Size: | 4 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
team | SByte | 0 | |
weapon | UByte | 0 | |
This packet is used to move various game objects like tents, intels and even grenades. When moving grenades in TC mode the voxlap client has a bug that changes grenades' models to small tents.
| |
Packet ID | 11 |
Total Size: | 15 Bytes |
Field Name | Field Type | Example | Notes |
object id | UByte | 0 | |
team | UByte | 0 | 2 = neutral |
x position | Float | 0 | |
y position | Float | 0 | |
z position | Float | 0 | |
Send on respawn of a player.
| |
Packet ID | 12 |
Total Size: | depends |
Field Name | Field Type | Example | Notes |
player id | UByte | 0 | |
weapon | UByte | 0 | |
team | SByte | 0 | |
x position | Float | 0 | |
y position | Float | 0 | |
z position | Float | 0 | |
name | CP437 String | Deuce | |
Sent when a block is placed/destroyed.
| |
Packet ID | 13 |
Total Size: | 15 Bytes |
Field Name | Field Type | Example | Notes |
player id | UByte | 0 | |
action type | UByte | 0 | See table below |
x position | SInt | 0 | |
y position | SInt | 0 | |
z position | SInt | 0 | |
Value | Type | Notes |
0 | build | places a block with the player's selected color |
1 | bullet and spade(left button) destroy | |
2 | spade(right button) destroy | destroys 3 blocks, one above and below additionally |
3 | grenade destroy | destroys all blocks within an 3x3x3 area |
Create a line of blocks between 2 points. The block color is defined by the Set Color
packet.
| |
Packet ID | 14 |
Total Size: | 26 Bytes |
Field Name | Field Type | Example | Notes |
player id | UByte | 0 | |
start x position | SInt | 0 | |
start y position | SInt | 0 | |
start z position | SInt | 0 | |
end x position | SInt | 0 | |
end y position | SInt | 0 | |
end z position | SInt | 0 | |
Server-->Client
Indicates that the map transfer is complete. Also informs the client of numerous game parameters. Be aware that CTF State or TC State may be appended to the packet after the game mode id portion.
| |
Packet ID | 15 |
Total Size: | 52 Bytes |
Field Name | Field Type | Example | Notes |
player id | UByte | 0 | |
fog (blue) | UByte | 0 | |
fog (green) | UByte | 0 | |
fog (red) | UByte | 0 | |
team 1 color (blue) | UByte | 0 | |
team 1 color (green) | UByte | 0 | |
team 1 color (red) | UByte | 0 | |
team 2 color (blue) | UByte | 0 | |
team 2 color (green) | UByte | 0 | |
team 2 color (red) | UByte | 0 | |
team name 1 | CP437 String | Blue | Always 10 characters long |
team name 2 | CP437 String | Green | Always 10 characters long |
gamemode id | UByte | 0 | 0 for CTF, 1 for TC |
Depending on the game mode id, the following data may be appended to the packet:
Total Size: 52 Bytes
Field Name | Field Type | Example | Notes |
team 1 score | UByte | 0 | |
team 2 score | UByte | 0 | |
capture limit | UByte | 0 | |
intel flags | UByte | 0 | see below |
team 1 intel location | Location Data | 0 | see below |
team 2 intel location | Location Data | 0 | see below |
team 1 base x position | Float | 0 | |
team 1 base y position | Float | 0 | |
team 1 base z position | Float | 0 | |
team 2 base x position | Float | 0 | |
team 2 base y position | Float | 0 | |
team 2 base z position | Float | 0 | |
The intel flags field bits state which team holds which intel. The first bit indicates the state of the intel by team 0 (blue), the second bit indicates the state of the intel by team 1 (green). If the bit is set, the team holds the intel, otherwise the intel is on the ground.
The intel location data is 12 Bytes long. If the intel is being held, the first byte is a UByte with the id of the holding player, then the rest are padding. If the intel is on the ground (not being held), the data will hold three Floats with its x, y, and z position.
Intel State | Field Name | Field Type |
Held | holding player id | UByte |
| padding | 11 Bytes |
Dropped | intel x position | Float |
| intel y position | Float |
| intel z position | Float |
Field Name | Field Type | Example | Notes |
territory count | UByte | 16 | Maximum is 16 otherwise the client will crash with 'Invalid memory access' |
Array[] of territory data | Float, Float, Float, UByte | | See table below |
Field Name | Field Type | Example |
x position | Float | 0 |
y position | Float | 0 |
z position | Float | 0 |
team id | UByte | 0 |
The values for team id are as follows:
0 = blue,
1 = green,
2 = neutral
Notify the client of a player's death.
| |
Packet ID | 16 |
Total Size: | 5 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 12 | Player that died |
killer ID | UByte | 8 | |
kill type | UByte | 0 | See table below |
respawn time | UByte | 1 | Seconds until respawn |
If sent any value higher than 6 in Ace of Spades (voxlap), the game will display the kill message as "Derpy Kill Message".
Value | Type |
0 | WEAPON (body, limbs) |
1 | HEADSHOT |
2 | MELEE (spade) |
3 | GRENADE |
4 | FALL |
5 | TEAM_CHANGE |
6 | CLASS_CHANGE |
Reasonable limits must be placed on the length and frequency of chat messages.
| |
Packet ID | 17 |
Total Size: | . Bytes |
Field Name | Field Type | Example | Notes |
player id | UByte | 0 | |
Chat Type | UByte | 0 | See table below |
Chat Message | CP437 String | "join /squad 1" | |
Value | Type | voxlap default color |
0 | CHAT_ALL | white |
1 | CHAT_TEAM | team color, black for spectator |
2 | CHAT_SYSTEM | red |
Sent when a client connects, or a map is advanced for already existing connections. Should be the first packet received when a client connects.
| |
Packet ID | 18 |
Total Size: | 5 Bytes |
Field Name | Field Type | Example | Notes |
Map size | Uint32 | 4567 | |
Sent when a client connects, or a map is advanced for already existing connections. Should be the first packet received when a client connects.
| |
Packet ID | 18 |
Total Size: | 9+ Bytes |
Field Name | Field Type | Example | Notes |
Map size | Uint32 | 283839 | |
CRC32 | Uint32 | 0x4c7ebe43 | |
Map name | CP437 String | "pinpoint2" | |
Sent just after Map Start, repeatedly until the entire map is sent. Should always be the next sequence of packets after a Map Start packet.
| |
Packet ID | 19 |
Total Size: | depends |
Sent when a player disconnects.
| |
Packet ID | 20 |
Total Size: | 2 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
Sent when a player captures a Command Post in Territory Control mode. Captures have effects on the client.
| |
Packet ID | 21 |
Total Size: | 5 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
entity ID | UByte | 0 | The ID of the CP being captured |
winning | UByte | 0 | (or losing) |
state | UByte | 0 | team id |
Display the TC progress bar.
| |
Packet ID | 22 |
Total Size: | 8 Bytes |
Field Name | Field Type | Example | Notes |
entity ID | UByte | 0 | The ID of the tent entity |
capturing team ID | UByte | 1 | |
rate | SByte | 2 | Used by the client for interpolation, one per team member capturing (minus enemy team members). One rate unit is 5% of progress per second. |
progress | Float | 0.5 | In range [0,1] |
Sent when a player captures the intel, which is determined by the server. Winning captures have effects on the client.
| |
Packet ID | 23 |
Total Size: | 3 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
winning | UByte | 0 | Was the winning capture |
Sent when a player collects the intel, which is determined by the server.
| |
Packet ID | 24 |
Total Size: | 2 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | |
Sent when a player dropped the intel. This will update the intel position on the client.
| |
Packet ID | 25 |
Total Size: | 14 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | ID of the player who dropped intel |
x position | Float | 32.0 | |
y position | Float | 32.0 | |
z position | Float | 32.0 | |
Id of the player who has been restocked.
| |
Packet ID | 26 |
Total Size: | 2 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | ID of the player who restocked |
Set the color of a player's fog.
|||
| ----------: | -------- |
| Packet ID | 27 |
| Total Size: | 5 Bytes |
Field Name | Field Type | Example | Notes |
fog color | UInt | 0h00fefefe | BGRA encoded |
Sent by the client when the player reloads their weapon and relayed to other clients after protocol logic is applied.
This has no effect on the animation but is used to trigger sound effects on the other clients.
| |
Packet ID | 28 |
Total Size: | 4 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | Player who reloaded |
clip ammo | UByte | 0 | |
reserve ammo | UByte | 0 | |
Sent by the client when the player changes teams. Is not relayed to all clients directly, but instead uses Kill Action then Create Player to inform other clients of the team change.
| |
Packet ID | 29 |
Total Size: | 3 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | Player who changed team |
Team ID | SByte | 0 | See values in table below |
Value | Type |
-1 | spectator |
0 | blue |
1 | green |
Sent by the client when the player changes weapon, and relayed to clients by the server after `filter_visibility`` logic is applied. Receiving clients will also be sent a preceding Kill Action to inform them the player has died both of which are sent as reliable packets.
| |
Packet ID | 30 |
Total Size: | 3 Bytes |
Field Name | Field Type | Example | Notes |
player ID | UByte | 0 | Player who's changed weapon |
Weapon ID | UByte | 0 | See values in table below |
Value | Type |
0 | rifle |
1 | smg |
2 | shotgun |
Client->Server
| |
Packet ID | 31 |
Total Size: | 2 Bytes |
Field Name | Field Type | Example | Notes |
Cached | UByte | 1 | 1 if cached, 0 otherwise |
Server->Client
Sent to the client for checking if client is compatible with version info (this isnt required to get version info). When sent, server waits for a Handshake Response with the challenge.
| |
Packet ID | 31 |
Total Size: | 5 Bytes |
Field Name | Field Type | Example | Notes |
Challenge | SInt | 42 | A number that should be sent back in handshake response |
Note that this packet has the same packet id as the
Client->Server
Send back the challenge number to the server, for validating the client (this isnt required to get version info).
| |
Packet ID | 32 |
Total Size: | 5 Bytes |
Field Name | Field Type | Example | Notes |
Challenge | SInt | 42 | Number sent to the client in Handshake Init |
Server->Client
Ask the client to send the client and operational system infos.
| |
Packet ID | 33 |
Total Size: | 1 SByte |
Client->Server
Send the client and operational system infos.
| |
Packet ID | 34 |
Total Size: | (varies) Bytes |
Field Name | Field Type | Example | Notes |
client_identifier | SByte | o | Number representing an ASCII character |
version_major | SByte | 1 | Current client major version |
version_minor | SByte | 0 | Current client minor version |
version_revision | SByte | 0 | Current client revision version |
os_info | CP437 String | Windows 10 | Operational System informations |
Each extension is given an unique id that is decided when it is first
registered. We differentiate between two types of packets:
Those that:
Type | Purpose | Extension id range |
HAS_PACKETS | introduce new packets to the protocol | 0-191 |
PACKETLESS | don't use and need any packets | 192-255 |
Each extension is given one legacy packet id equal to 64+extension_id
.
For PACKETLESS
extensions this would mean that their packet ids are out
of spec >255
, thus they don't have any.
An example for a packetless extension would be OpenSpades' UnicodeExt.
Each extension packet will contain 1 additional byte in its data, which is a
subpacket id, used to have multiple packets available for each extension. This
is always the case, even if an extension only needs 1 packet in total.
General extension packet structure:
Field name | Type | Notes |
Packet id | UByte | 64-255 |
Sub packet id | UByte | 0-255 |
Data | UByte[] | extension data |
- Packet ID: 60
- Total size:
2+2*n
Field name | Field type | Notes |
length | UByte | length+1 entries will follow |
entries | ExtInfoEntry | see below |
ExtInfoEntry
Field name | Field type | Notes |
ext. ID | UByte | see #Overview |
version | UByte | starting at 0 |
The server MUST send an ExtInfo
packet on connect. The client can store
the list of extensions and MUST reply with an ExtInfo
packet that lists
the extensions it supports.
It SHOULD omit any extensions that the server does not support from it's
reply.
The Powerthirst Edition is a mod of the version 0.75 that adds a few new features, such as:
- Longer user names
- 64 players
- Support for AngelScript scripts
This version adds 4 new packets, extends 2 packets, and duplicate maps 1 packet over another.
The World Update packet has up to 64 fields now instead of 32.
Server->Client
Sent when a client connects, or a map is advanced for already existing connections.
Should be the first packet received when a client connects.
The version must exist and be >= 1 (and <= the client's Powerthirst proto version or otherwise the client will refuse to connect) to enable certain Powerthirst features such as long-name support.
Field Name | Field Type | Example | Notes |
Map size | Uint32 | 4567 | |
PT version | UInt32 | 4 | |
Server->Client
This is just a remapping of the Map Chunk packet to 2 packets back to stop vanilla clients from connecting.
| |
Packet ID | 17 |
Total Size: | 9 bytes |
Server->Client
| |
Packet ID | 31 |
Total Size: | (varies) bytes |
Field Name | Field Type | Example | Notes |
Script size | Uint32 | 4567 | |
Module name | CP437 String | | |
Server->Client
This is just a remapping of the Map_Chunk packet to 2 packets back to stop vanilla clients from connecting.
| |
Packet ID | 32 |
Total Size: | (varies) bytes |
Server->Client
Once this is sent, the script is loaded.
| |
Packet ID | 33 |
Total Size: | (varies) bytes |
Field Name | Field Type | Example | Notes |
Module name | CP437 String | | |
Server->Client
| |
Packet ID | 34 |
Total Size: | (varies) bytes |
Field Name | Field Type | Example | Notes |
Function name | 0-terminated CP437 String | void main() | Must be an AngelScript prototype, not just the name itself |
Parameters | See below | | |
Start from after the 0-byte in the Function name string. Then, loop through these IDs:
- 0:
ASP_TERM
: End of parameter list.
- 1:
ASP_INT
: Read a 32-bit little-endian int. AngelScript type: "int"
- 2:
ASP_FLOAT
: Read a 32-bit little-endian single-precision float. AngelScript type: "float"
- 3:
ASP_PSTRING
: Read an 8-bit uint, then read that many bytes as a string (do NOT add in a terminating NUL). AngelScript type: "const string &in"