Nem todo jogador quer criar conta, digitar um nome ou interagir com qualquer tela depois de morrer. Alguns simplesmente fecham a aba. Se o seu jogo só captura dados de quem registra score, você está perdendo a maioria das partidas — e com elas, insights valiosos sobre como os jogadores realmente jogam.
A solução é um sistema de scores anônimos que captura 100% das partidas sem exigir login, cadastro ou qualquer ação do jogador. Neste tutorial, mostramos como o Navistron implementa esse sistema usando três caminhos de captura — incluindo navigator.sendBeacon para sessões abandonadas — garantindo que nenhum dado se perca.
O Problema: Dados que Você Nunca Vê
Em jogos web tradicionais, o fluxo é: jogar → morrer → digitar nome → salvar score. Mas na prática, existem três tipos de jogador:
- O competitivo — digita nome e salva. Aparece no ranking. Fácil de capturar
- O casual — joga por diversão. Quando vê a tela de game over, clica "Skip" ou "Pular". Não quer aparecer em nenhum lugar
- O fantasma — fecha a aba no instante em que morre. Não clica em nada. Para o sistema tradicional, essa partida nunca existiu
Se você só captura o primeiro tipo, está vendo uma fração distorcida dos seus jogadores. Os dados agregados ficam enviesados para cima (só quem tem scores altos se registra) e você perde a visão real do comportamento da base.
A Solução: Três Caminhos de Captura
O Navistron resolve isso com um sistema de dual-track: scores registrados vão para a coleção scores (leaderboard público), e sessões anônimas vão para anonymous_sessions (telemetria interna). Três caminhos cobrem todos os cenários:
| Cenário | Tipo de Sessão | Como é Capturada | Coleção MongoDB |
|---|---|---|---|
| Jogador salva com nome | Registrada | fetch POST para /api/scores | scores |
| Jogador clica "Skip" | Unregistered | fetch POST para /api/anonymous-sessions | anonymous_sessions |
| Jogador fecha a aba | Unknown | navigator.sendBeacon para /api/anonymous-sessions | anonymous_sessions |
| Jogador clica "Jogar de Novo" | Unknown | fetch POST (fire-and-forget, sem await) | anonymous_sessions |
Resultado: 100% das partidas são capturadas. Nenhum registro se perde, independentemente do comportamento do jogador.
Estado Interno: pendingSession e sessionResolved
O sistema funciona com duas variáveis de estado no cliente:
pendingSession— Objeto criado no game over contendo os dados da partida: score, tier (índice e nome), boosts, spread, tempo sobrevivido (inteiro) e dificuldade (float com 2 casas decimais). Ficanullquando não há dados pendentessessionResolved— Flag booleana. Começafalseno game over. Viratruequando a sessão é resolvida (salva, pulada ou capturada via beacon). Impede envio duplicado
A cada game over, pendingSession recebe 7 campos calculados do estado do jogo: score, tier (0–6), tierName ("TIER I" a "TIER VII"), boosts (total coletado), spread (nível atual), time (segundos sobrevividos) e difficulty (multiplicador na morte). O campo type ainda não existe — ele é adicionado no momento da resolução.
Caminho 1: Jogador Salva — Score Registrado
Quando o jogador digita um nome (até 20 caracteres) e clica "Salvar & Ranking":
- O nome é sanitizado:
trim(),toUpperCase(),slice(0, 20). Se vazio, o clique é tratado como "Skip" e a partida vira sessão anônima - O record é enviado via
fetchPOST para/api/scorescom o nome sessionResolved = trueependingSession = null— a sessão foi resolvida- O leaderboard abre com o score do jogador destacado e auto-scrollado
Nenhuma sessão anônima é criada. O jogador optou pelo ranking — seus dados vão para a coleção pública.
Caminho 2: Jogador Pula — Sessão "Unregistered"
Quando o jogador clica "Skip" na tela de game over:
- Verifica:
pendingSessionexiste e!sessionResolved? Se sim, continua - Adiciona
type: 'Unregistered'ao objeto e envia viaAnonymousDB.save()— umfetchPOST padrão para/api/anonymous-sessions sessionResolved = trueependingSession = null- O leaderboard abre sem destaque (o jogador não tem score no ranking)
O jogador conscientemente escolheu não aparecer. O tipo 'Unregistered' reflete essa decisão ativa.
Caminho 3: Jogador Fecha a Aba — navigator.sendBeacon
Este é o caminho mais crítico — e o mais difícil de implementar corretamente. Quando o jogador fecha a aba (ou navega para outro site), a página está sendo destruída. Chamadas fetch tradicionais são canceladas pelo navegador durante o beforeunload.
A solução é navigator.sendBeacon() — uma API projetada especificamente para enviar dados durante o encerramento da página:
- Fire-and-forget — Não retorna response. Não pode falhar. O navegador garante o envio mesmo durante unload
- Non-blocking — Não atrasa o fechamento da aba
- POST body via Blob — O dado é empacotado em um
new Blob([JSON.stringify(session)], { type: 'application/json' }) - Suporte universal — Todos os navegadores modernos suportam sendBeacon (Chrome 39+, Firefox 31+, Safari 11.1+)
O Navistron registra um listener de beforeunload que verifica: se pendingSession existe e !sessionResolved, envia via sendBeacon com type: 'Unknown'. O jogador nunca sabe que seus dados foram capturados.
Caminho 3b: Safety Net — "Jogar de Novo" Sem Salvar
Existe um quarto cenário sutil: o jogador não clica em "Salvar" nem em "Skip" — ele vai direto para "Jogar de Novo". A função startGame() inclui uma safety net:
No início de cada novo jogo, verifica se pendingSession ainda existe e !sessionResolved. Se sim, envia os dados via AnonymousDB.save() com type: 'Unknown' — mas sem await (fire-and-forget para não atrasar o início do novo jogo).
Depois, reseta pendingSession = null e sessionResolved = false para o novo ciclo de jogo.
API: Sanitização e Whitelist de Tipos
A API POST /api/anonymous-sessions aplica as mesmas regras de sanitização que o endpoint de scores registrados, com uma adição: o campo type é validado contra uma whitelist:
- Se
body.typeé'Unregistered'ou'Unknown'→ aceita - Qualquer outro valor → fallback para
'Unknown' - Todos os campos numéricos:
Number() || 0(ou|| 1para spread e difficulty) playedAt: semprenew Date()no servidor — nunca vem do cliente
O documento salvo na coleção anonymous_sessions tem 9 campos: type, score, tier, tierName, boosts, spread, time, difficulty, e playedAt.
Telemetria: Combinando Dados Anônimos e Registrados
Os dados anônimos ganham valor quando combinados com os registrados. A camada de stats do Navistron não consulta as duas coleções separadamente: um único pipeline começa em scores, anexa anonymous_sessions com $unionWith e marca cada documento com registered: true|false. A partir daí, cada métrica decide o que considera:
| Métrica | Fonte | Como |
|---|---|---|
| Total de Partidas | registradas + anônimas | $sum: 1 sobre o fluxo unido, mais um $cond para separar os dois números |
| Horas Jogadas | registradas + anônimas | $sum: '$time' |
| Maior Score | registradas + anônimas | $max: '$score' |
| Score Médio / Tempo Médio | registradas + anônimas | $avg sobre todas as partidas do recorte |
| Pilotos Únicos | registradas apenas | $match: { registered: true } + $group por nome |
| Ranking por Piloto | registradas apenas | Sessões anônimas não têm nome para agrupar |
A página pública de telemetria exibe os cards "Registradas" (verde) e "Anônimas" (laranja) lado a lado, e o filtro de tipo permite ver só as anônimas, com a nota: "Nenhum dado pessoal é coletado — apenas métricas de gameplay."
Visualizando Dados Anônimos
Na mesma página, os gráficos e tabelas respeitam o filtro de tipo:
- Cards de métricas: partidas, registradas, anônimas, pilotos únicos, horas jogadas, tempo médio, score médio, maior score
- Gráfico de barras empilhadas: partidas por dia com registradas e anônimas em cores distintas
- Distribuições: tiers alcançados, faixas de score e horários de pico
- Lista de partidas: cada sessão anônima aparece como "ANÔNIMO", com score, tier, tempo e data, ordenável e paginada
Esses dados revelam padrões invisíveis: em que tier os jogadores abandonam? Qual o score médio de quem não se registra? As respostas informam decisões de design do jogo. O campo type (Unregistered ou Unknown) continua gravado no banco para análises internas, mas a interface pública não faz essa distinção.
Privacidade: Nenhum Dado Pessoal
O sistema anônimo do Navistron não coleta nenhum dado pessoal:
- Sem IP — O endereço IP não é armazenado no documento
- Sem cookies de tracking — Nenhum cookie é definido em sessões anônimas
- Sem fingerprinting — Nenhuma informação do navegador ou dispositivo é coletada
- Sem identificadores persistentes — Cada sessão é um documento independente, sem link entre partidas do mesmo jogador
- Apenas gameplay — Os 9 campos salvos são puramente métricas do jogo: score, tier, boosts, spread, tempo, dificuldade, tipo e data
Isso mantém conformidade com regulamentações de privacidade (LGPD, GDPR) sem precisar de banners de consentimento — não há dados pessoais para consentir.
FAQ — Perguntas Frequentes sobre Scores Anônimos
O navigator.sendBeacon funciona em todos os navegadores?
Sim, em todos os navegadores modernos: Chrome 39+, Firefox 31+, Safari 11.1+, Edge (Chromium). Cobrindo mais de 98% dos usuários ativos. Para navegadores muito antigos sem suporte, a partida simplesmente não é capturada — o sistema falha silenciosamente sem impactar a experiência.
O sendBeacon pode falhar durante o fechamento da aba?
Raramente. O sendBeacon foi projetado especificamente para esse cenário — ele enfileira a requisição no browser antes da página ser destruída. O navegador garante a entrega mesmo após o unload. No Navistron, a taxa de captura de sessões Unknown é consistente com o esperado.
Por que gravar "Unregistered" e "Unknown" se a interface mostra só "anônimo"?
Porque representam comportamentos fundamentalmente diferentes. Unregistered = o jogador viu a tela de game over e ativamente clicou "Skip". Unknown = o jogador fechou a aba sem interagir. O primeiro grupo tomou uma decisão consciente; o segundo pode ter tido problemas (crash, frustração, distração). Guardar o tipo permite análise de retenção mais precisa no banco, enquanto o público vê uma única categoria simples: partidas anônimas.
Os dados anônimos afetam o ranking?
Não. Sessões anônimas vão para a coleção anonymous_sessions, que é completamente separada da coleção scores (usada pelo ranking público). Os dados anônimos alimentam apenas a telemetria agregada — totais, médias e distribuições. Nenhuma sessão anônima aparece no leaderboard.
Preciso de consentimento (LGPD/GDPR) para salvar dados anônimos?
Quando os dados são verdadeiramente anônimos (sem IP, sem cookies, sem identificadores persistentes, sem dados pessoais), geralmente não é necessário consentimento explícito sob a LGPD (Art. 12) e GDPR (Recital 26). O Navistron salva exclusivamente métricas de gameplay — score, tier, tempo — sem qualquer dado que identifique ou possa identificar o jogador. Consulte um jurista para casos específicos.
