一个开源项目里的两套寻路:从“明知已有,另起一套”到“引流用户”
MaaEnd 地图定位与寻路事件举证与论证
省流:MaaEnd 是一个游戏自动化工具。仓库里有两套地图导航——我的 MapNavigator(C++)和 isHarryh 的 MapTracker(Go)。两套不是同时立项的。我先完成了整套系统的研究且已准备好合并,对方以研究纯 CV 为由取得素材和技术细节,随后另起一套竞争实现并推动先行合并。从推动先行合并,到替换业务代码,再到形成单向的 AI 入口,是同一条链上的逐步升级。
核心问题:Go 侧的导航方案及后续操作,是正常的多方案共存,还是有目的性的持续性恶意竞争?
时间线
- 02/04 — 我发布了 C++ 定位寻路方案(#352)的第一个演示视频。
- 02/05 — 提交 PR #352,在群内公开技术细节和性能数据。
- 02/14 — isHarryh 在群内索取我的地图素材和技术路线。当晚基于取得的同一素材启动纯 CV 实现与批测,次日建立 #560,连夜提交测试集,并以”为什么选择我们”的对比宣传文案将其包装为不需要训练数据的替代。
- 02/15 — 两份 PR 被放在负责人面前。isHarryh 发布题为”为什么选择我们”的对比宣传卡片。负责人两次要求”等等 cpp agent”。一小时后,#560 被合并进主线。
- 02/16 — #352 被关闭。
以上是起始节点。此后五个多月里,以下节点上出现了同方向的重复行为:
- 测试邀约 — C++ 侧排障用户被分钟级定向拉去试用 Go 方案。
- 特性跟进 — 点名的功能当晚紧急补齐,与关于 ADB 顺序的明确表态直接矛盾,削弱了”不内卷”的可信度。
- 亦步亦趋 — NavMesh 和 Web 化两次事件中,Go 侧在十天尺度内跟随 C++ 侧的实施方案。
- 故障拉踩 — 竞品卡死时发起对比。
- AI 被提示词引流到其中一边 — 项目级 AI Agent SKILL 文件仅配置 Go 方案,持续 52 天,影响开发者使用 AI 工具的初始检索方向。
结论先行
这不是偶然的多方案共存,而是有目的性的持续性恶意竞争。
02/14 对方已获取 C++ 方案全部技术细节,02/15 #560 在负责人两次要求等待后被先行合并。公开场合给出的”没法用”三条理由,被压测数据、代码审查和 26 条讨论记录逐一推翻。#1283、03/16–17 的即时宣传 Go 方案发生在 C++ 排障期间,03/22 Go 侧在 C++ 公开特性后当晚补齐对应功能,当事人的公开自述与 Git 提交记录直接矛盾。#3125 建立兼容层后仅 27 小时,#3155 建立反向兼容层;NavMesh #3084 → #3359 和 Web 化 #4172 → #4461 间隔分别为 13 天和 10 天。#3830 引入 TurnNode 后 #3855 直接替换业务代码中的 C++ 动作。#3208 单独加入 Go Skill,52 天后才出现对等入口,同提示词复现的首轮选择随入口配置变化方向。
在已知 C++ 方案(MapNavigator)存在且可审查的情况下,Go 侧(MapTracker)持续建立功能重叠的另一套实现。#1283 与 03/16–17 在 C++ 测试排障时即时宣传 Go 方案;03/22 在 C++ 特性公开后当晚补齐;#1487 在公开承诺 C++ 先行之后仍抢先合并 Go ADB。#3125 建正向兼容层 27 小时后即出现反向入口 #3155;NavMesh 和 Web 化均以 10–13 天延迟跟随 C++ 方案。#3830 引入 TurnNode 后 #3855 直接替换业务代码中的 C++ 动作。#3208 单独加入 Go Skill,52 天后才出现对等入口,同提示词复现的首轮选择随入口配置变化方向。
不建立在代码抄袭或恶意推断之上。项目管理端在选型评估与合并监督上的缺位,同样是导致这场长期内耗的重要因素。
一、明知已有,另起一套 — #352 建立后,对方获取了核心素材、技术路线、性能数据和地图参数,随后才立项 #560。
二、抢先合入 — #560 建立当天即与 #352 并排置于负责人面前,通过对比性宣传文案推动优先合并,当晚合入主线;#352 次日关闭。
三、方案能用 — 事后说法是原方案”写了但没法用”。压测数据与后续 26 条公开讨论记录均不支持这一说法。
四、定向引流 — #1283 创建后 60 秒内 @正在排障 C++ 的用户,合入同一分钟再次 @通知;03/16–17 在 C++ 排障线程中直接宣传 Go 方案;04/08 用户求助 MapNavigator 的具体报错、作者已被 @ 到场,回应是“操作是换成 MapTracker”。宣传方与方案作者为同一人。
五、言行不一 — 2 月至 7 月,四个独立节点上出现同一方向的行为。关于 ADB 支持顺序的明确表态与后续 Git 合并次序直接矛盾,“保持良性竞争,不内卷”这一自我描述的可信度被削弱。
六、公开对质 — 04/19 对质中对方的三项辩解,逐一被平台时间戳推翻。在场成员确认客观上形成了竞争;对其主观动机,看法不一。
七、亦步亦趋 — 兼容层:C++ 建 #3125,仅 27 小时 Go 建反向兼容层 #3155。NavMesh:C++ 原生实现 #3084 公开 13 天后 Go 侧提交同名 PR #3359。Web 化:C++ 提方案 #4172 10 天后 Go 侧跟进 #4461。TurnNode:#3830 引入后 #3855 将业务路径切换至 MapTracker TurnNode。同方向的跟随模式在四次事件中仍然成立。
八、引流用户 — 项目级 Skill 文档仅配置 Go 侧方案,持续 52 天(05/28—07/20)。以 #3208 末位提交加入,对称性恢复后对方反向指责”抄袭”。
1. 明知已有,另起一套
要验证 #560 是否属于独立开发,先确认一件事:#560 建立前,其作者是否已经知道 #352 的存在、技术路线、核心素材和开发进度。
如果事先知情,“互不知情”这个理由就站不住。
记录
02/04 19:30 — 运行成果首次公开展示。
Lemon-miaow 在群里发送游戏内实机运行视频及带路径点标注的地图截图。群成员讨论“原来是玛丽在拉”、“这下只需要路径点了”。isHarryh 在同一对话序列中发言。
02/05 15:45—16:21 — #352 建立并公开解答技术细节。
定位运行结果截图公开,包含坐标 (985.5, 1125.0)、Score=0.86 及 Time=612.9ms。15:46,Lemon-miaow 贴出 PR #352 链接。

在群内讨论中,Lemon-miaow 解答了关于特征过滤的提问,并公开了性能数据:“目前大概差不多平均一次 120ms”。
02/14 19:26 — 介入同一命题并提出否定评价。
isHarryh 引用 Lemon-miaow 前一日发送的网格图,发表意见:“这种小地图匹配不应该直接模板匹配”、“高度同质化的小地图送到图像分类模型效果应该是不行的”。
02/14 19:39—19:45 — 直接索取素材并获知架构路线。
- isHarryh 提出:“能给我发一下解包图吗?我研究一下纯 CV”。Lemon-miaow 在群内发送了
map.zip(134.07MB) 压缩包。 - isHarryh 发言:“先立个 flag 在这,我感觉纯 CV 是可以定位的”。
- Lemon-miaow 详细说明了自身使用的底层架构:“总之我确信上 yolo 匹配哪张模板图再去 SAD 又稳又快”、“我现在做的就是这套”,并贴出耗时日志(第一轮搜索 152ms 后进精搜,后续帧 33-46ms)。
- isHarryh 当即回应:“哇 还有分级搜索”。
核心素材由对方打包提供,架构路线在同一对话中被完整披露。
02/14 20:33—20:38 — 地图缩放系数双边核对。
isHarryh 询问拼接规律;Lemon-miaow 说明系手写脚本拼接,并需缩放为 0.16x。isHarryh 回复:“对,我比较过了”、“我测的是 0.1625”。
02/14 21:13 — 03:51 — 获取素材后连夜启动同类实现与批测。
- 21:31,isHarryh 宣布“我寻思纯 CV 定位是可行的”。23:50,发布 159 张测试集的压测结果(平均 15ms,成功率 91.82%),并断言“理论上现在已经具备实现自动走路送货的条件了”。
- 02/15 01:10,Go 方案提交测试集 PR。01:15,Lemon-miaow 再次贴出 #352 链接并声明:“我都写完了”。isHarryh 随后追问 Accuracy,并提出“举办一个 YOLO vs CV 定位识别大赛”。
- 03:35—03:51,Go 侧上传演示视频,并在说明中进行对比宣传:“与 yolo 相比的优势,小地图定位无需搜集数据集、无需训练模型”。同时自述局限:“但是这些小地图方案很难处理高度”。
推论
四条事实:
- 知道 PR 存在:#352 于 02/05 公开,02/15 被当事人直接引用并追问。
- 知道技术路线:YOLO 前置、SAD 精搜、分级搜索的架构在 02/14 被完整交代。
- 拿到核心素材:
map.zip由对方直接提供,缩放参数经过核对。 - 知道开发进度:“我都写完了”与对方追问同屏,#352 标记为 Ready。
反方的解释检验
| 反方的解释 | 对应的记录 |
|---|---|
| “不知道有这个 PR” | 02/05 在群内公开,02/15 01:15 被当事人直接引用。 |
| “不知道对方进度” | “我都写完了”与对方追问同屏,#352 处于 Ready 状态。 |
| “碰巧同期做了相同的事” | 核心素材由对方提供,缩放系数经双边对齐,架构路线在同一对话中被完整披露。 |
小结
“互不知情独立开发”被客观记录否决。 后续所有争议均发生在 Go 侧已充分掌握 C++ 侧方案信息的前提下。
注:知情本身不构成违规。本章只确认信息对称前提,行为性质在下一章展开。
2. 抢先合入
确认事先知情后,核查合并流程:#560 是作为常规技术验证接受评估,还是在负责人明确要求等待的情况下,通过竞争性包装和群内推动促成 #560 先行合并,并由此导致 #352 被关闭。
记录
02/15 17:54—19:12 — 并排对比与宣传。
项目负责人在群里贴出两个 PR(#560 与 #352)询问区别。
isHarryh 在群里发布宣传卡片并 @项目负责人,标题为“为什么选择我们🔥——对比 Yolo 方案的七大优点💡”。
项目负责人当场提出质疑:“不对啊,另一个 PR 也不是 yolo 啊?”Lemon-miaow 澄清:“yolo 仅返回 zoneid,本质还是 cv”。宣传卡片将对方描述过度简化。
在此屏截图中,项目负责人已在 GitHub #560 页面留下明确评论:“等等 cpp agent 啊”。
19:27—19:29 — 管理者重申等待评估。
项目负责人明确说明准备搞两个 agent:“就是我准备搞两个 agent,现在不是只有一个 go 吗,我准备再搞一个 cpp 的”、“等我晚点看看()”。isHarryh 发布修改后的卡片(六大优点),并向负责人承认:“不行,我这个 CV 有魔改”、“不是标准实现”。
20:09—20:34 — 催促合并。
在负责人两次要求“等一等”后,isHarryh 在群内发送 #560 链接,连发“准备完毕”、“测试无误”,并在 20:30 发言:“合完立即开干送货”。群内随后出现“合!”、“加速!”等催合言论。面对有人询问“那檬喵的怎么办”,isHarryh 回复:“也可以合,爱用哪个用哪个”。
代码于 20:34:21 被合并(操作人:dongwlin)。此时距离负责人要求“等等 cpp agent”仅过去约一小时。
20:40—20:51 — 先行合并的既成事实。
20:40,项目负责人回到群里表示:“草,怎么都已经和过了”。因先行合并已经造成主线上的既成事实,20:51,负责人告知 C++ 作者:“你的 PR 只能关了啊,这是是我的锅,cpp agent 拖了好几天,没想到还能再出一个 PR”。
20:54,isHarryh 承认:“我没有做分层地图”,21:01 确认:“Yolo 仍是目前唯一能分层的方案,不能丢”。
02/16 02:03 — PR #352 关闭。
页面定格为 Closed with unmerged commits。
推论
已获取细节 → 竞争性对比宣传 → 负责人要求等待 → 群内催合 → 第三方在等待评估尚未完成时执行合并 → 负责人事后发现 → #352 被关闭。
抢先合入的代码当晚即被确认缺少分层地图(Tier)功能。
反方的解释检验
| 当事人可能解释 | 平台记录客观反证 |
|---|---|
| “只是做验证,没想替代” | 发布题为“为什么选择我们🔥”的推广卡片并 @负责人。 |
| “两份方案可以共存” | 结果是 #352 闭案退场,因为负责人明确告知“你的 PR 只能关了啊”。 |
| “方案功能更全面” | 合并当晚本人承认缺少分层功能,确认“Yolo 仍是目前唯一能分层的方案”。 |
| “合并按钮不是我按的” | 合并由第三方操作属实,但推广卡片与“测试无误、合完立即干”等催合论调均出自本人。 |
小结
抢先合入事实确立。 一份 Ready 状态的方案,在负责人两次要求等待后,被一份已知存在功能缺口的方案抢先合并,导致前者退出主线。
3. 方案能用
事后流传的说法是“原方案写了但没法用,也没有文档”。用压测数据和代码审查结果核验这个说法。
记录
02/28 20:22 — 重新加入群聊。
Lemon-miaow 重新加入开发群。
02/28 — C++ 路线性能日志。
按各自公开日志样本,C++ 单帧耗时约在 10-15ms 区间;同期 Go 侧公开报告耗时约为 27-30ms。注意两套截图字段口径不完全相同,比较仅限这些公开样本与各自 PR 报告,不外推为所有场景下的绝对性能结论。
C++ 公开压测图中跟踪平均耗时 11.5ms(P99 25.0ms),Go 侧公开压测图中推理平均耗时 30.2ms(P99 40.0ms)。
02/28 21:32 — PR #867 提交与原始数据公开。
Lemon-miaow 提交 PR #867,公开了 600MB 测试视频及原始 CSV 压测文件。负责人指派 isHarryh 审查,isHarryh 发言:“我无权 review 该 PR”。

03/01 — Harry 回应 CV 组长称呼:表示已卸任。
isHarryh 在群内表示“现在不是 CV 组长了”“已卸任”。
03/02 — #912 分离 minicv。
#912 把 MapTracker 的 CV 逻辑抽为 minicv 独立包。

03/03凌晨 — Go 侧实机问题。
主线 Go 方案在测试中出现需要前台运行、原地跳跃、过桥失败等问题,isHarryh 要求用户手调 arrival_threshold 参数。
03/04 — 自动行走成果。 Lemon-miaow 展示自动行走录像,负责人调侃”给 cv 组长一点压力”,isHarryh 回应称 Go move 延迟主要在截图。
#867 审查与合并节点: 02/28 21:31:37 建立 → 03/02 17:36 MistEO 最终复核 → 03/02 19:47 合并(+2460 行)。
03/10 — C++ 寻路状态机合入主线。
PR #1170 将 C++ 寻路状态机正式合并(6 commits / 55 files / +7806 −43)。状态机包含 7 个闭环状态:Bootstrap(启动定位)→ AlignHeading(对齐朝向)→ AdvanceOnRoute(沿路点推进)→ WaitZoneTransition(等待区域切换)→ WaitRelocation(等待重定位)→ RecoverRejoin(恢复并重回路线)→ ExactTargetRefine(终点逼近)→ Finished / Failed。此时距 #352 关闭不到一个月,C++ 侧已具备从定位到寻路到状态控制的完整链路。

推论
后续 Benchmark、审查和主线合并证明 C++ 核心技术路线具备实际可用性;“技术路线本身没法用”不成立。它不能反向证明 #352 在 02/15 已不存在任何工程接入问题。C++ 方案后续通过压测数据、公开原始 CSV、经完整公开审查并留下 26 条讨论记录后合入主线。
反方的解释检验
| 反方的解释 | 对应的记录 |
|---|---|
| “写了但没法用” | #867 公开全量 Benchmark 与原始 CSV,通过审查并合入 main 主线。 |
| “没有文档” | #867 与 #1170 均包含完整性能报告与状态机指引,通过审查。 |
| “Go 方案当时很稳定” | 03/03 测试暴露强依赖前台、过桥失败卡死等问题。 |
小结
“技术路线本身没法用”的说法不成立。 后续 Benchmark、审查和主线合并证明了 C++ 核心技术路线具备实际可用性。这不能反向证明 #352 在 02/15 已不存在任何工程接入问题。
4. 定向引流
核查 C++ 方案测试排障期间,是否存在针对特定测试人员的即时定向宣传。
记录
03/14—15 — 分钟级邀测。
- 03/14 18:42,Lemon-miaow 发送 C++ 运行视频,并 @风归云冬 协助排障。
- 03/15 01:56:15,isHarryh 创建针对进近体验的 PR #1283。
- 03/15 01:57(创建不到 60 秒),isHarryh 在群里 @风归云冬 发送 PR 链接,提示其删除参数测试。
- 03/15 11:07:19,PR #1283 完成合并。
- 03/15 11:07(合并同一分钟内),isHarryh 再次 @风归云冬 宣布:“PR 严肃合并,欢迎试用优化版 go”。
03/16 — 排障线程内即时宣传 Go 方案。 风归云冬反馈 C++ 方案洞穴识别卡顿,Lemon-miaow 正与其分析日志。isHarryh 在同一线程介入问:“@风归云冬 go 新版试了吗”,并在 00:00 直接贴出现成 Go 配置文件。
03/22 — 特性点明后当晚紧急跟进。 10:45,Lemon-miaow 点明 C++ 方案具备无坐标 HEAD 终点朝向控制。当天 22:51,isHarryh 宣布也增加了该功能。Git 记录显示其 PR #1529 首次 commit 时间为当晚 23:18:55。

03/26 — 框架接入后的质问。 用户将 C++ 方案接入 maafw 框架后,isHarryh 发言质问:“客户调研:为什么没有用 go”。
04/08 — 用户求助 MapNavigator,被直接告知“操作是换成 MapTracker”。
07/19 — 质疑 C++ 路径的线程内即时展示 Go 工具。
uy/sun 贴出 MapNavigator(C++)的一段绕行路径,问“为什么不直接直走跳下去”;Lemon-miaow 回复“safe”并附路径对比图,群内跟进“起伏更小吧”。uy/sun 随后追问“能够判断这种断崖吗?”。超越一梦想提出“添加单向路径()”,并预期“我猜很复杂”。星光(isHarryh)随即接话“easy”“人工画一条就行”,并连发两张 MapTracker 路网编辑器的案例截图——均为在地图上手动摆点连线、以红框标注单向箭头。
讨论对象是 C++ 方案的能力边界,回应内容是 Go 侧工具的现成界面,与 03/16 排障线程中的介入模式一致。
07/29 — 时隔四个多月,isHarryh 仍出现在同一目标用户的提问下。
风归云冬在开发群提问 maaend_entities.json 中是否包含采集物坐标;Lemon-miaow(MapNavigator)随即回应可以编写脚本提供该数据,风归云冬表示”来一个”。同一问题下,isHarryh 也引用该提问作出回复,先答“应该有的”,随后再次引用同一提问,指引运行 map_tracker_master.py 后访问 http://127.0.0.1:8060/web/navmesh-editor 查看,并附工具截图演示。03 月的记录已显示该用户是持续引流对象;四个多月后,同一用户提出技术问题时,isHarryh 依然在场并作出回应。
08/05 — 用户抱怨 MapNavigator,isHarryh 引用后回复“冷知识跑不通可以换一个工具”,并自注“(明示)”。
推论
对象、时间、内容三点交叉,无法以巧合解释。
- 对象 — 指定正在帮 C++ 侧排障的单一用户,不是群发。
- 时间 — 创建后 60 秒内通知,合并同一分钟内再次通知。
- 内容 — 推送内容精准针对对方测试时暴露的参数痛点。
04/08 的记录连交叉推断都省了。 提问明确指向 MapNavigator,作者已被 @ 到场,回应是“操作是换成 MapTracker”——字面即为换用竞品,且不含任何排障内容。
反方的解释检验
| 反方的解释 | 对应的记录 |
|---|---|
| “只是正常通知新版本” | 通知不是群发。指定了正在协助竞品排障的单一用户,且卡在创建和合并读秒阶段。 |
| “改进就该给用户试用” | 推送本身没问题。但掐秒推送给“正在测试对手方案的唯一人员”,针对性明显。 |
| “只是提供另一个选择” | 04/08 的提问是 MapNavigator 的具体报错,管理员已 @ 作者到场处理。回应不含任何针对该报错的排查,内容是换用竞品;真正的解法五分钟后由 MapNavigator 作者给出。同日 11:45 再次出现“建议更换接口”。 |
| “只是开玩笑” | 是不是玩笑不改变记录本身:同一天、同一类线程内出现两次,方向一致。群内以“惨案了”“maa怎么这么坏”作回应,也说明它当时并未被当作普通的技术建议。 |
小结
定向导流事实成立。 精度达分钟级别,目标为正在协助竞品排障的单一用户。04/08 的记录最为直白:用户求助 MapNavigator 的具体报错、作者已被 @ 到场,得到的回应是“操作是换成 MapTracker”,同日再补一句“建议更换接口”。07/19 与 07/29 的记录显示,五个月后该模式仍在延续:C++ 方案被质疑、被求助或被提及的线程中,Go 侧工具随即被展示。
5. 言行不一
核查在不同场景下,同类行为是否重复出现,并比对公开言论与 Git 提交记录。
记录
03/18 — 公开自述与代码合并矛盾。
isHarryh 公开答复:“什么时候 cpp 支持了我再支持……保持良性竞争,不内卷”。
平台记录显示:Go 侧 ADB 支持 PR #1487 于 03/21 合并;C++ 侧 PR #1999 于 04/07 合并。Go 侧实际领先合并 17 天。

推论
公开自述与代码行为矛盾。 关于 ADB 支持顺序的明确表态与后续 Git 合并次序直接矛盾,“保持良性竞争,不内卷”这一自我描述的可信度被削弱。上述宣传与补齐行为进一步佐证了同类模式的持续性。
反方的解释检验
| 反方的解释 | 对应的记录 |
|---|---|
| “ADB 的话只是随口说说” | 公开答复承诺清晰,3 天后其提交的 Go ADB 支持先行合并。 |
| “朝向控制本来就在做” | PR #1529 commit 显示核心代码首次写入于对方点明功能当晚 23:18:55。 |
小结
偶发解释被排除。 行为模式在四个独立节点重演,公开表态与代码提交记录直接冲突。
6. 公开对质
正面检验当事人在 04/19 对质中亲口抛出的辩解理由及现场群员评价。
记录
起因与辩解。
针对“为什么一个项目做两套”的质问,isHarryh 回复:“正在严肃垄断”,并给出三条理由:“进展太慢”、“功能没有涵盖所有需求”、“不认同设计理念”,补充称“写是写完了但没法用”。
当事人承认。
- 18:06 isHarryh:“那我素质可能有点差了,抱歉没有考虑你的感受。”
- 18:26 isHarryh:“现在看来,我承认结果从主观上看确实对你不尊重。”
第三方定性。
- 暗檬:“甲乙两个施工队同时开工。”
- issue小王:“你俩搁这竞标呢。”
- uy/sun:“确实客观上竞争了。”
- EeeMao:“没必要把人想得那么阴暗。”(本文接受此保留意见,不推定主观恶意)
反方的解释检验
| 反方的解释 | 对应的记录 |
|---|---|
| “不知道 C++ 方案在开发” | 02/14 本人向 C++ 作者索取素材、技术路线、性能数据和地图参数,当天同步的技术细节覆盖了整套方案的核心架构。群聊时间线(第一章)证明其完全知情。 |
| “进展太慢” | 实际为代理部署排期问题,项目负责人已明确承认责任在自身。 |
| “功能没有涵盖所有需求” | 合并当晚本人亲口承认自身缺少分层(Tier)功能,确认 C++ 是唯一可分层方案。 |
| “不认同设计理念” | 可解释另起一套的偏好,但无法解释绕过等待评估抢先合并的流程问题。 |
| “写是写完了但没法用” | #352 在 #560 创建前已经转为 Ready,定位核心功能已完成,公开记录显示剩余条件主要是 cpp-algo 接入。现有公开记录也未显示项目存在必须在 02/15 当晚完成选型或立即合并寻路实现的明确期限。见第三章压测数据、原始 CSV、26 条讨论记录及合入主线记录。 |
小结
公开理由均无法在已披露的记录中无矛盾地成立。 当事人已承认结果上不尊重。在场成员确认客观上形成了竞争;对其主观动机,看法不一。
7. 亦步亦趋
核查 04/19 冲突后,Go 侧是否在 NavMesh、兼容层和工具 Web 化上重复了同方向的跟随模式。
记录
05/21 — C++ 原生 NavMesh 发布。
Lemon-miaow 提交 PR #3084,发布预烘焙多边形三角网格的全图及视频。

05/23—05/25 — 兼容层争夺。
05/23,Lemon-miaow 提交 PR #3125,创建 MapTrackerMove→MapNavigatorCompatible 正向兼容层,使既有 MapTracker 路线无需调整坐标即可切换到 C++ 方案。

约 27 小时后,isHarryh 提交 PR #3155(+1157 −27),反向建立 MapNavigateAction→MapTrackerMoveCompatible 入口,PR 正文明确引用 #3125。#3125 提供从 MapTracker 到 MapNavigator 的兼容路径,27 小时后 #3155 以反向兼容层回应,方向相反。

06/03 — 名字叫 NavMesh,内容完全不是。
C++ 发布 NavMesh 前,公开仓库记录中未见 Go 侧提交路网或 NavMesh 相关实现。C++ 发布 NavMesh 仅 13 天后,isHarryh 提交 PR #3359,命名为 MapTrackerNavMesh——与 C++ 侧的 MapNavigatorNavMesh 仅差一个单词。PR 正文自述为“一种基于文本的路网数据文件”,代码结构由离散点与连线构成(固定路网 Waypoint Graph),无多边形面。NavMesh 的技术定义是以多边形(通常为三角形)网格表示可行走区域,而这份 PR 的数据结构是点线图,不具备网格面的特征。名称与其自身文档描述矛盾。

能走到目的地,不代表它就是 NavMesh。 寻路的数据有两种常见做法,各有各的名字。一种是 NavMesh(导航网格):用一片片多边形把能走的地面铺满,哪里能走、哪里是边界,看这片面就知道,没铺到的地方就是走不了的地方。另一种是 Waypoint Graph(路点图):在地图上人工点几个点、连几条线,能走的只有这些线,线以外没有任何信息。
两种都能寻路,都能“说哪走哪”,走不走得到跟叫什么名字没关系。问题只有一个:MapTrackerNavMesh 存的是点和线,是第二种,名字却用了第一种的。
准确地说:NavMesh 与 Waypoint Graph 是路径规划中两类并列且各有定义的数据结构——前者的可行走性是多边形面本身的属性,面的边界即障碍边界;后者的可行走性只存在于人工摆放的点与边上。MapTrackerNavMesh 按定义属于后者,却使用了前者的名称。这不是功能强弱之争,是类别错配。
第四节 07/19 的对话正好印证了这点。被问到“能够判断这种断崖吗”,isHarryh 的回答是“人工画一条就行”,并贴出手动摆点连线、红框标注单向箭头的编辑器截图。断崖在哪、哪条路只能单向走,全都得靠人一条条画上去——真正的 NavMesh 里这些是面自己带的信息,不用人画。约束靠人工逐条标注而非从几何面推导,正是路点图的定义特征。此时距 06/03 命名矛盾被指出已过一个半月。
06/04 — 公开宣传。
isHarryh 在群内发布滑索寻路演示视频,配文“此视频您将看到我们最新成果的前瞻效果演示”,获群成员回应“强强!!!!”。宣传涵盖自动识别滑索、自动上滑索、容忍选错 / 阻挡、NavMesh 等要素。此时距离 PR #3359 提交仅过一天。

06/05—06/11 — 宣传与故障对比。
然而同期公开测试与开发者反馈中仍出现过桥、阻挡和运行稳定性等问题。在此前提下:
- 06/05,针对 C++ A* 路线提问,isHarryh 回复“用 MapTrackerGoal 就行了,包对”。
- 06/11,在用户反馈 C++ 编辑器卡死后,isHarryh 引用该报告发言:“看看我们的 nav mesh editor 会卡死吗”。
- C++ 作者指出其“连个面都没有”,被 Go 侧定性为“开始攻击了”。
07/13 — #4172 工具 Web 化。
Lemon-miaow 提交 PR #4172,将 MapNavigator 工具从 tk OpenCV GUI 迁移到 Web UI(Vue + Element Plus + FastAPI),形成统一的暗色地图编辑器。
07/23 — #4461 跟进 Web 化。
#4172 合并十天后,isHarryh 提交 PR #4461,同样将 MapTracker 工具从 Python OpenCV GUI 迁移到 Web UI,描述为”MapTracker Master Tools 聚合式 Web 工具现已可用”。
07/29 — 命名争议持续至今,未见更正。
命名矛盾指出后近两个月,文档未作调整。截至 2026-07-29 11:00(对应固定版本 MaaEnd/MaaEnd@0c7221a),.agents/skills/go-map-tracker-guide/SKILL.md 仍称 map_tracker_master.py 提供”NavMesh 编辑”功能;docs/zh_cn/developers/components/map-tracker.md 仍称 MapTrackerGoal 节点”基于 NavMesh 自动规划路径”,entity_id 参数说明仍写作”NavMesh 顶点关联的实体 ID”。底层数据结构未变,命名也未变。

07/30 — 隔了十一天,还是那句“自己画”。
19:11,用户明欣提问 MapTrackerGoal 可不可以指定目标的碰撞箱,并说明遇到的现象:“指定侧面的点位会在从另一侧寻路的时候卡住”。群成员 ocsin 的判断是“这应该是 navmesh 打点打少了”。isHarryh 随后回复“要什么路线自己画(”,并连发两张 MapTracker 编辑器截图;针对其中的路网放大图,他解释“你选择这个点(箭头所指)作为终点的话”“一般都会途径紫色线条,不会穿越中心的”。19:39,明欣自行确认了原因:“原来是填到演算台中间了”。
这段对话有三处值得看。
一是距 07/19 的“人工画一条就行”只过了十一天,同一个人对同一类问题的回答仍然是“自己画”。路线由人手工绘制,不是从地形几何算出来的——两次表述完全一致。
二是“打点打少了”这个说法本身。真正的 NavMesh 里不存在“点打少了”,面铺到哪里就能走到哪里,走不到只可能是面没铺;会因为“点少”而走不通的,只有路点图。群成员在排障时脱口而出的是路点图的那一套概念,用的却是 NavMesh 这个名字。名字张冠李戴的后果,不只停留在 PR 标题上,已经进入了使用者理解问题的方式。
三是 isHarryh 自己给出的解释:终点选在某个点上,路径“一般都会途径紫色线条,不会穿越中心的”。这句话就是路点图的定义——能走的只有人工连出来的那些线,线与线之间的区域没有任何可通行信息。NavMesh 的多边形面内部任意位置都可穿行,根本不会有“不会穿越中心”这一说。明欣把终点填在演算台中间就卡住,也正是因为那块地方没有人画过点。
距 06/03 命名矛盾被公开指出,已近两个月。
#3830 → #3855 — TurnNode 引入并替换主体业务。
isHarryh 在 PR #3830 中为 MapTracker 新增转向节点(TurnNode)。在此基础上,PR #3855 将该业务路径从 MapNavigateAction 切换至 MapTracker TurnNode,形成对既有 C++ 动作的实际替换。
反方的解释检验
| 反方的解释 | 对应的记录 |
|---|---|
| #3155 只是正常兼容层 | 两个兼容层方向完全相反。#3125 把 Go 路线迁向 C++;仅 27 小时后 #3155 建立反向兼容层(+1157 行),且 PR 正文明确引用 #3125。这不像独立规划的功能需求,更像对竞争方动作的即时回应。 |
| “NavMesh 是独立开发的” | 同名 PR MapTrackerNavMesh,提交于 C++ 原生 NavMesh 公开仅 13 天后。代码底层仅为离散点连线,无三角面,不构成 NavMesh 的技术定义。时间线、命名和自身文档描述的”文本路网数据文件”三处矛盾。 |
| “能用就行,名字不重要” | 这里争的不是好不好用。NavMesh 和路点图是两种不同的东西,都能寻路;MapTrackerNavMesh 存的是点和线,是路点图,名字却用了 NavMesh。07/19 被问到能不能判断断崖,本人的回答是”人工画一条就行”;07/30 被问到寻路卡住,回答仍是”要什么路线自己画”,并解释路径”不会穿越中心”——能走的只有人工连出来的线,这正是路点图的定义。同一场对话里,群成员的排障用语是”navmesh 打点打少了”,混用已经从代码蔓延到了使用者。截至 07/30,文档、工具路由和日常使用里的这个名字都没改。 |
| “Web 化是正常技术演进” | 单独看,Web 化可以属于正常技术演进;但在同一仓库、同位竞争模块和既有持续跟进模式中,十天内出现相同迁移方向,构成方向一致的补充证据。该节点不单独作为独立证据。 |
| “只是行业都在做 Web 工具” | 同一仓库、同位竞争模块、同一迁移方向在十天内出现,难以仅用行业趋势解释。 |
| “TurnNode 替换是正常重构” | #3830 引入 MapTracker 专属转向节点,#3855 将环境监测业务的原有动作路径切换至 MapTracker TurnNode。该切换将业务依赖单向指向竞争模块,两个 PR 均未通知 C++ 侧或提出迁移方案。 |
小结
亦步亦趋的模式再次出现。 兼容层、NavMesh 与 Web 化三个节点,均表现为 C++ 侧先行、Go 侧随后作出同方向回应;TurnNode 则进一步表现为从自己模块蔓延到了公共业务,并对既有 C++ 动作形成实际替换。
其中 NavMesh 这一项和其他几项不太一样:它不只是时间上跟得紧,而是在 C++ 的 NavMesh 公开 13 天后,把一份点线路网也叫成了 NavMesh。这跟谁做得好不好没关系——两种东西都能寻路;问题是名字张冠李戴。被公开指出后,这个名字到 07/30 仍挂在文档、工具路由和日常使用里,没改;同一天群内答疑时,本人对“怎么让它走某条路”的回答依然是“自己画”,群成员排障时说的则是“navmesh 打点打少了”。
8. 引流用户
核查仅配置 Go 侧 Skill 对开发者及 AI 工具初始选择的影响。
记录
末位提交与审查缺口。
05/28 03:01,UI 重构 PR #3208 在通过机器人审查后,追加最后一个 commit docs: 加个妙妙SKILL,写入 Go 侧配置 go-map-tracker-guide。

52 天仅 Go 侧有 Skill。
该文档包含强指令:“当你判断确实正在进行 MapTracker 的开发工作时,务必无条件地先读取……”。在此后 52 天内(05/28—07/20),项目中仅有 Go 侧 Skill 写入项目配置,AI 编码工具在此基础上优先检索 Go 方案。
实测对照显示:仅有 Go 侧 Skill 时,AI 首轮直接抓取 Go 文档并做优先推荐(在扫库 4 分钟后自行纠正)。
平权后的指责。
07/20 C++ 侧补齐 map-navigator-guide 后,isHarryh 于次日截图指责:“连 skill 都抄是有点好笑的”。
反方的解释检验
| 反方的解释 | 对应的记录 |
|---|---|
| ”只是随手加了个文档” | Skill 以 UI 重构 PR 的末位提交加入;两次自动审查均发生在该提交之前,之后未见新的正式审查记录。 |
| “没有规定两边都要有 Skill” | 没有。但结果就是单侧 Skill 持续 52 天,AI 实测首轮直接抓取 Go 文档优先推荐。Skill 以 UI 重构 PR 的末位提交加入,未见独立审查。 |
| “Skill 只是辅助,不影响行为” | 实测 AI 在单侧配置下首轮抓取 Go 文档并做优先推荐。本次对照样本中的首轮影响真实存在。 |
小结
#3208 以末位提交单独加入 Go Skill,52 天后才出现对等入口,干预了初始检索方向。对称性恢复后,当事人随即反向指责对方”抄袭”。
结论
这不是偶然的多方案共存,而是有目的性的持续性恶意竞争。
02/14 对方已获取 C++ 方案全部技术细节,02/15 #560 在负责人两次要求等待后被先行合并。公开场合给出的”没法用”三条理由,被压测数据、代码审查和 26 条讨论记录逐一推翻。#1283、03/16–17 的即时宣传 Go 方案发生在 C++ 排障期间,03/22 Go 侧在 C++ 公开特性后当晚补齐对应功能,当事人的公开自述与 Git 提交记录直接矛盾。#3125 建立兼容层后仅 27 小时,#3155 建立反向兼容层;NavMesh #3084 → #3359 和 Web 化 #4172 → #4461 间隔分别为 13 天和 10 天。#3830 引入 TurnNode 后 #3855 直接替换业务代码中的 C++ 动作。#3208 单独加入 Go Skill,52 天后才出现对等入口,同提示词复现的首轮选择随入口配置变化方向。
在已知 C++ 方案(MapNavigator)存在且可审查的情况下,Go 侧(MapTracker)持续建立功能重叠的另一套实现。#1283、03/16–17、03/22 在 C++ 测试或公开特性后宣传 Go 或补齐对应功能。#3084 → #3359、#4172 → #4461 以 10–13 天延迟跟随。#3830 → #3855 替换业务代码中的 C++ 动作。#3208 单独加入 Go Skill,52 天后才出现对等入口,同提示词复现的首轮选择随入口配置变化方向。
不建立在代码抄袭或恶意推断之上。项目管理端在选型评估与合并监督上的缺位,同样是导致这场长期内耗的重要因素。
后日谈:把画面放回时间轴
2026 年 8 月 8 日 21:37,isHarryh 在 MaaEnd 开发群发布了一段约一千五百字的声明,随后退群。声明全文如下(群聊原图,未裁切):
声明第二段开头写道:
“这也涉及近期某篇博客长文中被精心编排的‘证据链’,出于对事实的基本尊重,有几处被滤去的画面值得重新放回时间轴上。”
此前一天,08/07 12:44,isHarryh(群昵称“星光”)在群内发言:
“但我觉得或许能早日在某人的新闻学博客上看见我的这些发言了”
那确实,这不就看见了嘛。
以下按声明的论述顺序,逐条核对其中的说法与平台记录。所引发言均附原始截图,截图未经裁切。
一、“某人尝试一类方案未达预期”
“2026 年年初,MaaEnd 启动寻路系统。在某人尝试一类方案未达预期,并且正在开发另一个新方案时,我出于重新检验前者的技术路线的目的,完成了从识别到寻路操作的完整可用方案。”
核验这一说法,先确认“未达预期”所指的方案,以及它的实际结局。
02/05 15:45 — #352 创建。
声明中“未达预期”的方案,是 #352 feat: 小地图定位。架构为:YOLO 前置分类锁定层级(Tier)并输出 ZoneId,再以 CV 模板在单张底图内局部检索;带速度预测的 ROI 防抖,正文记录了实测单帧耗时。同日群内贴出的定位结果见正文第 1 章。
02/16 02:03 — #352 由作者关闭。
平台记录显示,#352 未收到任何 CHANGES_REQUESTED,也没有评审否决记录,由作者本人关闭。关闭前后的经过见下文二。
03/02 — #867 合并。
声明中“正在开发的另一个新方案”,是 #867 feat: cppMapLocator,2026-03-02 合并,沿用至今。它沿用 #352 的架构,实现语言由 Python 改为 C++。
“未达预期”没有对应的评审记录。
未达预期的东西,现在还天天在跑呢。
二、被滤去的那个晚上
声明中关于 #352 关闭的一段:
“而紧接着发生了一件在整个叙事中被轻轻带过的、最值得重新思考其动机的事:某人未留一词,关闭了他的新方案的 PR,直接退出了开发者群聊。”
“悄无声息地,没有沟通、没有交接、没有协调,也没有一句‘我认为这样不对’。而我的方案已是唯一在跑的可用系统,面对某人沉默的离场,我选择继续维护自己的代码。”
核验这一段,需要确认 #352 关闭之前的几个小时里,平台和群内分别留下了什么记录。
平台时间线。 北京时间,取自 GitHub API:
| 时间 | 事件 |
|---|---|
| 02-05 15:45:01 | Lemon-miaow 创建 #352 |
| (挂起 10 天 10 小时 18 分) | |
| 02-15 17:54:41 | isHarryh 创建 #560“添加基于 CV 的通用小地图定位识别” |
| 02-15 20:34:21 | #560 合并 —— 从创建到合并 2 小时 39 分 40 秒 |
| 02-16 00:51:56 | isHarryh 创建 #564“MapTracker 小地图定位支持 Tier 层级匹配” |
| 02-16 02:03:44 | Lemon-miaow 关闭 #352 |
| 02-16 20:11:23 | #564 合并 |
| 02-17 00:56:50 | isHarryh 创建 #575“添加自动寻路功能” |
两份 PR 从创建到结局:
#352 开启 10 天 10 小时 18 分 未合并
#560 开启 2 小时 39 分 40 秒 已合并#352 关闭于 #560 合并后 5 小时 29 分 23 秒,距 #564 创建 1 小时 11 分 48 秒。
群内记录。 #560 合并后,群内发言如下(截图见正文第 2 章,此处按时间排列):
| 时间 | 发言人 | 原话 |
|---|---|---|
| 02-15 20:40 | 项目负责人 | “草,怎么都已经和过了” |
| 02-15 20:51 | 项目负责人 | “那 @檬喵Lemon_miaow 你的 PR 只能关了啊,这是是我的锅,cpp agent 拖了好几天,没想到还能再出一个 PR” |
| 02-15 20:54 | isHarryh | “不能否认Yolo方案的PR确实有可以互补的点” |
| 02-15 20:56 | 项目负责人 | “要不搞个兜底方案什么的 ()” |
| 20:56 后 | Lemon-miaow | “可是”“我有 tier 啊” |
| 项目负责人 | “比如 cv 识别不出来的时候用 yolo 兜一下 ()” | |
| isHarryh | “我没有做分层地图” | |
| isHarryh | “分层地图会有的,加几张大图的事儿” | |
| Lemon-miaow | “我看未必” | |
| Lemon-miaow | “就加几张图能卡我 11 天换 yolo” | |
| isHarryh | “我现在立即加一下试一下” |
以上发言均在 #352 关闭之前。
这一晚上群里可热闹了,悄无声息在哪呢。
另有两项平台记录与这一晚相关:
其一:#564 的标题。 #564 标题为“MapTracker 小地图定位支持 Tier 层级匹配”,创建于 02-16 00:51:56,早于 #352 关闭 1 小时 11 分 48 秒。
Tier 分层是 #352 正文列出的第一条特性,2026-02-05 起公开可读,原文:
“1. 支持多层级(Tier)精确分类
在包含大量层级和未来即将到来的更多区域的复杂场景中,传统的底图遍历匹配极易导致耗时剧增与跨层误判。本方案引入轻量级 YOLO 模型作为前置分类,直接锁定玩家当前所处的特定层级并输出对应 ZoneId,将 CV 匹配范围精准限定在单张底图内,有效解决了跨层级定位难题。”
前脚还是“我没有做分层地图”,不到四个小时,#564 标题就是 Tier 层级匹配了,这速度是真的快。
其二:#560 之前的 PR 记录。 #560 是 isHarryh 在本仓库的第一个 map 相关 PR。此前他的 12 个 PR 为:拼图任务 8 个、构建脚本 2 个、JSON Schema 1 个、format CI 1 个。
声明将 #560 的动因表述为“出于重新检验前者的技术路线的目的”。被检验的技术路线写在 #352 正文中,02-05 起公开可读;#560 创建于 10 天之后。
“紧接着”与时间线一致,“没有沟通”与群内记录不符。
三、“一段被自己亲手清空的参与记录”
“一段被自己亲手清空的参与记录,是否还能在此后重新拾起,作为指控他人的筹码?”
核验“清空”一说,只需查看 #352 当前的状态:
状态 CLOSED(未合并)
正文 3946 字符 在
commit 4 个 在
评论 3 条 在
创建 2026-02-05 15:45:01
关闭 2026-02-16 02:03:44在 GitHub 上关闭一个 PR,不会删除正文、commit、评论或时间戳。页面截图见正文第 2 章末尾。
关个 PR 就算清空了啊?东西都好好在这呢。
Lemon-miaow 在本仓库共有 108 个 PR,其中关闭且未合并的只有 #352 一个。
参与记录未被清空。
四、“数据会说话”
“现在,就 rc2 版的遥测情况而言,非常抱歉地看到在环境监测任务的数据上,某人自己原本长期负责的寻路节点失败率已是我所负责部分的 2 倍。此时此刻哪个系统更加稳定,数据会说话。”
核验这一说法,先确认所引数据的来源和统计范围。
08/07 10:23 — 面板发布。
声明所引数据,出自 isHarryh 于 08/07 10:23 在群内发布的 Sentry 面板,发布时的原话为“正式版的公开处刑面板已经搭好了”。声明中的“rc2”指 v2.23.0-rc.2,08-08 01:45 发布,至声明发出时上线不到 20 小时。
08/07 12:03—12:12 — 同日的三条发言。
同日中午,isHarryh 在群内另有三条发言:
| 上下文 | 原话 |
|---|---|
| 有人问“寻路统一了嘛” | “并非统一,单方面替换” |
| 有人说“说上滑索有改” | “等几个小时看看替换前的遥测结果,我看看哪来的自信” |
| 有人说“然后今天 4 次上滑索都失败了 ()”“待会看看日志” | “不用看了,已经停止维护了,等替换吧” |
面板的统计范围。 面板统计的是 EnvironmentMonitoring 下两组硬编码的 *Move 节点,范围小于环境监测模块,更小于整个寻路系统。两组标题分别为“环境监测:MapNavigator方式运行统计”和“环境监测:MapTracker方式运行统计”,公式为 failure_rate()。
08/22 03:01 — 面板的后续状态。
截图为原图,未裁切:
看笑了。
截图时,面板标题为“组件 | MapTracker”;时间窗 Aug 8–Aug 9;版本 MXU@2.4.3+MaaEnd@v2.23.0-rc.2;每 10 分钟一个点。左下 failure_rate() 为 55.98%,右下为 28.75%。右上角 Editors 一栏仅有 isHarryh 一人。
图中间的组件及其标题由 Lemon-miaow 添加,截图中原样保留。添加时 Lemon-miaow 拥有这张面板的编辑权限,截图时该权限已被收回;权限变更的具体时间,Lemon-miaow 一侧没有记录。
最上方两个文本组件由 isHarryh 撰写,原文:
神人
“⬇️8月13日来看的是吧,当时都没人用rc2版本了,我猜你是筛选日期都不会筛选,也是给我看笑了🤣,现在我给你筛选好了哈;还跑到Sentry里写这些东西🤣,你也是第一人👍”
说明
“此面板是8月8日2.23.0-rc2版本中,环境监测任务各寻路方式当时的失败率。考虑到现MapTracker已不再维护,此数据仅供历史参考。”
关于 08/13 的时间窗。 文本组件中对时间窗的批评成立。08/13 Lemon-miaow 查看这张面板时,时间窗为“最近 24 小时”;当时 rc.2 的使用者已经很少,窗口内样本量不足,这一窗口选择有误。
本节以下数据,一律采用 isHarryh 筛定的窗口:北京时间 2026-08-08 00:00 至 08-10 00:00,版本 v2.23.0-rc.2,环境监测的 *Move 节点。
行,那就按你筛好的来。
按 rc.2 代码中的实际调用分组。 每个 *Move 节点调用哪种寻路,写在 v2.23.0-rc.2 的 pipeline 中,字段为 custom_action。按该字段的实际取值分为两组:
| rc.2 上调用的动作 | 节点数 | 调用 | 失败 | 失败率 |
|---|---|---|---|---|
MapNavigateAction | 7 | 117 | 2 | 1.71% |
MapTrackerMove / MapTrackerGoal / MapTrackerToward | 24 | 348 | 91 | 26.15% |
调用与失败为 Sentry 存储的 span 条数;采样外推后两列同乘 8,比值不变(查询时间 2026-09-30)。原始数据:d1-rc2-window-em-move-by-node.csv。
MapNavigateAction 的 2 次失败,分别发生在 08-08 23:36(GoToIndoorCropsMove)和 08-09 11:17(GoToTreeOfTheOldCourtyardMove),见 d1b-rc2-window-mapnavigateaction-failures.csv。
按实际调用分组,声明中“某人自己原本长期负责的寻路节点失败率已是我所负责部分的 2 倍”一句对应的数值为:调用 MapTracker 的一组 26.15%,调用 MapNavigator 的一组 1.71%。
这 7 个节点的来历。 它们原先都调用 MapTrackerMove,由环境监测维护者 he0119(uy/sun)在四个 PR 中改为 MapNavigateAction:
| 节点 | PR | 合并时间 |
|---|---|---|
GoToAncientTreeMove、GoToIndoorCropsMove、GoToTreeOfTheOldCourtyardMove | #3608“修正多个任务的点位” | 06-15 23:46 |
GoToCisternOriginiumSlugsMove、GoToVigorousCisternOriginiumSlugMove | #3804“微调优化路径” | 06-22 20:22 |
GoToCollapsedTianshiPillarMove | #3892“优化倒塌的天师桩路线” | 06-26 23:58 |
GoToAnnularWaterfallMove | #4663“修复 ADB 快速传送与任务定位按钮复用” | 08-02 00:31 |
#3608 的说明为“为环境监控路线添加跨层级的 NAVMESH 目标支持”;同一 PR 也把 GoToFloraObservationPointMove 改成了 MapNavigateAction,该节点此后的变化见下文。#3892 对应的 issue 是 #3823“拍摄倒塌天师桩,天师桩被角色挡住,导致环境监测失败”。
rc.2 上,这 7 个节点都以 NAVMESH 目标寻路,路线在运行时由 navmesh 计算;其中 GoToTreeOfTheOldCourtyardMove 的目标带 target_tier: "Wuling_L4_330",是跨层目标。
面板左侧失败的来源。 截图左下的图表,在该窗口内有数十个非零的 10 分钟桶。实际调用 MapNavigateAction 的 7 个节点,整个窗口只失败上述 2 次,最多产生 2 个非零桶。由此可知,左侧组件中包含 rc.2 上调用 MapTracker 的节点。
08/13 按节点拆分时,左侧的失败集中在 GoToFloraObservationPointMove 一个节点,其调用全部失败。
该节点在 v2.23.0-rc.2 上的写法(FloraObservationPoint.json:376-408,节选):
"GoToFloraObservationPointMove": {
"desc": "自动寻路前往植物观察点",
"action": "Custom",
"custom_action": "MapTrackerGoal",
"custom_action_param": {
"map_name": "map02_lv004",
"target": [550.3, 252.6],
"on_finish": {
"action": "Custom",
"custom_action": "MapTrackerToward",
"custom_action_param": { "angle": 0 }
}
},
"next": ["FloraObservationPointTakePhoto"]
}它原先调用的是 MapNavigateAction(父提交中的写法)。改成 MapTrackerGoal 的是 #4779“feat(EnvironmentMonitoring): 优化拍照流程与武陵路线(子模块)”,he0119 提交,2026-08-05 20:06 由 zmdyy0318 合并,提交 f490cc9d。rc.1、rc.2 都包含这个提交。
在 isHarryh 筛定的窗口内,该节点调用 38 次,失败 38 次。同一窗口失败率为 100% 的节点共三个,在 rc.2 上调用的均为 MapTracker:
| 节点 | rc.2 上的动作 | 调用 | 失败 |
|---|---|---|---|
GoToFloraObservationPointMove | MapTrackerGoal | 38 | 38 |
GoToHangingVinesMove | MapTrackerGoal | 28 | 28 |
GoToWaterTemperatureControllerMove | MapTrackerMove | 19 | 19 |
其中 GoToFloraObservationPointMove 属于标题为“环境监测:MapNavigator方式运行统计”的左侧组件。
GoToWaterTemperatureControllerMove 在 rc.2 上的写法。 08-04,isHarryh 在 #4743“净水温控装置改为MapTrackerGoal”中,把该节点由 MapTrackerMove 改为 MapTrackerGoal。08-06 合并的 #4797“寻路节点增加成功埋点”(zmdyy0318,提交 1d616200f)又把它改回 MapTrackerMove,这是该 PR 中唯一一处 custom_action 取值变化。此后 rc.1、rc.2、v2.23.0 以及 v2.24.0-beta.1、beta.2 都按 MapTrackerMove 运行,直到 beta.3 随 #4785 改用 MapNavigator。
rc.2 上运行的是被改回的 MapTrackerMove。不计这一节点,调用 MapTracker 的一组为 329 次调用、72 次失败,失败率 21.88%。
两侧“成功”的判定标准不同(见五):26.15% 是 MapTracker 一组失败率的下界,1.71% 是 MapNavigator 一组失败率的上界,两侧的实际差距至少等于这两个数之差。
rc.2 这个版本。 v2.23.0-rc.2 的 tag 指向 #4843“perf: 修改埋点字段”(zmdyy0318)的合并提交。该 PR 于 08-08 01:34 合并,11 分钟后 rc.2 发布。rc.2 是第一个上报 Node.Action.Succeeded / Node.Action.Failed 的版本;此前的 rc.1 上报的是 #4797 引入的 Node.PipelineNode.Succeeded。
同日 22:48,v2.23.0 正式版发布,rc.2 在发布约 21 小时后被取代。两者之间只有一个提交(#4846,账号切换的 OCR 兜底),环境监测的 pipeline 与埋点相同。面板的时间窗 Aug 8–Aug 9 覆盖了 v2.23.0 发布后的 25 小时;截至 08/22 的截图,面板的版本过滤仍只取 rc.2。
都没人用 rc2 了,那面板到 08/22 还只筛 rc2 是怎么回事啊。
面板说明称“此数据仅供历史参考”。上文表格同样取自 rc.2,用途限于核对面板在其自身窗口内的分组;迁移前后各模块、各版本的完整数据见第八节。
同一节点换引擎前后。 本段数据取自 isHarryh 筛定窗口之外的版本。按引擎分组的横向比较,两组跑的是不同路线:调用 MapNavigator 的 7 个节点,是维护者从 MapTrackerMove 换出来的路线,其余 24 个留在 MapTracker 上,组间差异混入了路线差异。排除路线差异的做法,是对同一节点比较换引擎前后,并以始终调用 MapNavigateAction 的 7 个节点作对照。
环境监测的 31 个 *Move 节点,迁移前取 v2.23.0,迁移后取 v2.25.0、v2.26.0、v2.27.0、v2.28.0、v2.29.0、v2.30.0、v2.30.1 七个正式版合计(含各 MXU 版本;v2.24.0 没有正式版;窗口截至 2026-09-30 13:30,查询时间 2026-09-30):
| 节点组 | v2.23.0(失败 / 调用) | v2.25.0 至 v2.30.1 七个正式版合计(失败 / 调用) |
|---|---|---|
对照:始终调用 MapNavigateAction 的 7 个 | 445 / 9,446(4.71%) | 1,411 / 30,478(4.63%) |
| v2.23.0 上调用 MapTracker 的 24 个 | 13,286 / 38,762(34.28%) | 3,081 / 93,568(3.29%) |
其中 GoToFloraObservationPointMove | 5,042 / 5,042(100.00%) | 64 / 4,109(1.56%) |
其中 GoToHangingVinesMove | 4,213 / 4,214(99.98%) | 266 / 4,691(5.67%) |
其中 GoToWaterTemperatureControllerMove | 3,551 / 3,559(99.78%) | 421 / 4,395(9.58%) |
| 其余 21 个 | 480 / 25,947(1.85%) | 2,330 / 80,373(2.90%) |
调用与失败为 Sentry 存储的 span 条数,口径同上文。原始数据:d2-em-move-by-node-and-release.csv。迁移由 #4785 完成,同时重写了各节点的目标点配置,例如 GoToFloraObservationPointMove 的目标改为带 target_tier 的跨层 NAVMESH 目标。
对照组合计前后持平,单节点有升有降。24 个节点的变化集中在上表三个节点:它们在 v2.23.0 上占这一组失败的 96.4%(12,806 / 13,286),迁移后失败率在 1.56% 至 9.58% 之间。
“我看看哪来的自信.jpg”
其余 21 个节点由 1.85% 升至 2.90%,个别节点上升明显,如 GoToCloudrestEcoSiteMove 由 0.27% 升至 12.11%。前者是失败率的下界,后者是上界(见五),这一行判断不了方向。
按失败率剔除节点,结论随剔除对象而变。 在 v2.23.0 上,只剔除 MapTracker 一侧上述三个节点,MapTracker 一组为 1.85%,低于 MapNavigator 一组的 4.71%;同样剔除 MapNavigator 一侧失败率最高的 GoToTreeOfTheOldCourtyardMove(17.77%),MapNavigator 一组降为 2.52%。三个节点换用 MapNavigator 后都能到达,把它们剔除,剔掉的正是调用 MapTracker 时失败、换用 MapNavigator 后到达的那部分样本。面板把其中的 GoToFloraObservationPointMove 计入了“MapNavigator方式”一侧。
关于这张面板的其他记录。
同一 Sentry 组织内,正式版全量口径(2026-08-13 查询,窗口为查询时的最近 24 小时):76,906 次调用,1,727 次失败,成功率 97.75%。这个口径是全部导航白名单节点,不限模块。面板取的是其中环境监测的一组 *Move 节点。
将版本过滤器由 rc.2 改为 beta.4,面板、公式与节点选择保持不变(同一次查询):
| 失败率 | |
|---|---|
| MapNavigator 侧 | 6.30% |
| MapTracker 侧 | 8.82% |
面板发布当天(08/07,见本节开头 10:23 的截图),群内有人就两侧的上报基准提问:
isHarryh:“不过还是可以看失败成功分布的”
uy/sun:“诶,不是说都上报错了吗”
isHarryh:“按 trace.status 分组就行”
提问针对的是上报本身有没有问题,回答给出的是一个分组操作。
两侧上报基准的实际情况:08/07 10:25 截图对应的窗口内,环境监测模块中,MapNavigator 侧 14 个调用点产出 44 个事件(3.1 个/点),MapTracker 侧 72 个调用点产出 129 个事件(1.8 个/点),每调用点事件数相差 1.7 倍,两侧 span 发射基准不一致。
该截图的画面截止于 MapTracker 一侧的 internal_error 一行,cancelled 分桶不在画面内,因此 64/129 是 MapTracker 一侧 ok 率的上限。
面板与查询均在共享的 Sentry 组织内,组织成员可自行复现;每个节点的 custom_action 取值可在仓库对应的 tag 中查阅。
截至本文发布:v2.30.1。
v2.30.1 于 2026-09-26 14:47 发布,是本文发布时最新的正式版。这个版本上,环境监测的 31 个 *Move 节点全部调用 MapNavigateAction,两组用的是同一套判定标准。仍按 rc.2 上的实际调用分组(窗口为北京时间 09-26 14:47 至 09-30 13:30,含各 MXU 版本;查询时间 2026-09-30):
| rc.2 上调用的动作 | 节点数 | v2.30.1 上调用的动作 | 调用 | 失败 | 失败率 |
|---|---|---|---|---|---|
MapNavigateAction | 7 | MapNavigateAction | 17,887 | 759 | 4.24% |
MapTrackerMove / MapTrackerGoal / MapTrackerToward | 24 | MapNavigateAction | 54,083 | 1,334 | 2.47% |
其中 GoToFloraObservationPointMove | 2,206 | 17 | 0.77% | ||
其中 GoToHangingVinesMove | 2,320 | 175 | 7.54% | ||
其中 GoToWaterTemperatureControllerMove | 2,363 | 183 | 7.74% | ||
| 合计 | 31 | 71,970 | 2,093 | 2.91% |
v2.30.1 的数据仍在 Sentry 的全量保留期内,上表为实际上报的 span 条数;约 30 天以前的数据只保留 1/8 抽样,本节其余各表为抽样后的条数,比较时看失败率。原始数据:d3-v2.30.1-em-move-by-node.csv。
rc.2 上失败率 26.15% 的 24 个节点,在 v2.30.1 上为 2.47%;当时 100% 失败的三个节点,现为 0.77%、7.54%、7.74%。
“数据会说话”所依据的分组,与 rc.2 的代码不一致。
数据确实会说话,就是说出来的跟声明里的是两回事。
五、“哪个系统更加稳定”
要比较“哪个系统更加稳定”,先确认两侧遥测中“成功”的判定条件是否相同。
MapNavigator(agent/cpp-algo/source/MapNavigator/)
定位失败时:
if (!position_provider.WaitForFix(&pos, initial_expected_zone, ...) || is_stopping()) {
return false;
}抵达终点时(navigation_session.cpp:102-113):
if (!final_goal.zone_id.empty() && position.zone_id != final_goal.zone_id) {
return false; // 区域必须匹配
}
const double distance = std::hypot(final_goal.x - position.x, final_goal.y - position.y);
return distance <= FinalGoalAcceptanceBand(); // 距离必须达标MapTracker(agent/go-service/maptracker/default/move.go。以下行号取自 origin/v2,2026-08-24)
到达判定(:241-250):
// Check arrival
dist := curLoc.DistanceTo(targetLoc)
reachedByRotation := math.Abs(internal.DeltaRotation(targetRot, initRot)) > 90.0
if dist < param.ArrivalThreshold || reachedByRotation {
if reachedByRotation {
log.Info().Int("index", i).Float64("dist", dist).Int("targetRot", targetRot).Int("initRot", initRot).Msg("Target point reached (ordinary approach, guessed by rotation)")dist 与 reachedByRotation 为或关系:方位角相对起始方位变化超过 90°,即判定到达,此时不检查距离。默认 ArrivalThreshold 为 2.5(:82),这条分支将其完全绕过。
绕行障碍、被外力推开、定位结果瞬时跳变,都可能使方位角变化超过 90°。
该分支触发时的实际距离写入了日志(Float64("dist", dist)),判定逻辑不使用这个值。日志对这一分支的描述是 guessed by rotation,原文出自 MapTracker 源码。
对,就是猜的。
其一:initRot 的零值。(:182-193)
// Show navigation UI
var initRot int
if initResult, err := doInfer(ctx, ctrl, param); err == nil && initResult != nil {
initRot = int(initResult.Loc.AngleTo(targetLoc))
...
} else if err != nil {
log.Debug().Err(err).Msg("Initial infer failed for moving UI")
}该段注释为 “Show navigation UI”:initRot 最初为绘制导航界面取值,随后被复用于到达判定。
若此次定位失败,initRot 保持零值 0,即正北。此时 |DeltaRotation(targetRot, 0)| > 90 等价于目标方位位于南半边,约占一半方向;循环首轮即判定到达,角色在该路径点上未发生移动。
触发条件是 :184 的 doInfer 失败,且循环内 :228 的首次 doInfer 成功;定位持续失败时,:221 的 arrival timeout 会正确返回 false。因此这一路径命中的是瞬时的定位坏样本。
var initRot int 声明在 for i, targetLoc := range param.Path 的循环体内,每个路径点重新初始化一次;多点路径的节点,暴露次数等于路径点数。
其二:精调阶段的复测结果被丢弃。
reachedByRotation 触发之后(:251-255):
if enableFineApproach {
runFineApproach(ctx, ctrl, ca, param, targetLoc, curRot)
}
breakrunFineApproach 首先重新测距,若距离超过阈值(:645-655):
// Guard against residuals that are too large for fine approach.
if dist >= param.ArrivalThreshold {
log.Warn().
...
Msg("Fine approach residual exceeds arrival threshold, giving up the fine approach")
return fineApproachDiverged, result
}这条日志的含义是“残差超出到达阈值,放弃精调”,即复测得到的距离否定了上一步的“到达”判定。
调用方对 runFineApproach(...) 的返回值没有赋值,下一行即为 break。fineApproachReached / fineApproachExhausted(“accepting current position”)/ fineApproachDiverged 三种结果均被忽略。
并且 enableFineApproach 在默认配置下只在最后一个路径点为真(:80、:178-179):
enableFineApproach := (param.FineApproach == FINE_APPROACH_ALL_TARGETS) ||
(param.FineApproach == FINE_APPROACH_FINAL_TARGET && i == len(param.Path)-1)中间路径点不进入精调,也就不产生这次复测。
Run() 返回 true 的条件是:所有目标点均跳出内层循环,且 on_finish 成功。普通路径不做终局位置校验。
同一代码库中,goal.go 的 zipline 分支设有终局校验(validateZiplineArrival,距离不达标则 DisableVertex 并重新规划);普通路径的到达判定没有采用这一校验。
上述路径在遥测中均记为成功。
成功事件的判据是 Run() 返回 true,上述路径全部返回 true。
MapNavigator 的成功率,是真实到达率的下界。
MapTracker 的成功率,是真实到达率的上界。
一个下界和一个上界,不能直接比大小。
"Target point reached (ordinary approach, guessed by rotation)" 只有 Info 日志,未进遥测;mxu.node 的 span.duration 恒为 0,也无法用“秒过”作代理。假阳性率在现有仪器下不可量化。
简单来说就是作弊了。
关于两套埋点的一致性。 2026-08-06 与 08-07 先后合入的两套埋点(节点级成功事件、stage=action),在 rc.2 窗口内 234 个节点上失败数完全一致,逐节点差异为 0,可以排除漏报。两套埋点读取的是同一个 Run() 返回值,二者一致由构造决定,不能作为到达判定的独立验证。
“不利于” MapNavigator 的一组数字。
同一窗口(2026-08-07T17:30Z – 20:15Z,正向指定 release:"MXU@2.4.3+MaaEnd@v2.23.0-rc.2"):
| 引擎 | 调用 | 失败 | 成功率 |
|---|---|---|---|
| MapTracker 合计 | 1597 | 25 | 98.43% |
| MapNavigator 对照 | 392 | 8 | 97.96% |
MapTracker 高 0.47 个百分点。这张表是 08-08 按当时的全量数据算的;Sentry 现在只保留这一窗口的 1/8 抽样,失败条数太少,按引擎拆开后复现不出原比例,抽样数据见 d5-rc2-0807-window-by-node.csv,说明见 附件说明。
该数字经过两次计算。第一次按 span.description 分组翻页,215 个分组只汇总到 2557 次调用中的 2233 次,得 98.33%;该次的版本过滤采用排除法,漏掉了窗口内最大的版本 MXU@2.4.1+MaaEnd@v2.22.0,据此曾算出 73.52%,已作废。上表为改用正向 release 指定、分块对账后的结果。
这组数字与本节结论一致:98.43% 的分子包含“方位角摆过 90° 即判到达”的样本,以及精调复测已判定距离超限、仍记为成功的样本;97.96% 的分子只包含通过 zone 与距离校验的样本。
以上代码均可在仓库历史中查阅;MapTracker 的目录此后随 #5239 移除。
声明以两侧成功率的高低论证“哪个系统更加稳定”,所比较的是两套不同的判定标准。
这边蒙的也算到了,那边验过才算到,拿这俩成功率比谁更稳,可不就是拿开卷考的分数去比闭卷考嘛 ()
六、“艺术孤品”
“开源本应是开放的公共资源,最怕的恰恰是把东西做成只有一个人能维护的‘艺术孤品’。事实上,最终所有的责任和后果理应由那个人自己来负责。”
这是声明的最后一段。核验这一段,需要确认 MapTracker 工具的前端源码何时进入仓库。
07/24 22:26 — #4461 工具改为 Web UI,前端只以编译产物入库。
#4461 feat(tools): MapTracker可视化工具现已升级为Web UI:isHarryh 提交,2026-07-24 22:26:36 合并,23 个文件,+1721 / −6995。此前的编辑器是仓库内的 Python 源码(map_tracker_editor.py 1794 行、nav_mesh_editor.py 1854 行,以及 _internal/gui_pages.py、_internal/gui_widgets.py),此 PR 将其删除,换成 Vue 写的 Web 前端。前端只以 tools/map_tracker/_frontend/dist/static_files.zip 入库,内含 Vite 打包后的 25 个文件(index.html、assets/index-CouAZ6vQ.js 等),.vue 源码不在仓库内,PR 描述也没有给出源码位置。
08/06 19:53 — #4803 上传工具前端源码。
#4803 feat(tools): 修订及上传 MapTracker 工具 Vue 侧源码:2026-08-06 19:53:38 创建,20:03:43 合并,由 isHarryh 本人合并,无人工 review,42 个文件,+8726 / −113。
新增内容为 tools/map_tracker/_frontend/ —— NodeEditorPage(1478 行)、NavMeshEditorPage(915 行)、CollectTestDataPage(462 行) 等。
上传之前,这套前端的源码没有开源。 从 07-24 22:26 到 08-06 20:03,近 13 天里仓库内只有编译产物 dist/static_files.zip;isHarryh 名下的公开仓库中,截至 09-30 也没有单独的前端仓库。#4803 只含一个提交,作者时间 08-06 19:50:23,PR 描述写的是“并同时上传了 Vue 侧源码,以便后续需要时参考”。
之前谁想改这个工具的前端,就只能对着这个 zip 发呆 ()
合并时间距其在 #4787 中“本人将不再参与 MaaEnd 项目的开发及维护”的声明,不足 20 小时。
08/07、08/08 — 两次关于维护的发言。
此后 isHarryh 在群内两次提到维护:08-07 的“不用看了,已经停止维护了,等替换吧”,上下文与截图见四;08-08 20:40—21:37,有人报“送货还有个老问题 滑索规划有问题”,回复是“很遗憾滑索寻路将不再维护了,需要另请高明迁移”,位于本篇开头声明截图的最上方。
MapTracker 工具的 Web 前端,自 07-24 入库起近 13 天没有开源,源码在作者宣布退出后不足 20 小时才上传。
七、“需要的不是证据”
“将一场技术路线分歧,完全转写为一个人的‘主观恶意’,需要的不是证据,而是对‘另一颗心’的读心术——而这恰恰是那篇文章做得最轻车熟路的事。”
同一份声明的前文另有一句:
“而紧接着发生了一件在整个叙事中被轻轻带过的、最值得重新思考其动机的事。”
声明全文约一千五百字,其中 commit hash、PR 编号、链接与时间戳的数量均为 0。声明开篇称“有几处被滤去的画面值得重新放回时间轴上”。
一千五百字,一个链接都没有,“需要的不是证据”倒是说到做到。
声明在批评“读心术”的同时,以“最值得重新思考其动机”作为论点,全文未附任何可核验的记录。 声明涉及的各处画面,本篇第一至第六节已与对应的 PR、commit、时间戳和原始截图逐条对照。
八、“迁移后的内容的稳定性超越迁移前的水平”
“至于某人,衷心祝愿他可以早日将我的全部工作从项目中抹除干净,好好露一手完成各种修复补丁、使迁移后的内容的稳定性超越迁移前的水平。”
这一标准可以用平台记录和遥测数据逐项核验。先列迁移过程。
08/05—08/29 — 迁移 PR 的合并与进入的版本。
七个迁移 PR 建立于 2026-08-05 19:40:56 至 19:41:20,前后 24 秒。下表按合并时间排列,另含三个相关 PR。“首个测试版”“首个正式版”指最早包含该 PR 合并提交的版本;版本时间取 GitHub Release 的发布时间(北京)。v2.24.0 只发布了测试版,没有正式版。
| PR | 内容 | 合并(北京) | 合并人 | 首个测试版 | 首个正式版 |
|---|---|---|---|---|---|
| #4781 | 生态农场 | 08-06 00:51 | Lemon-miaow | v2.23.0-beta.6(08-06 01:52) | v2.23.0(08-08 22:48) |
| #4783 | 售卖弹性物资 | 08-09 21:28 | zmdyy0318 | v2.24.0-beta.2(08-10 01:34) | v2.25.0(08-22 00:08) |
| #4786 | 赠送干员礼物 | 08-09 21:52 | zmdyy0318 | v2.24.0-beta.2(08-10 01:34) | v2.25.0(08-22 00:08) |
| #4780 | 自动采集 | 08-09 23:14 | zmdyy0318 | v2.24.0-beta.2(08-10 01:34) | v2.25.0(08-22 00:08) |
| #4785 | 环境监测 | 08-10 23:07 | he0119 | v2.24.0-beta.3(08-11 02:23) | v2.25.0(08-22 00:08) |
| #4784 | SeizeDeliveryJobs | 08-22 00:24 | zmdyy0318 | v2.26.0-beta.1(08-22 00:31) | v2.26.0(08-29 00:02) |
| #5075 | 大地图选点改用 WorldMap | 08-25 20:55 | zmdyy0318 | v2.26.0-beta.6(08-25 23:37) | v2.26.0(08-29 00:02) |
| #4782 | 基质刷取 | 08-25 23:05 | zmdyy0318 | v2.26.0-beta.6(08-25 23:37) | v2.26.0(08-29 00:02) |
| #5223 | 栖云生态点的间接引用 | 08-26 09:12 | he0119 | v2.26.0-beta.7(08-27 00:52) | v2.26.0(08-29 00:02) |
| #5239 | 移除 MapTracker | 08-26 14:16 | Lemon-miaow | v2.26.0-beta.7(08-27 00:52) | v2.26.0(08-29 00:02) |
进度追踪 issue #4793 于 08-25 23:35:53 关闭。
声明中用于论证“哪个系统更加稳定”的数据,取自环境监测模块。声明发布时,该模块的迁移 PR #4785 仍处于 OPEN 状态,尚未合并。
关于“抹除干净”。
删除 MapTracker 的操作此前已发生过一次,执行者为 isHarryh:
| 时间(北京) | 事件 |
|---|---|
| 08-05 19:41 | 七个迁移 PR 建立完毕,全部 OPEN |
| 08-05 22:31:53 | isHarryh 创建 #4787“移除MapTracker”,+74 / −13869 |
| 08-06 01:07:11 | #4787 由 isHarryh 本人合并。此时七个迁移 PR 中已合并 1 个 |
| 08-06 01:23:27 | zmdyy0318 创建 #4792“Revert “refactor(map): 移除MapTracker”” |
| 08-06 01:38:05 | #4792 合并,+13869 / −74 |
| (其间 21 天,七个迁移 PR 逐个合入) | |
| 08-26 14:02:41 | Lemon-miaow 创建 #5239“Reapply “refactor(map): 移除MapTracker (#4787)” (#4792)”,+71 / −33617,229 个文件 |
| 08-26 14:16:32 | #5239 合并,d382169f。17 项检查通过,0 项失败 |
从 #4787 合并到回滚完成:30 分 54 秒。
从 #4792 合并到同一个删除动作重新落地:20 天 12 小时 38 分 27 秒。
#5239 的标题由 GitHub 自动生成:它 revert 的是 #4792,而 #4792 revert 的是 #4787。删除 MapTracker 的那个 commit,作者仍为 isHarryh。
兜了一大圈,删掉 MapTracker 的 commit,作者还是 isHarryh 本人,挺有意思的。
08/26 — 调用清零,最终清理由 #5239 完成。
删除之前,逐项核对业务模块对 MapTracker 的调用是否已经清零。
直接调用。 上面的迁移清单按 pipeline 中 custom_action 取值为 MapTracker* 的调用点计数,#4793 记为 241 处,七个迁移 PR 合入后为 0。
间接引用。 按 MapTracker* 检索覆盖不到间接引用,例如:
"custom_action": "SubTask",
"custom_action_param": {
"sub": ["SceneEnterWorldWulingMarkerStone_Ship2DestinationThenWalkAndFight"]
}该调用位于 EnvironmentMonitoring/MarkerStoneMonitoringTerminal/CloudrestEcoSite.json,被引用的节点定义在 MapTracker/Example/WLMarkerStoneShip2.json 中。其 custom_action 取值为 SubTask,按 MapTracker* 检索无法命中。
改用“pipeline/MapTracker/ 下定义的 32 个节点名在别处被引用的次数”重新检索,全树命中 1 处,即上面这一处。#5223(+82 / −282)处理它并删除该示例文件,08-26 09:12:45 由 he0119 合并。
另有两棵资源树此前未纳入统计:assets/resource_adb/ 内为两条注释;assets/resource_macos/pipeline/MacOSKeyMap.json 内为三个节点覆盖,其被覆盖对象定义在 MapTracker/OpenWorld.json,由 goal.go 引用。二者都不是业务调用,随 #5239 一并删除。
大地图选点:MapTrackerBigMapPick 由 #5075 替换。
custom_action 取值为 MapTrackerBigMapPick 的调用点,在 #5075 合并前共 54 处:SceneManager/SceneTeleportWuling.json 52 处,SceneManager/SceneTeleportValleyIV.json 2 处。替换它们的是 #5075“feat(WorldMap): 大地图点位识别”,Lemon-miaow 提交,08-19 00:08:12 创建,08-25 20:55:29 由 zmdyy0318 合并,+2980 / −1011,31 个文件。合并后,业务 pipeline 中对 MapTrackerBigMapPick 的引用为 0;它的 Go 实现 agent/go-service/maptracker/bigmap/pick.go 以及文档、schema 中的余下引用,随 #5239 删除。
WorldMap 基于 MapLocator 工作,为其他服务提供地图元素查找能力,是现在传送业务的核心。
点击之前的校验。 两种实现在点击之前做的事不同:
MapTrackerBigMapPick(bigmap/pick.go:121-144):推算当前视口,把目标坐标换算成屏幕坐标;坐标落进视口,就在该处点击并返回 true。点击前不检查那个位置上有没有传送点图标。MapFind(WorldMapSolver::ConfirmSpot):求解视口后,在折算出的屏幕位置附近匹配图标模板,命中点与期望位置的偏差超过 10 个底图像素即判认错;确认不到图标就不交出坐标,节点判失败。WorldMap 文档原话:“给了icon时,确认不到图标就不给坐标”。
这一处替换提供了一组对照。埋点由 #5200“chore: 大地图传送增加埋点上报”加入,zmdyy0318 于 08-25 01:23:26 合并,+486 / −54,改动的两个文件正是 SceneManager/SceneTeleportValleyIV.json 与 SceneManager/SceneTeleportWuling.json——#5075 随后改写的那两个。埋点的合并 commit 11ac982a 最早出现在 v2.26.0-beta.5,WorldMap 的合并 commit 75a8e192 最早出现在 v2.26.0-beta.6。因此 beta.5 运行的是加了埋点的 MapTrackerBigMapPick,beta.6 运行的是 WorldMap。两版为同一批节点、同一套埋点、相邻的两个 release(同一看板,14 天窗口,查询时间 2026-09-02 03:48):
| 版本 | 大地图选点实现 | 调用 | 失败 | 成功率 |
|---|---|---|---|---|
| v2.26.0-beta.5 | MapTrackerBigMapPick | 15K | 102 | 99.3332% |
| v2.26.0-beta.6 | WorldMap(MapFind + Click) | 16K | 27 | 99.8386% |
失败率 0.6668% → 0.1614%。上表是看板读数;同一窗口内只取 __ScenePrivateMap* 传送节点做 span 级查询,beta.5 15,128 次调用、失败 40 次,beta.6 17,144 次调用、失败 0 次(采样外推值),方向相同,逐节点计数见 d6-bigmap-teleport-beta5-beta6.csv。beta.6 的一次成功,比 beta.5 多要求一道图标确认。
另有一份统计,见 #5075 的评论(zmdyy0318,2026-08-26 11:11:37):99.05% → 99.89%,方向一致。其口径与上表不同:过滤到 *EnterValleyIVTheHub* / *EnterWorldWuling* 两组传送节点的子集,样本 11,158 与 3,591,取数时点也早 7 天。两份并列于此,正文以上表为准。
多验了一道,失败还少了一大截呢。
#5239 合并前的核对结果:
| 业务模块直接调用 | 0 |
| 业务模块间接引用 | 0 |
大地图选点 MapTrackerBigMapPick 调用 | 0 |
| 其余 | MapTracker 自身的 pipeline、Go 服务(含 bigmap/pick.go)、底图资产、macOS 键位覆盖,互相引用,由 #5239 成组删除 |
最终清理由 #5239 执行:229 个文件,186 个删除,+71 / −33617。
MapTracker 移除 commit:d382169f,2026-08-26 14:16:32 合并。
合并后 origin/v2 上:agent/go-service/maptracker/ 不存在,assets/resource/pipeline/MapTracker/ 不存在,全仓 custom_action 取值为 MapTracker* 的调用点为 0,MapTrackerBigMapPick 为 0。截至 2026-09-29 的 origin/v2(d95235ac),全仓不区分大小写检索 maptracker,命中 0 处。
迁移完成后成功率:d382169f 首次随 v2.26.0-beta.7 发布,v2.26.0 是首个不含 MapTracker 的正式版(tag 时间 2026-08-28 22:54:37)。该版本过去 24 小时,“MapNavigator 版本成功率”看板口径:330K 次调用,5,101 次失败,成功率 98.4559%(查询时间 2026-09-02 03:48)。同一窗口 v2.27.0-beta.3:61K 次调用,830 次失败,98.6557%。
这张看板带有过滤条件,完整如下:span.op = mxu.node、has task,按任务白名单取值,排除 *Main*、*Node*、AutoDelivery*、MapNavigator* 四类节点名,以及 span.description 含 MapTracker 的 span。排除项里有失败率高的节点——同一天的节点级面板上,AutoDeliveryNavigateDestination 2.9K 次调用失败率 16.81%,AutoDeliveryRouteDestinationDeliverTargetMap02Lv00502WithZipline 469 次调用失败率 50.32%。因此 98.4559% 的适用范围仅限这组白名单节点。第四节以节点选择范围为由指出那张面板的局限,同一标准也适用于这张看板。
该数字与第五节的 rc.2 数字属于不同总体:第五节为 2026-08-07T17:30Z – 20:15Z 窗口、MapNavigator 侧 392 次调用,此处为 24 小时、330K 次调用,二者不作比较。可比的对照是上面 beta.5 → beta.6 一组。
各模块迁移前后。
以上为白名单节点的合计。声明所定标准针对“迁移后的内容”,以下按模块拆分,逐版本列出(查询时间 2026-09-30,窗口 2026-08-06 00:00 至 2026-09-29 21:00):
口径:span.op = mxu.node、has task;节点名含 Goto / GoTo / Move / Navigate / Teleport / Route / Turn 等关键词;排除 task 为 *Main、*Node*、MapNavigator*、AutoDelivery*、SeizeDeliveryJobs、DeliveryJobs 的 span;排除 span.description 含 MapTracker 的 span 与大地图传送节点 __ScenePrivateMap*(这类节点单独见上文 beta.5 → beta.6 一组);单版本调用超过 500;按 MaaEnd 版本聚合,合并不同的 MXU 版本。计数为 Sentry 采样外推值。“首个版本”是 GitHub 上第一个包含迁移 PR 合并提交的 tag。原始数据:d4-modules-nav-nodes-by-release.csv,按 release 中 MaaEnd@ 之后的版本号合并即得表中各格。各模块的迁移 PR 与首个版本见本节开头的表。
| 模块 | 迁移前 | 迁移后首个正式版 | 最新 |
|---|---|---|---|
| 环境监测 | 71.53%(v2.23.0) | 95.18%(v2.25.0) | 97.37%(v2.30.1) |
| 自动采集 | 99.04%(v2.23.0) | 97.89%(v2.25.0) | 98.28%(v2.30.1) |
| 赠送干员礼物 | 99.95%(v2.23.0) | 99.97%(v2.25.0) | 99.94%(v2.30.1) |
| 售卖弹性物资 | 99.78%(v2.23.0) | 99.96%(v2.25.0) | 99.85%(v2.30.1) |
| 基质刷取 | 98.06%(v2.25.0) | 98.80%(v2.26.0) | 97.52%(v2.29.0) |
| 生态农场 | — | 83.48%(v2.23.0) | 96.38%(v2.30.1) |
| 有迁移前数据的五个模块合计 | 97.74% | 98.29% | 98.00% |
| 选剑演武(对照) | 96.31%(v2.23.0) | — | 99.84%(v2.30.1) |
合计行为环境监测、自动采集、赠送干员礼物、售卖弹性物资、基质刷取五个模块按调用数加权,各模块取本表同列的版本。
声明用于论证的环境监测,是七个迁移模块中迁移后提升最大的一个。
拿来论证的偏偏是涨得最多的那个模块,有点尴尬。
需要一并列出的反例与限制。
- 自动采集迁移后略低。 v2.23.0 99.04%,v2.30.1 98.28%,低 0.76 个百分点。迁移初期的 v2.24.0-beta.2 至 beta.4 为 94–95%,此后逐版回升。节点粒度也有变化:按任务级 span 计,v2.23.0 每次运行约 57 个导航节点,迁移后 12–18 个,逐节点成功率与逐次运行成功率不能直接换算。
- 合计的提升主要来自环境监测。 不计环境监测,其余四个模块合计迁移前 99.04%、最新 98.04%,差距主要在自动采集,它的节点粒度迁移前后不同(见上一条)。
- 环境监测的跃升发生在迁移后一版。 #4785 的首个版本是 beta.3,beta.3 仍为 73.18%,升至 95.37% 的是 beta.4。beta.3 → beta.4 之间合入的有 Lemon-miaow 的两项引擎修复(#4939 转向、#4947 尾部裸坐标)和两次地图模型更新(#4938、#4948),也有 he0119 的三项修复(#4922 拍照判定、#4928 修复多个任务的路径、#4942 路径优化)。这次跃升与上述修复落在同一版本区间,不能单独归因于迁移。
- 生态农场没有迁移前的数据。 #4781 08-06 00:51 合并,首个版本 v2.23.0-beta.6,看板上没有它迁移前的版本,图上全部是迁移后的点。它在 beta.4 从 79.80% 升至 96.06%,同一区间合入了 #4941(ModiAWA,工业设备选项点击)和 Lemon-miaow 的 #4943(线路)。
- 基质刷取迁移前后持平。 迁移前 98.06–98.45%,迁移后 97.52–99.20%,v2.29.0 比迁移前低约 0.5 个百分点。v2.30.0 起该任务的统计有误,失败率虚高约 5 个百分点,图中截止到 v2.29.0。原始值如下:v2.30.0 94.35%,v2.30.1 94.86%;这段偏差最早见于 v2.30.0-beta.5(96.29%)。
- 赠送干员礼物、售卖弹性物资,迁移前后都在 99.8–100%。 售卖弹性物资 v2.27.0 有一版 99.10%。
- SeizeDeliveryJobs(#4784,首个版本 v2.26.0-beta.1)不在这张图上,上面的过滤条件排除了这个任务。单独统计,其余口径相同:“走到仓库”一类节点,迁移前 v2.23.0 98.61%、v2.25.0 98.68%,迁移后 v2.26.0 至 v2.30.1 在 98.56–99.53%。迁移后多出“送往目的地”一类节点(
AutoDeliveryNavigateDestination),迁移前的遥测里没有对应节点;它 v2.26.0 83.24%,v2.30.1 93.12%,截至 v2.30.1,七个迁移模块里只有这一段还在 95% 以下。原始数据:d7-seizedeliveryjobs-by-release.csv。 - 选剑演武全程未迁移。 v2.23.0 与 v2.26.0 的 pipeline 里都是 2 处
MapNavigateAction,不在迁移清单里。它从 96.31% 到 99.84%,是同期 MapNavigator 自身的变化;beta.3、beta.4 两版它也掉到过 89–90%。
判定标准对读图的影响。
图中橙色的点来自 MapTracker,是真实到达率的上界;蓝色的点来自 MapNavigator,是下界(判定代码见五)。
因此图中数据按两种情况解读:
- 蓝点高于橙点:真实提升至少等于图中差值。 环境监测从 71.53% 到 97.37%,这 25.84 个百分点是提升的下限。
- 蓝点持平或更低:这组数据无法判断方向。 自动采集低 0.76 个百分点,基质刷取 v2.29.0 低约 0.5 个百分点。一个偏高的数和一个偏低的数相差不到一个百分点,这两处记为“方向未定”。
按声明所定的标准,迁移后的总体水平已超过迁移前,环境监测提升最大;其余模块持平、方向未定或缺少迁移前数据。 有迁移前数据的五个模块合计,迁移前 97.74%,迁移后首个正式版 98.29%,最新 98.00%;迁移后一侧全部来自 MapNavigator,是下界,迁移前一侧以 MapTracker 为主,偏高。
祝愿收到了,谢谢你喵^^
数据附件。 后日谈所引的 Sentry 数字,原始查询结果导出为 CSV:打包下载(sentry-data.zip),逐个文件与说明见 README.md。导出只含节点名、版本号与状态计数,不含 trace、用户与设备信息;每个文件的时间窗与查询串记录在 queries.json。
本文所引 PR、commit、代码与截图均可公开查阅,遥测数据可在共享的 Sentry 组织内复现。如有与记录不符之处,欢迎指正。