Introduction

Serverlist

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 URLs

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.

Parsing Algorithm

  1. strip protocol prefix (e.g. "aos://" -> "1124169524:43887")
  2. search for colon (e.g. ":" -> "1124169524" ":" "43887")
    1. if none found
      1. port is default port (32887)
    2. if found
      1. split at colon (e.g. ":" -> "1124169524" ":" "43887")
      2. port is second part
      3. continue with first part
  3. search for dot (e.g. "." -> none found in "1124169524")
    1. if at least one (or better, exactly four) found (e.g. "192.168.1.1")
      1. URL is IP
    2. if none found
      1. URL is an integer address, parse it as explained here

Decoding integer addresses

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:

  1. to get the four IP parts:
    1. apply bitwise AND with 255 (e.g., "1124169524" & 255 -> 52)
    2. right-shift the URL by 8 bits and then apply bitwise AND with 255 (e.g., ("1124169524" >> 8) & 255 -> 119)
    3. right-shift the URL by 16 bits and then apply bitwise AND with 255 (e.g., ("1124169524" >> 16) & 255 -> 1)
    4. right-shift the URL by 24 bits and apply bitwise AND with 255 (e.g., ("1124169524" >> 24) & 255 -> 67)
  2. the four IP parts are 52, 119, 1, 67
  3. 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}

Protocol

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

Primer

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:

Shorthanddetails
SByte8 bits of arbitrary data. Usually accompanied by a note
UByteUnisgned 8 bit number
SInt32bit singed integer (little endian)
UInt32bit unsigned integer (little endian)
Float32bit IEEE float (little endian)
CP437 StringString encoded with CP437. Usually fixed-length.

Connection

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.

NumberAoS version
30.75
40.76

Send this magic number as part of the enet_host_connect(ENetHost, ENetAddress, channels, int) function

Disconnect Reasons

Whenever the connection is closed by the server, there is a reason supplied to the client in the event's data (event.data).

NumberReason
1Banned
2IP connection limit exceded?
3Wrong protocol version
4Server full
10Kicked

About Coordinates

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.

Packets

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.

Table of Contents

Position Data

Client <-> Server

This packet is used to set the player's position.

Packet ID:0
Total Size:13 Bytes

Fields

Field NameField TypeExampleNotes
xFloat0
yFloat0
zFloat0

Orientation Data

This packet is used to set the player's orientation.

Packet ID1
Total Size:13 Bytes

Fields

Field NameField TypeExampleNotes
xFloat0
yFloat0
zFloat0

World Update (version 0.75)

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 ID2
Total Size:13 Bytes

Fields

Field NameField TypeExampleNotes
players positions and orientationsArray[32] of Player Position DataSee below table for data

'Player Position Data'

Total Size:769 Bytes

Fields

Field NameField TypeExampleNotes
x positionFloat00 for non-players
y positionFloat00 for non-players
z positionFloat00 for non-players
x orientationFloat00 for non-players
y orientationFloat00 for non-players
z orientationFloat00 for non-players

World Update (version 0.76)

Updates position and orientation of all players. Unlike 0.75, this only sends information to the necessary players.

Packet ID2
Total Size:1 + 25n Bytes

Fields

Field NameField TypeExampleNotes
players positions and orientationsArray[] of Player Position Data, variable sizeSee below table for data

'Player Position Data'

Total Size:24 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0
x positionFloat0
y positionFloat0
z positionFloat0
x orientationFloat0
y orientationFloat0
z orientationFloat0

Input Data

Contains the key states of a player, packed into a SByte.

Packet ID3
Total Size:3 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0
key statesUByte0Each bit in the SByte represents a key, as defined in the table below.

Key States

PlacementKey
1up
2down
3left
4right
5jump
6crouch
7sneak
8sprint

Weapon Input

Contains the weapon input state.

Packet ID4
Total Size:3 Bytes
Field NameField TypeExampleNotes
player IDUByte0
weapon inputUByte0The lowest bit represents the primary fire, the second lowest represents the secondary fire.

Hit Packet

Client-to-Server

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 ID5
Total Size:3 Bytes

Fields

Field NameField TypeExampleNotes
player ID hitUByte0
hit typeUByte0See values in table below

Hit Types

ValueType
0torso
1head
2arms
3legs
4melee

Set HP

Server-to-Client

Sent to the client when hurt.

Packet ID5
Total Size:15 Bytes

Fields

Field NameField TypeExampleNotes
HPUByte0
typeUByte00 = fall, 1 = weapon
source x positionFloat0
source y positionFloat0
source z positionFloat0

Grenade Packet

Spawns a grenade with the given information.

Packet ID6
Total Size:30 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0
fuse lengthFloat0
x positionFloat0
y positionFloat0
z positionFloat0
x velocityFloat0
y velocityFloat0
z velocityFloat0

Set Tool

Sets a player's currently equipped tool/weapon.

Packet ID7
Total Size:3 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0
toolUByte0Tool values are listed below

Tools

ValueType
0spade
1block
2gun
3grenade

Set Color

Set the color of a player's held block.

Packet ID8
Total Size:5 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0
blueUByte0
greenUByte0
redUByte0

Existing Player

Set player's team, weapon, etc.

Packet ID9
Total Size:depends

Fields

Field NameField TypeExampleNotes
player IDUByte0
teamSByte0
weaponUByte0
held itemUByte0
killsUInt0
blueUByte0
greenUByte0
redUByte0
nameCP437 StringDeuce

Short Player Data

Like the Existing Player packet, but with less information.

Packet ID10
Total Size:4 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0
teamSByte0
weaponUByte0

Move Object

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 ID11
Total Size:15 Bytes

Fields

Field NameField TypeExampleNotes
object idUByte0
teamUByte02 = neutral
x positionFloat0
y positionFloat0
z positionFloat0

Create Player

Send on respawn of a player.

Packet ID12
Total Size:depends

Fields

Field NameField TypeExampleNotes
player idUByte0
weaponUByte0
teamSByte0
x positionFloat0
y positionFloat0
z positionFloat0
nameCP437 StringDeuce

Block Action

Sent when a block is placed/destroyed.

Packet ID13
Total Size:15 Bytes

Fields

Field NameField TypeExampleNotes
player idUByte0
action typeUByte0See table below
x positionSInt0
y positionSInt0
z positionSInt0

Fields

ValueTypeNotes
0buildplaces a block with the player's selected color
1bullet and spade(left button) destroy
2spade(right button) destroydestroys 3 blocks, one above and below additionally
3grenade destroydestroys all blocks within an 3x3x3 area

Block Line

Create a line of blocks between 2 points. The block color is defined by the Set Color packet.

Packet ID14
Total Size:26 Bytes

Fields

Field NameField TypeExampleNotes
player idUByte0
start x positionSInt0
start y positionSInt0
start z positionSInt0
end x positionSInt0
end y positionSInt0
end z positionSInt0

State Data

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 ID15
Total Size:52 Bytes

Fields

Field NameField TypeExampleNotes
player idUByte0
fog (blue)UByte0
fog (green)UByte0
fog (red)UByte0
team 1 color (blue)UByte0
team 1 color (green)UByte0
team 1 color (red)UByte0
team 2 color (blue)UByte0
team 2 color (green)UByte0
team 2 color (red)UByte0
team name 1CP437 StringBlueAlways 10 characters long
team name 2CP437 StringGreenAlways 10 characters long
gamemode idUByte00 for CTF, 1 for TC

Depending on the game mode id, the following data may be appended to the packet:

CTF State

Total Size: 52 Bytes

Field NameField TypeExampleNotes
team 1 scoreUByte0
team 2 scoreUByte0
capture limitUByte0
intel flagsUByte0see below
team 1 intel locationLocation Data0see below
team 2 intel locationLocation Data0see below
team 1 base x positionFloat0
team 1 base y positionFloat0
team 1 base z positionFloat0
team 2 base x positionFloat0
team 2 base y positionFloat0
team 2 base z positionFloat0

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.

