Construído em par com agentes de IA — cada commit diz qual:
FPS gratuito de navegador em Three.js: arena de sniper estilo CS 1.6
(awp_map) numa Brasília fictícia e satírica. Facções, personagens originais,
mapas, arsenal, bots, rounds e Capture the Flag. Sem download, sem instalação,
sem cadastro.
| O que | Quanto | Onde confere |
|---|---|---|
| Código do jogo | 25.821 linhas em 28 arquivos | cat public/js/*.js | wc -l |
game.js |
6.543 linhas | wc -l public/js/game.js |
main.js |
1.639 linhas | wc -l public/js/main.js |
| Armas com GLB | 26 | ls public/models/weapons/*.glb | wc -l |
| GLBs de personagem | 45 | ls public/models/characters/*.glb | wc -l |
| Props em GLB | 108 | ls public/models/props/*.glb | wc -l |
| Clipes de animação versionados | 526 | git ls-files public/models/anims | wc -l |
| Personagens jogáveis | 44, em 5 facções | array CHARACTERS de characters.js |
| Mapas no registro | 5 | objeto MAPS de maps.js |
| Arnêses visuais em HTML | 12 | ls public/*.html | wc -l |
| Scripts do arnês | 154 | ls tools/eval/*.mjs tools/eval/*.py | wc -l |
| Scripts de pipeline | 43 | ls tools/*.mjs | wc -l |
| Tarefas de entrada escritas | 15 | ls docs/issues/[0-9]*.md | wc -l |
| Versão | 2.0.0-alpha.32 |
public/js/version.js e package.json (batem) |
Bloco gerado por
node tools/gen-docs.mjs. Fonte:o comando da coluna direita de cada linha
Hoje o jogo é só contra bots. Não existe multiplayer entre humanos: um
grep RTCPeerConnectionno repositório devolve zero, e não há netcode empublic/js/nem emsrc/. Multiplayer por WebRTC é a maior frente aberta do projeto — quando existir, esta linha muda junto com o código, não antes.
▶ Jogue: https://www.csbrasil.online
O jogo já se chamou CS BRASIL. É o mesmo jogo — o domínio continua o mesmo, e o nome antigo segue registrado como nome alternativo pra quem procura por ele.
Este jogo nasceu gerado por IA a partir de um único prompt — o prompt original está em
docs/historico/PROMPT.md.
| Você é… | Leia |
|---|---|
| curioso | esta página, e depois https://www.csbrasil.online |
| dev novo (ou agente) | STATUS.md → docs/README.md → CONTRIBUTING.md |
| quer entender a stack | docs/docs/stack.md — Three.js, Astro, Supabase, geração de asset, skills |
| quer contribuir hoje | docs/issues/ — tarefas de entrada com arquivos e critério de aceite |
| quer saber o que está quebrado | KNOWN-BUGS.md — defeitos com arquivo:linha e passo de reprodução |
Site de documentação (Docusaurus, com instrumentação de IA, quality gates e
arquitetura): cd docs && npm install && npm start → http://localhost:3000/docs/.
| Camada | Ferramenta | Versão |
|---|---|---|
| Motor 3D (WebGL) | Three.js, vendorizado | r160 |
| Jogo | ES modules vanilla, zero build | 28 arquivos |
| Site | Astro com SSR | ^7.1.1 |
| Hospedagem | adapter Vercel | ^11.0.3 |
| Banco | Postgres gerenciado (RLS; schema privado, fora do repo) | ^2.110.7 |
| Browser nas réguas | Playwright | ^1.62.1 |
| Pipeline de GLB | gltf-transform | ^4.4.1 |
| Compressão de malha | meshoptimizer | ^1.2.0 |
| Imagem (build e API) | sharp · resvg | ^0.35.3 · ^2.6.2 |
| Esta documentação | Docusaurus | 3.6.3 |
| Runtime de CI | Node | 22 |
Three.js sai de public/vendor/three.module.js (sem CDN, sem npm no runtime). Astro e Vercel de package.json + astro.config.mjs + vercel.json. Dos scripts de tools/, 94 importam Playwright, 36 importam gltf-transform e 4 importam meshoptimizer.
Bloco gerado por
node tools/gen-docs.mjs. Fonte:dependencies/devDependencies do package.json · REVISION de public/vendor/three.module.js
O detalhe de cada uma — por que o jogo não tem build, como o asset é gerado (mint.gg, Tripo3D, Meshy, OpenRouter), o que Playwright e gltf-transform fazem aqui, e o que são as skills de agente — está em Stack e ferramentas.
Um repositório, duas zonas com regras diferentes:
- O JOGO —
public/· JavaScript vanilla com ES modules, Three.js vendorizado empublic/vendor/(a revisão está na tabela de stack acima), zero build. Nunca vira framework. Não existepublic/index.html: o HTML do jogo ésrc/pages/index.astro, servido na rota/. (Este README já afirmou o contrário por meses e mandava todo dev novo para o arquivo errado.) - O SITE —
src/· Astro com SSR na Vercel. Landing, ranking global, perfis públicos, páginas de conteúdo e as rotas/api/*. Aqui framework é bem-vindo — mas o jogo continua intocado.
O ranking e a telemetria vivem num Postgres gerenciado (schema privado, fora do repo) — o ranking está
desligado por flag hoje, a coleta não parou (ver a seção abaixo). A
service_role key fica só no servidor; a anon key é pública por design, e a
segurança vem das policies e dos grants por coluna.
Sem contagem na árvore abaixo — os números vivem no bloco gerado lá em cima.
Índice por número escrito à mão desatualiza no primeiro commit; é a mesma razão
de o tools/eval/ARCH.md ser gerado.
STATUS.md estado de hoje (leia primeiro)
astro.config.mjs Astro + adapter Vercel · `site` COM www (canonical)
vercel.json build, headers de segurança (CSP…) e cache
src/
layouts/Layout.astro shell do site: nav, footer, CSS global, JSON-LD base
lib/site.ts nome, host, descrições e @id de JSON-LD (fonte única)
lib/supabase.ts client admin (service_role, só no servidor)
lib/safe-url.ts allowlist de avatar + fetch com trava de SSRF
lib/ratelimit.ts rate limit durável (contado no Postgres)
data/jogo.ts armas/mapas/personagens em forma de dado, pro site
pages/index.astro O JOGO, na rota `/`
pages/ranking.astro leaderboard (SSR, com cache de CDN)
pages/u/[...path] perfil público por jogador + badge PNG
pages/sitemap.xml.ts sitemap dinâmico, cobre os perfis
pages/api/* register, submit-match, leaderboard, badge, avatar…
public/ O JOGO (vanilla, zero build)
js/ vendor/ models/ style.css robots.txt llms.txt og-image.png
audio/ ⚠ NÃO versionado — ver "Áudio" abaixo
tools/ pipeline de asset (gen-asset, gen-image, otimização de GLB)
gen-arch.mjs GERA tools/eval/ARCH.md (índice + tabela de conflito)
gen-docs.mjs GERA os blocos numéricos deste README e de docs/
tools/eval/ o arnês de medição e os portões de qualidade
docs/ documentação para devs (Docusaurus) + as issues de entrada
.agents/skills/ skills de agente (.claude/skills/ são symlinks pra cá)
Duas pastas NÃO vêm no clone, por decisão registrada: public/audio/ (direitos
incertos) e references/ (telas-alvo da UI e frames de referência, que ficam só na
máquina do dono). O que sobrevive das references/ são os números medidos:
tools/eval/ref_ui.json e tools/eval/ref_viewmodel.json, esses versionados. Régua que
precisa rodar em CI lê o JSON, nunca o PNG.
O site completo (é o modo que você quer — inclui o jogo):
git clone https://github.com/rubenmarcus/csbrasil.git
cd csbrasil
npm install
cp .env.example .env # opcional: sem envs, o ranking responde 503 e o resto roda
npm run fetch-audio # opcional: sem o pacote, o jogo usa sons sintetizados
npm run dev # http://localhost:4321 — o jogo está em /Build e preview:
npm run build # gera dist/ (client + server)
npm run preview # serve dist/client estaticamentepython3 -m http.server -d public serve só os assets do jogo — o HTML não
está lá. Use npm run dev.
npm run check # npm run syntax && npm run audio:check && npm run eval:ctfhud && npm run eval:vm && npm run eval:invariants && npm run eval:kick && npm run eval:bots
npm run check:fast # npm run syntax && npm run docs:check && npm run arch:check && npm run audio:check && npm run feet:check && npm run eval:ctfhud && npm run eval:pause && npm run eval:ctfround && npm run eval:ctfwin && npm run eval:spawn && npm run eval:regen && npm run eval:pegada && npm run eval:ctflabels && npm run anims:checkpackage.json tem 41 scripts. Vários trazem uma chave //nome logo acima com o motivo de existirem — é onde mora o porquê.
Bloco gerado por
node tools/gen-docs.mjs. Fonte:node -p "Object.keys(require('./package.json').scripts)"
npm run arch # regenera tools/eval/ARCH.md (índice + tabela de conflito)
npm run docs # regenera os blocos numéricos deste README e de docs/Nada commita com invariante vermelha. O catálogo do arnês está em
tools/eval/README.md.
Documentação que carrega número é gerada. npm run docs:check (dentro do
check:fast) reprova quando um bloco gerado diverge do código — foi assim que
uma linha afirmando "game.js tem 3.234 linhas" sobreviveu até o arquivo
dobrar de tamanho.
npm run checklê GLBs depublic/models/. Numa árvore sem os assets baixados,eval:invariantseeval:vmfalham comENOENT— é ambiente, não regressão.
| Tecla | Ação |
|---|---|
| W A S D | Mover |
| Mouse | Mirar |
| Shift | Correr |
| Ctrl ou C | Agachar — mira bem mais estável |
| Espaço | Pular |
| Clique esq. | Atirar |
| Clique dir. | Luneta / ADS |
| R | Recarregar |
| 1 / 2 / 3 | Primária / pistola / faca |
| Z / X / V | Rádio estilo CS (comandos de voz) |
| M | Trocar de time (a qualquer momento) |
| Tab | Placar |
| Esc | Pausar |
Regras, lidas das constantes de public/js/game.js:
| Regra | Valor | Constante |
|---|---|---|
| Facções · personagens | 5 · 44 (B 9 · C 9 · E 8 · F 9 · U 9) | CHARACTERS |
| Mapas no menu | 5 — 2 abrem em rodadas, 3 em captura | MAPS / ctfMode |
| Respawn | 2,2 s | RESPAWN_DELAY |
| Round | 99 s, 3 vitórias | ROUND_TIME / ROUNDS_TO_WIN |
| Captura | alvo = todas as bandeiras do mapa, 2 rodadas (rede de segurança 480 s) | capsToWin = ctfPts.length / CTF_ROUNDS_TO_WIN |
| Regeneração de vida | DESLIGADA — ?regen=1 religa |
REGEN |
Ranking / páginas /u/ |
DESLIGADOS — é uma flag, volta numa linha | RANKING_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 1 a 8 bots por lado (settings.bots); o padrão é 4×4. O time
com mais abates leva o round. AWP mata com um tiro em qualquer lugar do corpo.
Multikills disparam anúncios estilo Unreal Tournament.
Regeneração de vida está DESLIGADA (decisão do dono, 05/08/2026): vida só
volta com respawn. ?regen=1 religa a regra antiga — inteira, com a simetria
jogador↔bot. Ela foi desligada porque era invisível: sem ícone, sem som e
sem linha nas configurações, e regra que o jogador não percebe é
indistinguível de defeito.
Os mapas registrados, e em que modo cada um abre:
| Id | Nome no menu | Abre em | Arquivo em public/js/ |
Linhas |
|---|---|---|---|---|
awp_map |
Praça dos Três Poderes | rodadas | map_brasilia.js |
1.795 |
fy_pool_day |
Piscina da Treta | rodadas | — |
— |
fy_havan |
Loja H (Estacionamento) | captura | map_havan.js |
1.885 |
fy_ferrovelho |
Ferro Velho do Zé | captura | map_ferrovelho.js |
1.854 |
fy_quebrada |
Quebrada (Rua do Baile) | captura | map_quebrada.js |
1.531 |
5 mapas registrados — 2 abrem em rodadas e 3 em captura. ctfMode abre o mapa em captura, não prende: o jogador troca no menu (é a MOD1). Há 6 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
RANKING_ON = false em src/lib/site.ts. Decisão do dono em
04/08/2026: "vamos desabilitar o ranking por enquanto, depois a gente ajeita".
O motivo é o de sempre nesta base — o modelo é client-authoritative, o placar
é forjável, e publicar classificação forjável é publicar número errado.
É flag, não remoção. Com false:
/rankinge/u/*respondem 200 com aviso +noindex— não 404. As URLs estão indexadas e vão voltar no mesmo endereço;- o link some do nav e do rodapé do site;
/api/leaderboardresponde{disabled:true}; o cliente não conhece a flag, ele reage à resposta da API. Uma fonte de verdade, no servidor.
O que NÃO parou: a coleta. submit_match continua gravando (valida token,
rate limit por nick/IP/dia, tetos absolutos e consistência física) e a telemetria
nova continua medindo. Quando o ranking voltar, o histórico está lá.
- Schema e migrations:
supabase/(a contagem está no bloco gerado no topo) - O que foi endurecido no pré-release:
docs/seguranca.md
A correção definitiva é o servidor de jogo escrever com service_role e o RLS
bloquear o cliente; está na fila junto do multiplayer.
A pasta não é versionada: as vozes e memes têm direitos incertos, e o repositório público leva só o código. Sem o pacote, o jogo usa sons sintetizados e funciona normalmente.
npm run fetch-audio # ou: AUDIO_PACK_URL=<zip> bash scripts/fetch-audio.shO jogo carrega audio/manifest.json (veja audio/manifest.example.json, esse
sim versionado). Samples originais do CS 1.6 são propriedade da Valve e não
são distribuídos aqui.
sitecomwwwnoastro.config.mjs— todo canonical sai daí/sitemap.xmldinâmico, cobrindo uma URL por jogadorpublic/robots.txtepublic/llms.txt- JSON-LD: um único nó
VideoGamecom@idestável, maisItemList,ProfilePage/Person,HowTo,FAQPageeBreadcrumbListpor página Cache-Controlde CDN em/ranking,/u/*e/mapa
O código está sob GNU AFFERO GENERAL PUBLIC LICENSE — é o que vale hoje, e a fonte é o arquivo LICENSE na raiz do repositório. Nenhum outro arquivo tem autoridade sobre isso.
Bloco gerado por
node tools/gen-docs.mjs. Fonte:head -1 LICENSE
Migrado de MIT para AGPL-3.0 em 07/08/2026. As contribuições feitas antes da troca entraram sob MIT — licença permissiva e compatível com a AGPL: elas continuam MIT dentro do todo, e o conjunto é distribuído sob AGPL-3.0. Quem contribuiu antes não perde nada nem precisa de novo consentimento (a direção incompatível seria a inversa).
- Three.js r160 — MIT (© Three.js authors), em
public/vendor/. - Código, texturas, personagens e logo: originais.
- Áudios: fornecidos pelo usuário; verifique direitos antes de uso comercial. Sons do CS 1.6 não inclusos (Valve).
- Paródia independente, sem afiliação com a Valve. Counter-Strike é marca da Valve Corporation.
Sátira política fictícia. Feito para rir, não para brigar.
