SOTA Log Parser v0.0.4 - 可供下载!
由英文机器翻译而来。 查看原文
日志早已知道的事
一次 sotalogparser 之旅,以及那些塑造了它的错误答案。
《Shroud of the Avatar》记录下了一切。每一次挥击、每一次捕捉、每一次失败的大师之作、每一个在你附近说过的字,都带有时间戳,以纯文本形式存放在你已拥有的文件夹中。一个玩了两年的玩家,手头就有几百万行这样的记录。
几乎没有人去读它们,因为聊天日志不是一份记录——它是制作记录的原材料。这是一个负责阅读的应用:一个跨平台的桌面应用,将该文件夹解析为可查询的索引,并以十三种方式报告。
这些规则是基于一个真实的语料库推导出来的,该语料库包含384万行,识别率为99.91%。正是这个语料库,让这篇文章中有趣的部分得以存在。下面每一个不显而易见的决定,起初都是一个看似合理的答案,而语料库证明了它是错误的。

事物的形态
三层结构,严格单向依赖。
| 解析、发现、索引、报告。无 UI 依赖。 |
| Avalonia 视图和视图模型。 |
| 仅入口点。 |
.NET 10、Avalonia 12、MVVM。一个代码库为六个运行时生成自包含的单文件构建——Windows、macOS 和 Linux,各自 x64 和 arm64。所有值得测试的内容都在 Core 中,这就是为什么有 522 个测试且没有一个需要窗口。
流水线
LogLineReader 拆分 [时间戳] 消息 并折叠续行。ChatLogParser 运行一个有序的规则列表,先匹配者胜出。未被任何规则认领的内容成为 UnrecognizedEvent。
最后一部分是一条规则,而不是一个后备方案。绝不丢弃一行。未识别桶是规则集缺口唯一可见的机制——静默丢弃不匹配的内容会恰好隐藏修复它所需的信息。该应用会显示覆盖率百分比,并允许你将日志过滤到仅剩未认领的行,这就是 99.91% 之所以成为 99.91% 的原因。
索引
一个日志文件对应一个 LogSummary。整个语料库中的所有内容都通过一个 SQLite 索引处理:解析、附加场景上下文、写入事实行、每个文件一个事务,仅重新读取大小或修改时间发生变化的文件。384万行索引大约在二十秒内完成,占用约 19 MB。
该索引是一个可丢弃的缓存,绝不是事实来源。模式或解析器变更会提升版本号,文件会被删除并重建,而不是迁移。其中没有任何内容不能从日志中重新生成,因此其中没有任何内容值得冒迁移错误的风险。
关于它,有两件事是语料库教会我们的。
解析器对每一行都是无状态的,因此只有区域变更行知道任何事件发生的地点。一个追踪器会锁定最后进入的场景,并在会话开始时重置。大约四分之一真实事件发生在任何 Entering 行之前——它们落入 (unknown) 场景,这是一个报告必须显示的真实桶,而不是需要过滤掉的缺口。
战斗有方向性。语料库中四分之一的攻击行是怪物击中玩家,它们的能力看起来和玩家的一模一样。如果不按方向拆分,“你的技能”列表会包含玩家从未使用过的技能。
十三份报告
导航栏对它们进行分组。每个报告在首次打开时懒加载,并在日期、场景或角色过滤器更改时重新加载;所有内容都可导出为 CSV。
概览和日志浏览器
索引覆盖了什么,以及解析器所看到的单个日志的每一行。浏览器是为上述覆盖规则而存在的——过滤到 Unrecognized,你看到的就是解析器剩余的盲点,按顺序排列。
战斗——技能 vs 怪物
每个技能对每个目标的表现如何,以及某个配对在何处超越了其余数据所预测的结果。65 个技能与 282 个目标交叉是一个双向表,而有趣的单元格是那个同时超越其行和列的单元格。三个选择,每一个起初都是错误的:
中位数抛光,而非均值。使用均值时,一个异常单元格会拉高其自身技能行和目标列,因此每个相邻项都显得有抗性。一个真实发现,六个虚构的。
不加权。对中位数加权会让任何持有其行超过一半观测值的单元格定义自己的基线——而这正是你最想衡量的、被大量刷取的配对。
同时设置离散度门槛和样本门槛。一个仅对两个目标使用的技能没有独立的基线,因此其两个单元格之间的差距看起来像是一个巨大的交互效应。针对真实语料库,这一个门槛就是一个可辩护的发现与八个伪影之间的区别。
战斗——什么击中了你