Intel

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 StateField NameField Type
Heldholding player idUByte
padding11 Bytes
Droppedintel x positionFloat
intel y positionFloat
intel z positionFloat

TC State

Field NameField TypeExampleNotes
territory countUByte16Maximum is 16 otherwise the client will crash with 'Invalid memory access'
Array[] of territory dataFloat, Float, Float, UByteSee table below
Territory Data
Field NameField TypeExample
x positionFloat0
y positionFloat0
z positionFloat0
team idUByte0

The values for team id are as follows: 0 = blue, 1 = green, 2 = neutral

Kill Action

Server->Client

Notify the client of a player's death.

Packet ID16
Total Size:5 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte12Player that died
killer IDUByte8
kill typeUByte0See table below
respawn timeUByte1Seconds until respawn

Kill Types

If sent any value higher than 6 in Ace of Spades (voxlap), the game will display the kill message as "Derpy Kill Message".

ValueType
0WEAPON (body, limbs)
1HEADSHOT
2MELEE (spade)
3GRENADE
4FALL
5TEAM_CHANGE
6CLASS_CHANGE

Chat Message

Two-way

Reasonable limits must be placed on the length and frequency of chat messages.

Packet ID17
Total Size:. Bytes

Fields

Field NameField TypeExampleNotes
player idUByte0
Chat TypeUByte0See table below
Chat MessageCP437 String"join /squad 1"

Chat Type

ValueTypevoxlap default color
0CHAT_ALLwhite
1CHAT_TEAMteam color, black for spectator
2CHAT_SYSTEMred

Map Start (version 0.75)

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.

Packet ID18
Total Size:5 Bytes

Fields

Field NameField TypeExampleNotes
Map sizeUint324567

Map Start (version 0.76)

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.

Packet ID18
Total Size:9+ Bytes

Fields

Field NameField TypeExampleNotes
Map sizeUint32283839
CRC32Uint320x4c7ebe43
Map nameCP437 String"pinpoint2"

Map Chunk

Server->Client

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 ID19
Total Size:depends

Fields

Field NameField TypeExampleNotes
Map DataUByte0DEFLATE/zlib encoded AOS map data

Player Left

Server->Protocol

Sent when a player disconnects.

Packet ID20
Total Size:2 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0

Territory Capture

Server->Protocol

Sent when a player captures a Command Post in Territory Control mode. Captures have effects on the client.

Packet ID21
Total Size:5 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0
entity IDUByte0The ID of the CP being captured
winningUByte0(or losing)
stateUByte0team id

Progress Bar

Server->Client

Display the TC progress bar.

Packet ID22
Total Size:8 Bytes

Fields

Field NameField TypeExampleNotes
entity IDUByte0The ID of the tent entity
capturing team IDUByte1
rateSByte2Used by the client for interpolation, one per team member capturing (minus enemy team members). One rate unit is 5% of progress per second.
progressFloat0.5In range [0,1]

Intel Capture

Server->Protocol

Sent when a player captures the intel, which is determined by the server. Winning captures have effects on the client.

Packet ID23
Total Size:3 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0
winningUByte0Was the winning capture

Intel Pickup

Server->Protocol

Sent when a player collects the intel, which is determined by the server.

Packet ID24
Total Size:2 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0

Intel Drop

Server->Protocol

Sent when a player dropped the intel. This will update the intel position on the client.

Packet ID25
Total Size:14 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0ID of the player who dropped intel
x positionFloat32.0
y positionFloat32.0
z positionFloat32.0

Restock

Server->Protocol

Id of the player who has been restocked.

Packet ID26
Total Size:2 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0ID of the player who restocked

Fog color

Server->Client

Set the color of a player's fog. ||| | ----------: | -------- | | Packet ID | 27 | | Total Size: | 5 Bytes |

Fields

Field NameField TypeExampleNotes
fog colorUInt0h00fefefeBGRA encoded

Weapon Reload

Client-->Server->Protocol

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 ID28
Total Size:4 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0Player who reloaded
clip ammoUByte0
reserve ammoUByte0

