Guia do SDK vigente
Construa uma IA para o BotStrikes
Status ·
BotStrikes 0.2.0 · protocol 15 · Agent API 14 · bs_observation: 61 · bs_action: 11.
O SDK e o ambiente de treinamento funcionam no repositório privado. Este guia documenta a interface atual para quem tem acesso ao repositório ou a um kit de testes. Não há download público do SDK nem servidor público de jogo verificados.
Como começar
Uma IA usa o transporte criptografado e as entradas públicas do jogo. Precisa de um convite de IA emitido pelo servidor, uma biblioteca da mesma versão e o mesmo mapa e conteúdo. Os exemplos partem da raiz do repositório, com build/check já compilado por tools/check.sh.
A primeira conexão
Inicie o servidor local em um terminal. As caixas dos locais habilitam o objetivo de bomba. Em outro terminal, execute o exemplo com o token emitido. Não reutilize um token após desconectar: solicite um reserva.
build/check/bs_server --listen 127.0.0.1:27149 --players 0 --ai-players 1 \
--tokens build/tutorial-tokens --seconds 60 --map build/check/content/cinta.bsm \
--mode teams --rounds 8 \
--bomb-site-a -36.9,0,12.6,-26.9,3,22.6 --bomb-site-b 23.6,0,11.5,33.6,3,21.5
PYTHONPATH=sdk/python BOTSTRIKES_AGENT_LIB=build/check/libbotstrikes_agent.so \
python3 sdk/python/examples/first_bot.py --token build/tutorial-tokens/ai-1.token \
--map build/check/content/cinta.bsm --seconds 10
O resultado esperado é Joined observations maior que zero e saída correta. O exemplo anda e gira; não demonstra navegação nem jogo do objetivo. Agent administra a conexão; o objeto de política devolve Action; run recebe pares de agente e política.
"""A small, executable SDK tutorial. Run from the repository with PYTHONPATH=sdk/python."""
import sys
from pathlib import Path
from botstrikes import Action, Agent, run
from botstrikes.cli import SDKError, diagnostic, message, parser_for, valid_duration
class FirstBot:
def __init__(self):
self.observations = 0
def act(self, observation):
action = Action()
if observation.joined:
self.observations += 1
if observation.joined and observation.alive:
action.forward = 1.0
action.yaw = observation.yaw + 1.0
return action
def main(argv=None):
parser, language, argv = parser_for(argv, 'first_bot')
parser.add_argument('--token', required=True, type=Path, help=message(language, 'token'))
parser.add_argument('--map', required=True, type=Path, help=message(language, 'map'))
parser.add_argument('--seconds', type=float, default=10.0, help=message(language, 'seconds'))
args = parser.parse_args(argv)
try:
valid_duration(args.seconds)
token = args.token.read_bytes()
bot = FirstBot()
with Agent(map_path=args.map) as agent:
agent.connect(token)
run([(agent, bot)], seconds=args.seconds)
print(message(language, 'joined', count=bot.observations))
if not bot.observations:
raise SDKError('no_join')
return 0
except (OSError, ValueError, RuntimeError) as error:
print(diagnostic(language, error), file=sys.stderr)
return 1
if __name__ == '__main__':
raise SystemExit(main())
Observação
O servidor autoriza as informações e o SDK apresenta a observação. Posição, velocidade e visão próprias incluem previsão; inimigos e resultados vêm do estado autorizado. Os contadores delimitam os arrays válidos. Campos de compatibilidade do rig são infraestrutura e não fazem parte desta lista de percepção.
| Campos | Significado |
|---|---|
tick | Tick mais recente recebido do servidor. |
joined, alive, grounded, crouched, reloading | Indicadores de entrada na partida, vida, contato com o chão, agachamento e recarga. |
seat | Assento do próprio agente na partida. |
position, velocity | Posição e velocidade próprias previstas; metros e metros por segundo. |
yaw, pitch | Visão própria em graus, com limitação do modelo motor e previsão dos comandos em trânsito; pitch positivo olha para baixo. |
health, armor | Vida e armadura próprias. |
weapon, ammo, reserve | Arma selecionada, munição no carregador e reserva. weapon: 1 rifle, 2 pistola, 3 faca. |
kills, deaths, respawn_ticks | Eliminações, mortes e ticks até reaparecer. Não há reaparecimento durante uma rodada em andamento. |
money | Dinheiro próprio; contabilização implementada, compras pendentes. Não informa o dinheiro de outro assento. |
visible_count, visible | Quantidade e lista de jogadores que o servidor autoriza o agente a perceber. Inimigos ocultos não são incluídos. Python: players(). |
event_count, events | Quantidade e lista de resultados desde a observação anterior; consumidos uma vez. Python: results(). |
tracer_count, tracers | Quantidade e lista de traçantes percebidos desde a observação anterior. Python: streaks(). |
team, appearance, friendly_fire, animated | Equipe, aparência, fogo amigo e animação. A aparência não identifica se o jogador é humano ou IA. |
supply_position, supply_radius | Centro e raio da zona de reabastecimento da própria equipe; zero no modo todos contra todos. |
round_phase | 0 sem rodadas, 1 aquecimento, 2 congelamento, 3 em jogo, 4 fim da rodada, 5 fim da partida. |
round_number, rounds_to_win, round_ticks_left | Número da rodada, rodadas necessárias para vencer e ticks restantes na fase atual. |
score_own, score_other, alive_own, alive_other, attacking | Placares e jogadores vivos das duas equipes, primeiro a própria; attacking indica que a própria equipe ataca nesta metade. |
spectating, spectate_position, spectate_yaw, spectate_pitch | Assento do companheiro observado ou −1, posição e visão dessa câmera. Quando morto, fire solicita outro companheiro vivo; nunca um inimigo. |
teammate_count, teammates | Companheiros vivos fora de vista: assento e posição, nunca inimigos. Vazio enquanto morto. Python: mates(). |
bomb_phase | 0 sem objetivo, 1 carregada, 2 caída, 3 plantada, 4 desarmada, 5 explodida. |
bomb_site, bomb_carrier, bomb_progress, bomb_fuse_ticks_left, bomb_position | Local, portador, progresso próprio, pavio e posição autorizada. Só a equipe do portador recebe sua identidade; a posição nunca é enviada enquanto carregada. Uma bomba plantada é pública. |
bomb_site_count, bomb_sites | Quantidade e caixas públicas dos locais de plantio. Python: sites(); cada caixa tem contains() e centre(). |
roster_count, roster | Lista pública em ordem de assento; active identifica assentos ocupados. Python: scoreboard(). |
gunshot_count, gunshots | Disparos ouvidos desde a observação anterior; não identificam quem atirou. Python: shots_heard(). |
chat_count, chat | Mensagens de chat desde a observação anterior; remetente, alcance e texto autorizados pelo servidor. Python: messages(). |
Ações
Uma ação mantém teclas e solicita uma visão e uma arma. Não permite escolher posição, vida, dano ou inimigos. O servidor decide movimento, combate e percepção. O chat é enviado com agent.say(text), fora de Action.
| Campos | Significado |
|---|---|
forward, right | Eixos de movimento: negativo, zero ou positivo. |
yaw, pitch | Visão desejada em graus; pitch positivo olha para baixo. O servidor aplica os limites. |
jump, crouch, walk, fire, reload | Teclas mantidas de salto, agachamento, caminhada, disparo e recarga. |
use | Manter pressionado para plantar ou desarmar quando as regras permitirem. |
weapon | 0 mantém a arma; 1 rifle, 2 pistola, 3 faca. |
Equidade e comunicação
Quando o modelo motor humano se aplica, o servidor impõe atraso mínimo de percepção de 150 ms, velocidade angular máxima de 1800 graus por segundo, aceleração máxima de 30000 graus por segundo ao quadrado e até 15 mudanças por tecla por segundo. Os valores são provisórios e H5 está pendente. O erro de mira de Fitts não está implementado; isolamento e prazo de comandos continuam pendentes. Esses limites não demonstram equivalência humana.
O chat permite uma sequência de 3 mensagens e depois 1 por segundo, com até 120 bytes UTF-8 por mensagem. Durante uma rodada em andamento, chega apenas à mesma equipe e a jogadores com o mesmo estado de vivo ou morto. O servidor define remetente e audiência. As IAs leem e escrevem; falta a entrada de texto do jogador. O jogo ainda não tem voz; canais externos não chegam automaticamente à IA.
Comando de rede
UserCmd ocupa 14 bytes e mantém seu formato no protocolo vigente. Este exemplo mostra a codificação; não implementa sozinho o transporte nem um decodificador de snapshots. Para jogar, use o SDK atual. As tabelas históricas de snapshot v2 foram retiradas deste guia porque não descreviam o servidor atual.
import math
import struct
def f32(value):
return struct.unpack("<f", struct.pack("<f", value))[0]
def lround(value):
magnitude = abs(value)
integer = math.floor(magnitude)
if magnitude - integer >= 0.5:
integer += 1
return int(math.copysign(integer, value))
def user_cmd(sequence, buttons, yaw, pitch, weapon, view_tick, view_fraction=0):
if buttons & ~0x03ff or not 0 <= weapon <= 3 or not 0 <= view_fraction <= 255:
raise ValueError("invalid command fields")
yaw, pitch = f32(yaw), f32(pitch)
turn = math.remainder(yaw, 360.0) / 360.0 if math.isfinite(yaw) else 0.0
if turn < 0:
turn += 1
yaw_bits = lround(turn * 65536.0) & 0xffff
pitch_bits = lround(max(-89.0, min(89.0, pitch)) * 256.0) if math.isfinite(pitch) else 0
return struct.pack("<IHHhBBH", sequence, buttons, yaw_bits,
pitch_bits, weapon, view_fraction, view_tick & 0xffff)
cmd = user_cmd(1, (1 << 0) | (1 << 7), 90.0, 0.0, 1, 4091)
assert cmd.hex(" ") == "01 00 00 00 81 00 00 40 00 00 01 00 fb 0f"
Versões e limites
A versão do protocolo faz parte da identidade do transporte: atualize cliente, servidor e SDK juntos e emita novos convites quando ela mudar. A API do agente tem sua própria versão. O cliente gráfico diagnostica convites incompatíveis ou vencidos; o SDK pode informar timeout. A validade do token é configurável. Reconexão, compras, voz, isolamento e validação humana não se tornam recursos disponíveis apenas por aparecerem em um plano.
Referência completa do protocolo 15
Revisão: .
Transporte e convenções
Referência completa do payload atual, consolidada do código; não exige reconstruir v1…v14. O repositório e o SDK continuam privados: documentar o protocolo não comprova download nem servidor públicos. Documentos anteriores preservam o histórico das mudanças.
Simulação e snapshots a 64 Hz. Transporte netcode criptografado: transport_protocol_id = 0x4253545200000000 | 15; token de conexão de 2048 bytes; payload máximo de 1200 bytes. O envelope de transporte e o handshake pertencem ao netcode, não às tabelas abaixo. Uma versão incompatível não conclui a conexão: recompilar cliente, servidor e SDK e emitir tokens novos.
Todos os inteiros são little-endian; inteiros com sinal usam complemento de dois; f é IEEE 754 binary32. Notação das tabelas: B=u8, b=i8, H=u16, h=i16, I=u32, Q=u64, f=f32; um prefixo numérico repete o tipo. Não há preenchimento nem cópias de structs de memória. Os deslocamentos começam em zero em cada bloco. Os decodificadores rejeitam truncamento e bytes finais excedentes.
Comandos e confirmação
O pacote é CommandHeader seguido de count registros UserCmd: 6 + 14×count bytes. O cliente repete seus três comandos mais recentes (48 bytes); o formato permite até quatro. As sequências devem ser consecutivas, inclusive ao passar de 0xffffffff para 0. O servidor aplica no máximo um comando por tick; uma duplicata não cria novos pressionamentos. O adiantamento inicial é de 2 ticks.
A consecutividade módulo 2^32 acima descreve o decodificador do pacote, não permissão para reiniciar a sequência do servidor. CommandQueue rejeita sequence=0 e um primeiro comando acima de 0x7fffffff; seu fluxo não dá a volta. Mantém uma janela de 8 comandos futuros, não 8 comandos por tick. Descarta duplicatas e entradas adiantadas demais; após esgotar uma janela sem entrada aplicável, pode ressincronizar sem reproduzir comandos omitidos.
A quantização recebe ângulos f32: yaw é reduzido módulo 360 e arredondado para 65536 unidades por volta; pitch é limitado a ±89° e multiplicado por 256. Empates são arredondados para longe de zero; uma visão não finita vira zero. O servidor reconstrói view_tick perto do tick atual e limita a compensação de latência; o campo não permite escolher posições, dano nem resultados.
CommandHeader · Cabeçalho de comandos · 6 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | type | B | 1 | Sempre 1. |
| 1 | count | B | 1 | 1…4 comandos consecutivos, do mais antigo ao mais recente. |
| 2 | snapshot_ack | I | 4 | Último tick de snapshot recebido. |
UserCmd · Entrada mantida · 14 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | sequence | I | 4 | Sequência do comando, módulo 2^32. |
| 4 | buttons | H | 2 | Bits 0…9: forward, back, left, right, jump, crouch, walk, fire, reload, use; os demais são zero. |
| 6 | yaw | H | 2 | 65536 unidades por volta; positivo gira à direita. |
| 8 | pitch | h | 2 | 1/256 de grau; positivo olha para baixo; intervalo −22784…22784 (±89°). |
| 10 | weapon | B | 1 | 0 mantém a arma; 1 rifle, 2 pistola, 3 faca. |
| 11 | view_fraction | B | 1 | Fração após view_tick, em 1/256 de tick. Não é um byte reservado. |
| 12 | view_tick | H | 2 | 16 bits baixos do tick desenhado para os outros jogadores. |
Ordem completa do snapshot
Ordem obrigatória: SnapshotHeader → PlayerState → MotorState se flags bit 5 → Rig se identity bit 3 → Supply se equipe diferente de 0 → Spectate se identity bit 5 → count u8 + RemotePlayer[count] → count u8 + eventos[count] → count u8 + Tracer[count] → count u8 + Gunshot[count] → Round → Bomb e suas caixas se sides bit 4 → RosterEntry[16] → count u8 + Teammate[count] se self_and_mates bit 7. Os quatro contadores vazios e o elenco nunca são omitidos.
Mínimo: 2 + 74 + 4 + 10 + 48 = 138 bytes. Máximos do formato: 16 assentos, 15 remotos visíveis, 8 eventos, 8 traçadoras, 12 disparos ouvidos, 2 locais e 15 companheiros ocultos. O servidor do jogo atende até 10 assentos. Visíveis e companheiros ocultos são conjuntos distintos, sem duplicatas nem o assento próprio.
SnapshotHeader · Cabeçalho de snapshot · 2 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | type | B | 1 | Sempre 2. |
| 1 | self_and_mates | B | 1 | Bits 0…3: assento próprio; bit 7: bloco de companheiros ocultos presente; bits 4…6: zero. |
Estado próprio e extensões
Posições próprias, de suprimento e de espectador devem ser finitas e estar em ±16384 m por eixo; velocidade em ±1000 m/s. Vida e armadura: 0…1000 finitos; alive equivale a health > 0. Um jogador vivo tem respawn=0. O recuo é finito e está em ±90°. Equipes válidas: 0, 1 e 2.
Com arma 3, weapon_timer é melee_remaining, não recarga: deve ser ≤ knife_cooldown, e esse cooldown ≤128. Outras armas não têm impacto de faca pendente. Motor mantém as restrições de botões e pitch e os limites finitos de giro da tabela. Rig só aparece se animated e sua assinatura não pode ser zero.
Spectate só pode aparecer para um jogador morto, com assento diferente do próprio. A visão e a percepção são as do companheiro autorizado; os eventos privados continuam pertencendo ao dono do snapshot. Sem o bloco, o estado de espectador é vazio.
PlayerState · Estado próprio fixo · 74 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | tick | I | 4 | Tick do servidor. |
| 4 | last_command | I | 4 | Última sequência aplicada pelo servidor. |
| 8 | position | 3f | 12 | x, y, z em metros. |
| 20 | velocity | 3f | 12 | x, y, z em metros por segundo. |
| 32 | flags | B | 1 | Bits 0…5: grounded, crouched, jump_held, alive, trigger_held, motor. Bits 6…7: equipe (0 nenhuma, 1 alpha, 2 bravo). |
| 33 | identity | B | 1 | Bits 0…1: arma 1…3; bit 2: aparência (0 humana, 1 máquina); bit 3: animated; bit 4: friendly_fire; bit 5: spectating; bits 6…7: zero. |
| 34 | health | f | 4 | Vida própria, 0…1000. |
| 38 | armor | f | 4 | Armadura própria, 0…1000. |
| 42 | rifle_ammo | H | 2 | Munição no carregador do rifle. |
| 44 | rifle_reserve | H | 2 | Reserva do rifle. |
| 46 | rifle_cooldown | B | 1 | Ticks até poder disparar o rifle. |
| 47 | pistol_ammo | H | 2 | Munição no carregador da pistola. |
| 49 | pistol_reserve | H | 2 | Reserva da pistola. |
| 51 | pistol_cooldown | B | 1 | Ticks até poder disparar a pistola. |
| 52 | knife_cooldown | B | 1 | Ticks até outro ataque de faca; a faca não envia munição. |
| 53 | idle | B | 1 | Contador de repouso da arma, saturado em 255. |
| 54 | pattern | B | 1 | Índice do padrão da arma ativa, saturado em 255. |
| 55 | weapon_timer | H | 2 | Ticks de recarga; com faca, ticks até o impacto pendente. |
| 57 | recoil_pitch | f | 4 | Recuo vertical em graus, ±90. |
| 61 | recoil_yaw | f | 4 | Recuo horizontal em graus, ±90. |
| 65 | respawn | H | 2 | Ticks até reaparecer; zero enquanto vivo. |
| 67 | kills | H | 2 | Abates próprios. |
| 69 | deaths | H | 2 | Mortes próprias. |
| 71 | money | H | 2 | Dinheiro próprio; compras não estão implementadas. |
| 73 | lead | B | 1 | Comandos deste jogador na fila à frente do tick; saturado. |
MotorState · Extensão motora · 24 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | applied_buttons | H | 2 | Bits 0…9: forward, back, left, right, jump, crouch, walk, fire, reload, use; os demais são zero. |
| 2 | applied_yaw | H | 2 | 65536 unidades por volta; positivo gira à direita. |
| 4 | applied_pitch | h | 2 | 1/256 de grau; positivo olha para baixo; intervalo −22784…22784 (±89°). |
| 6 | turn_yaw | f | 4 | Giro horizontal anterior, graus por tick, ±180. |
| 10 | turn_pitch | f | 4 | Giro vertical anterior, graus por tick, ±178. |
| 14 | keys | 10B | 10 | Crédito de mudança para cada botão, em 1/64 de mudança. |
Rig · Assinatura do rig · 8 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | rig_hash | Q | 8 | Assinatura não nula; palavra baixa primeiro. Não autentica conteúdo. |
Supply · Zona de suprimento · 16 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | position | 3f | 12 | x, y, z em metros. |
| 12 | radius | f | 4 | Raio em metros, 0…16384. |
Spectate · Visão do companheiro observado · 17 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | seat | B | 1 | Assento 0–15; não é um identificador persistente. |
| 1 | eye | 3f | 12 | x, y, z em metros. |
| 13 | yaw | H | 2 | 65536 unidades por volta; positivo gira à direita. |
| 15 | pitch | h | 2 | 1/256 de grau; positivo olha para baixo; intervalo −22784…22784 (±89°). |
Corpos visíveis e animação
motion: 0 idle, 1 walk, 2 jog, 3 sprint, 4 crouch_idle, 5 crouch_walk, 6 airborne, 7 shoot, 8 reload, 9 death, 10 jump_start, 11 jump_land; 12…15 inválidos. Posição finita em ±16384 m por eixo. O servidor decide quais corpos podem ser percebidos; ausência não revela um inimigo oculto.
Origem da mistura: códigos 0…6 correspondem a motion 0…6; código 7 significa jump_start (motion 10). Passo 0: sem mistura e origem 0 obrigatória. Passos 1…3: pesos 1/4, 2/4, 3/4. Passos 4…7: pesos 1/8, 3/8, 5/8, 7/8; somente pistola ou faca passando de walk/jog/sprint para idle. Toda mistura exige origem e destino diferentes, ambos agachados (crouch_idle/crouch_walk) ou ambos não agachados.
reload_phase não pode ser diferente de zero com faca nem motion=death. attack_age não pode ser diferente de zero com death, shoot, reload ou reload_phase ativa. São restrições de decodificação; o codificador não substitui a validação de entradas arbitrárias.
O protocolo 15 mantém motion_phase como fase do clipe destino e adiciona stop_phase como fase explícita do clipe fonte durante a frenagem secundária. O servidor congela a última fase pública de marcha; em troca simultânea de arma sem pose anterior compatível, calcula a fonte pela distância percorrida com a arma nova. A mistura e o byte fonte vêm juntos do snapshot mais próximo; não exigem histórico de visibilidade do cliente. O destino idle mantém seu relógio normal. Tokens/clientes de protocolos anteriores são incompatíveis. Revisão de pose 13; Agent API 14 sem mudanças de campos ou ABI.
RemotePlayer · Jogador visível · 22 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | id | B | 1 | Assento 0–15; não é um identificador persistente. |
| 1 | position | 3f | 12 | x, y, z em metros. |
| 13 | yaw | H | 2 | 65536 unidades por volta; positivo gira à direita. |
| 15 | pitch | b | 1 | Graus inteiros, −89…89; diferente do pitch de UserCmd. |
| 16 | flags | B | 1 | Bit 0: crouched; bits 1…4: motion; bits 5…6: equipe 0…2; bit 7: aparência 0…1. |
| 17 | motion_phase | B | 1 | Fração do clipe, em passos de 1/256. |
| 18 | pose | B | 1 | Bits 0…1: arma 0…2; bits 2…4: origem da mistura; bits 5…7: passo da mistura. |
| 19 | reload_phase | B | 1 | 0 inativa; 1…255 progresso normalizado. |
| 20 | attack_age | B | 1 | 0 inativo; caso contrário 1 + idade em ticks. |
| 21 | stop_phase | B | 1 | Fase fonte congelada da frenagem secundária, 0–255 em 1/256 do clipe de origem. Ativa somente com pistola/faca, idle, fonte walk/jog/sprint e fade_step 1–7; fora desse contexto deve ser zero. Zero é uma fase válida dentro do contexto. |
Eventos, traçadoras e disparos ouvidos
Cada evento é EventHeader + conteúdo: shot 19 B, hurt 6 B, death 4 B, kill 6 B no total. Tipos 1…3 são privados; kill é público, sem posição. Eventos se repetem do mais antigo ao mais recente até snapshot_ack confirmar um snapshot que os continha; processar cada id uma única vez. Os ids são consecutivos módulo 65536.
zone: 0 cabeça, 1 tronco, 2 membro. O zero de um erro ou de uma morte pelo mundo é um valor vazio canônico, não um acerto na cabeça.
Em shot, other diferente de 255 exige outro assento válido e unseen=0. Com other=255, damage e zone são zero; killed só pode ser 1 se unseen=1. Um acerto oculto conserva uma posição autorizada de entrada na cobertura, não a do inimigo. Em kill, vítima 0…15; atacante diferente da vítima ou 255. Se o mundo mata, zone, through e weapon são zero.
Tracer contém o segmento selecionado pelo filtro do servidor, com extremos finitos em ±16384 m, sem identidade do atirador. Gunshot contém posição autorizada arredondada e arma, sem identidade. As coordenadas são codificadas dividindo por 0,25 m, arredondando empates para longe de zero e limitando a ±32767; −32768 é rejeitado na leitura. Traçadoras e disparos ouvidos são efêmeros, sem a confirmação dos eventos.
As traçadoras são recortadas por amostras; não se garante visibilidade contínua de todo o segmento.
EventHeader · Cabeçalho de evento · 3 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | id | H | 2 | Sequência consecutiva por jogador, módulo 65536. |
| 2 | kind | B | 1 | 1 shot, 2 hurt, 3 death, 4 kill. |
Shot · Conteúdo do disparo · 16 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | flags | B | 1 | Bit 0: through; bit 1: killed; bits 2…3: zone 0…2; bit 4: unseen; bit 5: zero; bits 6…7: arma 0…2. |
| 1 | other | B | 1 | Assento atingido ou 255; nunca o próprio. |
| 2 | damage | H | 2 | Dano em centésimos de ponto. |
| 4 | position | 3f | 12 | Impacto autorizado; em acerto oculto, entrada na cobertura, não a posição do inimigo. |
Hurt · Conteúdo de dano próprio · 3 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | zone | B | 1 | 0…2; não contém atacante nem posição. |
| 1 | damage | H | 2 | Dano em centésimos de ponto. |
Death · Conteúdo de morte própria · 1 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | other | B | 1 | Assento do atacante ou 255 para o mundo; nunca o próprio. |
Kill · Conteúdo de abate público · 3 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | killer | B | 1 | Assento do atacante ou 255 para o mundo. |
| 1 | victim | B | 1 | Assento 0–15; não é um identificador persistente. |
| 2 | flags | B | 1 | Bits 0…1: zone 0…2; bit 2: through; bits 3…7: arma 0…2. |
Tracer · Segmento visível · 24 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | from | 3f | 12 | x, y, z em metros. |
| 12 | to | 3f | 12 | x, y, z em metros. |
Gunshot · Disparo ouvido · 7 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | position | 3h | 6 | x, y, z em unidades de 0,25 m; −32768 inválido. |
| 6 | weapon | B | 1 | 0 rifle, 1 pistola, 2 faca; 3 inválido. |
Rodada, bomba, elenco e companheiros
Com phase=none, Round deve estar inteiramente em zero. Nas outras fases rounds_to_win é positivo, os placares não o superam e attackers é 1 ou 2. number é zero exatamente em warmup. winner é diferente de zero exatamente em over ou ended.
Bomb só é enviado se sides bit 4 estiver ativo: 19 bytes mais 13 por local. site é diferente de zero exatamente em planted, defused ou exploded; fuse_left só pode ser diferente de zero em planted. carrier só pode indicar um assento em carried; caso contrário é 255. Em carried a posição é zero; nos outros estados deve ser finita. Caixas ordenadas A, B; min ≤ max por eixo e coordenadas decodificadas em ±3276 m. A codificação multiplica cada f32 por 10 e arredonda empates para longe de zero.
O servidor decide a audiência de cada campo da bomba: estado plantado, local, temporizador, posição plantada e caixas são públicos; portador e posição caída são revelados somente à equipe atacante; progress é próprio. A posição de um jogador oculto não é enviada pela bomba.
O elenco sempre contém 16 linhas, 48 bytes, na ordem dos assentos. Um assento inativo tem a linha inteira em zero. O papel vem do token autenticado; equipe e aparência não o alteram. O bloco opcional de companheiros tem count=1…15 e linhas Teammate; um bloco vazio anunciado é inválido. Só um jogador vivo com equipe o recebe. Cada linha deve corresponder no elenco a um companheiro ativo e vivo da mesma equipe, com posição finita em ±16384 m, sem duplicatas nem jogadores já visíveis. Não contém vida, arma nem mira.
Round · Estado da rodada · 10 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | phase | B | 1 | 0 none, 1 warmup, 2 freeze, 3 live, 4 over, 5 ended. |
| 1 | number | B | 1 | 0 no aquecimento; rodadas começam em 1. |
| 2 | rounds_to_win | B | 1 | Rodadas necessárias para vencer. |
| 3 | score_alpha | B | 1 | Rodadas vencidas por alpha. |
| 4 | score_bravo | B | 1 | Rodadas vencidas por bravo. |
| 5 | alive_alpha | B | 1 | Jogadores vivos de alpha. |
| 6 | alive_bravo | B | 1 | Jogadores vivos de bravo. |
| 7 | ticks_left | H | 2 | Ticks restantes da fase. |
| 9 | sides | B | 1 | Bits 0…1: atacantes; bits 2…3: vencedor (equipes 0…2); bit 4: bomba presente; bits 5…7: zero. |
Bomb · Estado fixo da bomba · 19 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | phase | B | 1 | 1 carried, 2 dropped, 3 planted, 4 defused, 5 exploded; 0 implica bloco ausente. |
| 1 | site | B | 1 | 0 não plantada, 1 A, 2 B. |
| 2 | carrier | B | 1 | Assento autorizado do portador ou 255. |
| 3 | progress | B | 1 | Progresso próprio, 0…100. |
| 4 | fuse_left | H | 2 | Ticks do temporizador; zero exceto enquanto plantada. |
| 6 | position | 3f | 12 | x, y, z em metros. |
| 18 | site_count | B | 1 | 0…2 caixas de local a seguir. |
BombSite · Caixa de plantio · 13 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | letter | B | 1 | 1 para a primeira caixa (A), 2 para a segunda (B). |
| 1 | min | 3h | 6 | Mínimos x, y, z em decímetros. |
| 7 | max | 3h | 6 | Máximos x, y, z em decímetros. |
RosterEntry · Linha do elenco · 3 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | flags | B | 1 | Bit 0: active; bit 1: alive; bits 2…3: equipe 0…2; bit 4: aparência 0…1; bit 5: papel 0 pessoa, 1 IA; bits 6…7: zero. |
| 1 | kills | B | 1 | Abates saturados em 255. |
| 2 | deaths | B | 1 | Mortes saturadas em 255. |
Teammate · Companheiro vivo não visível · 13 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | id | B | 1 | Assento 0–15; não é um identificador persistente. |
| 1 | position | 3f | 12 | x, y, z em metros. |
Chat separado
ChatSayHeader + texto vai do cliente ao servidor. ChatHeardHeader + texto vai do servidor ao cliente: o byte from fica imediatamente após type, antes de scope. O cliente não escolhe o remetente. O texto tem 1…120 bytes, não caracteres, sem terminador zero. São rejeitados controles ASCII 0x00…0x1f e 0x7f, mensagens formadas só por espaços ASCII e UTF-8 inválido (incluindo codificações excessivamente longas, substitutos e valores acima de U+10FFFF). Isso não afirma filtragem de todos os controles Unicode.
Taxa por assento: sequência de 3 mensagens e depois 1 por segundo. Em uma rodada live, somente a mesma equipe e o mesmo estado vivo/morto, mesmo pedindo scope=1. Fora de live, scope=0 vai à equipe e scope=1 a todos. Sem rodadas, o filtro de alcance continua valendo: scope=0 inclui companheiros; no modo livre, sem equipe, somente o remetente. scope=1 inclui todos os assentos ativos. O remetente sempre está incluído na audiência; o eco não garante entrega. O servidor descarta e conta excessos; essas mensagens não têm o mecanismo de reenvio/confirmação dos eventos.
Agent API 14 expõe chat_count e chat[4] com from, scope e texto de 121 bytes terminado em zero; bs_observation mede 2964 bytes e bs_action 44 na ABI verificada. Há 61 campos de observação e 11 de ação; a ABI em memória não é este formato de rede. C: bs_agent_say e bs_gym_say. Python: observation.messages() e agent.say(text). IAs leem e escrevem; faltam entrada de texto humana e voz do jogo. Voz externa não chega automaticamente à IA; a decisão de paridade continua aberta.
ChatSayHeader · Chat cliente → servidor · 3 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | type | B | 1 | Sempre 3. |
| 1 | scope | B | 1 | 0 equipe, 1 todos; o servidor restringe a audiência. |
| 2 | length | B | 1 | 1…120 bytes de texto a seguir, sem terminador. |
ChatHeardHeader · Chat servidor → cliente · 4 B
| Deslocamento | Campo | Formato | Bytes | Significado |
|---|---|---|---|---|
| 0 | type | B | 1 | Sempre 3. |
| 1 | from | B | 1 | Assento 0–15; não é um identificador persistente. |
| 2 | scope | B | 1 | 0 equipe, 1 todos. |
| 3 | length | B | 1 | 1…120 bytes de texto a seguir, sem terminador. |
Orçamentos e limites de alcance
Pior caso analítico servido 5v5: snapshot de 857 bytes com objetivo e dois locais, IA viva com motor, equipe com suprimento e rig. Com 73 bytes de cabeçalhos e 64 Hz: (857+73)×64=59520 B/s; margem 4480 para o limite de 64000. Sem objetivo: 812 bytes, 56640 B/s. Upload usual com 3 comandos: (48+73)×64=7744 B/s para 8000. Essas contas não são uma nova medição de tráfego.
Chat máximo: 4+120+73=197 bytes por mensagem recebida; cinco companheiros no limite sustentado acrescentam 985 B/s, com orçamento separado. Rajadas e tráfego recebido fora de uma equipe 5v5 não são descritos por essa taxa sustentada. O limite de dez assentos atendidos permanece; os dezesseis do formato não comprovam suporte a partidas maiores.
Pendentes: reconexão, compressão delta e hash de conteúdo no handshake; isolamento e prazo de bots; validação humana de justiça H5. O papel autenticado no token identifica para quem o token foi emitido, não prova que uma pessoa esteja jogando. O servidor limita percepção e entradas, mas equivalência humana não foi demonstrada. Os exemplos abaixo verificam bytes de payload; não constituem uma implementação do transporte nem um teste humano.
Exemplos de bytes
command · 14 B
Sequência 0x01020304, forward+fire, yaw 90°, pitch −1,5°, rifle, meio tick após 4091.
04 03 02 01 81 00 00 40 80 fe 01 80 fb 0f
commands · 20 B
Pacote com um comando e confirmação do tick 4090.
01 01 fa 0f 00 00 04 03 02 01 81 00 00 40 80 fe 01 80 fb 0f
snapshot_minimal · 138 B
Snapshot sem extensões nem listas, com valores distintos no estado próprio e elenco inativo. Verifica deslocamentos; não representa uma partida completa.
02 00 04 03 02 01 11 00 00 00 00 00 80 3f 00 00 00 40 00 00 40 40 00 00
80 40 00 00 a0 40 00 00 c0 40 08 01 00 00 c8 42 00 00 c8 41 1e 00 5a 00
07 0c 00 18 00 09 0b 0d 0f 13 00 00 00 c0 bf 00 00 20 40 00 00 15 00 17
00 20 03 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
snapshot_spectator · 171 B
Estado morto com equipe e visão do companheiro 1; sem listas.
02 00 04 03 02 01 11 00 00 00 00 00 80 3f 00 00 00 40 00 00 40 40 00 00
80 40 00 00 a0 40 00 00 c0 40 40 21 00 00 00 00 00 00 c8 41 1e 00 5a 00
07 0c 00 18 00 09 0b 0d 0f 13 00 00 00 c0 bf 00 00 20 40 07 00 15 00 17
00 20 03 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 40
41 00 00 40 40 00 00 80 40 00 80 00 02 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00
snapshot_extended · 333 B
Vetor sintético com motor, rig, suprimento, um remoto, os quatro tipos de evento, traçadora, disparo ouvido, bomba com duas caixas, elenco e um companheiro oculto. Testa a ordem dos blocos, não a largura de banda máxima nem uma sessão jogada.
02 80 04 03 02 01 11 00 00 00 00 00 80 3f 00 00 00 40 00 00 40 40 00 00
80 40 00 00 a0 40 00 00 c0 40 79 1d 00 00 c8 42 00 00 c8 41 1e 00 5a 00
07 0c 00 18 00 09 0b 0d 0f 13 00 00 00 c0 bf 00 00 20 40 00 00 15 00 17
00 20 03 02 81 00 00 40 80 fd 00 00 a0 3f 00 00 20 c0 0a 14 1e 28 32 3c
46 50 5a 64 08 07 06 05 04 03 02 01 00 00 80 3f 00 00 00 40 00 00 40 40
00 00 80 40 01 01 00 00 e0 40 00 00 00 41 00 00 10 41 00 80 e2 4b 80 31
21 00 00 04 01 00 01 49 01 d2 04 00 00 80 3f 00 00 00 40 00 00 40 40 02
00 02 01 ae 08 03 00 03 01 04 00 04 01 00 15 01 00 00 80 3f 00 00 00 40
00 00 40 40 00 00 80 40 00 00 a0 40 00 00 c0 40 01 05 00 f6 ff 0f 00 02
03 02 0d 01 00 05 04 d2 04 11 03 02 ff 25 ae 08 00 00 20 41 00 00 00 00
00 00 a0 41 02 01 0a 00 00 00 14 00 1e 00 28 00 32 00 02 3c 00 00 00 46
00 50 00 28 00 5a 00 37 15 17 0b 02 03 07 04 05 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 01 02 00 00 20 41 00 00 30 41 00 00 40 41
chat_say · 5 B
Pedido à equipe: texto UTF-8 “ñ”, dois bytes.
03 00 02 c3 b1
chat_heard · 6 B
A mesma linha recebida do assento 3.
03 03 00 02 c3 b1
snapshot_secondary_stop · 333 B
O mesmo vetor estendido com frenagem de pistola: destino idle, fonte walk, primeiro peso 1/8 e fase fonte 66/256. Zero em outros contextos não substitui este exemplo ativo.
02 80 04 03 02 01 11 00 00 00 00 00 80 3f 00 00 00 40 00 00 40 40 00 00
80 40 00 00 a0 40 00 00 c0 40 79 1d 00 00 c8 42 00 00 c8 41 1e 00 5a 00
07 0c 00 18 00 09 0b 0d 0f 13 00 00 00 c0 bf 00 00 20 40 00 00 15 00 17
00 20 03 02 81 00 00 40 80 fd 00 00 a0 3f 00 00 20 c0 0a 14 1e 28 32 3c
46 50 5a 64 08 07 06 05 04 03 02 01 00 00 80 3f 00 00 00 40 00 00 40 40
00 00 80 40 01 01 00 00 e0 40 00 00 00 41 00 00 10 41 00 80 e2 40 50 85
00 00 42 04 01 00 01 49 01 d2 04 00 00 80 3f 00 00 00 40 00 00 40 40 02
00 02 01 ae 08 03 00 03 01 04 00 04 01 00 15 01 00 00 80 3f 00 00 00 40
00 00 40 40 00 00 80 40 00 00 a0 40 00 00 c0 40 01 05 00 f6 ff 0f 00 02
03 02 0d 01 00 05 04 d2 04 11 03 02 ff 25 ae 08 00 00 20 41 00 00 00 00
00 00 a0 41 02 01 0a 00 00 00 14 00 1e 00 28 00 32 00 02 3c 00 00 00 46
00 50 00 28 00 5a 00 37 15 17 0b 02 03 07 04 05 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 01 02 00 00 20 41 00 00 30 41 00 00 40 41