Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

442 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CORO SOLTO: Treta Suprema

license: AGPL-3.0 CI pr-gates astro three.js supabase vercel

Construído em par com agentes de IA — cada commit diz qual:

Claude Fable 5 Claude Opus Kimi K3 Gemini OpenRouter Tripo3D Meshy mint.gg

CORO SOLTO: Treta Suprema — arena de sniper estilo CS 1.6 numa Brasília fictícia

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 RTCPeerConnection no repositório devolve zero, e não há netcode em public/js/ nem em src/. 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.


Comece por aqui

Você é… Leia
curioso esta página, e depois https://www.csbrasil.online
dev novo (ou agente) STATUS.mddocs/README.mdCONTRIBUTING.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 starthttp://localhost:3000/docs/.

Stack

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.

Arquitetura

Um repositório, duas zonas com regras diferentes:

  • O JOGO — public/ · JavaScript vanilla com ES modules, Three.js vendorizado em public/vendor/ (a revisão está na tabela de stack acima), zero build. Nunca vira framework. Não existe public/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.

Rodar localmente

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 estaticamente

python3 -m http.server -d public serve só os assets do jogo — o HTML não está lá. Use npm run dev.

Portão de qualidade

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:check

package.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 check lê GLBs de public/models/. Numa árvore sem os assets baixados, eval:invariants e eval:vm falham com ENOENT — é ambiente, não regressão.

Controles

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 global — DESLIGADO

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:

  • /ranking e /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/leaderboard responde {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.

Áudio (public/audio/)

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.sh

O 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.

SEO / AEO

  • site com www no astro.config.mjs — todo canonical sai daí
  • /sitemap.xml dinâmico, cobrindo uma URL por jogador
  • public/robots.txt e public/llms.txt
  • JSON-LD: um único nó VideoGame com @id estável, mais ItemList, ProfilePage/Person, HowTo, FAQPage e BreadcrumbList por página
  • Cache-Control de CDN em /ranking, /u/* e /mapa

Licenças / créditos

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.

About

FPS de navegador estilo CS 1.6 — arena de sniper satírica Petistas × Bolsonaristas numa Brasília fictícia. 100% Three.js + vanilla JS, zero build, gerado com Kimi K3.

Topics

Resources

Contributing

Security policy

Stars

81 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages