← Back to blog

SOTA Log Parser v0.0.4 - Pronto para download!

Traduzido automaticamente do inglês. Ver original

By admin · 7/31/2026

O que os logs já sabiam

Um tour pelo sotalogparser e pelas respostas erradas que o moldaram.

Shroud of the Avatar registra tudo. Cada golpe, cada captura, cada obra-prima fracassada, cada palavra dita perto de você, com carimbo de tempo, em texto simples, em uma pasta que você já possui. Um jogador de dois anos está sentado sobre alguns milhões de linhas disso.

Quase ninguém as lê, porque um log de chat não é um registro — é a matéria-prima para um. Este é um aplicativo que faz a leitura: um aplicativo de desktop multiplataforma que analisa essa pasta em um índice consultável e relata sobre ela de treze maneiras.

As regras foram derivadas contra um corpus real de 3,84 milhões de linhas e reconhecem 99,91% delas. Esse corpus é a razão pela qual as partes interessantes deste post existem. Cada decisão não óbvia abaixo foi uma resposta plausível primeiro, e o corpus é o que provou que ela estava errada.


A forma da coisa

Três camadas, estritamente unidirecionais.

SotaLogParser.Core

Análise, descoberta, indexação, relatórios. Sem dependência de UI.

SotaLogParser.App

Views e view models Avalonia.

SotaLogParser.Desktop

Apenas ponto de entrada.

.NET 10, Avalonia 12, MVVM. Um único código-fonte produz builds autônomos de arquivo único para seis runtimes — Windows, macOS e Linux, x64 e arm64 cada. Tudo que vale um teste vive no Core, e é por isso que há 522 deles e nenhum requer uma janela.

O pipeline

LogLineReader divide [timestamp] mensagem e dobra linhas de continuação. ChatLogParser executa uma lista ordenada de regras, a primeira correspondência vence. O que nada reivindica se torna um UnrecognizedEvent.

Essa última parte é uma regra, não um fallback. Nunca descarte uma linha. O balde de não reconhecidos é o único mecanismo pelo qual uma lacuna no conjunto de regras é visível — descartar silenciosamente o que não corresponde esconderia exatamente a informação necessária para corrigi-la. O aplicativo exibe a porcentagem de cobertura e permite filtrar um log apenas para as linhas não reivindicadas, que é como 99,91% chegou a 99,91%.

O índice

Um arquivo de log recebe um LogSummary. Tudo em todo o corpus passa por um índice SQLite: analisar, anexar contexto de cena, escrever linhas de fato, uma transação por arquivo, reler apenas arquivos cujo tamanho ou tempo de modificação mudou. 3,84M de linhas indexadas em cerca de vinte segundos em aproximadamente 19 MB.

O índice é um cache descartável e nunca uma fonte de verdade. Mudanças de esquema ou parser aumentam uma versão, e o arquivo é excluído e reconstruído em vez de migrado. Nada nele não pode ser regenerado a partir dos logs, então nada nele vale o risco de um bug de migração.

Duas coisas sobre isso que precisaram de um corpus para aprender.

O parser é sem estado por linha, então apenas uma linha de mudança de zona sabe onde algo aconteceu. Um rastreador trava a última cena entrando e reseta em um início de sessão. Aproximadamente um quarto dos eventos reais ocorre antes de qualquer linha Entering — eles caem em uma cena (unknown), que é um balde real que os relatórios devem mostrar, não uma lacuna a ser filtrada.

O combate tem uma direção. Um quarto das linhas de ataque no corpus são mobs atingindo o jogador, e suas habilidades parecem exatamente com as do jogador. Sem dividir por direção, "suas habilidades" lista habilidades que o jogador nunca usou.


Treze relatórios

A barra lateral os agrupa. Cada um carrega preguiçosamente na primeira abertura e recarrega quando o filtro de data, cena ou personagem muda; tudo exporta para CSV.

Visão geral e Navegador de logs

O que o índice cobre, e cada linha de um único log como o parser a viu. O navegador existe para a regra de cobertura acima — filtre para Não reconhecidos e você está olhando para os pontos cegos restantes do parser, em ordem.

Combate — Habilidades vs mobs