镜像问题,也是最新的报告:哪些生物攻击你,用什么攻击,造成多少伤害,以及你的防御有多频繁地阻止它。它绝不是不是上一份报告的反向箭头——重用外向机制会在四个方面出错,每一个都会产生一个看似合理的数字,而不是一个可见的失败。
超过一半的来袭火力落在宠物身上。
尝试次数 | |
|---|---|
瞄准玩家 | 51,138 |
瞄准宠物 | 64,248 |
而且它们的行为完全不同:瞄准玩家的攻击中有 52% 命中但未造成任何伤害,而瞄准宠物的攻击中只有 0.5%。合并后,防御概况既不属于前者也不属于后者。宠物被排除在外——但它们的份额被报告出来,因为“这覆盖了投向你的攻击的 44%”是读者有权知道的信息。
显而易见的减伤数字遗漏了大部分减伤。外向报告中的公式计算闪避、格挡、招架和擦伤,得出 18% 的来袭伤害。但 52% 的来袭攻击是命中但未造成任何伤害——被该公式排除在外。诚实的数字是66.2%。同时,格挡和抗性在来袭行中完全不存在——在 384万行中各有零个——因此这些列被省略,而不是显示为结构性零,因为一个始终为零的列读起来像是“你从未格挡过任何东西”。
闪避从不指明闪避了什么。所有 7,120 次来袭闪避和所有 17,474 次外向闪避都落在未命名桶中,无论是什么引发了它们。因此每个按能力计算的比率都除以确实命名了能力的攻击,而闪避率则存在于按生物汇总的层面,在那里它是真实的。
排名依据是每次挥击的伤害,因为三种看似合理的排序几乎不一致:
尝试次数 | 每次挥击 | 每次命中 | 穿透率 | |
|---|---|---|---|---|
巨型鳄鱼,基础 | 4,310 | 2.1 | 58 | 4% |
剃刀猪 (T18),基础 | 217 | 23.4 | 461.5 | 5% |
活力卡瓦斯 (T14),暗影爆裂 | 309 | 247.9 | 247.9 | 100% |
按频率排名,报告会以一只造成两点伤害的鳄鱼开头。按每次命中伤害排名,它会以一个二十分之一概率发生的事件开头。只有每次挥击将命中率纳入考量,并回答这要花我多少代价。
战斗——记录

个人最佳:每个技能的最大暴击、承受的最重一击,每条都可追溯到其来源行。这是唯一一份必须撤销应用其余部分所依赖的汇总的报告。宠物伤害归功于主人,这对总量是正确的,但对记录来说是错误的:
玩家 | 宠物 | |
|---|---|---|
最大暴击 | 1,843 (连锁闪电) | 6,735 (毒蛇之刺) |
承受的最重一击 | 1,206 | 2,628 |
落在宠物身上的攻击比落在玩家身上的多——64,245 对 44,329——因此“你承受的最重一击”除非你检查,否则可能是别人的瘀伤。最佳记录按角色分别计算,从不合并,平局时取最早者,持续伤害被排除在外:语料库中最大的持续伤害是 13,555,超过了所有攻击,将其合并进去会静默地回答一个不同的问题。
世界——场景活动
时间花在了哪里,每个地方适合做什么。有三件事必须做对:
一个比率需要一个你可信的分母。在 Arlia 的三分钟收集了一个 10,000 XP 的奖励——每小时 206,897,仅次于一个被刷了八十小时的地点。每个场景的数字仍然显示,旁边是它来自的分钟数,但排名应用了 30 分钟的下限,并说明排除了多少个场景。
击杀和死亡是同一行从两端读取。281 行死亡行是 246 次击杀、22 次死亡、15 次旁观者和 2 次自杀。按行计数报告的击杀数比实际多出五分之一,并隐藏了每一次死亡。
尸体战利品是战斗;采集是收集。Tanglemire 记录了 743 次攻击、1,305 次采集和零次尸体战利品。一个合并的战利品桶会以任何总数较大者来标记,因此一个有狼的钓鱼点会变成一个战斗区域。
世界——战利品和钓鱼
掉落来自哪里,每个地方实际产出什么;每次捕获按钓鱼地点、水域来源和物种进行透视。
制作——配方
每个配方的卓越率,大师之作可以推到多远,以及代价是什么。区间使用 Wilson 得分区间,而非正态近似——这使上界在八次成功七次时超过 1,在零次成功十二次时坍缩为一个点。还有三点:
一次尝试就是一次制作。两行完成行是替代关系,绝不会同时出现。语料库立即暴露了这个错误:Dark Red Dye 记录了 73,231 次普通和 73,433 次卓越,因此将卓越视为尝试的子集会得出超过 100% 的成功率。
大师之作失败会命名比尝试层级低一级的物品。按打印的数字计数会凭空发明一条平滑曲线。
显著性和效应量是分开的量。区间决定一个配方是否真的与整体率不同;观察到的率决定它是否差异到值得提及。混淆它们会标记三个聚集在 45% 附近的食物配方之一,而让它的两个邻居未被标记。
更多——进度
获得的经验和训练的技能。安静的片段被保留——仅由有事件发生的日子构建的序列会把一个月画成一段不间断的运行,这与读者打开它想发现的内容恰恰相反。
而且步骤不是等级。游戏不会宣布每一个等级:裁缝运行 44、48、51、54,因此 85 次公告覆盖了 117 个等级。报告显示步骤和观察到的范围,并标记那些经过的等级多于公告的行。“获得的等级”根本不在日志中,报告如实说明,而不是猜测。
相关的陷阱,贯穿整个语料库:经验读数是一个运行总数,每个池有四种不同的读数——终身获得、角色池、共享池,以及它们的总和,作为一个块写在一起。读数总和为1.57万亿,而实际授予的只有1.87亿。奖励按种类正向选择,绝不用“任何不是读数的东西”这样的表述,因为这种表述会静默地接纳以后添加的每种读数类型。
更多——对话
扁平的聊天流被重新编织成交流。日志中根本没有对话标记,因此每当谈话停止十分钟,一个运行就会被拆分。两条规则赢得了它们的地位:一个只有玩家而没有其他人的运行不是对话(否则在 Traders 上做广告的会话会用单方面的“交流”填满报告),以及没有时间戳的消息保持其文件位置(将空值排序到前面会打开一个与整个对话宽度相同的缺口)。
“我”按行针对日志自身的角色解析——一个有六个角色的语料库对我是谁有六个答案。
更多——经济
最值得注意的是它拒绝计算什么。日志没有价格,也没有购买行,因此售出单位是可知的,而收入则不是。任何地方唯一的金币数字是 你捡起了 N 金币。,这与你的商人无关。报告如实说明,因为打印在销售表旁边的金币数字会被读作收入。
它确实呈现的是常设订单拆分——订单履行与浏览——这是数据中卖家可以采取行动的唯一一件事,并且在语料库中干净地分开了:每种原材料 100% 订单,每种精制商品 100% 浏览。
更多——交易

