Pular para o conteúdo
Voltar para projetos
Autoria e desenvolvimento

Crash Game

Um cassino multiplayer em tempo real: a curva sobe, todos veem o mesmo multiplicador ao mesmo tempo e cada jogador decide quando sacar antes do crash.

Uma rodada do Crash Game a 3,49x no navegador e a mesma tela no celular

Resultados-chave

100 ms
entre atualizações do multiplicador

Igual para todos os jogadores

3
microsserviços NestJS

Mais gateway e provedor de identidade

503
testes automatizados

Passando

0
números de ponto flutuante em dinheiro

BIGINT em centavos

Contexto e problema

A cada rodada, os jogadores apostam durante uma janela de 10 segundos; depois o multiplicador começa em 1.00x e sobe até um ponto de crash sorteado pelo servidor. Quem saca antes leva aposta × multiplicador; quem fica até o crash perde a aposta. O desafio é fazer todos verem o mesmo multiplicador ao mesmo tempo, sem confiar no cliente e sem ponto flutuante tocando em dinheiro.

Solução implementada

Três microsserviços NestJS conversando por RabbitMQ, um gateway Kong, login pelo Keycloak e um frontend React que recebe o jogo por WebSocket.

  • games: cria a rodada (seed, hash, crash point), aceita apostas e cashouts, liquida perdas, histórico, ranking e verificação.
  • wallets: uma carteira por usuário, com saldo, reservas, depósito, saque e liquidação.
  • websockets: o relógio do jogo; conduz as fases e calcula o multiplicador a cada 100 ms.
  • frontend: apostas, cashout, carteira, ranking e o verificador provably fair.

Arquitetura

O navegador só fala com o backend pelo Kong e recebe o jogo pelo WebSocket. Cada serviço valida o JWT do Keycloak pela chave pública (JWKS), sem segredo compartilhado.

Na aposta, o games valida e publica bet.placed; o wallets reserva o valor (o saldo total não muda, mas o disponível cai) e o websockets avisa todos os jogadores. No cashout, o pagamento é calculado pelo servidor e o wallets libera a reserva e credita o lucro.

Decisões de engenharia

  • Dinheiro nunca é ponto flutuante: BIGINT em centavos e multiplicador inteiro (× 10.000); o pagamento é floor(aposta × multiplicador / 10.000), então nunca se credita um centavo a mais.
  • O servidor é a única fonte da verdade: o frontend nunca envia multiplicador; o cashout recalcula o valor pelo relógio do servidor, limitado ao crash point.
  • Provably fair de verdade: HMAC-SHA256(serverSeed, roundId) define o crash point, o hash da seed é publicado antes da rodada e o verificador refaz as contas no navegador com WebCrypto.
  • Multiplicador e^(0,00006 · t): 2x em 11,6 s, 5x em 26,8 s e 10x em 38,4 s.

Resultados

503 testes automatizados passando e um único comando (bun run dev) que sobe infraestrutura, os três serviços e o frontend, esperando tudo ficar saudável.

Arquitetura

Arquitetura de serviços

Kong como gateway, Keycloak para identidade e RabbitMQ para os eventos de domínio.

Microserviços NestJSDadosloginHTTP + JWTWebSocketWebSocketNavegador · React 19Keycloak login OIDC+ PKCEKong :8000 gatewayúnicogames rodadas ·apostas · cashoutwallets saldo ·reservaswebsockets loop dojogo · socket.ioRabbitMQ · eventosde domínioPostgreSQL games ·walletsRedis estado ao vivo

passo a passo

etapa 0/0

Ciclo de uma rodada

Da criação da rodada, com o hash da seed publicado, até a revelação da seed no crash.

Carregando diagrama…

Tecnologias utilizadas

Backend

  • BunMonorepo, runtime e scripts
  • NestJSServiços games, wallets e websockets
  • TypeScriptModo estrito em todo o monorepo
  • Socket.IOJogo em tempo real no navegador
  • KeycloakLogin OIDC com Authorization Code + PKCE
  • KongGateway único para a API

Frontend

  • ReactFrontend do jogo, com TanStack Query e Zustand
  • ViteBuild e dev server do frontend

Dados & Mensageria

  • PostgreSQLDados de games e wallets
  • RabbitMQEventos de domínio entre os serviços
  • RedisEstado ao vivo da rodada

Outras

  • PrismaAcesso ao PostgreSQL

Cloud & DevOps

  • DockerTudo sobe com um comando

Principais funcionalidades

  • Provably fairHash da seed antes, verificação no navegador depois
  • Tempo realMultiplicador a cada 100 ms via WebSocket
  • Carteira com reservaSaldo disponível e reservado
  • Login com KeycloakOIDC com Authorization Code + PKCE

Imagens e demonstrações

  • Cashout em 3,22x: o jogador recebeu R$ 55,61 líquidos
  • Crash em 6,02x: quem não sacou perde a aposta
  • Verificador provably fair, no navegador
  • Carteira com saldo disponível e reservado
  • Uma fila por serviço no RabbitMQ

Sobre o projeto

Desafio técnico da Jungle Gaming. O ponto de crash é provably fair: o hash da seed é publicado antes da rodada e, depois do crash, qualquer pessoa confere no navegador que o resultado não foi manipulado.

Outros projetos

destaque

Strata

Do planejamento ao código, com o time inteiro na mesma tarefa

Plataforma para times de software que reúne quadro, chat, documentos e um editor de código. Cada tarefa vira um canvas infinito com a especificação, o código e o time, e o contexto não se perde entre ferramentas.

tipos de ambiente no canvas
7
plano Orbit, gratuito
US$ 0
Página inicial do Strata com o quadro de tarefas sobre um fundo espacial
Detalhes Demo
destaque

Distributed Wagering Processor

Serviço financeiro que aplica apostas, vitórias, derrotas, reembolsos e estornos nas carteiras dos jogadores, com saldo e extrato corretos mesmo quando as mensagens chegam duplicadas, fora de ordem ou ao mesmo tempo.

  • Bun
  • NestJS
  • TypeScript
  • PostgreSQL
  • MikroORM
  • AWS SQS
testes automatizados
248
mesma aposta em paralelo
50×
instâncias disputando uma carteira
3
Wallet Console e Flow Explorer mostrando uma aposta e duas requisições disputando a mesma carteira
Detalhes GitHub
destaque

Voto Aberto

Plataforma de transparência parlamentar: o que cada deputado federal propôs e como votou, direto dos dados abertos da Câmara, com link para conferir cada informação na fonte.

  • Next.js
  • TypeScript
  • SQLite
  • Zod
  • Vitest
  • Node.js
deputados da 57ª legislatura
648
votações nominais
1.597
proposições
94.480
Página inicial do Voto Aberto no computador e o placar de uma votação no celular
Detalhes GitHub