Como cada habilidade se sai contra cada alvo, e onde um par supera o que o resto dos dados prevê. 65 habilidades cruzando 282 alvos é uma tabela de duas vias, e a célula interessante é aquela que supera tanto sua linha quanto sua coluna. Três escolhas, cada uma das quais estava errada primeiro:

  • Polish mediano, não médias. Com médias, uma célula excepcional puxa para cima sua própria linha de habilidade e sua coluna de alvo, então cada vizinho lê como resistente. Uma descoberta real, seis inventadas.

  • Não ponderado. Ponderar a mediana permite que qualquer célula segurando mais da metade das observações de sua linha defina sua própria linha de base — que é precisamente o par mais cultivado que você mais quer medir.

  • Um limite de dispersão além de um limite de amostra. Uma habilidade usada contra apenas dois alvos não tem linha de base independente, então a lacuna entre suas duas células lê como uma interação enorme. Contra o corpus real, este único limite é a diferença entre uma descoberta defensável e oito artefatos.

Combate — O que atinge você

A questão espelhada, e o relatório mais novo: quais criaturas atacam você, com o quê, por quanto, e com que frequência suas defesas param. É enfaticamente não o relatório anterior com a seta invertida — reutilizar a maquinaria de saída teria sido errado de quatro maneiras, cada uma das quais produz um número plausível em vez de uma falha visível.

Mais da metade do fogo recebido atinge o pet.

tentativas

mirando no jogador

51.138

mirando no pet

64.248

E eles se comportam de maneiras nada parecidas: 52% dos golpes mirando no jogador conectam sem causar nada, contra 0,5% dos golpes mirando em um pet. Agrupados, o perfil defensivo não pertence a nenhum dos dois. Pets são excluídos — e sua parcela relatada, porque "isso cobre 44% do que foi jogado em você" é algo que um leitor merece saber.

A figura de mitigação óbvia omite a maioria da mitigação. A fórmula do relatório de saída conta esquivas, bloqueios, aparas e golpes de raspão, e chega a 18% de entrada. Mas 52% dos ataques recebidos são conectados, sem causar nada — excluídos por essa fórmula. A figura honesta é 66,2%. Enquanto isso, bloqueios e resistências estão ausentes das linhas de entrada inteiramente — zero de cada em 3,84M de linhas — então essas colunas são omitidas em vez de mostradas como zeros estruturais, porque uma coluna que é sempre zero lê como "você nunca bloqueou nada".

Uma esquiva nunca nomeia o que foi esquivado. Todas as 7.120 esquivas recebidas e todas as 17.474 de saída ficam no balde sem nome, seja o que for que as provocou. Então cada taxa por habilidade divide pelos ataques que nomearam uma habilidade, e a taxa de esquiva vive no roll-up por criatura onde é verdadeira.

E a classificação é por dano por golpe, porque as três ordenações plausíveis mal concordam:

tentativas

por golpe

por acerto

passa

Crocodilo Massivo, básico

4.310

2,1

58

4%

Razorback (T18), básico

217

23,4

461,5

5%

Vigorem Cavas (T14), Shadow Blast

309

247,9

247,9

100%

Classifique por frequência e o relatório abre em um crocodilo fazendo dois de dano. Classifique por dano por acerto e abre em um evento que acontece uma vez em vinte. Apenas por golpe dobra a taxa de conexão e responde quanto isso vai me custar.

Combate — Recordes

Recordes pessoais: maior crítico por habilidade, golpe mais forte sofrido, cada um rastreável até a linha de onde veio. Este é o único relatório que deve desfazer um roll-up do qual o resto do aplicativo depende. Dano de pet é creditado ao dono, o que é certo para totais e errado para um recorde:

jogador

pet

maior crítico

1.843 (Chain Lightning)

6.735 (Wyvern Sting)

golpe mais forte sofrido

1.206

2.628

Mais golpes atingem o pet do que o jogador — 64.245 contra 44.329 — então "o golpe mais forte que você levou" é um hematoma de outra pessoa a menos que você verifique. Recordes são por personagem e nunca agrupados, empates vão para o mais antigo, e dano ao longo do tempo é excluído: o maior desses no corpus é 13.555, que supera todos os ataques, e dobrá-lo silenciosamente responde a uma pergunta diferente.

Mundo — Atividade de cena

Onde o tempo foi, e para que cada lugar era bom. Três coisas que tem que acertar:

  • Uma taxa precisa de um denominador em que você possa confiar. Três minutos em Arlia coletaram uma recompensa de 10.000 XP — 206.897 por hora, segundo lugar à frente de um local cultivado por oitenta horas. A figura por cena ainda é mostrada, ao lado dos minutos de onde veio, mas as classificações aplicam um piso de 30 minutos e dizem quantas cenas isso excluiu.

  • Mortes e mortes são as mesmas linhas lidas de extremidades opostas. 281 linhas de morte são 246 mortes, 22 mortes, 15 espectadores e 2 auto-mortes. Contar linhas relata um quinto a mais de mortes do que foram feitas e esconde cada morte.

  • Saque de cadáver é combate; colheita é coleta. Tanglemire registra 743 ataques, 1.305 colheitas e nenhum saque de cadáver. Um balde de saque combinado o rotula pelo total que vence, então um local de pesca com lobos sai como uma zona de combate.

Mundo — Saque e Pesca

De onde vieram os drops e o que cada lugar realmente rende; cada captura pivotada por onde você pescou, pela água de onde veio e por espécie.

Artesanato — Receitas

Taxas excepcionais por receita, até onde uma obra-prima pode ser empurrada e quanto custou. Intervalos são o intervalo de pontuação de Wilson, não a aproximação normal — que coloca o limite superior acima de 1 em sete sucessos em oito e colapsa para um ponto em zero em doze. Mais três:

  • Uma tentativa é um artesanato. As duas linhas de conclusão são alternativas, nunca ambas. O corpus entrega o erro imediatamente: Dark Red Dye registra 73.231 comuns e 73.433 excepcionais, então tratar excepcional como um subconjunto de tentativas produz uma taxa de sucesso acima de 100%.

  • Uma falha de obra-prima nomeia o item um passo abaixo de onde a tentativa foi feita. Contabilizar pelo número impresso inventa uma curva suave do nada.

  • Significância e tamanho de efeito são quantidades separadas. O intervalo decide se uma receita realmente difere da taxa geral; a taxa observada decide se difere o suficiente para mencionar. Confundi-los sinalizou uma de três receitas de comida agrupadas perto de 45% e deixou seus dois vizinhos sem marcação.

Mais — Progressão

Experiência ganha e habilidades treinadas. Fatias silenciosas são mantidas — uma série construída apenas a partir de dias em que algo aconteceu desenha um mês como uma sequência ininterrupta, exatamente o oposto do que o leitor abriu para descobrir.

E passos não são níveis. O jogo não anuncia cada nível: Tailoring vai 44, 48, 51, 54, então 85 anúncios cobrem 117 níveis. O relatório mostra passos e o intervalo observado, e marca linhas onde mais níveis passaram do que foram anunciados. "Níveis ganhos" simplesmente não está no log, e o relatório diz isso em vez de adivinhar.

A armadilha relacionada, em todo o corpus: leituras de experiência são totais acumulados, e há quatro diferentes por pool — ganho vitalício, o pool do personagem, o pool compartilhado e sua soma, escritos juntos como um bloco. As leituras somam 1,57 trilhão contra 187 milhões realmente concedidos. Prêmios são selecionados positivamente por tipo, nunca como "qualquer coisa que não seja uma leitura", porque essa frase admite silenciosamente cada tipo de leitura adicionado depois.

Mais — Conversas

O fluxo de chat plano re-rosqueado em trocas. Não há marcador de conversa no log, então uma sequência é dividida onde quer que a conversa pare por dez minutos. Duas regras ganham seu lugar: uma sequência com ninguém além do jogador não é uma conversa (caso contrário, uma sessão passada anunciando no Traders enche o relatório com "trocas" unilaterais), e uma mensagem sem timestamp mantém sua posição no arquivo (classificar nulos para a frente abre uma lacuna da largura de toda a conversa).

"Eu" é resolvido por linha contra o próprio personagem do log — um corpus com seis personagens tem seis respostas para quem sou eu.

Mais — Economia

Mais notável pelo que se recusa a calcular. O log não tem preços nem linhas de compra, então unidades vendidas são conhecíveis e receita não é. A única figura de ouro em qualquer lugar é You picked up N gold., que não tem nada a ver com seu vendedor. O relatório diz isso, porque um número de ouro impresso ao lado de uma tabela de vendas será lido como receita.

O que ele superfície é a divisão de pedido permanente — pedido atendido contra navegado — que é a única coisa nos dados em que um vendedor pode agir, e que saiu claramente dividida no corpus: cada matéria-prima 100% pedidos, cada bem refinado 100% navegado.

Mais — Trocas