Change Team

Client-->Server-->Protocol-->Kill Action & Create Player

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 ID29
Total Size:3 Bytes
Fields
Field NameField TypeExampleNotes
player IDUByte0Player who changed team
Team IDSByte0See values in table below
Team IDs
ValueType
-1spectator
0blue
1green

Change Weapon

Client-->Server-->Protocol-->Kill Action & Change Weapon

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 ID30
Total Size:3 Bytes

Fields

Field NameField TypeExampleNotes
player IDUByte0Player who's changed weapon
Weapon IDUByte0See values in table below
Weapon ID
ValueType
0rifle
1smg
2shotgun

Map Cached (version 0.76)

Client->Server

Packet ID31
Total Size:2 Bytes

Fields

Field NameField TypeExampleNotes
CachedUByte11 if cached, 0 otherwise

Version Handshake Init (EP)

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 ID31
Total Size:5 Bytes

Fields

Field NameField TypeExampleNotes
ChallengeSInt42A number that should be sent back in handshake response

Note that this packet has the same packet id as the

Version Handshake Response (EP)

Client->Server

Send back the challenge number to the server, for validating the client (this isnt required to get version info).

Packet ID32
Total Size:5 Bytes

Fields

Field NameField TypeExampleNotes
ChallengeSInt42Number sent to the client in Handshake Init

Version Get (EP)

Server->Client

Ask the client to send the client and operational system infos.

Packet ID33
Total Size:1 SByte

Version Response (EP)

Client->Server

Send the client and operational system infos.

Packet ID34
Total Size:(varies) Bytes

Fields

Field NameField TypeExampleNotes
client_identifierSByteoNumber representing an ASCII character
version_majorSByte1Current client major version
version_minorSByte0Current client minor version
version_revisionSByte0Current client revision version
os_infoCP437 StringWindows 10Operational System informations

Other Resources

Extension Negotiation

Overview

Each extension is given an unique id that is decided when it is first registered. We differentiate between two types of packets:

Those that:

TypePurposeExtension id range
HAS_PACKETSintroduce new packets to the protocol0-191
PACKETLESSdon't use and need any packets192-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 nameTypeNotes
Packet idUByte64-255
Sub packet idUByte0-255
DataUByte[]extension data

ExtInfo Packet

  • Packet ID: 60
  • Total size: 2+2*n
Field nameField typeNotes
lengthUBytelength+1 entries will follow
entriesExtInfoEntrysee below

ExtInfoEntry

Field nameField typeNotes
ext. IDUBytesee #Overview
versionUBytestarting at 0

Protocol Flow

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.

Powerthirst Edition

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.

Map StartColor

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.

Packet ID18

Fields

Field NameField TypeExampleNotes
Map sizeUint324567
PT versionUInt324

Map ChunkColor

Server->Client

This is just a remapping of the Map Chunk packet to 2 packets back to stop vanilla clients from connecting.

Packet ID17
Total Size:9 bytes

Fields

Field NameField TypeExampleNotes
Map DataUByte0DEFLATE/zlib encoded AOS map data

Script BeginColor

Server->Client

Packet ID31
Total Size:(varies) bytes

Fields

Field NameField TypeExampleNotes
Script sizeUint324567
Module nameCP437 String

Script ChunkColor

Server->Client

This is just a remapping of the Map_Chunk packet to 2 packets back to stop vanilla clients from connecting.

Packet ID32
Total Size:(varies) bytes

Fields

Field NameField TypeExampleNotes
Script DataUByte0DEFLATE/zlib encoded AngelScript source code

Script EndColor

Server->Client

Once this is sent, the script is loaded.

Packet ID33
Total Size:(varies) bytes

Fields

Field NameField TypeExampleNotes
Module nameCP437 String

Script CallColor

Server->Client

Packet ID34
Total Size:(varies) bytes

Fields

Field NameField TypeExampleNotes
Function name0-terminated CP437 Stringvoid main()Must be an AngelScript prototype, not just the name itself
ParametersSee below

Script Parameters

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"