玩家对玩家的交易被重新折叠在一起。一笔交易是一串行——打开、放上物品和收回、关闭——没有一行说明什么易手了。四件事:
金币是一个运行总数,而不是一个变化。该行说明新的数字。语料库中一笔真实交易在完成前将金币运行到 99,000 再回到 0;将这些行相加会报告从未移动过的九万九千金币。
双方被不对称地记录。游戏记录伙伴添加和移除物品,但只记录玩家添加——从不记录撤回。因此玩家一方是“提供了什么”,可能被夸大;伙伴一方是精确的。报告说明哪一方是哪一方。
大多数“交易”是与自己进行的。106 笔完成的交易中有 64 笔是玩家自己的小号之间进行的,当两个小号的日志都存在时,同一笔交易被存储两次,每方一次。已标记并可过滤。
伙伴名称有时会被游戏弄乱——
Zealoty,,1]Zealoty。那是在日志中的,不是解析器做的。修复只匹配那种确切的双重形状,因为真实名称包含逗号。
配套网站
该应用可以选择登录一个配套网站并发布四个数据集:个人最佳、钓鱼捕获、技能对目标单元格和怪物能力。
后两个与前两个不同,这种差异必须被尊重。它们是累积总数,因此重新发布是替换而不是累积——这意味着发布必须始终不受日期和场景过滤,因为在替换语义下,一个过滤后的切片不会发送较弱的数字,它会删除更完整的数字。一个超过服务器单元格上限的角色会被整体跳过,而不是截断,原因相同。
上传的是充分统计量——计数、Σln(1+伤害)、Σln(1+伤害)²——而不是拟合结果,因为中位数抛光不能跨玩家合并,而总和可以。它们是可加的,这正是精确的社区合并和常数时间留一法成为可能的原因。(1 + 是承重的:逆运算是 exp(x) − 1,用普通 exp 读回在几百伤害时差不到百分之一,在三千时差三分之一。处处看似合理,处处错误。这就是网站第一个实现发布的方式。)
语料库在这里也教会了一件事。一个能力在很大程度上是其生物的属性——74 个能力中有 22 个恰好属于一个生物,272 个生物中有 169 个有两个或更少的能力。因此没有双向模型可拟合;取而代之的是,一个能力仅在三个或更多生物使用它时才与自身比较,留一法,因此持有该能力大部分观测值的生物不能定义自己的基线并报告自己为普通。土元素持有地震 3,637 次挥击中的 2,955 次,结果为 0.08×;一个包含自身的池会读作约 1.0,什么也说不出来。
账户是可选的,并保持可选。恢复会话读取一个文件,不发出任何请求。第一个数据包在你点击登录或发布时离开机器,绝不会更早。没有报告依赖登录状态,发布控件在未登录时是隐藏的,而不是显示为禁用。
隐私
聊天日志包含其他玩家的消息,这一事实约束了代码,而不是在政策中被提及。
索引存储原始消息文本——那就是对话报告。它位于你的应用数据目录中,在仓库之外,其中任何内容都不会被上传。未来的任何负载都不应在未经用户明确同意的情况下改变这一点。
发布确实发送的是角色名称、技能和生物名称、数字、场景和时间。其中一个值得注意:日志从不说攻击者是生物还是另一个玩家,因此攻击者名称可能是真实人物的。这就是为什么发布在首次上传前会为每个数据集显示实际行,而不是仅计数。上传是对外的,无法从这里撤销。
真实日志被 git 忽略;测试夹具是手工编辑过的。
它在哪里
端到端工作。十三份报告,导航栏中没有占位符,522 个测试,六个发布目标。解析器覆盖了语料库的 99.91%,剩余的 0.09% 在应用中是可见的,而不是被扫走。
如果有一条贯穿始终的主线,那就是语料库是对的,推理是错的,一次又一次。上面每一个以“三件事”开头的标题,都是一串看起来正确的数字,直到有人用三百万行实际发生的事去检查它们。这就是整个方法:用语料库检查模式,而不是推理游戏大概会写什么。
Downloads
- ⬇ SotaLogParser-0.0.4-linux-arm64-0.0.4.zip41 MB · 4 downloadssha256 4730c85a2f0e24fd99c482c910bc85555ae7fe0f3020fd05d6d99c9bda8451db
- ⬇ SotaLogParser-0.0.4-linux-x64-0.0.4.zip43 MB · 5 downloadssha256 f2b238d29931a4870c9b3833383f77f2738598cf7cf1134d7d8ca4a3464025a4
- ⬇ SotaLogParser-0.0.4-osx-arm64-0.0.4.zip44 MB · 5 downloadssha256 19b63abac37cdc3978b9e1912075ca5d0c68b2c06f9e049e6ef0d7e49849e26b
- ⬇ SotaLogParser-0.0.4-win-arm64-0.0.4.zip68 MB · 6 downloadssha256 290855d72cf515bdcc9be5595b84a17e33e7eded4c930c3cef93fcd18de7f499
- ⬇ SotaLogParser-0.0.4-osx-x64-0.0.4.zip46 MB · 5 downloadssha256 8403aa7488fa4c58a833b98e5e11106730c3db1f691596dbe998847bc8182cbd
- ⬇ SotaLogParser-0.0.4-win-x64-0.0.4.zip70 MB · 8 downloadssha256 9e8ba3948b49a650859eb28859cc8aec104ae98db27fce708470e37454537266