Trocas jogador a jogador dobradas de volta juntas. Uma troca é uma sequência de linhas — aberta, coisas colocadas e retiradas, fechada — e nenhuma linha única diz o que mudou de mãos. Quatro coisas:

  • Ouro é um total acumulado, não uma mudança. A linha declara a nova figura. Uma troca real no corpus leva o ouro a 99.000 e de volta a 0 antes de completar; somar as linhas relata noventa e nove mil de ouro que nunca se moveu.

  • Os dois lados são registrados assimetricamente. O jogo registra o parceiro adicionando e removendo itens, mas apenas o jogador adicionando — nunca retirando. Então o lado do jogador é "o que foi oferecido" e pode exagerar; o lado do parceiro é exato. O relatório diz qual é qual.

  • A maioria das "trocas" é com você mesmo. 64 de 106 trocas concluídas são entre os próprios alts do jogador, e onde os logs de ambos os alts estão presentes, a mesma troca é armazenada duas vezes, uma de cada lado. Sinalizado e filtrável.

  • O nome do parceiro às vezes é mutilado pelo jogoZealoty,,1]Zealoty. Isso está no log, não é algo que o parser faz. O reparo corresponde apenas a essa forma duplicada exata, porque nomes reais contêm vírgulas.


O site companheiro

O aplicativo pode opcionalmente entrar em um site companheiro e publicar quatro conjuntos de dados: recordes pessoais, capturas de pesca, células habilidade-vs-alvo e habilidades de monstros.

Os dois últimos são diferentes dos dois primeiros de uma maneira que deve ser respeitada. Eles são totais cumulativos, então uma republicação substitui em vez de acumular — o que significa que a publicação deve sempre ser sem filtro por data e cena, porque sob semântica de substituição, uma fatia filtrada não envia figuras mais fracas, ela exclui as mais completas. E um personagem acima do limite de células do servidor é pulado inteiro em vez de truncado, pela mesma razão.

O que sobe são as estatísticas suficientes — contagem, Σln(1+dano), Σln(1+dano)² — em vez do resultado ajustado, porque o polish mediano não agrupa entre jogadores e somas agrupam. Elas são aditivas, que é o que torna o agrupamento exato da comunidade e o leave-one-out em tempo constante possíveis. (O 1 + é estrutural: o inverso é exp(x) − 1, e lê-los de volta com exp simples está menos de um por cento errado em algumas centenas de dano e um terço errado em três. Plausível em todos os lugares e errado em todos os lugares. É como a primeira implementação do site foi lançada.)

Uma coisa que o corpus ensinou aqui também. Uma habilidade é em grande parte uma propriedade de sua criatura — 22 de 74 habilidades pertencem a exatamente uma, e 169 de 272 criaturas têm duas ou menos. Então não há modelo de duas vias para ajustar; o que o substitui compara uma habilidade contra si mesma apenas onde três ou mais criaturas a usam, leave-one-out, para que a criatura segurando a maioria das observações de uma habilidade não possa definir sua própria linha de base e se reportar como comum. Earth Elemental segura 2.955 dos 3.637 golpes de Earthquake e sai em 0,08×; um pool auto-inclusivo teria lido cerca de 1,0 e dito nada.

A conta é opcional e permanece opcional. Restaurar uma sessão lê um arquivo e não faz nenhuma solicitação. O primeiro pacote deixa a máquina quando você clica em Entrar ou Publicar, nunca antes. Nenhum relatório depende de estar conectado, e os controles de publicação são ocultos quando desconectado em vez de mostrados desabilitados.


Privacidade

Logs de chat contêm mensagens de outros jogadores, e esse fato restringe o código em vez de ser mencionado em uma política.

O índice armazena texto de mensagem bruto — isso é o relatório de conversas. Ele vive no diretório de dados do seu aplicativo, fora do repositório, e nada dele é nunca enviado. Nenhum payload futuro deve mudar isso sem perguntar ao usuário em palavras.

O que a publicação envia são nomes de personagens, nomes de habilidades e criaturas, figuras, cenas e tempos. Um deles merece cuidado: o log nunca diz se um atacante era uma criatura ou outro jogador, então um nome de atacante pode ser o de uma pessoa real. É por isso que a publicação mostra as linhas reais uma vez por conjunto de dados antes do primeiro upload em vez de apenas contá-las. O upload é voltado para fora e não pode ser desfeito daqui.

Logs reais são git-ignorados; fixtures de teste são manualmente redigidas.


Onde está

Funcionando de ponta a ponta. Treze relatórios, sem placeholders restantes na barra lateral, 522 testes, seis alvos de lançamento. O parser tem 99,91% do corpus e os 0,09% restantes são visíveis no aplicativo em vez de varridos.

Se há um fio condutor em tudo isso, é que o corpus estava certo e o raciocínio estava errado, repetidamente. Cada título acima que começa com "três coisas" é uma lista de números que pareciam corretos até alguém verificá-los contra três milhões de linhas do que realmente aconteceu. Esse é o método inteiro: verifique o padrão contra o corpus em vez de raciocinar sobre o que o jogo provavelmente escreve.

Downloads