双向跨服改背包,简直是刷物 Bug 的温床

2026 年 7 月那会儿,为了方便玩家在不同子服之间倒腾装备,我写过一个叫 CrossInv 的双向跨服背包互访插件:玩家站在 A 服敲个命令打开 54 格箱子,就能直接实时读写自己在 B 服的背包。

结果实战跑下来我后背直发凉:跨服双向实时覆写玩家背包实在太危险了!一旦两边网络稍微抖动一下,或者有人故意用双开客户端卡进出服的时间差,两边服务器同时往数据库写全量背包快照,立马就会撞上时序竞态,不是把刚拿出来的神装覆盖没了,就是被卡出复制刷物 Bug。

只准从老服往新服单向流转:SOURCE 与 SINK

痛定思痛,在 8 月到 9 月 9 日(Commit 418b06b),我彻底推翻了双向同步的思路,从头设计了 igng-dev/igng-onewayvault(单向保险库)。

新架构把规则定得死死的:老周目服在配置里只能设为 SOURCE(源端,只准敲 /transfer 往外存东西),新周目服只能设为 SINK(汇端,只准敲 /vault 往里取东西),绝不允许逆向回流。

在 SOURCE 端,玩家打开的是一个带本地 CRC 校验和 WAL(预写日志)保护的缓冲箱,只有关上箱子确认寄出的那一刻,才通过幂等批次号一次性写进 MySQL;而在 SINK 端,由于有些仓鼠玩家攒了几千格物资,我专门写了基于游标的稀疏分页读取,翻到哪一页才加载哪一页的 NBT 数据,打开几百页的仓库也完全不卡。

异常立刻打入隔离态,恢复必须两名管理员异体审批

最关键的是取货那一刻的安全闭环。每一件物品从数据库发往玩家背包时,都必须走 RESERVED -> DELIVERY_STARTED -> DELIVERED 状态机。如果在 DELIVERY_STARTED 阶段服务器突然崩了或者玩家掉线了,系统绝不自作主张瞎重试(因为自动重试最容易被利用来刷双倍物品),而是立刻把这批物资打入 QUARANTINED(隔离态)冻结起来。

想要把隔离态的物资恢复给玩家,单靠一个管理员敲命令都不行——我引入了金融级的双人异体审批(Two-Man Rule):必须先由一名管理员执行 /owv admin resolve 提出处置方案,再由另一名不同的在线管理员执行 /owv admin approve 复核通过才能放行。甚至连 MySQL 数据库底层我都挂上了触发器,硬性锁死了发起人和审批人绝对不能是同一个 UUID,从制度和代码两头彻底堵死了内鬼和误操作。