Pular para o conteúdo principal
CORO SOLTO: Treta Suprema — o canarinho, mascote do jogo, girando

O que é, e como rodar

CORO SOLTO: Treta Suprema (ex-CS BRASIL) é um FPS de navegador escrito em JavaScript vanilla sobre Three.js r160, no estilo do Counter-Strike 1.6: rounds, bots, AWP, placar por Tab, rádio de voz. Roda num link, sem instalar nada.

Os números abaixo não são escritos à mão: eles são regerados por node tools/gen-docs.mjs a partir do código, e npm run docs:check (dentro do check:fast) reprova o quality gate quando qualquer um deles diverge da árvore. Antes disso esta página envelhecia no primeiro commit — ver o que é gerado, e o que não é.

O queQuantoOnde confere
Código do jogo32.001 linhas em 44 arquivosgit ls-files public/js/*.js | xargs wc -l
game.js6.910 linhaswc -l public/js/game.js
main.js2.698 linhaswc -l public/js/main.js
Armas com GLB26git ls-files 'public/models/weapons/*.glb' | wc -l
GLBs de personagem45git ls-files 'public/models/characters/*.glb' | wc -l
Props em GLB108git ls-files 'public/models/props/*.glb' | wc -l
Clipes de animação versionados573git ls-files public/models/anims | wc -l
Personagens jogáveis44, em 5 facçõesarray CHARACTERS de characters.js
Mapas no registro12objeto MAPS de maps.js
Arnêses visuais em HTML15git ls-files 'public/*.html' | wc -l
Scripts do arnês195git ls-files 'tools/eval/*.mjs' 'tools/eval/*.py' | wc -l
Scripts de pipeline54git ls-files 'tools/*.mjs' | wc -l
Tarefas de entrada escritas26git ls-files 'docs/issues/[0-9]*.md' | wc -l
Versão2.0.0-alpha.173public/js/version.js e package.json (batem)

Bloco gerado por node tools/gen-docs.mjs. Fonte: o comando da coluna direita de cada linha

E as regras de partida que mais mudam de lugar, todas lidas das constantes de public/js/game.js:

RegraValorConstante
Facções · personagens5 · 44 (B 9 · C 9 · E 8 · F 9 · U 9)CHARACTERS
Mapas no menu12 — 2 abrem em rodadas, 10 em capturaMAPS / ctfMode
Respawn2,2 sRESPAWN_DELAY
Round99 s, 3 vitóriasROUND_TIME / ROUNDS_TO_WIN
Capturaalvo = todas as bandeiras do mapa, 2 rodadas (rede de segurança 480 s)capsToWin = ctfPts.length / CTF_ROUNDS_TO_WIN
Regeneração de vidaDESLIGADA — ?regen=1 religaREGEN
Ranking / páginas /u/DESLIGADOS — é uma flag, volta numa linhaRANKING_ON em src/lib/site.ts

Bloco gerado por node tools/gen-docs.mjs. Fonte: constantes de public/js/game.js · RANKING_ON de src/lib/site.ts

O menu aceita de 2×2 a 8×8 bots (o motor aceita de 1 a 8 por lado); o padrão é 4×4.

Dois desses são escolha recente, não defeito

A regeneração de vida foi desligada em 05/08 (REGEN = QS.get('regen') === '1'). Ela existia, estilo CoD — 6 s sem tomar dano e 22 HP/s —, e o dono a reportou como bug ("a vida do 1st player volta a 100, não sei porque") justamente porque era invisível: sem ícone, sem som, sem linha nas configurações. Regra que o jogador não percebe é indistinguível de defeito. Ela continua inteira atrás de ?regen=1, com a simetria jogador↔bot. Quem religar tem que entregar o feedback junto — e resolver o que ela vinha tapando: sem cura, kit ou colete, cada vida depois do primeiro contato já estava perdida.

O ranking foi desligado e trocado por telemetria anônima. /ranking e /u/* respondem 200 com aviso + noindex (não 404 — as URLs estão indexadas e vão voltar), e /api/leaderboard responde {disabled:true}.

O quality gate NÃO está verde, e isso é declarado

Quantas invariantes passam não é derivável do código — é o resultado de uma execução, e depende até de qual insumo existe na máquina. Por isso esse placar não é repetido aqui: ele mora no cabeçalho de KNOWN-BUGS.md, colado de uma execução real, com a lista das vermelhas, causa raiz e arquivo:linha de cada uma. É esse arquivo que é mantido dia a dia.

Para o estado de hoje, rode — não repita número de cabeça:

npm run eval:vm && node tools/eval/invariants.mjs --json   # 10-12 min

A ordem importa: invariante de viewmodel medida com o JSON de ontem inventa vermelha (ver Como colaborar).

Rodar em 3 comandos

git clone https://github.com/rubenmarcus/csbrasil.git && cd csbrasil
npm install
npm run dev # abre em http://localhost:4321 — essa página JÁ É o jogo

O pacote de áudio (npm run fetch-audio) é opcional: sem ele o jogo usa sons sintetizados. A pasta public/audio/ não é versionada.

Linux, WebGL e modo compatibilidade

O jogo tenta WebGL2 e WebGL1, começando pela escolha padrão do navegador e reduzindo antialias, preferência de GPU e stencil antes de desistir. Quando cai em WebGL1, llvmpipe/SwiftShader ou outro degrau reduzido, ativa qualidade baixa apenas naquela sessão: DPR 0,75, sem bloom/sombras e com retratos estáticos na seleção.

Use ?safe=1 para priorizar WebGL1 e o caminho de menor custo. Se nem esse modo abrir, confira chrome://gpu ou a seção Graphics de about:support, ligue aceleração por hardware e atualize Mesa/driver pelo gerenciador da distribuição. Uma página não pode forçar um driver quando o navegador recusa criar até o contexto WebGL1.

Alternativa sem Astro (zero dependência de build)

O arnês de avaliação traz um servidor estático de 24 linhas que serve public/ e mapeia / para o fonte da página do jogo:

node tools/eval/serve.mjs 8123   # http://localhost:8123

Ele existe exatamente porque src/pages/index.astro é HTML puro — dá pra servir o arquivo cru sem passar pelo Astro (tools/eval/serve.mjs:15).

A pegadinha que custa a primeira hora de todo mundo

Não existe public/index.html. Servir a pasta public/ estaticamente te dá um índice de diretório com eval.html, mapview.html e companhia — nenhum deles é o jogo. O HTML do jogo é src/pages/index.astro, servido na rota raiz pelo Astro. Não há rota /game.

A confirmação independente está no próprio arnês: tools/eval/serve.mjs:15 precisa de um caso especial if (p === '/') que lê src/pages/index.astro do disco, justamente porque não há index.html em public/ pra servir.

Esta seção já foi uma lista de erros do README

Até 04/08/2026 ela existia porque o README.md da raiz mandava rodar cd public && python3 -m http.server e falava num "jogo em /game/". As duas linhas foram corrigidas — o README hoje diz o certo. O que sobrou é o fato em si, que continua sendo a primeira pedra no caminho de quem chega.

Estrutura real do repositório

Duas zonas de código e uma terceira zona que é a razão desta doc existir (o arnês):

Nenhuma contagem aqui: a árvore diz o que é cada coisa, e os números vivem na tabela gerada lá em cima. Misturar os dois é como o ARCH.md escrito à mão nasceu errado.

public/                 O JOGO — vanilla ES modules, ZERO build
js/
game.js a classe Game (loop, bots, tiro, HUD) — o maior arquivo do repo
main.js menu, wiring de DOM, persistência
vmattach.js springs.js weapons.js fparms.js handik.js viewmodel/armas
maps.js o REGISTRO de mapas (quem não está aqui não é jogável)
map_brasilia.js map_piscina.js map_havan.js
map_ferrovelho.js map_quebrada.js os mapas registrados
map_piscinao_ramos.js "Piscinão" — existe no disco, FORA do registro
mapprops.js map_decals.js props e grafite
bloom.js textures.js vao.js stylize.js gpuparticles.js gráficos/FX
characters.js glbchars.js personagens
audio.js version.js site-bg.js
models/ armas, personagens, props e clipes de animação em GLB
vendor/ Three.js vendorizado (sem CDN, sem npm no runtime)
style.css o HUD inteiro
*.html arnêses visuais (eval, mapview, weapontest, vm-inspect…)

src/ O SITE (Astro + adapter Vercel)
pages/index.astro ⚠ ISTO É O JOGO (HTML + import map + HUD)
pages/sobre.astro landing/FAQ com JSON-LD
pages/personagens.astro como-jogar.astro ranking.astro mapa.astro
pages/u/[...path].astro perfil público
pages/api/*.ts SSR: leaderboard, submit-match, register, badge, avatar
layouts/Layout.astro shell do site (não do jogo)
lib/ supabase, svg, geo, fmt

tools/
eval/ O ARNÊS — réguas, quality gate e sondas. Ver "Quality gates"
invariants.mjs o quality gate
ref-measure.py mede os frames de referência (a doutrina da casa)
harness.mjs sobe o Game real em node com DOM stubado
ARCH.md BAR.md mapa de conflito (gerado) e a régua visual
gen-arch.mjs gera e VALIDA o ARCH.md
gen-docs.mjs gera e VALIDA os blocos numéricos desta documentação
gen-asset.mjs gera prop 3D por texto (Tripo/Meshy)
gen-image.mjs gera arte 2D por texto (OpenRouter)

(banco: schema/migrations são PRIVADOS — fora do repo)
.github/workflows/ci.yml o quality gate rodando em CI

Os mapas registrados hoje, e em que modo cada um abre:

IdNome no menuAbre emArquivo em public/js/Linhas
praca_poderesPraça dos Três Poderesrodadasmap_brasilia.js1.830
piscina_tretaPiscina da Tretarodadasmap_piscina.js810
loja_hLoja H (Estacionamento)capturamap_havan.js1.964
ferro_velhoFerro Velho do Zécapturamap_ferrovelho.js1.888
quebradaQuebrada (Rua do Baile)capturamap_quebrada.js1.599
posto_tretaPosto da Tretacapturamap_posto.js489
upa_24hUPA 24h da Tretacapturamap_upa.js288
obras_prefeituraObras da Prefeituracapturamap_obras.js240
atacadao_tretaAtacadão da Tretacapturamap_atacadao.js255
parque_tretaParque da Tretacapturamap_parque.js402
velho_oesteVelho Oeste da Tretacapturamap_velho_oeste.js433
penitenciariaPenitenciária da Tretacapturamap_penitenciaria.js247

12 mapas registrados — 2 abrem em rodadas e 10 em captura. ctfMode abre o mapa em captura, não prende: o jogador troca no menu (é a MOD1). Há 14 arquivos map_*.js em public/js/ — arquivo no disco não implica mapa jogável.

Bloco gerado por node tools/gen-docs.mjs. Fonte: objeto MAPS de public/js/maps.js

As duas zonas

Em uma linha cada: public/ é o jogo (vanilla, ES modules, sem framework e sem bundler) e src/ é o site (Astro com SSR, onde framework é bem-vindo). O que cada regra da fronteira paga, e por que ela é dura, está em Stack e ferramentas — uma página só, para não haver duas versões da mesma fronteira.

O que você precisa saber antes de editar é a consequência: o jogo é carregado pela página Astro via import map com versão e hash do conteúdo (src/pages/index.astro).

Preserve o manifesto publicado

scripts/module-cache.mjs deriva o hash dos módulos publicados sob public/js/ e o import map aplica essa revisão ao grafo inteiro. Não faça bump manual e não inclua bancadas que scripts/prune-dist.mjs remove. npm run eval:shaderbudget (SB7) confere as duas propriedades.

Comandos que você vai usar

npm run dev            # site + jogo (Astro, :4321) — a rota / JÁ É o jogo
npm run build # dist/client + dist/server
npm run eval:vm # enquadramento do viewmodel — RODE ANTES das invariantes
npm run eval:invariants # as invariantes — node puro, 10-12 min
npm run eval:bots # botsim 60 s por mapa, sementes fixas
npm run eval:mat # material/luz/fog/textura nos mapas
npm run docs # regenera os blocos numéricos desta documentação
node tools/eval/serve.mjs 8123 # servidor estático sem Astro

E os dois quality gates, com a lista exata do que cada um roda — direto do package.json:

npm run check:fast   # node tools/eval/runner.mjs syntax eval:release eval:telemetry eval:identity eval:error-console eval:error-origin eval:webgl eval:webglguard eval:maprotate eval:shaderlog eval:shaderbudget eval:botbrain eval:prune eval:vminspect eval:faccao eval:mapid eval:mapjson eval:mapcontrato eval:pickuparma eval:parquewheel eval:redesign eval:matchoptions eval:charvoice eval:screenquery docs:check arch:check audio:check feet:check eval:vmlabhud eval:ctfhud eval:pause eval:ctfround eval:ctfwin eval:spawn eval:regen eval:pegada eval:dmgdir eval:ctflabels anims:check anims:merge:check walls:check media:check menuwalls:check travessao:check eval:medianet eval:posters eval:grafitelayout eval:simclock eval:backendhints changelog:check eval:velhooeste eval:penitenciaria eval:comentario eval:fixture eval:preload eval:docsautoria eval:replaycam

package.json tem 119 scripts; o motivo de cada um mora em SCRIPTS.md (migrado das chaves //nome em 18/08/2026) — é onde está o porquê.

Bloco gerado por node tools/gen-docs.mjs. Fonte: node -p "Object.keys(require('./package.json').scripts)"

O check:fast cobre as réguas de node puro; o CI (.github/workflows/ci.yml) roda o mesmo conjunto mais os passos que exigem rede.

Use o check:fast no loop, o portao-browser antes do PR

O portao-browser gasta 10-12 min porque sobe o jogo cinco vezes. O check:fast cobre as réguas que nasceram dos bugs mais recentes (menu de pausa, rodada de captura, regeneração, manifesto de animação) e roda em cerca de um minuto.

Onde ir agora

A ordem da barra lateral é a ordem de leitura, e cada página entrega uma coisa:

  1. Stack e ferramentas — com o que isso é feito, com a versão declarada de cada peça. É onde a fronteira public/ × src/ está explicada por inteiro.
  2. Instrumentação de IA — como o trabalho é feito aqui. Se você nunca colaborou com agentes num repositório, comece por essa.
  3. O quality gate — o que é uma invariante, como se escreve uma, as duas leis da casa e o teste de mutação da própria régua. É a página mais útil do site.
  4. Arquitetura — como N agentes editam o mesmo arquivo sem colidir, e a tabela de conflito. Leia antes de tocar em game.js.
  5. Como colaborar — o que um PR precisa pra entrar, e as tarefas de primeira contribuição já escritas em docs/issues/ (com um abrir-issues.sh pronto — elas ainda não foram abertas no GitHub).
  6. Licença — o LICENSE na raiz declara (hoje AGPL-3.0); as superfícies que repetem o nome e mudam junto estão no CONTRIBUTING.md.
  7. Estado atual — fontes vivas de produção, dados e dívida conhecida desde a última medição colada.

Para onde o projeto vai não está nesta documentação: é o docs/ROADMAP.md, e o plano executável é o plans/08.