先交代背景:这是我自己的《海岛奇兵》私服,客户端版本 57.135,除了服务端,还有一个在浏览器里重建基地渲染与编辑的 webclient。客户端自己随包带了一整套 csv/——建筑、障碍物、装饰、陷阱、词条、globals,连「移除这块石头要多少黄金、掉多少木材、司令部要到几级」都写在里面。
以前这些东西在引擎和外壳里各抄一份。最近十天把抄的部分一条条删掉,改成直接读表,编号从 C35 排到 C68。这篇不打算按提交顺序复述,只挑几件值得记的事:为什么非读表不可、怎么让「读没读」变成会红的测试,以及表里没有的东西为什么不能自己编。
手抄的代价不是「抄错一次」
手抄最坏的形态不是抄错,是抄对之后没人知道它已经过期。客户端表里一格改了,引擎里那份副本不会跟着动,最后的表现往往离源头很远:水面的颜色差 1、障碍物清除不扣钱、同一件装饰可以摆一百个,而测试全绿。
这次清出来的几处都挺典型:
| 位置 | 表里的真值 | 手抄时的表现 |
|---|---|---|
src/terrain.h | globals.csv 三张水面调色板,共 12 格 | 像素对比只会报「某片水颜色差 1」 |
removeAt | obstacles.csv 的 ClearCost 600…25000 | 白送删除,不扣黄金、不掉战利品 |
| 装饰放置 | decos.csv 的 MaxCount 只有 0/1/2 | 任意复制,从不出现上限提示 |
| 攻击面板 | buildings.csv 四列状态效果 | 17 件物件、78 级带值,面板上一片空白 |
所以 C44 之后换了做法:数值只从表里来,引擎只做加法和截断,外壳不自己造数。唯一的例外是种子,注释里单独标了。
先让「读没读」可判定
读表本身不难,难的是下个会话怎么知道这一格真的读了。C44 是从一张随包的 texts_patch.csv 开始的:6 行里 2 格非空,其中一个 TID 在 texts.csv 里根本不存在,客户端能显示全靠 patch。补完之后生成物里多了一段可核对的东西:
window.hbTextMeta.patch = { present: true, rows: 6, cells: 2, sha256: '3e815929…' }验收链上的 TEXTS OK 会把这两个数和 patch 的 sha256 打出来。C45 更进一步,写了 tools/globals_check.py,把 src/terrain.h 里手抄的三张水面调色板逐格钉回 csv/globals.csv,字段顺序也一起核。
这条链现在跑到 PARITY OK ×96、三条浏览器通路 413/413/366 ✓,ENGINE TEXTS OK 扫 27 份源码、949 条字符串字面量,非 ASCII 必须为 0。它不能拦住「读错了列」,但能拦住「忘了读」。
表里没有的,照抄不自造
C63 是最难的一格。StatusEffectType 有 Fire、Stun、SpeedBoost、Panic;SpeedBoost 四种,四列用分号平行分段,Barbed Wire_lvl1 的数值是 -750(负数表示减速)。类型词在 texts.csv 里没有精确格,只有 速度加成 命中一条,其余零命中。
这里做了个很土的决定:类型词保留表里的原始取值,不翻译、不造句。提示句也照客户端那一条 TID_STATBAR_HINT_BURN_DAMAGE(「点燃目标」),哪怕它对 Stun 不贴切——客户端那个位置就是这句话。
C61 更说明问题。障碍物清除在客户端有整套流程文案:TID_POPUP_CLEAR_OBSTACLE_TITLE、TID_AVAILABLE_LOOT、TID_CAN_NOT_CLEAR_OBSTACLE,连「您需要达到hb.remove,这四段话一句都没用上。改成读表之后,扣黄金、进战利品、卡门槛全部按行来,ClearTimeSeconds 九行全是 0,也照读、不排倒计时。
C68 的装饰上限只有 0/1/2 三档,No_Decos 是 0,Speaker 是 2。250 件 Unlockable 是 TRUE(解锁即免费),留空的 11 件正好是带 BuildCost 的付费件。外壳现在会数当前件数、到顶时原样显示 TID_BUILDING_CAP_REACHED,付费件放置真扣钱。
一个彩蛋:客户端自己留了 TID 语言
C49 翻 csv/locales.csv 时发现,语言列表里有一行的名字就叫 TID,除了 ShowTID = TRUE 什么都不声明,字体也是空的。选中它时,界面上每个词就是词条自己的名字——这张「译文表」就在 texts.csv 的第 0 列,5,755 个数据行、5,755 格全等于行名,是客户端自己写的。
仓库此前只把 TID 当「第 0 列的列名」,从没用过。现在 ?lang=TID 是第 21 种随包语言,语言选择器里也有它。写测试时拿它当两侧对照:同页 ZH-HANS 还是「建造」,TID 页就应该是 TID_BUTTON_BUILD。
代价
每加一列,要同时改引擎的 catalog、JSON 键、外壳渲染、白名单和至少四条回归,然后去构建机上跑完整条 remote_acceptance.sh。词条白名单从 26 条涨到 86 条,语言从 20 种变成 21 种,425 个物件里 422 个能对上客户端 TID。
但换来的是另一种提交说明:不再是「把数值改对」,而是「这四列以前没读,现在读了,回归怎么重推」。客户端更新时,把新的 csv/ 丢进语料,红了就修——事实来源不在我这边,手抄才需要理由。
