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, o clique é tratado como "Skip" e a partida vira sessão anônima
  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 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étricaFonteComo
Total de Partidasregistradas + anônimas$sum: 1 sobre o fluxo unido, mais um $cond para separar os dois números
Horas Jogadasregistradas + anônimas$sum: '$time'
Maior Scoreregistradas + anônimas$max: '$score'
Score Médio / Tempo Médioregistradas + anônimas$avg sobre todas as partidas do recorte
Pilotos Únicosregistradas apenas$match: { registered: true } + $group por nome
Ranking por Pilotoregistradas apenasSessõ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.