O Navistron possui um sistema de telemetria pública que captura dados de todas as sessões de jogo — registradas e anônimas — e os apresenta em dashboards acessíveis a qualquer visitante. O diferencial: nenhuma informação pessoal é coletada. Sem IP, sem cookies, sem fingerprinting, sem contas. Neste artigo, vamos explorar cada camada do sistema: coleta, armazenamento, processamento e visualização.
Os Três Caminhos dos Dados: Registrado, Unregistered e Unknown
Quando uma partida termina no Navistron, a função endGame() cria um objeto pendingSession com 7 campos de gameplay: score, tier, tierName, boosts, spread, time e difficulty. Esse objeto fica em espera até que o jogador tome uma ação. Dependendo do que acontece a seguir, os dados seguem um de três caminhos:
- Registrado — O jogador digita um nome e clica "Save & Ranking". Os dados vão para a collection
scorescom o nome do piloto. Uma flagsessionResolved = truemarca a sessão como resolvida. Nenhum registro anônimo é criado. - Unregistered — O jogador clica "Skip". Os dados são salvos na collection
anonymous_sessionscomtype: "Unregistered"viafetchpadrão. O jogador deliberadamente optou por não se registrar. - Unknown — O jogador fecha a aba ou inicia um novo jogo sem resolver a sessão pendente. Os dados são salvos com
type: "Unknown". Se o evento ébeforeunload, usanavigator.sendBeacon()para garantir envio mesmo durante o fechamento da página.
Dessa forma, nenhuma partida é perdida: toda sessão gera dados de telemetria, seja no ranking público ou nas estatísticas anônimas.
sendBeacon: Capturando Dados ao Fechar a Aba
O navigator.sendBeacon() é uma API do navegador projetada especificamente para enviar dados de telemetria durante o fechamento de uma página. Diferente do fetch tradicional, o sendBeacon é "fire-and-forget" — o navegador garante o envio mesmo que a aba esteja sendo destruída.
O Navistron usa o sendBeacon no handler de beforeunload: quando o jogador fecha a aba com uma sessão pendente, os dados são serializados em um Blob com tipo application/json e enviados para /api/anonymous-sessions. Isso captura jogadores que simplesmente abandonam o jogo sem clicar em nenhum botão — uma parcela significativa que seria invisível sem essa técnica.
Para as ações de "Skip" e "novo jogo sem resolver", o salvamento usa fetch assíncrono normal, já que a página continua aberta e pode aguardar a resposta.
As 4 Collections do MongoDB
O banco de dados navistron no MongoDB utiliza 4 collections para telemetria:
- scores — Partidas registradas com nome do piloto. 9 campos: name, score, tier, tierName, boosts, spread, time, difficulty, playedAt. Alimenta o ranking e perfis de jogador.
- anonymous_sessions — Partidas não registradas. 9 campos: type (Unregistered/Unknown), score, tier, tierName, boosts, spread, time, difficulty, playedAt. O campo
typeé validado no servidor — valores fora de "Unregistered" e "Unknown" são rejeitados e convertidos para "Unknown". - sponsors — Dados de patrocinadores: name, link, linkText, value, totalClicks, createdAt. O campo
totalClicksé incrementado atomicamente a cada clique. - sponsor_clicks — Log de eventos de clique em links de patrocinadores: sponsorId, clickedAt, userAgent e referer. Esta é a única collection que armazena User-Agent, e apenas para análise de cliques em patrocinadores.
Aggregation Pipelines: 9 Métricas em Paralelo
O coração do sistema de telemetria é a função getAggregatedStats(period), que executa 9 aggregations em paralelo combinando dados de scores e anonymous_sessions. Os períodos suportados são: all (todo o histórico), daily (últimas 24 horas), monthly (últimos 30 dias) e yearly (últimos 365 dias).
As métricas computadas incluem:
- Totais — Total de partidas registradas (
countDocumentsem scores) + anônimas (em anonymous_sessions) - Tempo de jogo — Soma total (
$sum: '$time') e média de tempo de sobrevivência, combinando ambas as collections - Scores — Score médio (
$avg: '$score') e maior score de todos os tempos ($max), comparando registrados e anônimos - Pilotos únicos — Contagem de nomes distintos na collection scores (
$group: { _id: '$name' }) - Jogos por dia — Agrupamento por data com zero-fill para os últimos 30 dias
- Distribuição de tiers — Agrupamento por tierName para análise de progressão
- Top jogadores — Top 10 por melhor score, com contagem de partidas e tempo total
- Jogos recentes — Últimas 10 partidas com todos os campos
Ao usar Promise.all() para executar as 9 queries em paralelo, o tempo de resposta é determinado pela query mais lenta — não pela soma de todas.
Dashboard: 8 Gráficos com Chart.js
O Dashboard (/dashboard) é uma página client-side que busca dados de duas APIs em paralelo: GET /api/stats (scores registrados) e GET /api/anonymous-sessions (sessões anônimas). Os dados são visualizados em 8 gráficos interativos:
- Jogos por Dia (30 dias) — Gráfico de linha mostrando tendência de atividade diária
- Horários de Pico (UTC) — Gráfico de barras com distribuição horária das partidas (24 horas)
- Distribuição de Tiers — Gráfico doughnut mostrando em qual tier os jogadores mais morrem
- Distribuição de Scores — Gráfico de barras com faixas de pontuação (0–50, 50–100, 100–200, ..., 50K+)
- Sessões Anônimas/Dia — Gráfico de linha empilhado separando Unregistered vs Unknown
- Tipo de Sessão — Doughnut comparando proporção Unregistered vs Unknown
- Tiers Anônimos — Doughnut de distribuição de tiers apenas em sessões anônimas
- Scores Anônimos — Barras com faixas de pontuação das sessões anônimas
Além dos gráficos, cards de métricas exibem: Total de Jogos, Tempo Total de Jogo, Pilotos Únicos, Score Médio, Tempo Médio, Maior Score, Jogadores Ativos (7 dias), e métricas equivalentes para sessões anônimas.
Stats Pages: Telemetria Pública com SSR e ISR
As páginas de estatísticas públicas são renderizadas no servidor com Incremental Static Regeneration, cada uma com intervalo de revalidação diferente:
- /stats (todo o histórico) — revalida a cada 5 minutos (300s)
- /stats/daily (últimas 24h) — revalida a cada 2 minutos (120s) — dados mais voláteis
- /stats/monthly (últimos 30 dias) — revalida a cada 5 minutos (300s)
- /stats/yearly (último ano) — revalida a cada 10 minutos (600s) — dados mais estáveis
Todas as páginas de stats exibem: Total de Jogos (registrados + anônimos), Pilotos Únicos, Tempo Total de Jogo, Score Médio, Tempo Médio, Maior Score, tabela de distribuição de tiers e tabela de jogos por dia (quando aplicável). Cada página inclui JSON-LD com schema schema.org/Dataset para indexação como dados estruturados.
Tracking de Patrocinadores: Cliques e Relatórios
O sistema de telemetria também acompanha a interação com patrocinadores. Quando um jogador clica em um link de patrocinador (no leaderboard in-game), duas operações são executadas no servidor:
- O campo
totalClicksdo documento do patrocinador é incrementado atomicamente ($inc: { totalClicks: 1 }) - Um evento de clique é registrado em
sponsor_clickscom: sponsorId, clickedAt, userAgent e referer
O painel admin (protegido por senha) apresenta relatórios com: receita total, total de cliques, cliques dos últimos 7 e 30 dias, tabela de cliques por patrocinador, e gráfico de cliques por dia.
Stats API: Métricas dos Registrados
A API GET /api/stats fornece 10 aggregations em paralelo exclusivamente sobre a collection scores: total de jogos, totais/médias de tempo e score, pilotos únicos, jogadores ativos na semana, jogos por dia (30 dias com zero-fill), horários de pico (24h), distribuição de tiers, top 10 jogadores (com best score, partidas e tempo total), últimos 10 jogos, e distribuição de scores por faixas.
A API GET /api/anonymous-sessions fornece 7 aggregations em paralelo sobre anonymous_sessions: total de sessões, contagem por tipo (Unregistered/Unknown), totais/médias de tempo e score, sessões por dia (30 dias empilhadas por tipo), distribuição de tiers, últimas 15 sessões, e distribuição de scores.
Privacidade: Zero Dados Pessoais
A telemetria do Navistron foi projetada com privacidade por padrão:
- Sem IP — Nenhuma API armazena endereço IP do jogador
- Sem cookies — Nenhum cookie de rastreamento é emitido pelo jogo
- Sem fingerprinting — Nenhum identificador de dispositivo, canvas fingerprint ou localStorage de tracking
- Sem contas/login — O nome do piloto é voluntário e digitado do zero a cada partida
- User-Agent apenas em cliques de patrocinador — A única informação semi-identificável é o User-Agent, armazenado exclusivamente na collection
sponsor_clickspara análise de dispositivos em cliques de links externos - Timestamps server-side — Todos os
playedAteclickedAtsão gerados no servidor comnew Date(), nunca confiando em dados do cliente - Dados públicos — Todas as estatísticas são acessíveis publicamente nas páginas /stats
FAQ — Perguntas Frequentes sobre a Telemetria do Navistron
Meus dados pessoais são coletados?
Não. A telemetria coleta exclusivamente dados de gameplay: score, tier, boosts, spread, tempo e dificuldade. Nenhuma informação pessoal — IP, email, localização, identificador de dispositivo ou cookie — é armazenada. O único dado "pessoal" é o nome de piloto, que é voluntário, pode ser qualquer texto, e só é registrado se o jogador clicar em "Save & Ranking".
Qual é a diferença entre sessões Unregistered e Unknown?
Unregistered significa que o jogador clicou o botão "Skip" — uma ação deliberada de não registrar o nome. Unknown significa que o jogador fechou a aba, navegou para outra página, ou iniciou um novo jogo sem decidir — a sessão foi capturada automaticamente via sendBeacon ou no início da próxima partida.
Os dados de telemetria são públicos?
Sim. Todas as estatísticas são acessíveis em /stats e suas subpáginas (diário, mensal, anual). O dashboard completo também é público, com gráficos interativos. As páginas de stats incluem metadata schema.org/Dataset para indexação como dados abertos.
Como os horários de pico são calculados?
A API agrupa todas as partidas registradas pela hora UTC do campo playedAt usando $hour do MongoDB, produzindo uma distribuição de 0 a 23. Horários sem partidas são preenchidos com zero. O resultado é exibido como gráfico de barras no dashboard, revelando quando a comunidade mais joga.
O sendBeacon garante 100% de captura?
Não com 100% de certeza. O sendBeacon é "best-effort" — funciona na grande maioria dos navegadores modernos, mas pode falhar em navegadores muito antigos ou em situações extremas (crash do navegador, kill forçado do processo). Ainda assim, a taxa de captura é significativamente superior a fetch em beforeunload, que frequentemente é cancelado pelo navegador.
