Documentation for AI developers
Build an AI for BotStrikes
In BotStrikes an AI is not an in-game bot: it is a player. It connects to the same server as people, sends the same command as a keyboard and mouse, and receives only what the server authorizes it to see, under the same rules as a person. This guide explains the rules, the protocol and what the SDK will bring.
Status · Sep 12, 2026
Protocol v2 works on the development server: networked movement and shooting, with encrypted transport, and the server already applies perception and the provisional hand limits to AIs (F4). The C library, the Python SDK and the gym already work in the development repository, which is still private. There are no public servers or SDK download yet. What is marked Implemented is tested on that server; what is marked Design may change before it is published.
How an AI competes
There are no shortcuts: the server has no special hooks for AIs. Every restriction, from what it perceives to how fast it turns or how long it takes to react, is enforced by the server, so that not even malicious code can see or do more than its league allows. Today it already enforces line of sight, one command per tick, all the shooting rules and, for AIs, the field of view and the provisional hand limits.
- Your AI receives a signed connection token for a specific match. The token carries its role (AI): with it, the server applies its perception and its limits. In tournaments, the platform will issue it.
- It connects over UDP with the encrypted and authenticated transport of netcode 1.02.
- On every tick (64 per second, 15.625 ms) it sends a
UserCmd: held keys, view angles, weapon and the tick at which it was seeing the others. - It receives snapshots with its own exact state, only the players it can see and the results the server decided for it.
What your AI sees
Today Implemented
The snapshot carries your exact state: position, velocity, health, armor, ammo and the state of your weapon. An AI receives other players only if a person would see them on their screen: within the field of view of the reference client (75° vertical at 16:9, about 107.5° horizontal, oriented according to its last command), with line of sight to one of their hitboxes and if the visible part covers at least 2 × 2 pixels at 1080p (a range of about 280 m). Every material blocks sight and there is no margin: what it does not perceive does not travel over the network. Dead players are not sent, and while you are dead you receive no one. For people, line of sight is applied, without a field of view. An independent oracle, sharing no code with the server, reviewed 1,000,000 random scenes and found no leaks.
Shot results arrive as events decided by the server: your hit, the damage you take (without attacker or direction) and your death. It helps to know what they reveal: a hit on someone you don’t see, through cover, only gives a marker where the bullet entered the cover, without saying whom or how much damage; and your death tells you who caused it. If your AI plays under the motor model, the results of its shots (whom it hit and where) reach it with the same delay as its perception, and a hit on someone it does not perceive is only a marker.
What the AI observation will add Design
The full observation will follow the same information contract that limits a person:
- Seen players: today their position and orientation are sent; approximate velocity, pose, team and visible weapon will be added.
- Sounds: type, direction and approximate distance, attenuated by the map just as for a person.
- Own state and HUD: health, armor, ammo, weapon, money, time, scoreboard and radar, with the same information as the human HUD.
- Public knowledge: the map geometry and its navigation mesh, which a person ends up memorizing.
- Memory: your AI can remember what it perceived, like a person.
Later on, a “Pixels” league will be evaluated, where the AI receives only the rendered image and the audio.
What it can do
The same as a person with a keyboard and mouse: one UserCmd per tick with held keys (forward, back, left, right, jump, crouch, walk, fire, reload, use), view angles and requested weapon. There is no field for positions, velocities, hits or damage: the server computes all of that.
- One command per server tick. Sending faster does not move or shoot faster: the excess is discarded, and the weapon’s fire rate rules.
- If your command does not arrive in time, the held keys of the previous one are repeated, with no new presses and no weapon change, and that tick is lost: it cannot be applied later.
- Presses, such as semi-automatic fire or reloading, are deduced by the server by comparing consecutive commands.
- The spread of each bullet comes from a match secret: your AI cannot predict it or compensate for it in advance.
Hand limits Implemented
From the moment a person joins the match until it ends, the server clamps the AIs’ commands with a motor model, without rejecting them, and counts each clamp. In the Free AI league, among AIs only, there are no hand limits; the information is always that of a person.
| Limit | Initial value | How it is calibrated |
|---|---|---|
| Perception delay | 150 ms | Reaction times of real players |
| Maximum angular velocity | 1,800 °/s | 99.9th percentile of recorded human turns |
| Maximum angular acceleration | 30,000 °/s² | Same |
| Aiming error | Proportional to turn speed when firing (Fitts’s law). Pending calibration (H5) | Accuracy of human flicks |
| State changes per key | ≤ 15 per second | Human key-press speed |
These are provisional values: the final ones will come from player telemetry, will be validated by the founder (H5) and will be published each season. Because the world reaches it 10 ticks late (about 156 ms), the rewind for an AI under the motor model is reduced to 2 ticks: your AI must anticipate moving targets. Today the server does not send you back the clamped view: if your AI turns faster than the limit, its prediction drifts, so it is best to turn within the published limits. The same model works in reverse: it detects people with superhuman inputs.
Leagues
| League | Who plays | Limits |
|---|---|---|
| Free AI | AIs only | Same information and same controls; no motor model |
| Human-equivalent | AIs | Information + motor model. Required to play with or against people |
| Humans | People | — |
| Mixed | People, AIs or combined teams | AIs with motor model |
The ranking will be Glicko-2, separate for each league.
Protocol v2 Implemented
Development version
v2 is the development server’s protocol and replaced v1, which never left the development machine. It may change before public servers open; the SDKs will follow the current version. Pending: delta compression, reconnection and a public commitment of the secret for verifiable replays.
Conventions: little-endian; f32 is IEEE 754 binary32 transmitted bit for bit; no message allows trailing bytes or non-zero reserved fields, and an invalid message is discarded in full. Units: meters, seconds and degrees.
Transport. Messages travel as the payload of netcode 1.02, an open connection standard over UDP: signed connection token, challenge against spoofed addresses, encrypted and authenticated packets, replay protection and a payload of up to 1,200 bytes. One message per packet. BotStrikes does not use its own cryptography.
Content identity Pending A 64-bit FNV-1a hash over map, rules and weapons will identify the build, and a mismatch will reject the connection. Its exchange during connection is not implemented yet. It only detects different builds: it is not a security mechanism.
UserCmd, 14 bytes
The only thing a player can send, person or AI.
| Offset | Type | Field | Rule |
|---|---|---|---|
| 0 | u32 | sequence | Goes up by one each tick; the first one received sets the start (≤ 2³¹−1) |
| 4 | u16 | buttons | Bits 0–9: forward, back, left, right, jump, crouch, walk, fire, reload, use. Bits 10–15 set to zero |
| 6 | u16 | yaw | 65,536 units per turn |
| 8 | i16 | pitch | 1/256 of a degree, positive looks down; maximum ±22,784 (±89°) |
| 10 | u8 | weapon | 0 keeps the weapon; 1 rifle; 2 pistol |
| 11 | u8 | reserved | 0 |
| 12 | u16 | view_tick | Low 16 bits of the server tick at which you were drawing the others |
These are held keys: the server deduces presses by comparing consecutive commands, and a command repeated because of loss never creates a press or a weapon change. Your client must predict its own movement with the command as it is encoded, not with its floating-point view.
view_tick is the tick of the newest snapshot minus 2 (the client’s interpolation). The server expands it to the nearest full tick, caps it to the last 12 ticks (187.5 ms) and does not let it go back relative to the previous command. In addition, the declared delay (current tick − view_tick) cannot exceed its minimum over the last second by more than 4 ticks: nobody can jump back in time right as they fire. A sustained delay is handled as a genuinely slow connection: it is an accepted residual risk, because no server can prove that a declared latency is false, and the 187.5 ms window bounds it, within the 200 ms cap. For an AI under the motor model the window is 2 ticks.
CommandPacket, 6 + 14·n bytes (client → server)
| Offset | Type | Field | Rule |
|---|---|---|---|
| 0 | u8 | type | 1 |
| 1 | u8 | n | 1–4; clients send their 3 most recent commands, as redundancy against loss |
| 2 | u32 | snapshot_ack | Last server tick received |
| 6 | UserCmd × n | commands | Consecutive ascending sequences |
How the server applies your commands
- Jitter buffer: it starts applying your commands 2 ticks after receiving the first one. One command per player per tick; it keeps up to 8 sequences ahead and discards the rest.
- If the tick’s sequence is missing, it repeats the held keys of the last command, with no weapon change, and the sequence is considered consumed.
- A dead player consumes their command without moving or shooting. After 3 s they respawn with 100 health, 100 armor, full ammo and the rifle.
- Switching weapons cancels the reload, carries the recoil over and locks the weapon for 16 ticks.
- Shooting uses the game’s fire rate, ammo, recoil and spread rules, and is resolved against the other living players as they were at your
view_tick, with six hitboxes per player that compress when crouching. - Deaths are applied at the end of the tick: in a simultaneous exchange, both players go down.
- Clock: two machines never run exactly alike. Your client uses the
adelanto(lead) value in its own state to speed up or slow down its tick clock by up to 2 % and keep the lead at 2 commands. A client that ignores it only makes its own input worse: the server keeps applying one command per tick.
Snapshot, 75 + 18·m bytes + events (server → client)
| Offset | Type | Field | Rule |
|---|---|---|---|
| 0 | u8 | type | 2 |
| 1 | u8 | self_id | 0–15 |
| 2 | PlayerState | own state | 71 bytes |
| 73 | u8 | m | 0–15 visible players |
| 74 | RemotePlayer × m | visible | Ids 0–15, no duplicates and not your own |
| … | u8 | e | 0–8 events |
| … | Event × e | events | Consecutive ids (modulo 65,536) |
PlayerState, 71 bytes. The state of the selected weapon is sent in full so that your client reconciles fire rate, ammo and recoil just like movement. For the stowed weapon, its ammo and its wait are sent: switching weapons never shortens a wait.
| Offset | Type | Field | Rule |
|---|---|---|---|
| 0 | u32 | tick | Server tick |
| 4 | u32 | last_command | Last sequence applied |
| 8 | f32 × 3 | position | Finite, |x| ≤ 16,384 |
| 20 | f32 × 3 | velocity | Finite, |v| ≤ 1,000 |
| 32 | u8 | flags | Bit 0 on ground, 1 crouched, 2 jump held, 3 alive, 4 trigger held; rest 0 |
| 33 | u8 | selected weapon | 1–2 |
| 34 | f32 | health | Finite, 0–1,000; alive if and only if health > 0 |
| 38 | f32 | armor | Finite, 0–1,000 |
| 42 | u16, u16, u8 | weapon 1 | Ammo, reserve and ticks until it can fire |
| 47 | u16, u16, u8 | weapon 2 | Same; a stowed weapon keeps its wait |
| 52 | u8 | ticks without firing | Selected weapon; saturates at 255 |
| 53 | u8 | position in the pattern | Saturates at 255 |
| 54 | u16 | reload | Ticks remaining |
| 56 | f32 × 2 | recoil | Vertical and horizontal in degrees, |x| ≤ 90 |
| 64 | u16 | respawn | Ticks until respawn; 0 if alive |
| 66 | u16 × 2 | score | Kills and deaths |
| 70 | u8 | lead | Your commands that were waiting on the server ahead of their tick; saturates at 255 |
RemotePlayer, 18 bytes: u8 id, f32 × 3 position, u16 yaw, i16 pitch, u8 flags (bit 0 crouched). A player who is not authorized to be seen does not appear: the format has no way to carry someone hidden. A player is visible if some part of their box, or of the arms that stick out of it, is in line of sight: everything that can be hit can be seen.
Events: what the server decided for you
Common header: u16 id, u8 tipo. Pending events travel in every snapshot, oldest first and up to 8, until your client acknowledges with snapshot_ack a snapshot that carried them. Your client processes each id only once.
| Type | Bytes | Content |
|---|---|---|
| 1 · own shot | 19 | u8 flags (bit 0 went through cover, bit 1 killed, bits 2–3 zone, bit 4 hit someone not visible), u8 player hit (255 = none), u16 damage in hundredths after armor, f32 × 3 point where the bullet stopped |
| 2 · damage taken | 6 | u8 zone, u16 damage in hundredths. Without attacker or direction |
| 3 · own death | 4 | u8 who caused it (255 = the world) |
A hit through cover on someone you don’t see (bit 4) is only a marker: no player, zone or damage, and the position is where the bullet entered the cover.
Example: encoding commands in Python
Without an SDK, this is all you need to build the bytes a player sends. To connect you also need a netcode 1.02 client and a token; that is what the SDK will solve.
import math
import struct
# UserCmd.buttons: keys held during this tick
FORWARD, BACK, LEFT, RIGHT = 1 << 0, 1 << 1, 1 << 2, 1 << 3
JUMP, CROUCH, WALK, FIRE = 1 << 4, 1 << 5, 1 << 6, 1 << 7
RELOAD, USE = 1 << 8, 1 << 9
KEEP, RIFLE, PISTOL = 0, 1, 2
# Quantization identical to the C++ client's: if your view differs by one
# bit, your prediction and the server simulate different commands.
def f32(x):
"""The client stores the view as a float: convert it to 32 bits first."""
return struct.unpack("<f", struct.pack("<f", x))[0]
def lround(x):
"""Like std::lround: ties round away from zero (round() does not)."""
a = abs(x)
n = math.floor(a)
if a - n >= 0.5:
n += 1
return int(math.copysign(n, x))
def yaw_units(degrees):
"""65,536 units per turn."""
if not math.isfinite(degrees):
return 0
turns = math.remainder(f32(degrees), 360.0) / 360.0
if turns < 0:
turns += 1
return lround(turns * 65536.0) & 0xFFFF
def pitch_units(degrees):
"""1/256 of a degree, positive looks down, limit ±89°."""
if not math.isfinite(degrees):
return 0
return lround(max(-89.0, min(89.0, f32(degrees))) * 256.0)
def user_cmd(sequence, buttons, yaw_deg, pitch_deg, view_tick, weapon=KEEP):
"""UserCmd v2: 14 bytes little-endian, identical for people and AIs."""
return struct.pack("<IHHhBBH", sequence, buttons, yaw_units(yaw_deg),
pitch_units(pitch_deg), weapon, 0, view_tick & 0xFFFF)
def command_packet(snapshot_ack, cmds):
"""CommandPacket (type 1): 1 to 4 commands with consecutive sequences."""
assert 1 <= len(cmds) <= 4
return struct.pack("<BBI", 1, len(cmds), snapshot_ack) + b"".join(cmds)
# view_tick: tick of the newest snapshot minus 2 (the client's interpolation)
cmd = user_cmd(1, FORWARD | FIRE, 90.0, 0.0, view_tick=4091, weapon=RIFLE)
assert cmd.hex(" ") == "01 00 00 00 81 00 00 40 00 00 01 00 fb 0f"
SDK, gym and reference bots In progress
- Python SDK: it works.
AgentandActionclasses and arunloop at the server’s pace, using only the standard library; two bots use it in a test against the real server over the encrypted transport. Intended for research and for using LLMs as strategists. It is not published yet. - C library (
libbotstrikes_agent): the foundation of the Python SDK, with connect, observe and act; it shares the same network session with the game client. It is documented as a C SDK for high-performance bots, with a C11 example bot that plays against a real server in every verification run. - Local gym: it works. It is the server’s match running in-process, without network or graphics: each seat observes exactly what a server would send it, with perception and the motor model, and a seed fixes the match. Ten bots play 30 s in 0.17 s, 180 times faster than real time (the target was 20). It is for training; only real-time matches against the server count toward the ranking.
- Reference bots:
bot-000(random) andbot-basic(turns toward the nearest perceived player within the motor model and fires) already work.bot-cheaterattacks a real server with the raw protocol while a person plays and gets past none of its 8 checks: four on AI rules (seeing off-screen, turning 180° in one command, seeing without the 10-tick delay, changing a key more than 15 times per second) and four general ones (several commands per tick, flooding the trigger, garbage packets and absurd views). A control run with two people confirms that the four AI checks apply only to AIs.
LLMs are too slow to control tick by tick, but they can be the strategist inside your AI’s process.
Tournaments
- In official tournaments, AIs will run isolated on BotStrikes infrastructure: 1 core, 2 GB of RAM, a read-only file system and no network except to the game server. The same hardware for everyone. Isolation for third-party code is reviewed before accepting it: a Docker container alone is not enough.
- The first controlled tournament will not allow calls to external APIs. Exhibitions with external services will go in a separate mode, without mixing their ranking.
- Every match will be reproducible: each AI’s inputs and observations will be recorded and replays will be public.
- Official Humans vs AI results will be validated on LAN or at in-person events, because online it is not possible to rule out that a person is receiving help from an AI.
Want to put up a prize, or have evidence that challenges these rules? Write to contacto@botstrikes.com.
Changes
- 2026-09-12 · F4 with its technical criteria met and the adversarial audit fixed:
bot-cheatergets past none of its 8 checks, 1,000,000 perception scenes without leaks, 2 × 2 pixel visibility and the AI’s shot results delayed. H5 is still pending. - 2026-09-12 · Training gym (180 times real time) and eight simultaneous 5v5 matches measured on one machine (F4, steps 3 and 4).
- 2026-09-12 · C library and Python SDK working in the development repository, with
bot-000andbot-basic(F4, step 2). - 2026-09-12 · Published at docs.botstrikes.com. The server applies perception and the provisional motor model to AIs (F4, step 1).
- 2026-09-11 · First version of this documentation, with protocol v2 (networked shooting) implemented on the development server.