info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
侠客行Ⅰ("The Quest of Oriental Chivalry"),XiaKeXing Gaming Group 1996-1999,MudOS v22b25 世系,由 sdong 移植。与 `jym`(金庸梦)共享同一套地图骨架(`d/taihu/gumu/houtang.lpc` 等场景内容相同,只是文件头"破解"署名不同:这份是"Cracked by Kafei",`jym`/`xkm` 都是"Cracked by Roath"),应是同一款底层代码库的不同流通版本。
English
The Quest of Oriental Chivalry ("Xiake Xing I"), by the XiaKeXing Gaming Group, 1996-1999, MudOS v22b25 lineage, ported by sdong. Shares the same map skeleton as this project's jym (Jin Yong's Dream) — scenes such as d/taihu/gumu/houtang.lpc are identical in content, differing only in the "cracker" credit header ("Cracked by Kafei" here versus "Cracked by Roath" for jym/xkm) — evidently different circulating copies of the same underlying codebase. Its map covers classic sect landmarks like Huashan, Wudang, Shaolin, the Beggars' Sect, Emei, the Murong clan, and Zhongnan, plus geographic settings such as Dongting Lake, Lake Tai, Huangshan, and the Western Xia region; registration opens with a GB/BIG5 encoding choice, typical of these older cross-strait shared designs.
README
内容亮点
- 1996-1999 年 MudOS v22b25 世系的老牌"侠客行I",地图涵盖华山、武 当、少林、丐帮、峨嵋、慕容、终南等经典门派地标,以及洞庭湖、太 湖、黄山、西夏等地理场景。
- 注册时需要先选择 GB/BIG5 编码,是这批老档案里典型的两岸三地共用 设计。
- 管理员权限的存储方式很特别:既不是常见的
wizlist文本文件,也 不是securityd.lpc里的表格常量,而是一份纯文本 mapping 存档 (data/securityd.o)——而且已有管理员账号被硬编码锁定在特定 局域网 IP(192.168.0.40),换了地址登录会被直接拒绝(详见下方 管理员账号说明)。
本次修复的关键 bug
1. §7.52 从零手写的 HTTP 服务器:adm/daemons/network/http.lpc
(经典的 Truilkan/Jacques Interstice httpd.c,和 AGENTS.md 里
mnhf 那份是同一血统)直接呼叫
socket_create()/socket_bind()/socket_close(),没有任何存
在性判断,导致整个档案在 WASM 下编译失败。搜过了整个代码库,没
有任何其他文件呼叫这个精灵,所以按 §7.52 的默认做法,把每一个碰
socket 的函式(setup()、write_data_retry()、
store_client_info()、listen_callback()、
close_connection() 里的 socket_close)都掏空成空函式,而不
是一个一个补呼叫点。
2. §8.1 check_legal_name() bug:adm/daemons/logind.lpc 用字
节数长度上限(2-8,原意是 1-4 个字)加上 i%2 奇偶门槛,还有一
个破坏性的"修正"骇客——只要(已经被作者自己注释掉的)
is_chinese 检查判定某个字符不是中文,就 name[j]+=128,在
UTF8 码点索引下这会直接把合法名字的字符搞坏。已把长度上限改成
字符数 1-4,并且彻底删掉这个 +=128 的破坏性副作用(原本的
is_chinese 拒绝逻辑本来就是注释掉的,这里只是去掉遗留下来的
破坏性动作,没有恢复任何主动校验)。adm/simul_efun/
chinese.lpc 里真正的 is_chinese() 本身已经是正确的码点区间
检查。
管理员账号 / Admin account
这份档案的管理员权限不是从 adm/etc/wizlist/wizlist.h 读取
的(这两个文本文件在实际代码里完全没被引用,纯属遗留)——真正的
数据来源是 securityd.lpc 自己的存档文件
data/securityd.o(一份纯文本的 mapping 存档格式)。已经直接编辑
这个存档,往 wiz_status 和 wiz_sites 里加入 fluffos 条目
(权限 (admin),站点正则 .* 不限制来源地址——注意已有的
waiwai/xgslxz 管理员账号被锁定在 192.168.0.40 这一个特定地
址,logind.lpc 的 get_id() 会对任何有巫师等级但 wiz_sites 里
没有条目的账号一律拒绝登录,所以新增账号必须显式给一个站点正则)。
- ID:
fluffos - 密码 / Password: 注册时自设
- 权限 / Level:
(admin),wizlist命令确认显示"目前权限: (admin)",且未被站点检查拒绝。
警告:对外公开架设前请务必修改此密码。
本地运行
cd libs/xkx2000zxb
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40140。
NOTES · 移植与修复记录
WASM 修复摘要(迁移自 meta.json 的 group_note)
侠客行I("The Quest of Oriental Chivalry"),XiaKeXing Gaming Group 1996-1999,MudOS v22b25 世系,由 sdong 移植。WASM 修复:(1)§7.52 类的从零手写 HTTP 服务器:adm/daemons/network/http.lpc(经典的 Truilkan/Jacques Interstice httpd.c,AGENTS.md 里 mnhf 已经记载过同一血统)直接呼叫 socket_create()/socket_bind()/socket_close(),没有任何存在性判断,导致整个精灵在 WASM 下编译失败。没有任何其它档案呼叫这个精灵(已 grep 确认零外部呼叫者),按 §7.52 的默认做法,把每一个碰 socket 的函式(setup()、write_data_retry()、store_client_info()、listen_callback()、close_connection() 里的 socket_close 呼叫)都掏空成 no-op,而不是逐个呼叫点修补。(2)adm/daemons/logind.lpc 里 §8.1 类的 check_legal_name() bug:按字节数算的长度界限(2-8,本意是 1-4 个字符)加上一个 i%2 奇偶门槛,外加一个破坏性的"修正"骇客(对任何被那个——已经被禁用、注释掉的——is_chinese() 检查标记的字符做 name[j]+=128),在 UTF8 码点索引下会把合法名字搞坏——已把长度界限改成按字符数(1-4),并彻底删掉 +=128 这个变异(原本基于 is_chinese() 的拒绝逻辑本来就已经被原作者注释掉了,这里只是去掉遗留下来的破坏性副作用,没有恢复任何主动校验)。adm/simul_efun/chinese.lpc 的 is_chinese() 本来就是正确的逐码点检查。管理员账号通过 securityd.lpc 真正持久化的存档档案(data/securityd.o,一份纯文本的存档映射字面量,不是那两个从未在实际代码里被读取过的遗留 adm/etc/wizlist.h/wizlist 文本档案)播种——把 fluffos 同时加入 wiz_status('(admin)')和 wiz_sites('.*',不受限,匹配既有的 'npc' 引导条目的写法),因为 logind.lpc 的 get_id() 会对任何巫师等级的 id 呼叫 SECURITY_D->valid_wiz_login(),如果 euid 在 wiz_sites 里完全没有条目就会拒绝登录,不管 get_status() 的等级是什么——既有的 waiwai/xgslxz 管理员账号被锁定在特定的 192.168.0.40 地址,如果照抄会挡住 WASM 测试/127.0.0.1 的登录。注册流程在一次连续的 WASM 客户端会话里完整验证过:GB/BIG5 编码选择→英文 id→y/n 创建确认→中文名字→密码+确认→接受天赋赠礼(y)→电子邮件(id@address 格式)→性别→带着完整角色属性表和可用的 score/look 指令进入游戏世界,全程没有任何意外错误。管理员权限已直接通过 'wizlist' 指令输出确认"目前权限:(admin)",没有站点检查拒绝。LPC 格式化工具对全部 8096 个档案运行(写入 8021 个,24 个因为杂乱的历史代码报错,51 个未改动);还原了 1 个档案(d/taihu/gumu/houtang.lpc)确认有转档之前就存在(作者一方,早于本轮)的未结束字符串损坏(它的 item_desc 映射里两处 'hua1'/'hua2' 条目缺少闭合引号),被格式化工具的分词器进一步重新加了空格。没有 :: 父类呼叫拆分命中,没有 case 标签带尾随注释的候选。全部 7 个 map.lpc 档案确认内容完全相同(只是空白差异)。格式化后用同样的完整注册流程重新验证过——干净,管理员权限依然是 (admin),没有站点检查拒绝。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 39 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
深度功能测试(2026-08-17,round one)——干净通过,零 bug
第一次对这份档案做完整 §10.7 深度游玩测试(注册 → 探索 → 战斗 → 死亡 → 留言板发帖),全程零真实 bug。
批量编译扫描先行:lpcc_check.sh 全档案 8096 个文件(933 个通
过,144 个失败)——绝大多数失败是孤立的孤儿内容/未接线的网络精灵
(和早前 WASM 阶段已记录的模式一致,未逐个重新核实每一个)。其中
4 处(/adm/daemons/languanged、/adm/daemons/languaged)撞上
Eval interrupted: ... cost limit reached, limit: 700000 usec.——
但读过 languaged.lpc 的 create()(只有两个单行 read_file()
呼叫,完全不昂贵)后怀疑是 lpcc 批量编译共享同一进程、跨数千个档
案累积 eval-cost 债务导致的纯批量扫描artifact,不是真实可达
的 bug(languanged——带错字的那份——甚至没有任何调用者,是孤
儿档案;只有 languaged 被 feature/message.lpc/logind.lpc/
adm/simul_efun/message.lpc 真正呼叫)。
现场验证排除了这个疑虑:重开一个全新驱动进程,完整走一遍真实
注册流程(GB/BIG5→英文id→y/n→中文名字→密码+确认→天赋赠礼→电子
邮件→性别→进入游戏世界),全程会自然触发 logind.lpc/
feature/message.lpc 里对 languaged.lpc 的呼叫(每次 message()
广播都会用到),也会触发本档案角色基类 inherit/char/char.lpc 自
己的冷启动首次编译——两者都干净通过,debug.log 全程零
cost limit reached。结论:这 4 处失败确认是批量扫描本身的产
物,不是真实 bug,未修改任何配置或代码。
发现的真实设计(非 bug):新注册的普通玩家会先落在"侠客岛挂
名处",score/hp/i 等状态指令在这个阶段返回空字符串——
不是崩溃,是因为角色还没走完这份档案自己的游戏内"register
<email>"验证步骤(和账号创建时填的 email 是分开的两步);完成
register 后,服务器会主动断线("一封包括随机密码的 email 将在
一分钟内寄给您……请用新的密码连线"),要求玩家用新密码重新登录。
这个测试环境收不到真实邮件,所以没能验证一个走完这整套流程的普通
玩家账号;改用巫师账号(fluffos,wiz_sites 播种为 .* 不受
限)来绕过这道验证——巫师直接跳过挂名处,落在真实的沙滩起始房间,
score/hp/i 立即可用(完整属性面板正确渲染)。
战斗/死亡:goto /d/island/icefire3(冰火岛),两只"白熊"
(attitude: aggressive)当场主动攻击,完整交手数回合直至角色死
亡("你倒在地上,挣扎了几下就死了"),正确移动到死亡区"鬼门关"
(/d/death/gate),谣言广播正确。复活:确认 /d/death/npc/
wgargoyle.lpc 的自动复活 call_out 链明确排除巫师
(init() 里 if (... || wizardp(previous_object())) return;)
——所以巫师角色死后不会自动复活,这是有意设计(巫师通常有独立的
复活/immortal 处理),不是本档案独有的 bug;受限于邮件验证步骤
无法用真正的普通玩家账号走完自动复活流程,留给以后有办法收发测
试邮件时再验证。
留言板:goto /d/xiakedao/dadong(大山洞,有"侠客岛告示牌"),
post <标题>(标题跟在同一行)→ 内建行编辑器(结束离开用 '.')
→ read new 验证标题、正文、作者全部正确落盘。
测试账号(fluffos、qintestk)存档留在 data/ 下作为佐证,均
为未跟踪文件,未纳入本次提交。
深度功能测试(2026-08-19,round four)——补完普通玩家自动复活验证,顺带修好巫师 call 指令
本轮的主要目标是解决 round one 留下的唯一缺口:普通玩家(非巫 师)的死亡自动复活流程,当时因为"卡在电子邮件验证"而没能用真 正的普通玩家账号走完。
先弄清楚"电子邮件验证"到底是什么:读了 d/xiakedao/npc/
mux.lpc(挂名室的登记使 NPC,do_register())和它调用的
adm/daemons/regid.lpc::register_char(),发现这压根不是一个需要
真实 SMTP 的外部依赖——register_char() 只是:①把 registered
字段设成 "yes" 并存盘(这才是解锁 score/hp/i 等指令的真正
开关),②生成一个 5 字母随机密码,③用 write_file() 把这个明
文密码写进本地档案 /queue/register(REGDATA,一行
id:email:密码),④告诉玩家"一封邮件将在一分钟内寄给您"然后主
动把连线断掉。全程没有任何 SMTP/mail-send efun 呼叫——这一行
write_file() 显然是设计给一个从未随这份档案一起出现的外部发信
脚本用的,纯粱本地文本档案,测试环境完全可以直接读这个档案拿
到明文密码,不需要真的收发邮件。
"挂名处"的真实流程比 round one 记录的更复杂一层:新注册玩家
落地的房间其实是 /d/xiakedao/shatan1.lpc("沙滩",张三/李四两
个引路 NPC 之一),而不是有 register 指令的那个房间;这个房间
的 block_cmd() 白名单里根本没有 register,只有 follow——
必须先 follow zhang san(或 follow li si)才能被带到真正挂
着"登记使"木老 NPC、允许 register <email> 指令的
/d/xiakedao/register.lpc("侠客岛挂名处")。round one 用巫师账
号绕过了整段流程,所以没碰到这层,这次用真正的新建账号
qinfuhuo/test1234 走了一遍完整流程才发现。
完整验证过程:①register创建新账号 qinfuhuo(GB→英文
id→中文名"秦复活"→密码→天赋→email [email protected]→
gender),落地 shatan1;②follow zhang san 被带到挂名处,
register [email protected] 成功,服务器提示"一封包括随机密
码的 email 将在一分钟内寄给您……"并断线;③直接读
work/queue/register,拿到明文新密码 cecbx;④用新密码重新连
线,确认 registered 已经生效——score/hp/i 全部正常返回完
整数值面板(不再是空字符串),落地到真正的新手海滩(渔夫 NPC 那
个"沙滩",/d/xiakedao/shatan)。至此电子邮件验证缺口彻底解
开,且证明它从来都不是一个真正的外部依赖。
死亡与自动复活的真实验证:用巫师账号(新建 fluffos,
wiz_sites 早已播种为 .*)执行 call qinfuhuo->move("/d/island/
icefire3") 把 qinfuhuo 传送到冰火岛(跟 round one 巫师死亡测
试同一个危险房间,两只 attitude: aggressive 白熊),几秒内被
白熊咬死("你倒在地上,挣扎了几下就死了"),正确移动到
/d/death/gate(鬼门关),d/death/npc/wgargoyle.lpc(白无常)
的 init()——因为 qinfuhuo 不是巫师,没有触发 wgargoyle 的巫师
排除分支——正常挂上 death_stage call_out 链,5 句对白按 30 秒
+4×5 秒的节奏播完,随后 reincarnate() 被呼叫:精/气/精力/内力
全部重置回满血,物品正确按 DROP_CMD->do_drop() 掉落在死亡房间,
角色被送回 /d/xiakedao/shatan(因为 xkd/set 标记为真)。断线
重连复核:score 显示"你到目前为止总共死了 1 次,其中 1 次是正
常死亡",数值面板全部满血,debug.log 全程无任何报错——一次
真实、完整、无 bug 的普通玩家自动复活循环,确认为设计正确。
过程中顺带抓到一个真实 bug(已修复并现场验证):第一次尝试
call qinfuhuo->move(...) 时,巫师 call 指令直接崩溃:
执行时段错误:*Wrong permissions for opening file /log/nosave/CALL_PLAYER.lpc for overwrite.
"No such file or directory"这是 AGENTS.md §7.11("未存在的运行期目录导致 write_file() 静默/
崩溃中止")这个已知病灶家族的一个新实例:cmds/wiz/call.lpc 只
要目标物件是 /clone/user/user(也就是任何玩家角色本体——这是
call 指令最常见的使用场景),就会无条件呼叫
log_file("nosave/CALL_PLAYER", ...) 做审计记录;而这份档案的
adm/simul_efun/message.lpc::log_file() 是裸的
efun::write_file(filename, str, 1)(第三参数 1 = overwrite 模
式,会硬抛错而不是静默返回 0),完全没有 assure_file() 防护,
而这份档案从未附带 /log/nosave/ 目录——导致巫师 call 指令只
要目标是任何玩家角色就必崩,这是巫师工具箱里最常用的指令之一。
检查过实际死亡流程本身(feature/damage.lpc::die() 里
log_file("PLAYER_DEATH"/"PKILL_DATA", ...))——这几处目标都是
/log/ 根目录下(已存在),不受影响,所以死亡/复活主线没被这个
bug 波及,只有巫师 call 指令本身。修复:仿照 AGENTS.md §7.11 里
反复验证过的标准做法,在 message.lpc 顶部加一行 void
assure_file(string file); 前向声明(因为 assure_file() 定义在
后编译的 file.lpc 里),并在两处 efun::write_file() 前各加一
行 assure_file(filename)/assure_file(filename2)。现场验证:
修复前 call qinfuhuo->query("name") 直接崩溃报错;重启驱动加载
修复后同一条指令正常返回 "秦复活",之后的
call qinfuhuo->move(...) 也正常执行,/log/nosave/ 目录被自动
创建。这个具体实例未做跨库扫描(不在本轮任务范围内),但同样
的 log_file()/assure_file() 缺口在这份档案的
adm/simul_efun/message.lpc 里只有这一处未加保护,file.lpc 自
己的 assure_file() 实现本身没问题。
本轮 checklist 快速核对(均为已跨库扫描过的项目,逐一确认本 档案没有异常):
- §7.111(
master.lpc/standard_trace()的file_name(0)崩 溃):不适用/天然安全——work/adm/single/master.lpc的standard_trace()用的是%O格式化error["object"],根本没 有呼叫file_name(),不是这个漏洞的易感形状。 - §7.112(
init()里无重入保护的call_out("death_stage", ...)链):d/death/npc/wgargoyle.lpc、bgargoyle.lpc以及d/bwdh/sjsz{,2,3}/{east,west}_xiangfang.lpc全部已经带有query_temp("death_stage_active")防护——干净。唯一没加防护的d/death/npc/wgargoyle1.lpc经确认是死代码:全档案没有任何 地方clone/引用它,鬼门关(gate.lpc)和另一处死亡入口 (gateway.lpc)分别用的是已加防护的wgargoyle.lpc/bgargoyle.lpc,不影响任何真实游玩路径,未做修改。 - §7.113(断线重连不恢复
heart_beat):干净——adm/daemons/logind.lpc::reconnect()呼叫user->reconnect(),clone/user/user.lpc::reconnect()在玩家 本体上无条件set_heart_beat(1),属于已确认的正确谱系。 - §7.114(继承 mixin 里的
privateinput_to()回调):干净——feature/edit.lpc的input_line()没有private修饰符。 - §7.115(
QUEST宏指向不存在的档案):不适用——本档案include/globals.h里根本没有QUEST宏定义,全档案也没有QUEST->呼叫点。 - §7.90(eval-cost 默认值):已是
5000000(round one 已修)。
测试账号 qinfuhuo(已注册、已完整走过死亡复活)和 fluffos(巫
师,用于 call/goto 操作)存档留在 data/ 下作为本轮佐证,均
为未跟踪文件(work/data/login|user/{q,f}/*.o),未纳入本次提交;
work/queue/register、work/log/ 均已被仓库 .gitignore 整体排
除,无需清理。
§7.100 sweep (2026-08-19)
Fixed the corpus-wide inherit ROOM; ... replace_program(ROOM); redundant-replace bug (AGENTS.md §7.100). 2124 live occurrences deleted: 2123 via scripted sweep (fix_710_room.py), plus 1 hand-fixed roommaker-tool template (clone/misc/roommaker.lpc, simple string-builder variant). ~284 already-commented-out instances left untouched. Checked work/data/ (incl. d/shenlong/data/) for real .lpc source false-negatives per this batch's standing gotcha — none found. Verified via build-debug driver boot: clean compile, zero new "cannot replace"/"cannot bind" debug.log lines; confirmed serving via raw-socket connect on port 40140. Pre-existing untracked test-account debris confirmed left untouched.
``§7.112`` residual-gap closure (2026-08-20)
Corpus re-scan (grep -rl 'call_out("death_stage"' ... | filter for missing guard) found unguarded init()-scheduled death_stage() call_out chain(s) in d/death/npc/wgargoyle1.lpc that the original two-wave sweep (see AGENTS.md §7.112) missed -- same reconnect-triggered duplicate-chain bug, different filename/lineage. Added the standard query_temp("death_stage_active")/set_temp/delete_temp re-entry guard, adapted per file's own exit points. Compile-verified via lpcc --batch.
§7.30 uninitialized-mapping accessor sweep (2026-08-20)
Corpus-wide mechanical sweep of the feature/skill.lpc shared-lineage
bug (confirmed independently on xiakexing2017/jqxz2015/haiyang2
via round-four testing): 4 accessor(s) in this file returned a raw
never-initialized mapping instance variable (defaults to int 0,
not ([]), until first assigned), crashing any unguarded
keys()/sizeof()/indexing caller for a fresh/untrained character.
Fixed at the accessor level (mapp(x) ? x : ([])) per the documented
remedy. Verified via lpcc --batch static compile check only (not a
live boot) as part of a large mechanical sweep; not individually
functionally re-tested live on this lib.
§7.19 enable_player() reentrancy fix (2026-09-01)
Corpus-wide mechanical fix (AGENTS.md §7.19, Batch F of 6). feature/command.lpc's
enable_player() had no reentrancy guard at all: an NPC's init() (e.g.
d/zhongnan/npc/killer.lpc's first-time !query_temp("copied") branch) reaches
setup() -> enable_player() -> enable_commands(), which the driver docs and
this project's own live-verified prior findings (mhxy/wuhanzhan) confirm can
re-invoke the same object's init() synchronously while the original call is
still on the stack -- genuine reentrancy, "Too deep recursion" on a room's first
visit. feature/damage.lpc's revive() and cmds/std/sleep.lpc's
wakeup()/wakeup2() also re-invoke enable_player() while already living(),
so a bare living() guard would have broken legitimate revive/wakeup re-enables.
Fixed with a true reentrancy flag (nosave private int in_enable_player_now;,
set at entry, cleared before the function's single return path), matching the
reference fix on mhxy. Verified via single-file lpcc --batch PASS.
深度功能测试(2026-09-04,round three,shop + 拜师)
新角度:扬州醉仙楼购物 + 丐帮左全拜师。2026-08-17 round one 和
2026-08-19 round four 覆盖了战斗/死亡/复活/call,没有走商店或拜师。
实测过程
管理员 fluffos。第一输入是 Do you want to use BIG5 code?(y/n),回
n 用 GB。落地侠客岛沙滩 /d/xiakedao/shatan(巫师跳过挂名处)。原
登录密码是注册时自设、对不上 Mud@2026,本轮把 login.o 的 crypt 重
设成 Mud@2026(未提交)。waiwai/xgslxz 的 valid_wiz_login 锁定
192.168.0.40 是设计,没动;fluffos 的 wiz_sites 仍是 .*。
goto /d/city/zuixianlou,list 烤鸡腿四十文铜板 / 牛皮酒袋五十文 /
包子三十文(vendor_goods 三条,没有 start_more 分页)。F_DEALER
里丐帮穷叫化拒绝购买整段是注释掉的,仍按家族惯例先买再拜。
clone /clone/money/gold 后 buy jitui 成功(「你从店小二那里买下了
一根烤鸡腿」)。i 剩六十文铜钱 + 九十九两白银(10000−40 = 9960),
黄金条目消失,找零数学正确。
goto /d/gaibang/inhole,左全源码是 kungfu/class/gaibang/zuo-quan.lpc,
apprentice zuo 一次成功:左全收徒,score 「丐帮一袋弟子」、师父左
全、恭喜成为第二十代弟子。cmds/usr/save.lpc 真正调用两个 save()。
user.o 立刻带上 family_name":"丐帮" / master_name":"左全" /
generation":20。断线后再连(「重新连线完毕」),称谓/师傅/银子还在。
左全只收男性。
Bug:log_error() 把编译警告当「编译时段错误」打给每个连线玩家(AGENTS.md §7.10 / §7.103)
第一次进游戏时,look/score/i/list/goto 每碰到一个尚未编译的
档案,就往屏幕上倒 编译时段错误:...warning: Unused local variable
/ Unknown #pragma。adm/single/master.lpc 的 log_error() 对
this_player(1) 无条件 efun::write("编译时段错误:" + message),没
有把 warning 和真正的 error 分开——this_player(1) 在本驱动上就是当
前玩家,不分巫师。修复:加上 corpus 标准闸门
strsrch(message, "arning:") == -1(大小写都吃到)。警告仍写入
work/log/log。重启驱动后同一条 look/score/i/list/inhole 路径
编译时段错误 次数为 0。没有对 log_error() 呼叫 wizardp()(§7.10
ACL 陷阱)。
live debug.log 是 libs/xkx2000zxb/log/debug.log(Boot Time Fri Sep 4
02:10:11 然后修复后 02:12:02 2026),无 error: / Too deep recursion。
error_handler 把轨迹交回驱动 debug.log。管理员存档未提交。
深度功能测试(§10.7,2026-10-04)— negative range ends
scripts/lpc_fix_negative_ranges.py: 5 line(s) in 2 file(s) count a range from the end with < (x[a..-1] -> x[a..<1], x[-2..] -> x[<2..]; KB 06 §7.209). In this driver a negative constant end gave "" / ({ }) and a negative start the whole value, so each of these returned the wrong slice; the compile warning A negative constant as the second element of arr[x..y] is gone from the files that compile. Range lvalues (s[0..-1] = text, the prepend idiom) are left alone. A paired compile of the changed files at HEAD and in the working tree showed no new error.
深度功能测试(§10.7,2026-10-04)— compile-warning and load-failure pass (ported from xkx2001)
scripts/lpc_warnings.py xkx2000zxb: 166 distinct diagnostics at the end, 7991 files compile and 98 do not. A load check of every
.lpc (8099 files, 700 per VM boot) at HEAD and in the working tree: 7950 PASS -> 7986 PASS, no file that loaded before fails now
that is not a random draw. 1132 files changed. The code is the xkx2001 root's, so the pass is its pass (libs/xkx2001/NOTES.md has
every class, the typos found through the no-side-effects list and the list of files left alone):
lpc_twin_port.py adopt xkx2001 xkx2000zxb carried the fixes of the files whose code is the root's, then
lpc_warnings.py --fix, lpc_diamonds.py --fix-redundant, lpc_fix_void_override.py, lpc_fix_macro_redef.py,
lpc_fix_prototype_types.py, lpc_move_decl.py, lpc_fix_no_effect.py, lpc_fix_heredoc_terminator.py and (after a rescan)
lpc_name_winner.py handled the diagnostics that depend on this lib's own files.
Fixed by hand in this lib (each read first; nothing written new): d/city/qiyuan{2,3,4}.lpc (the root's lost closing quote of set("short", "棋室);; the splice that ports the root's fix refused these three files because the pre-image has an unterminated string, so the quote was closed by hand); kungfu/class/shenlong/lu.lpc (the damaged reply, restored from the sentence jym and xuanjianlu carry); d/zhongnan/bingdong.lpc (set("long",@long ... LONG — the opener is spelled in lower case, the terminator is not, so the block never ended; both are LONG now); d/dali/npc/daobaifeng.lpc (attempt_apprentice(object ob) tested me->query("family/family_name") with me undefined; shenzhou carries ob); d/zhongnan/gate.lpc and gate1.lpc (a varargs the port had left on a line of its own, now part of the function header).
What is left: the files that do not load in the root either (drafts, pasted logs, the TMI-2 network daemons without headers, wizard work), plus d/city/yuelao2.lpc (a quoted multi-line string and a stray ]) ); after the exits), d/city/grt.lpc (a pasted log), d/hangzhou/hangzhou.lpc and d/taihu/gumu/houtang.lpc (damaged bytes inside the text), d/dali/npc/a.lpc, d/qilian/npc/pixie_superskill.lpc, and the headers that were never part of the archive (/u/maco/poison_list.h, /d/quanzhou/obj/drug_list.h, /d/shaolin/npc/job.h).
深度功能测试(§10.7,2026-10-04)— paths named in another case
The archive comes from a case-insensitive host: files are stored as BAGUA0.lpc, longjing.C, d/SHAOLIN/, the code names them in lower
case (__DIR__ "bagua0", "tea/longjing", "/d/shaolin/..."), so the rooms, NPCs and items behind them could not be loaded (a .C file is
not an .lpc file; KB 03 §4.5). scripts/lpc_case_paths.py --convert --apply xkx2000zxb: 10 file(s) renamed to lower case (.C -> .lpc,
.H -> .h) and 0 path reference(s) in 0 file(s) rewritten to the new spelling; 0 skipped, 0 ambiguous. Load check of the 11 objects that were renamed, edited or name a renamed path (HEAD against the working tree, an object is the same under any spelling of its path): 1 PASS -> 11 PASS, 0 that loaded before fail now, 10 more load.
The 10 files are the tea items of d/city/npc/tea/*.C that d/city/npc/c_waiter.lpc carries out. Rescan: 157 distinct diagnostics, 8005 files compile and 94 do not (7991 / 98 / 166 before; the four peak-view rooms d/wudang/taoyuan/tyroad4-7.lpc now include /d/wudang/feng.h, see the sweep commit).
深度功能测试(§10.7,2026-10-05)— room base survives a raising NPC
scripts/lpc_resilient_room.py --apply: make_inventory() of inherit/room/room.lpc loads each inventory object as if (catch(ob = new(file)) || !objectp(ob)) return 0;. new() of a file that is not there returns 0 (the room just lacks that NPC), but new() of a file whose create() raises (a skill file for set_skill() this archive never had, a carry_object() item that is not there, a header that never shipped) raised inside the room and the room did not load at all. The wrong-case renames of this lib (the section above) make the NPCs behind them reachable for the first time, so a room that holds one with such a content gap would have gone from "NPC missing" to "room missing"; now it loads without that NPC, as it did before. The room base compiles and loads with the change (HEAD against the working tree). KB 03 §4.5.
- 2026-10-10:
/clone/misc/roommaker.lpc生成新文件用的 here-document 模板(@ROOM_CODE/@OBJ_CODE)里的inherit行,被 782409377fe 的重复继承清理当成代码删掉了(lpc_diamonds.py当时不识别 here-document),用它生成的房间/物品会因缺少继承而无法编译。已恢复该行;工具已修正。
深度功能测试(§10.7,2026-10-11):dns_master 空 if
adm/daemons/network/dns_master.lpcquery_services():if (! (mud_svc[mud]["tell"/"finger"] & SVC_KNOWN)) {里只有#if PREF_X & SVC_TCP … #endif,选项关闭时 if 体为空(“无副作用”警告);把#if/#endif挪到 if 外面(KB 04 §6.10,2026-10-07 那次扫描漏掉了! (带空格、大括号另起一行的写法)。载入无回退。
深度功能测试(§10.7,2026-10-11):dns_master 空 if
adm/daemons/network/dns_master.lpcquery_services():if (! (mud_svc[mud]["finger"/"tell"] & SVC_KNOWN))块里只有#if PREF_X & SVC_TCP … #endif,选项关闭时 if 为空(“无副作用”警告报在下一句);把#if/#endif挪到 if 外面(KB 04,2026-10-07 那次扫描漏了! (加括号换行这种写法)。载入无回退。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。36 个文件 39 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。
- 2026-10-14:1 个文件用了颜色宏(
HIW、NOR)却没有#include <ansi.h>(首个错误syntax error, unexpected L_STRING);开头补上;2 个文件的函数声明为int(或void)却返回字符串、对象(int query_seal() { return "…"; }),旧驱动照样把值交给调用者;改为实际返回的类型(scripts/lpc_fix_void_return.py)(KB 04 §6.1、§6.3)。新进程比对 HEAD 与工作树:3 个对象,新增载入 0,回退 0。
- 2026-10-14(更正):上一条把
auto_perform()也改成了mixed/string,其实那里返回值的类型是因为前面的错误(未定义的函数)才变成未知,不是真的返回字符串;恢复为int。
无门派玩家的 family 映射(2026-10-15,KB 05 §7.30 第三种)
mapping fam = ob->query("family"); 之后直接 fam["family_name"]:没有门派(新人或叛师后)的玩家在学习、练功、拜师、进入门派房间时报 Value being indexed is zero,而不是作者写好的提示。1 处在声明行加上 if (!mapp(fam)) fam = ([ ]);(scripts/lpc_fix_family_guard.py)。
其他体系的属性名(2026-10-15,KB 06 §7.241 第三类,--any-file)
另有 1 个文件(绝招、状态、物品、NPC 等,不限于 kungfu/skill)来自 gin/kee/sen/force 体系,同样按 kee→qi、gin/sen→jing、force→neili 改名(scripts/lpc_port_es_props.py --any-file)。
内力/上限当作伤害种类(补)(2026-10-15)
victim->receive_wound("force", n, me)(兵器吸内力)、receive_damage("max_qi", random(100))(走火入魔扣上限)等本库不接受的种类,1 处改为 add(type, -N)。
message_vision(msg, environment(me), me)(2026-10-15,KB 06 §7.242)
把房间当成了动作者:message_vision() 取房间的 name() 得 0,replace_string(msg, "$N", 0) 报错。在状态(中毒)里,这个错误发生在 apply_condition(name, duration - 1) 之前,状态永不结束,心跳里后面的恢复也每次被跳过。改为 message_vision(msg, me):/kungfu/condition/sanpoison、/kungfu/condition/xianglu-du。
family 映射(再补)(2026-10-15,KB 05 §7.30)
mapping fam; fam = (mapping)ob->query("family");(先声明后赋值)和泥潭式的 query("family", me) 同样直接取下标:无门派的新人拜师(attempt_apprentice/recognize_apprentice)、NPC 的 greeting、房间的 valid_leave、ask_me 等报 Value being indexed is zero。6 处补上 if (!mapp(fam)) fam = ([ ]);(刚 recruit_apprentice() 之后的除外)。
空手使用兵器绝招(2026-10-15,KB 06 §7.246)
perform 指令允许直接点名招式(如 perform stick.ban),这些绝招读了 me->query_temp("weapon") 却从不检查,空手时 weapon->name() 报错(有的已先扣了内力)。在赋值后补 if (!objectp(weapon)) return notify_fail("你必须手持兵器才能使用这一招。\n");(scripts/lpc_fix_perform_weapon.py):dagou-bang/ban、dagou-bang/chan、dagou-bang/sansha、damo-jian/weituo、huashan-jianfa/feilong、jinshe-zhuifa/huayu、jinshe-zhuifa/tuwu、liangyi-dao/sanshen、mo-bang/mo、riyue-bian/chanrao、taiji-jian/tongshou、wudang-jian/tongshou 等 14 个。
把经验、属性、内力当技能读(2026-10-15,KB 06 §7.247)
经验、属性、内力上限不是技能,xkx/ES 的 query_skill() 对它们只返回 0(风云的还会去载入同名技能文件而报错)。改为 query("...")(scripts/lpc_fix_pool_as_skill.py):驯马的 /d/dali/npc/a、/d/dali/npc/mafu(成功率少了福缘一项)。
临济庄 remove 没有运功时报错(2026-10-15)
linji-zhuang/remove 直接 switch (lj_exert["name"]),lj_exert 是 query_temp("lj_exert");没有正在运用的临济庄功夫时它是 0,exert remove 每次都报 Value being indexed is zero。补上 if (!mapp(lj_exert)) return notify_fail(...)。本库的临济庄各 exert 都不设置 lj_exert,所以这个 exert 以前每次都报错,现在只会提示没有在运功。
技能名拼错(2026-10-15,KB 06 §7.249)
query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):shenhuo-xuanming→shenghuo-xuanming(/d/kunlun/skill/dafeng-yunfei、dafeng-yunfei、xiaoyao-zhangfa);quanzhen-jianfa→quanzhen-jian(dugu-jiujian);xiaowu-xiang→xiaowuxiang(zhemei-shou);xiatian-gong→xiantian-gong(/d/zhongnan/skill/chunyang-quan)。
嵩山剑法「蓄力」没穿布衣时报错(2026-10-15)
songshan-jian/xuli 用 cloth = me->query_temp("armor/cloth") 取衣服,随后直接 cloth->query("material") 判断要不要显示“衣袖鼓起”的描写;身上没有布衣时(裸身或只穿铠甲)这里报错,而此时 apply/speed 已经扣掉、复原的 call_out 还没排上,身法就一直少了一截。两处判断前补 objectp(cloth) &&。
技能名拼错:技能文件里拼错自己的名字(2026-10-15,KB 06 §7.249)
技能自己的文件(或它的绝招目录)里,query_skill() 读的是差一个字母的自己的名字,永远得 0:“基本功夫要高过本技能”之类的限制从不生效,按等级取招、计算伤害的那一项一直是 0。改为本技能的名字(scripts/lpc_fix_skill_typo.py 的 self 判据):baitao-xuangong→bitao-xuangong(bitao-xuangong)。
地图数据库 mapdb 的空档防护(2026-10-15,KB 06 §7.250)
genmap 第一次载入时 if (!restore()) save(); 会在地图建好之前写出只有文件头的 data/npc/map.o;mapdb 读它成功后 map 为 0,所有查询都对 0 取下标(nitan170911、shenzhou 等 10 个库就带着这样的文件)。没有 map.o 时 query_random_room_path() 又对空数组取下标。mapdb(/clone/obj/mapdb.lpc)加一个 restore():读档后 map 不是映射就重置为 ([]);地图为空时 query_random_room_path() 返回 0。
随机取房间后不判断就过滤(2026-10-15,KB 06 §7.250)
/d/city/npc/ftb_zhu.lpc、/d/qilian/obj/jinhe.lpc 从 mapdb 随机取一个房间后直接 strsrch(room_path, ...) 排除区域;地图还没生成时取回的是 0,strsrch(0, ...) 报错,护镖等任务就此中断。条件前补 !stringp(room_path) ||,取不到房间时走原有的重试和“cannot find target room”提示。
临济庄「幽冥桩」的两处笔误(2026-10-15,KB 06 §7.245、§7.250)
linji-zhuang/youming:killer->querry(...)、me->querry(...) 调用的是不存在的函数,永远得 0,恶人比值只按 PK 数算,改为 query;visit_room() 先拿 range 作判断、后从参数里取 range,判断时它还是 0,效果遇到没载入的房间就停,两行取值挪到前面。
npc_setup 的浮点截断警告(2026-10-15,KB 04 §6.10)
/clone/obj/npc_setup.lpc:level = pow(10 * exp, 0.333334); 把浮点数隐式截成整数,编译时警告;改为 to_int(...),结果不变。
用兵器的绝招空手施展时报错(内联写法)(2026-10-15,KB 06 §7.246)
绝招不存兵器变量,直接写 (me->query_temp("weapon"))->query("name")(或在拒绝判断里写 (weapon = me->query_temp("weapon"))->query("skill_type")),之前又没检查兵器:空手 perform 时报 Bad argument 1 to EFUN call_other(),内力等往往已经扣掉。在第一次用到它的那条语句前补上“你必须手持兵器才能使用这一招”的拒绝(scripts/lpc_fix_perform_weapon.py):feiyu-bian/chanrao、yunu-jianfa/suxin。
存档读不出来:字符串里的换行、空行(2026-10-15,KB 03 §4.4)
data/npc/job_server.o:字符串里有原样的换行(save_object() 存成 \r,restore_object() 逐行读,原样的换行让字符串收不了尾),或有空行(Illegal file format),读档的对象(留言板、NPC、玩家)从空白开始。换行改写成 \r、删去空行(scripts/lpc_save_encoding.py --from-raw),驱动核对全部能读回(scripts/lpc_save_restore_check.py)。
移进不存在的巫师房间(2026-10-15,KB 06 §7.251)
用完的物品、要“离开”的 NPC、掷在空中的兵器被移进巫师的工作室(/u/aln/、/u/ryu/、/u/wsky/),本库没有这些房间。本库的 move() 用 call_other(dest, "???") 载入目的地,找不到文件就报 call_other() couldn't find object,后面的语句都不执行:药(人参、狐狸皮、虎骨,clone/drug/cb_renshen、clone/drug/hugu、clone/drug/hulipi)吃下后药效已经生效,随后报错:call_out("destroy", 31) 没有排上,药一直留在身上,药效过去后可以再吃;逍遥三笑散(d/xingxiu/npc/obj/sanxiao)弹出之后报错,毒药留在身上,可以一再使用;疯魔杖法的掷杖绝招(kungfu/skill/fengmo-zhang/feizhang、kungfu/skill/fengmo-zhang/fengmo):内力、精力已扣,杖已卸下,随后报错;接回杖的 check_fight 没有排上,fengmo 标记不再清除,重新登录之前不能再用;天山杖法「飞杖」(kungfu/skill/tianshan-zhang/feizhang):杖“撞到山石上断成无数截”之后报错,杖仍留在身上。7 个文件改为移进本库的虚空房间 VOID_OB(/clone/misc/void),与原作把它们“停放”在巫师房间的用意相同。
NPC 带着本库没有的物品,载入失败(2026-10-15,KB 06 §7.73)
7 个文件、7 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/shenlong/obj/cloth、/d/city/npc/obj/pinkcloth、/u/dali/npc/obj/baicloth)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 0 个,改后 7 个(7 个原来载入不了),没有退步。
set_skill() 遇到本库没有文件的武功不再抛错(2026-10-18,KB 06 §7.256)
feature/skill.lpc 里的 set_skill()、map_skill()、prepare_skill() 碰到没有武功文件的名字就 error("F_SKILL: No such skill (…)"),调用它们的 NPC create() 整个中断:NPC 载入不了,刷出它的房间也少一个人。本库有 4 个对象设了本库没有文件的武功(4 个名字,最多的是 stuff、huashan-zhangfa、jingding-zhang)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:4 个里 4 个通过。(scripts/lpc_guard_set_skill.py)
通向不存在房间的出口,按对面房间的回头出口改正(2026-10-18,KB 06 §7.257)
房间出口 "west": __DIR__"xxx" 指向的文件本库没有,玩家走过去就被卡住(笔误、改过名的房间,或者没带进档案的编号房间)。对面的房间存在、并且它相反方向的出口正好指回这个房间,说明出口原本就是通向它:/d/murong/qing3 的 east:kufang → nanshi。改动后每个出口都能解析到本库的文件(scripts/lpc_fix_dead_exits.py;目标在运行时本来就能载入的库——虚拟房间——不动)。
武功名拼错的 NPC(2026-10-19,KB 06 §7.258)
set_skill("kunlun-shnfa", …)、map_skill("dodge", "shaolin-shnfa") 这样的调用把武功名拼错了:本库没有这个武功文件,有的是拼对的那个。以前这种 NPC 在 create() 里报 No such skill 载入不了;武功缺失时跳过之后它们能载入,可是少了那门武功(set_skill 被跳过,map_skill 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 18 处、15 个文件:lingji-zhuang→linji-zhuang(7 处)、dashouyin→dashou-yin(5 处)、pudu-chang→pudu-zhang(2 处)、stuff→staff(1 处)、huagu-mainzhang→huagu-mianzhang(1 处)、banrou-zhang→banruo-zhang(1 处)、jingding-zhang→jinding-zhang(1 处)。(scripts/lpc_fix_skill_names.py)
出口指向不存在的房间(2026-10-19,KB 06 §7.257)
下列房间的出口写的文件不存在,而对面恰好只有一个房间的出口指回本房间,所以按对面的房间改正(笔误、大小写、路径少了一级):
/d/beijing/east/wangfudajie3.lpc的southwest出口:/d/beijing/zijincheng/dongchangan→/d/beijing/zijin/dongchangan/d/huangshan/ciguang.lpc的south出口:wengquan→wenquan/d/huangshan/tiandu1.lpc的up出口:jiyubei→jiyunbei
改后房间照常载入(与 HEAD 逐个对比,没有退步),出口现在指向存在的房间。
RANK_D 对 0 的称呼(2026-10-19,KB 06 §7.273)
adm/daemons/rankd.lpc 的 query_respect()、query_rude()、query_self()、query_self_rude() 里 6 个函数开头补上 if (!objectp(ob)) ob = this_object();。NPC 在 create() 里写 "…" + RANK_D->query_respect(this_player()) + "…"(inquiry 对话、chat_msg),驱动自己跑 reset() 时没有人在场,this_player() 是 0。旧驱动里 0->query("age") 返回 0,称呼走默认分支;新驱动直接报 Bad argument 1 to EFUN call_other(),房间的 make_inventory() 吞掉错误,被打死的 NPC 再也刷新不回来。用 scripts/lpc_reach_census.py --walk 找到(xkx100 的武当弟子、一灯、汴梁书生…);修在守护程序里,不动 1,800 个 NPC 文件。
x == "a" || "b":后面的字符串常量永远为真(2026-10-19,KB 06 §7.281)
if (arg == "yuyou" || "yu you") 里的 "yu you" 是非空字符串,永远为真,整个条件对任何 arg 都成立;作者要的是每个字符串都和变量比较。在每个字符串前补上变量和运算符(scripts/lpc_fix_literal_or.py;forrest2.lpc 是 dir == "north" && "south",手工改成 (dir == "north" || dir == "south") && ...):/d/gaibang/ml.h(1 处)。
比较链的人工修正(2026-10-20,KB 06 §7.282)
d/hangzhou/huagang1.lpc:挖泥的判断 arg != "泥" || arg != "mud" 永远为真,随便挖什么词都挖出湿泥,后面的“你要挖什么?”走不到;本意是参数是“泥”或 mud 时才挖,改成 == 和 ||。
if (x = 常量):条件里写成赋值的比较(2026-10-20,KB 06 §7.290)
if (arg = "button" || arg = "按钮")、if (!arg || arg = "") return 0;、else if (check = 0 && tempB != ""):把 == 写成了 =,条件的值就是那个常量,分支对所有非零数和任何字符串(连 "" 都算)永远成立、对 0 永远不成立,变量还被覆盖;驱动对此没有任何警告。每一处 = 改成 ==(scripts/lpc_fix_cond_assign.py,共 10 处)。改动:/d/city/jiuguan2.lpc(2 处)、/d/city/zuixianlou.lpc(1 处)、/d/heimuya/jiudian.lpc(1 处)、/d/kunlun/luanshiduibak.lpc(2 处)、/d/quanzhou/qzkedian.lpc(1 处)、/d/quanzhou/wglou1.lpc(1 处)、/d/xiakedao/no_pk_room.lpc(1 处)、/d/xingxiu/forest5.lpc(1 处)。
登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)
驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/adm/single/simul_efun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。
翻页器 start_more(read_file(...)):文件缺失时 explode(0)(2026-10-20,KB 06 §7.300)
东方故事系的翻页器 void start_more(string msg) { ... explode(msg, "\n") ... } 被这样调用:start_more(read_file(HELP_DIR + "help/topics"))、start_more(read_file(NEWS))。压缩包没带的文件使 read_file() 回 0,explode(0, ...) 在本驱动里报 Bad argument 1 to explode()(不带参数的 help 指令就是这样出错的),老驱动把 0 当空文本。在 start_more 开头加了 if (!stringp(msg)) return;(scripts/lpc_fix_start_more.py)。改动:/feature/more.lpc。
巫师命令不带参数时对 0 调用 efun(2026-10-21,KB 06 §7.336)
巫师(管理员)把命令敲成不带参数时,驱动把 0 传给字符串参数;下面的命令第一句就把这个 0 交给 efun,运行时报 Bad argument 1 to ...,这条指令从来没有用过。用 scripts/lpcc_bare.py 不登录、在一次 lpcc 求值里以 0 调用 main() 逐个确认后,由 scripts/lpc_fix_bare_arg.py 在第一处使用参数的语句前加守卫:文件自带 int help(object me) 的,守卫是 if (!arg) return help(me);(help() 定义在 main() 之后的 1 个文件补了原型 int help(object me);,否则编译报 Undefined function help)。改动(1 个文件,括号里是接住 0 的 efun):/cmds/wiz/test.lpc(present)。加载检查 HEAD 与工作树对照,没有退步。
拼错的 call_out() / input_to() 目标(KB 06 §7.349,2026-10-21)
计时器或输入提示给驱动的函数名和文件里定义的差一个字母;驱动对不存在的函数静默不理,所以对应的计时从来没有触发过。调用和配套的 remove_call_out() 改成已定义的拼法:药炉:burning2 / burning3 → 文件里定义的 buring2 / buring3;do_destroy → 文件里定义的 do_destory。改动:/d/huanghe/npc/tc_wang.lpc、/d/xingxiu/npc/obj/yaolu.lpc、/d/xingxiu/obj/yaolu.lpc。
保护任务的商人 clone/obj/job/job_protect_npc.lpc:两处笔误让整个文件从来没编译过(2026-10-21,KB 04 §6.29)
clone/obj/job/protect.h(保护商人的任务)克隆这个 NPC,但文件有两处笔误:int baohu(string age) 的函数体用的是 arg(Undefined variable 'arg'),而 init() 登记的是 do_baohu,函数名也对不上;query->("id") 多了一个 ->。改成 int do_baohu(string arg) 和 query("id")(if (!player = find_player(...)) 报的 Illegal LHS 只是前面语法错误引出来的连锁错误,这种写法本身是对的,没有动);文件第一次能编译后带出 7 个 Unused local variable 警告(sacrifice、exp_rate、skill_rate、lmt、menpai、killer、kill_num),按 KB 04 §6.10 删掉。baohu / protect 两个动词现在有处理函数,任务的商人也第一次能出现。改动:/clone/obj/job/job_protect_npc.lpc。
注册到不存在函数的 add_action(2026-10-21,KB 06 §7.349)
add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 25 条注册(11 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/beijing/obj/kuanglu.lpc(do_look)、/d/kunlun/npc/mingjiao_npc.lpc(do_qingjiao)、/d/quanzhou/npc/gongping-zi.lpc(do_bihua、do_halt)、/d/shenlong/obj/sg_mianzhao.lpc(do_open)、/d/xiakedao/obj/car.lpc(do_hire)、/d/xingxiu/npc/feng.lpc(do_ketou)、/d/xingxiu/obj/sanxiao.lpc(throw_ob2)、/d/xingxiu/xueling/exit1.lpc(do_ling)、/inherit/misc/fboard.lpc(do_list)、/kungfu/class/gaibang/zhulin3.lpc(do_bake、do_cut、do_kao、do_remove等) 等共 11 个文件。最多的函数名:do_bihua 3 处、do_qingjiao 2 处、do_halt 2 处、do_hire 2 处、do_cut 2 处。没有动(读过):/clone/obj/job/job_protect_npc.lpc 的 do_baohu(对象本身编译不过,登记留着,等对象能加载再处理)。
基类的 add_action 登记:处理函数只在后代里定义,基类自己被加载时报错(2026-10-21,KB 06 §7.349)
房间、NPC、公告板这类基类,init() 里登记的 add_action("函数", "动词") 的函数只由继承它的后代定义;基类对象自己被直接加载(房间可以走进去、NPC 可以克隆)时,访客输入这个动词,驱动报 *Function for verb 'x' not found.(第一轮扫描把这些当成“有后代定义,是活的”放过了,scripts/lpc_verify_handlers.py 在基类对象上跑 function_exists() 才看出来)。登记前加 if (function_exists("函数", this_object(), 1))(第三个参数 1:默认 function_exists() 把 protected / private 函数当作没有定义),后代照常登记。共 6 处、1 个文件(scripts/lpc_fix_dead_add_action.py --guard):/d/bwdh/kantai.lpc(do_abandon、do_practice、do_study)。
对象路径里的 ..:new(__DIR__ "../obj/x") 报 Read access denied(2026-10-22,KB 06 §7.351)
__DIR__ 是当前文件所在目录(带结尾斜杠),__DIR__ "../obj/x" 就是 /d/zhiye/npc/../obj/x;驱动的 legal_path() 不接受带 .. 的路径,new() / load_object() 直接抛 *Read access denied.(实测,find_object() 对它答 0)。走库自己的 carry_object() / 房间 objects 表的路径,库的基类会先规范化,能用;直接把路径交给 new() / clone_object() / load_object() / move() 的不行,调用到那一步才报错。改成规范化后的绝对路径(scripts/lpc_fix_dotdot_dir.py --direct):/d/wudang/npc/xiaocui.lpc(__DIR__ "../obj/nuerxiang" → "/d/wudang/obj/nuerxiang")。
(: 函数 :) 指针指向对象自己没有定义的函数(2026-10-22,KB 06 §7.355)
item_desc / inquiry / chat_msg_combat 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:编译不报错,用到时驱动报 *Undefined lfun pointer called: 函数名(look 某物、ask NPC about 话题 直接出错,战斗 / 闲聊里的指针在 NPC 的 chat() 里出错,这一拍心跳的后半段被跳过)。scripts/lpc_scan_callout_targets.py --kinds=lfun。 还剩 11 个函数没有补(inquiry 6, chat_msg_combat 5):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/d/city/npc/hua_girl.lpc:ask_me、/d/emei/npc/guide.lpc:ask_job_begin、/d/emei/npc/guide.lpc:ask_job_finish、/d/huanghe/npc/tc_hu.lpc:ask_job、/d/huanghe/npc/tc_you.lpc:ask_join、/d/shaolin/npc/xuanci.lpc:ask_tiaoshui、/d/xiakedao/npc/huang.lpc:auto_perform、/d/xiakedao/npc/laozu.lpc:auto_perform、/kungfu/class/shenlong/pang.lpc:auto_perform、/kungfu/class/shenlong/shou.lpc:auto_perform 等另外 1 个。
log/ 下的日志子目录没有人建:写日志的 write_file() / log_file() 报错(2026-10-22,KB 06 §7.358)
这个库的代码往 log/ 的 1 个子目录写日志:log/test(d/forest/clxiaoting.lpc)。压缩包、git clone 和站点包里都没有这些目录(本地启动没有写过它们,gen_keep_dirs.py 没记),write_file() 对不存在的目录抛 *Wrong permissions for opening file /log/dlog/money for append. "No such file or directory",调用它的函数在写日志那一行中断,后面的奖励、标志、步骤都没有执行。本库的 log_file() 会自己建目录(实测,或读源码:nitan 系的 assure_file()),所以这里只记了直接用 write_file() / save_object() 写的目录。把 log/test 记进 scripts/wasm_keep_dirs.txt(scripts/lpc_keep_log_dirs.py;站点和 pristine 树按它建空目录)。证明(scripts/lpcc_eval.py,在 /adm/daemons 这类目录下的求值对象里写 /log/zzdirprobe/...,新进程)见 KB 06 §7.358 的实测表。
(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)
inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 9 个文件里 11 行只含这个指针的行("song": (: ask_me :), "出发": (: ask_job_begin :), "结束": (: ask_job_finish :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。
汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 102 处(2026-10-22,KB 06 §7.360)
原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字);各派掌门的辈分判断(name[0..1] == "澄")永远不成立;改名官 zhuce 的长度检查按字节算,合法的名字一个也过不了。
本库 102 处(48 个文件):拼名字的赋值 76 处、与汉字常量比较 25 处、改名官的长度检查 1 处。例:name[0..01] → name[0..0](/clone/npc/qiu.lpc:295);name[4..5] → name[2..2](/clone/npc/qiu.lpc:295);name[0..3] → name[0..1](/d/city/npc/shiye.lpc:129)。scripts/lpc_fix_gbk_slice.py 把下标按字符重算(社区的 UTF-8 版 xkx100utf8 里就是同样的写法,作对照);没有改的:截断(name = name[0..7],现在最多 8 个字,只是放宽)、ASCII 的区间(fname[0..3] == "room"、ctime(time())[4..19])和上下文看不出是汉字的区间。证明(scripts/lpcc_eval.py,bixiecanyang;其余各库是同一份文本):让澄字辈掌门 attempt_apprentice() 一个名叫「张三」的访客,HEAD 的法名是 慧,改后是 慧三;抽样 28 个库的 HEAD / 工作树加载对比 0 退步。工具不幂等([0..3] 折半得 [0..1],又是对齐区间),改过的文件记在 scripts/gbk_slice_converted.txt,不要重跑。
血条 / 表情标题按字节算显示宽度(2026-10-23,KB 06 §7.365)
老档把汉字当两字节:bar_string[0..(val * N / max) * 2 - 1] 把「格数」换成字节下标,半格血条在本驱动(按字符算区间)上会整条画满;巫师表情 edemote 的 repeat_string("─", 13 - (strlen(emotion) / 2)) 也会少减汉字宽度。scripts/lpc_fix_display_pad.py:去掉 CJK 血条 / 空白条上的 * 2(jings/qis 赋值和 blank_string3[0..(23-a)] 的 23→11 一并改),表情标题去掉 / 2;ASCII 空格的 none_string / empty_string 仍保留 * 2(一格两个空格)。本库 3 处(1 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。
段延庆 yanqing 无法加载(2026-10-27,KB 04 §6.25 Also)
kungfu/class/dali/yanqing.lpc(jym 为 class_all)attempt_apprentice:query("PKS) 缺收尾引号、query_respect(ob) 无 ob、recruit 后嵌套第二个 void init() 未闭合函数。按 pkuxkx:补引号、ob→me、return 闭合,并把叛师检查并入第一个 init()。jym 另有 !==→!=。证明:HEAD 加载失败;改后加载成功。
深度功能测试(2026-10-27,§6.25 one-arg message_vision)
message_vision("…") 补第二参(me / TP / this_player())。
深度功能测试(2026-10-27,§6.10 未知 pragma save_binary/optimize)
删除本库中 FluffOS 不认的 #pragma save_binary / #pragma optimize… 行。