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árioTipo de SessãoComo é CapturadaColeção MongoDB
Jogador salva com nomeRegistradafetch POST para /api/scoresscores
Jogador clica "Skip"Unregisteredfetch POST para /api/anonymous-sessionsanonymous_sessions
Jogador fecha a abaUnknownnavigator.sendBeacon para /api/anonymous-sessionsanonymous_sessions
Jogador clica "Jogar de Novo"Unknownfetch 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). Fica null quando não há dados pendentes
  • sessionResolved — Flag booleana. Começa false no game over. Vira true quando 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":

  1. O nome é sanitizado: trim(), toUpperCase(), slice(0, 20). Se vazio, recebe "ANONYMOUS"
  2. O record é enviado via fetch POST para /api/scores com o nome
  3. sessionResolved = true e pendingSession = null — a sessão foi resolvida
  4. 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:

  1. Verifica: pendingSession existe e !sessionResolved? Se sim, continua
  2. Adiciona type: 'Unregistered' ao objeto e envia via AnonymousDB.save() — um fetch POST padrão para /api/anonymous-sessions
  3. sessionResolved = true e pendingSession = null
  4. 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 || 1 para spread e difficulty)
  • playedAt: sempre new 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 função getAggregatedStats() no data layer do Navistron faz queries em ambas as coleções em paralelo:

MétricaFonteCombinação
Total de Jogosscores + anonymous_sessionsSoma dos dois counts
Tempo Total de Jogoscores + anonymous_sessionsSoma dos totalTime de ambos
Maior Scorescores + anonymous_sessionsMath.max entre os dois highestScore
Score Médioscores apenasRegistrados somente (mais representativo)
Tempo Médioscores apenasRegistrados somente
Pilotos Únicosscores apenasSomente nomes registrados

A página pública de telemetria exibe ambos separadamente: "Jogos Registrados" (verde) e "Sessões Anônimas" (laranja), com uma nota explicativa: "Nenhum dado pessoal é coletado — apenas métricas de gameplay."

Dashboard: Visualizando Dados Anônimos

O dashboard privado do Navistron tem uma seção dedicada "ANONYMOUS TELEMETRY" que busca dados de GET /api/anonymous-sessions — 7 aggregations paralelas do MongoDB:

  • 7 cards de métricas: Total Anônimos, Unregistered ("Clicked Skip"), Unknown ("Left Without Action"), Tempo de Jogo, Score Médio, Tempo Médio, Maior Score
  • Gráfico de linha empilhada: Sessions por dia nos últimos 30 dias, com Unregistered (laranja) e Unknown (rosa) empilhados
  • Gráficos de rosca: Distribuição por tipo de sessão e por tier alcançado
  • Gráfico de barras: Distribuição de scores em faixas (0-50, 50-100, ..., 10K+)
  • Tabela de sessões recentes: 15 últimas com emoji indicador — ⏭ para Unregistered, ❓ para Unknown

Esses dados revelam padrões invisíveis: em que tier os jogadores abandonam? Qual o score médio de quem não se registra? Quantas sessões são abandonadas vs. puladas? As respostas informam decisões de design do jogo.

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 separar "Unregistered" de "Unknown"?

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). Distinguir os dois tipos permite análise de retenção mais precisa.

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.