The Story of Hero

✅ 可玩

金庸群侠传

jyqxc

🔑 fluffos / Mud2026Adm 更新 04ee5e4 2026-10-28 源码 下载 ZIP

▶ 开始游玩 · Play Now

游戏内标题为 "The Story of Hero",金庸题材武侠世界,地图相对精简,涵盖华山、武当、少林、丐帮、峨嵋、明教等门派场景。天赋数值(膂力、 悟性、根骨、身法)由系统整体随机产生,只能全盘接受或重掷,不支持逐项自选;内置的"新手指南"(`help newbie`)其实是一篇玩家自己写的攻略,细讲"较量"(气/精跌到 50% 自动停手)和"死战"(`kill`,真刀真枪打到死)两种战斗指令的区别。小说里的反派会直接站在普通街道上,例如白驼山少庄主欧阳克,战力远超新手能应付的范围,一言不合就可能一击致命。死亡后会被押往鬼门关,除常见的黑白无常外,还有一位不算常见的"实习无常"NPC 同行接引。与本项目里的 `jyqxc2`、 `jyqxc2013fwq`、`fy2mg`、`xkx2017` 共享同一份 `master.lpc` ("金庸群侠传"/XKX 框架家族)。

English

In-game titled "The Story of Hero," a Jin Yong-themed mudlib with a relatively compact map spanning sects like Mount Hua, Wudang, Shaolin, the Beggars' Sect, Emei, and the Ming Cult. Talent stats (strength, comprehension, bone structure, agility) are rolled by the system as a fixed set — accept the whole roll or reroll, no picking individual stats — and the built-in "help newbie" is a player-written strategy guide rather than an official template, explaining the difference between "fight" (spar, auto-stops at 50% health/energy) and "kill" (fight to the death). Villains from the novels wander ordinary streets far above newbie level — White Camel Manor's young master Ouyang Ke can be encountered and one-shot an unprepared player before the spar system's safety threshold has a chance to kick in. Death sends players to Ghost Gate Pass, escorted by an uncommon third psychopomp NPC (an "apprentice guard") alongside the usual White/Black Guards. Shares a common master.lpc with this project's jyqxc2, jyqxc2013fwq, fy2mg, and xkx2017 (the "Legend of Jin Yong's Heroes"/XKX framework family).

README

内容亮点

深度功能测试新发现的 bug(详见 NOTES.md)

adm/daemons/logind.lpc 里有一处调试用的 printf("%O\n", ob) (AGENTS.md §7.34)和一处食物/饮水初始化误判对象的 §8.9 bug (ob->query("age") 应为 user->query("age"))——已修复。死亡/复 活系统的三个鬼差文件(d/death/npc/{newgargoyle,bgargoyle, wgargoyle}.lpc)都有 AGENTS.md §7.68 归档的复活软锁死 bug;另外在 少林寺地牢的"狱卒"(d/shaolin/npc/yu-zu2.lpc)身上发现了同一个 bug 类在完全不同场景(关禁闭惩罚机制,不是死亡复活)下的实例——同 样的多阶段 call_out 链 + !present(ob) 兜底守卫组合,同样会让人 在服刑期间一旦"暂时不在场"就永久出不了狱。全部按已验证的修法拆分 修复,并且这次难得地现场完整验证了一次真实死亡→鬼门关→复活的完 整流程(前两轮姊妹档案都因为角色的自动逃跑机制而没能真正死亡)。

注册流程

英文名字(3-12 个英文字母)→ 确认建立新角色(y/n)→ 中文名字(1-6 个中文字)→ 密码(至少 5 位)→ 确认密码 → 系统自动产生一组天赋数 值(膂力、悟性、根骨、身法),直接询问是否接受(y/n,不接受会重新 产生一组)→ 电子邮件地址 → 性别(m/f)→ 进入游戏世界。

本次修复的关键 bug

没有发现 Chinese 姓名判断、宏定义或指令表相关的 bug——is_chinese() 本来就用正确的 Unicode 码点区间判断;check_legal_name() 里有一个 i%2==0 隔字检查的历史遗留写法,会让位于奇数位置的非中文字符漏 检,但因为真正的中文名字每个字都是中文,不会被这个写法误判为不合 法,所以不影响真实注册流程,未做修改。

管理员账号 / Admin account

管理员名单存储在纯文本文件 adm/etc/wizlist(LF 换行,与档案里原 有条目格式一致)里;账号本身通过正常注册流程创建,已在游戏内确认 "目前权限:(admin)" 显示正确。

警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。

本地运行

cd libs/jyqxc
~/src/fluffos/build-debug/src/driver config.fluffos

游戏端口:40129。

NOTES · 移植与修复记录

WASM 修复摘要(迁移自 meta.json 的 group_note)

金庸题材 mudlib(金庸群侠传),游戏内标题为"The Story of Hero"。修复了两个 bug:feature/name.lpc 的 short() 呼叫 capitalize(query("id")) 没做保护——只要有物件在没设置"id"属性的情况下走到 short(),就会崩溃报"Bad argument 1 to capitalize()",这份档案里约 49 个留言板分身(data/board/*.o 存档用一种这个驱动的 restore_object() 解析不了的旧式紧凑二进制编码,魔术字节"#inh"/"?inh")全都会撞上这个问题——"Illegal file format"失败在抛出异常之前会先清空物件的 dbase 映射(包括刚在 create() 里设置好的"id"),导致每次 look 一个摆着留言板的房间都会崩溃。已改成让 short() 的兜底逻辑用 stringp(id) 判断,而不是重新格式化这约 49 个旧式存档档案。另外通过 adm/etc/wizlist 把 fluffos/Mud2026Adm 播种为 (admin)。没有发现中文名字/宏定义/指令表相关的 bug(is_chinese() 本来就用正确的 Unicode 码点区间;check_legal_name() 的 i%2==0 隔字检查会漏检奇数位置的非中文字符,但从不会拒绝真实的中文名字,所以保持原样)。完整的注册→look→score→quit 流程在排版格式化前后各验证过一次;管理员流程通过可见的"目前权限:(admin)"显示验证。格式化工具还原了 3 个损坏的档案,都是 ASCII 地图(d/huashan/map.lpc、d/shaolin/npc/obj/map.lpc 及其在 d/shaolin/obj/map.lpc 下的副本)——和这条血统里手足档案同样的分词器混淆模式。

深度功能测试(§10.7,2026-08-04)

原生 driver(端口 40129)跑了一遍超出注册流程的完整游玩。读了 doc/help/newbie——比这批档案里常见的官方模板丰富得多,是一篇由 玩家(武痴/Hop)写的天资/练功攻略,明确解释了 set wimpy(逃跑系 数)、fight(较量,双方气/精跌到 50% 会自动停手)vs kill(死战, 打到一方死亡为止,"倘若对手比你强,你便会十分危险")的区别——这份 文档直接解释了前两轮(fy330/fy2mg)会话里反复观察到的"自动逃跑 安全网"机制的设计意图和触发阈值。

主动检查命中 2 处:adm/daemons/logind.lpc 单一姓名输入路径 (没有随机取名的平行分支)紧挨在 ob->set("name", arg) 之前的 printf("%O\n", ob) 调试残留(AGENTS.md §7.34);同一文件 enter_world() 里食物/饮水初始化用错对象的经典 §8.9 bug—— ob->query("age") == 14 应为 user->query("age"),紧跟在 user->setup() 之后。两处都已修复;注册后食物/饮水显示 279/280 (接近满值),确认 §8.9 修复生效。command_hook 有两份拷贝 (feature/command.lpc 是 F_COMMAND 宏实际指向的活文件, home/command.lpc 是未被引用的死拷贝),两份都是干净的 nomask,没有 private,不是 bug。没有 MESSAGE_D-> 未防护调用或 stat/water 键名不一致的问题。

注册流程:get_id → confirm_id(y/n) → get_name → new_password → confirm_password → get_gift(随机天赋展示,y/n 决定接受或重 摇,不是选择固定档位)→ get_email → get_gender → enter_world(),没有民族选择这一步(和 fy330/fy2mg 不同)。 注册了测试角色(秦冬 / id qindongxi),落在"客店",自带专属邮箱 ("秦冬的信箱")。

邮件/留言板测试:look mailbox 正确列出 mail/forward/from/ read/discard 指令说明;客店留言板 post/read new 正常工作 (用内建列编辑器"~e"/"~q"/"."结束的输入方式),验证完毕后 discard 掉了测试留言,没有留下痕迹。

战斗测试:真实死亡+复活全流程现场验证(本项目 §7.68 系列里少见 的完整现场复现):客店门外北大街站着"欧阳克"(金庸小说里的反派 角色,白驼山少庄主)。按新手指南的建议先用较为"安全"的 fight 指令交手——结果一招"白蛇吐信"就直接把测试角色从满血打死,"较量" 的 50% 自动停手阈值根本来不及触发(这是内容强度问题,欧阳克本来 就不是新手陪练对象,不是 bug,故未做任何代码改动)。角色死亡后被 送到"鬼门关","实习无常"(本轮刚修复的 newgargoyle.lpc)和 "白无常"同时在场。没有主动断线或制造中断,让复活序列完整跑完, 重连后确认角色已经复活、站在"武庙",score 显示精/气降到 40%(符合死亡惩罚),食物/饮水满格——这是目前这一系列里第一次 (bixiecanyang/fy330/fy2mg 都因为自动逃跑机制没能真正死亡) 现场完整验证了 §7.68 修复后undisturbed 路径的正确性,虽然仍然没有 专门复现"复活过程中被强制挪走"这个原始触发场景。

死亡/复活系统:发现并修复 §7.68 的又一实例,外加一个全新变体 (新增归档条目):d/death/npc/{newgargoyle,bgargoyle,wgargoyle}.lpc (实习无常/黑无常/白无常,和 bixiecanyang/fy2mg 同宗的死亡系统, 这里多了"实习无常"这个第三种鬼差角色,"白无常"死讯提示还带一段随 机游走的 chat_msg)都有同样的 if (!ob || !present(ob)) return; 复活软锁死守卫,bgargoyle.lpc 和 fy2mg 一样在多阶段复活循环前 多一段"活人闯入阴间就攻击"的判断。三个文件都已按已验证的修法拆分。

全新变体,与死亡系统无关:d/shaolin/npc/yu-zu2.lpc("狱卒", 少林寺地牢的看守)的 death_stage() 是同一个 bug 类在完全不同场 景下的实例——这不是阴曹地府的复活序列,而是少林寺对犯规弟子的"关 禁闭"惩罚机制:多阶段 call_out 链每 60 秒推进一次狱卒的台词,最 终把人放出监狱(move("/d/shaolin/woshi1"))。同样的 if (!ob || !present(ob)) return; 守卫意味着服刑期间任意一瞬间 "人不在场"(不只是真正离线,也可能是暂时的场景切换)就会让这个人 永久放弃出狱资格,卡在监狱里出不来,没有任何提示。已用同样的拆分 修法处理。这说明 §7.68 这个 bug 类不止出现在"阴曹地府"这一种叙事 包装里,任何"多阶段 call_out 链 + !present(ob) 兜底守卫"的组合都 值得同样检查——已经据此更新 AGENTS.md §7.68 的归纳表述。

quit 正常退出("当你下次连线进来时,会从这里开始。欢迎下次再 来!"),formatter 检查(五个改动文件均已是干净格式,无需重排 版)、git status --short libs/jyqxc/ 复查均确认改动范围干净—— 只有五处源码修改是跟踪变更,测试角色的新存档保持未跟踪、未提交。 debug.log 全程没有真实的 error:/denied/Bad argument/ Too deep recursion 行。

更正(2026-08-05):§7.68 复活软锁"修复"已撤销

上面提到的"鬼魂离开/不在场时被永久放弃复活流程"曾被当作 AGENTS.md §7.68 记录的一类 bug 修复(把单次判定改成每 5 秒重试)。经用户指出并 重新审视:这更可能是有意的游戏设计,不是 bug——大多数这类档案里 鬼魂根本无法自行移动,所以"不在场"要么从未真正发生,要么是"离开去 在阴间游荡,想回来时再走回这个房间、流程会通过 init() 重新从头开始" 这种有意为之的宽松机制,而不是需要强制追上玩家的错误。强行重试还可能 引入新问题:如果鬼魂之后又走回这个房间,旧的重试和 init() 重新触发的 新一轮流程可能同时运行,导致对话重叠错乱。已把这处改动撤销,恢复成 原始的 if (!ob || !present(ob)) return; 单次判定写法(bmxkx2001 除外——那份档案里这确实是一个真实存在、经过实际复现验证的 bug:鬼魂 本身完全无法移动,是另一个不相关的 NPC 强行把鬼魂拖走导致的)。详见 AGENTS.md §7.68 顶部的撤销说明。

§7.86 跨库扫描修复(留言板 post 崩溃)

§7.100 扫描修复(ROOM 基类多余 replace_program())

#define ROOM "/inherit/room/room":删除 803 处多余的、独立成行的 replace_program(ROOM);(保留 inherit ROOM;),与 jqxz2008 系列同一血统同一形状。clone/misc/roommaker.lpc 同样有两套模 板——"造一间空房间"的 heredoc 本来干净,"克隆我所在的房间"命令的 字符串拼接模板把同一枚多余的 replace_program(ROOM); 烤进了每一 个新克隆的房间,已同步修正。已用 build-debug 驱动干净启动验证 (0 个新增编译错误,端口正常监听);未做完整 §10.7 深度游玩测 试。预先存在的 work/data/{login,user}/q/qindongxi.o 未跟踪测试 存档未受触碰。

§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.

§10.7 深度功能测试第二轮(2026-09-01):门派拜师、商店买卖、PvP kill

——四处未受保护的 write_file()/log_file() 写入不存在的 /log/nosave/ 目录,导致真实死亡结算和玩家互殴全部崩溃

本轮测试角度和 2026-08-04 那次刻意不同:全程用原生 Python socket 脚本(未用 scripts/tmux_mud.sh),注册了新测试角色(秦风武 / qinfengwu),额外注册了一个 fluffos/Mud2026Adm(按 adm/etc/wizlist 早先播种的凭据登记,登记后自动获得 (admin) 权限, 用作第二条并行连线,负责 goto/summon/call 等测试用快捷操作, 不代表这是给普通玩家开的后门)。测试了上一轮明确跳过的三个角度:

拜师/门派测试:cmds/skill/bai.lpc(apprentice/bai 指令, cmds/skill/apprentice.lpc 是字节相同的拷贝)配合 feature/apprentice.lpc。d/baituo/npc/li.lpc(白驼山"李教头")是 一个会自动收徒的 NPC(attempt_apprentice() 直接 command("recruit ..."),不需要额外确认)。现场对秦风武执行 bai li,一次成功—— score 正确显示"白驼山派第三代弟子"、"你的师父是李教头", family/generation(师父的 2 代 +1)与 family/master_name 都正确 写入。同一房间东侧的"练功房"里还有个"教练"NPC(本局首次被访问), 连带验证了下面提到的 §7.19 检查,干净。

商店买卖测试:d/city/zahuopu.lpc(杨永福,F_DEALER,只挂了 buy/list 两个 add_action,没挂 sell)用来买东西, d/city/dangpu.lpc(唐楠,F_DEALER + 自己额外挂了 sell/value/redeem)用来卖东西——先用 value/id 关键字踩了个 自己的坑:"草鞋"的 id() 列表是 ({"sandals","cao xie","xie"}), 没有不带空格的 "caoxie",第一次拿这个打的 buy caoxie 自然失败 (present()/is_vendor_good() 找不到东西,不是 bug,是我的指令 打错了)。改用 buy sandals 后走通了完整一轮:杨永福 处 "你从杨永福那里买下了一双草鞋。"(花掉 1 两白银),带着这双草鞋走 到 dangpu,sell sandals 得到 "你卖掉了一双草鞋给唐楠。" 并进账 70 文铜板,跟 feature/dealer.lpc::do_sell() 的 value*70/100 折算一致。全程 debug.log 干净。

§7.19 检查(AGENTS.md §7.19:init()→setup()→ enable_player() 链条造成同栈重入):静态审查全档案(grep 所有 enable_player(/enable_commands(/setup( 调用点,脚本化提取每个 void init() 函数体检查是否直接调用了 setup()/enable_player()/ enable_commands())——零命中;feature/damage.lpc::revive() 里那 处 this_object()->enable_player(); 是从 call_out("revive", ...) 触发的独立调用栈,不是 init() 链条内重入,符合 AGENTS.md §7.19 "合法的 disable/re-enable 场景"描述,不是 bug。现场访问的全新 NPC(欧阳克、流氓×2、流氓头、菩提子、杨永福、唐楠、李教头、 "教练")首次进入房间时全部干净,没有 "Too deep recursion"。

发现并修复的真 bug——AGENTS.md §7.x 已知模式家族(log_file()/ write_file() 写入不存在的 /log/nosave/ 目录,异常未捕获,整段 调用链被吞掉)在本档案里至少 4 处,此前完全未修:

1. cmds/adm/call.lpc(cmds/arch/call.lpc 是字节相同拷贝,两份都 活跃,分别对应 (admin)/(arch) 两级指令搜索路径)——管理员 call <玩家>-><函数>(...) 指令,只要目标是玩家,就会先执行 log_file("nosave/CALL_PLAYER", ...),此调用先于真正的 call_other()。现场复现:call qinfengwu->die() 直接抛出 *Wrong permissions for opening file /log/nosave/CALL_PLAYER for append. "No such file or directory",die() 根本没被调用到。 2. adm/daemons/combatd.lpc::killer_reward()——AGENTS.md 在 6th+ 实例那条已经点名"jyqxc/jyqxc2... 携带同样未受保护的 write_file()... 截至目前仍未修复",本轮就是把这条已知欠账 补上。写入点在 if (userp(victim)) { ... if (userp(killer)) write_file("/log/nosave/KILL_PLAYER", ...) ... } 内,被 gate 在 "玩家杀玩家"分支,所以之前两轮打 NPC 的测试从没触发过。真正 PvP 死亡时,这次 write_file() 抛出的异常会把 killer_reward() 连同调用它的 die() 一起中断在 this_object()->move(DEATH_ROOM) 之前——受害玩家会卡在"已昏迷/已死亡但从未真正移出房间"的状态, 与 AGENTS.md 描述的"死亡循环"是同一类症状。 3. cmds/std/kill.lpc——kill <目标> 指令本体,me->kill_ob(obj) (发起攻击)之后紧跟着对玩家目标做 write_file("/log/nosave/ATTEMP_KILL", ...),同样没有保护。现场 复现:kill fluffos(对方也是真玩家)打过去,me 一方攻击已经 生效,但异常把 obj->fight_ob(me)(受害者的反击注册)和警告 提示整段吞掉——也就是说,任何玩家对任何玩家使用 kill 指令, 受害一方永远不会反击,是个非常常见指令路径上的真实功能性 bug,不是罕见分支。 4. cmds/skill/bai.lpc / cmds/skill/apprentice.lpc(字节相同)—— attempt_apprentice/recruit 流程里,专门给"风清扬"这个隐藏 NPC 准备的徒弟计数彩蛋逻辑,write_file("/log/nosave/FENG", ...) 同样没保护;顺手发现同一行还有一个独立的运算符优先级 typo—— me->query("family/master_id" == "feng qingyang") 把 == 写在了 query() 的参数里面,实际上先算出恒假的字符串比较,再拿 结果(永远是 0)去调 query(0),导致这个分支根本永远进不去 (这条彩蛋逻辑本来就因为这个 typo 而完全死代码,write_file() 的 bug 本身反而从未被触发过)。

修复方式:优先修共享入口——adm/simul_efun/file.lpc 的 log_file() 本来就是裸 write_file(LOG_DIR + file, text),同一份 文件里下面几行就有现成、正确、但从未被用到的 assure_file() 辅助函数(跟 AGENTS.md 记录的其它几个姊妹档案一模一样的"帮手函数 就在旁边却没人用"形状)。把 assure_file() 挪到 log_file() 前面定义(避免同驱动下"函数须先声明才能调用"的编译顺序坑, AGENTS.md 已记录过这个坑),再让 log_file() 内部先 assure_file(LOG_DIR + file); 再 write_file(...)——这一次性修好 了全档案所有经由 log_file() 写 nosave/ 的调用点(securityd.lpc 的升级日志、master.lpc/adm/single/master.lpc 的崩溃日志、 suicide.lpc、cmds/arch|adm/purge.lpc、call.lpc 两份拷贝等), 不用逐一点名修。上面第 2/3/4 条是绕开 log_file() 直接裸调 write_file() 的独立调用点,各自在 write_file() 前补一行 assure_file("/log/nosave/XXX");;第 4 条顺带把误放的 == 挪回 query() 外面。

现场验证(修复后重启 build-debug 驱动):

断线/重连对照测试(本轮新增角度,上一轮明确没测):用 call qinfengwu->die() 触发一次死亡,让"实习无常"五段对话跑到 第二段左右时主动断开测试连线(模拟真实掉线),空等超过一个 call_out 周期后重新登录——death_stage() 的 if (!ob || !present(ob)) return; 单次判定(此前 §7.68 更正说明 里明确保留、判定为设计而非 bug 的写法)在断线期间因为角色对象本身 仍然 present() 在鬼门关房间里(断线不移除角色,只是标记 "断线中")而完全不受影响,对话链在后台正常继续推进,重连后 look/score 都显示已经复活、状态正常——没有新问题,符合预期。

范围说明:本轮修复严格限定在"未受保护的 I/O 导致崩溃"这一类 程序性 bug(并顺手带了一个同一行上的运算符优先级 typo),没有触碰 任何战斗强度/门派设定/NPC 性格等内容设计;kill 指令自带的 "未成年玩家保护"门槛、age_modify/mud_age 计龄机制均予保留, 只是测试时借用来快速绕过门槛,不代表对其做了任何修改。

深度功能测试(§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-05)— tail() simul_efun

scripts/lpc_add_tail.py --apply: cmds/wiz/tail.lpc calls tail(file), an efun of the MudOS this archive ran on that FluffOS does not have (Undefined function tail: the command did not compile). adm/simul_efun/file.lpc now defines void tail(string file): the last 10 lines of the file, written to the player. Load check (HEAD against the working tree): 1 of the 1 tail command file(s) failed to load at HEAD, all load now, and the simul_efun file loads. KB 04 §6.2.

深度功能测试(§10.7,2026-10-05)— 房间列出的物件缺档时房间照常加载

房间基类 inherit/room/room.lpc:make_inventory() 用 catch() 包住 new(file),做不出来就返回 0;reset() 的 case 1: 拿到 0 就跳过这一项(if (!objectp(ob[list[i]])) break;)。档案里没有的 NPC 或物品(或 create() 出错的物件)原先会让列出它的房间整个加载失败(Bad argument 1 to environment(),或对 0 的 call_other()),连带这个房间后面的区域;现在房间照常加载,只是少了那个物件,下次 reset() 再试(KB 05 §7.25;scripts/lpc_resilient_room.py、scripts/lpc_room_reset_guard.py)。 验证:探针(scripts/lpc_room_reset_probe.py:房间基类列出一个不存在的物件,再调用 reset())HEAD 单件 崩溃、多件 崩溃,工作树 单件 OK、多件 OK。

深度功能测试(§10.7,2026-10-05)— 巫师留言板存档 data/board/*.o.gz

巫师留言板 wiz_b / towiz_b 以 .o.gz 存档(1996 年的帖子)。本机驱动 restore_object() 优先读 .o.gz,而留言板用 save_object(file) 写明文 .o,所以本机上新帖每次重启都退回旧快照;网页版驱动没有 zlib,只读 .o,而归档自带的 .o 是读不出的二进制。.gz 内容为 GB18030(转码时没被处理),转成 UTF-8 写成 .o,替换归档里那个读不出的二进制 .o;towiz_b 原文有 4 个残字节,转为替换符。删除 .o.gz(KB 01)。用 lpcc 载入留言板核对条数(jqxz2008:wiz_b 47 条、towiz_b 115 条)。

深度功能测试(§10.7,2026-10-06)— 白驼山掌门欧阳锋编译不过

d/baituo/ouyangfeng.lpc 有两处笔误,整个 NPC 从未编译成功:greeting() 里 write("你是谁?我要杀了你!哈 ̄哈 ̄哈 ̄") break; 缺分号,inquiry_map() 里 object me = this - player();(应为 this_player())。白驼山派的掌门因此不存在,拜师、问路(inquiry_map)都无从谈起。两处改正后,新进程里 HEAD 加载失败、工作树加载成功。同一文件在 14 个 xkx 系的库里一字不差。

深度功能测试(§10.7,2026-10-07)— 空的 if (current_water == 0) {}

3 个房间的 valid_leave() 里有一行什么也不做的 if (current_water == 0) {}(编译警告 Expression has no side effects),删去,行为不变:d/xingxiu/nanjiang2.lpc、d/xingxiu/nanjiang3.lpc、d/xingxiu/shanjiao.lpc。

深度功能测试(§10.7,2026-10-07)— 编译警告整理

scripts/lpc_warnings.py jyqxc --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 223 个 .lpc 和 8 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):207 -> 207 通过,无回退;改过的头文件的 36 个包含者 34 -> 34 通过。启动时警告 16 -> 0;新进程登录(输入名字)正常。

深度功能测试(§10.7,2026-10-12):编译警告收尾(一)

无门派玩家的 family 映射(2026-10-15,KB 05 §7.30 第三种)

mapping fam = ob->query("family"); 之后直接 fam["family_name"]:没有门派(新人或叛师后)的玩家在学习、练功、拜师、进入门派房间时报 Value being indexed is zero,而不是作者写好的提示。4 处在声明行加上 if (!mapp(fam)) fam = ([ ]);(scripts/lpc_fix_family_guard.py)。

其他体系的属性名(2026-10-15,KB 06 §7.241 第三类,--any-file)

另有 3 个文件(绝招、状态、物品、NPC 等,不限于 kungfu/skill)来自 gin/kee/sen/force 体系,同样按 kee→qi、gin/sen→jing、force→neili 改名(scripts/lpc_port_es_props.py --any-file)。

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.249)

query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):beimin-shengong→beiming-shengong(jiuyang-shengong、liumai-shenjian、taiji-shengong);mizongxinfa→mizong-xinfa(longxiang/fanchang)。

family 映射内联取下标(2026-10-15,KB 05 §7.30)

ob->query("family")["master_name"]、me->query("family")["family_name"](泥潭式为 query("family", ob)[...])直接对门派映射取下标:没有门派的人(新玩家、叛师者、大多数 NPC)在 NPC 的打招呼和问话、绝招的门派判断、房间的 valid_leave 里报 Value being indexed is zero,而不是得到拒绝。7 处(6 个文件)改为 dbase 路径查询 query("family/master_name"):有门派时值相同,没有时得 0;原有的判断照常生效(scripts/lpc_fix_family_index.py)。给映射赋值的写法未动。

NPC 带着本库没有的物品,载入失败(2026-10-15,KB 06 §7.73)

3 个文件、4 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/city/obj/tudao、/d/city2/obj/obj/diaopi、/d/xueshan/npc/obj/senggun)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 0 个,改后 3 个(3 个原来载入不了),没有退步。

去掉的预定义宏 SAVE_EXTENSION(2026-10-18,KB 04 §6.11)

老版 MudOS 给每个 LPC 对象预先定义了 SAVE_EXTENSION(".o");FluffOS 的 ChangeLog(COMPAT_BUSTER 一条)把它删了,改用 __SAVE_EXTENSION__。本库没有自己 #define 这个宏,用到它的文件一律 Undefined variable 'SAVE_EXTENSION',整个对象载入不了。改成 __SAVE_EXTENSION__(scripts/lpc_fix_predefined_macro.py):adm/daemons/network/netmail.lpc、cmds/arch/purge.lpc、d/quanzhou/npc/tang.lpc、d/quanzhou/npc/tang3.lpc、d/taishan/tang3.lpc、include/net/ftpdsupp.h。tang3/tang/meng-zhu 是武林盟主一类的 NPC,原来一个都载入不了;purge 是清除玩家档案的巫师指令;netmail 是网络邮件守护进程(本库缺 mailer.h 等头文件,改后仍载入不了)。改后用 lpcc 逐个载入这些对象:4 个通过,1 个还有别的错误(adm/daemons/network/netmail:/adm/daemons/network/netmail.lpc:17: error: Cannot #include mailer.h)。

水潭里 ging_cost 是 jing_cost 的笔误(2026-10-18)

shuitan 先算 jing_cost,倒水时却扣 receive_damage("jing", ging_cost),ging_cost 从没声明过:Undefined variable 'ging_cost',整个物件载入不了。改成 jing_cost:d/shaolin/obj/shuitan.lpc。改后用 lpcc 逐个载入这些对象:1 个通过。

留言板存档损坏时 setup() 不再抛错(2026-10-18)

data/board/*.o 里有几个是外来的二进制数据(以 ?inh 开头,restore_object() 报 Illegal file format - 1)。inherit/misc/bboard.lpc 的 setup() 里 restore() 直接抛,留言板对象(连带创建它的客店房间,比如 d/city/kedian)载入失败。改成 catch(restore());,留言板以空板启动(xkx2017 的同一个文件早就是这样写的)。上面「存档损坏」那一条里说的“setup() 已经包住了”并不准确。

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 载入不了,刷出它的房间也少一个人。本库有 1 个对象设了本库没有文件的武功(4 个名字,最多的是 xiake-force、xiake-strike、xiake-steps)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:1 个里 0 个通过,其余 1 个停在别的错误上(kungfu/class/xiake/master:*No program in object '/kungfu/class/xiake/master'!)。(scripts/lpc_guard_set_skill.py)

通向不存在房间的出口,按对面房间的回头出口改正(2026-10-18,KB 06 §7.257)

房间出口 "west": __DIR__"xxx" 指向的文件本库没有,玩家走过去就被卡住(笔误、改过名的房间,或者没带进档案的编号房间)。对面的房间存在、并且它相反方向的出口正好指回这个房间,说明出口原本就是通向它:/d/emei/duguangtai 的 north:huazangzn → huazangan;/d/emei/jietuopo 的 west:guanyitang → guanyintang;/d/emei/jiulaodong 的 northeast:jiudaoguai4 → jidaoguai4;/d/mingjiao/jmqshulin6 的 west:jmqjiguan → jmqshenmu。改动后每个出口都能解析到本库的文件(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 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 4 处、3 个文件:xinxiu-duzhang→xingxiu-duzhang(2 处)、tiaji-jian→taiji-jian(2 处)。(scripts/lpc_fix_skill_names.py)

set_name 的 id 参数传成字符串(2026-10-19,KB 06 §7.260)

set_name("海口大碗", "bowl"); 把物品的 id 写成了字符串,本库的 set_name(string name, string *id) 要的是数组,驱动在编译时报 Bad type for argument 2 of set_name ( string * vs string ),整个物件载入不了;旧驱动不检查参数类型,字符串里照样找得到这个词,物品能用 id 称呼。改成 ({ "bowl" }),共 2 个文件:/d/baituo/npc/obj/bowl.lpc、/d/baituo/obj/bowl.lpc。(scripts/lpc_fix_set_name_id.py)

全局头文件丢了 1 个宏(2026-10-19,KB 04 §6.16)

/include/globals.h 里没有定义 MAILDIR,用到它们的物件(名单里 1 个)编译时报 Undefined variable/Undefined function,整个物件载入不了(编译器在第一个未定义的名字上停下,后面的错误和警告都看不到)。同系的库在各自的头文件里都有这几行:照其中和本库共有文件最多的那个库,原样补到 /include/globals.h 最后一个 #define 之后,宏体里出现的路径在本库里都存在。名单里的物件除了这几个宏之外还有别的错误,暂时仍载入不了。(scripts/lpc_restore_defines.py)

RANK_D 对 0 的称呼(2026-10-19,KB 06 §7.273)

adm/daemons/rankd.lpc 的 query_respect()、query_rude()、query_self()、query_self_rude() 里 4 个函数开头补上 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 文件。

玩家打造兵器模板没有打造者时(2026-10-19,KB 06 §7.275)

/d/npc/m_weapon/weapon/m_blade.lpc、/d/npc/m_weapon/weapon/m_club.lpc、/d/npc/m_weapon/weapon/m_staff.lpc、/d/npc/m_weapon/weapon/m_sword.lpc、/d/npc/m_weapon/weapon/m_whip.lpc(共 5 个)的 create() 开头 me = this_player(); 之后无条件读 me->query("weapon/name") 等打造数据。房间刷新时没有人在场(me 为 0),或者 NPC 在别的 NPC 的命令环境里 carry_object() 这个模板(NPC 的 weapon 属性是文件名字符串),旧驱动里读出来是 0 或字符串,switch 照样落到 default;新驱动报 Bad argument 1 to EFUN call_other() / Bad argument 1 to switch,NPC 和它所在的房间载入不了(--walk 普查里一个玩家走进房间就触发)。在 //读入数据 之后加 if (!me || !mapp(me->query("weapon"))) { setup(); return; }:没有打造者就留一个没有名字的空模板,玩家打造的流程不变。

补回丢失的标点(2026-10-19,KB 03 §4.6)

转换时丢掉的分号、逗号、引号或文件末尾的 },共 1 个文件,修前 HEAD 载入失败。别的库里同路径的完好副本只比这里少几个标点,按它补回(括号里是那个库):/d/city2/npc/zhengmen.lpc(jqxz2008)。修后与 HEAD 逐个对比载入:这 1 个物件全部载入,没有退步(scripts/lpc_restore_from_twin.py、lpc_fix_eaten_quote.py --comma、lpc_fix_eof_braces.py)。

旧驱动的类型宽松:缺省参数、无类型形参、数组初始化、class 变量(2026-10-19,KB 04 §6.19、§6.20)

原来的 MudOS 把调用少传的参数补 0、把没写类型的形参当 mixed、不检查变量的声明类型与初始值,也允许变量叫 class;这个驱动在编译时全部拒绝,整个物件载入失败。修了 1 个文件:/d/heimuya/ren.lpc(scripts/lpc_round_files.py 的修复轮:调用处补写 0、无类型形参补 mixed、string X = ({...}) 补星号、void 函数的返回值,以及 class 变量改名 pick_class)。逐个文件在 HEAD 和改后载入对比:这些文件里 1 个原来载入失败、现在载入,没有退步。

x != "a" || x != "b" 和 x == "a" && x == "b":同一个值的比较链永远成立/永远不成立(2026-10-20,KB 06 §7.282)

if (arg != "wan" || arg != "丸") return notify_fail(...) 对任何 arg 都为真,dir == "south" && dir == "north" 永远为假:条件下面的守卫要么永远拦、要么永远不拦。作者要的是“既不是 A 也不是 B”(&&)或“是 A 或 B”(||)。!= 链的 || 改成 &&(仅当条件下面是 return 的拒绝/守卫),== 链的 && 改成 ||(scripts/lpc_fix_cmp_chain.py)。改动:/d/mingjiao/shanmen.lpc(1 处)。

行编辑器指令编译失败:(: this_object(), ({ "do_to", me, arg }) :)(2026-10-20,KB 06 §7.284)

Illegal to use local variable in functional.:call_other 形式的函数指针 (: ob, ({ "函数", 参数 }) :) 的数组里只能是常量,写了局部变量 me / arg 整个指令就编译不过,to 这类先开编辑器、再回调自己的指令因此从未能用。改成 (: call_other, this_object(), "do_to", me, arg :)(参数在建立指针时绑定,编辑器再把输入的文字接在最后,do_to(me, arg, text) 收到全文;scripts/lpc_fix_functional_locals.py)。改动:/cmds/usr/to.lpc(1 处)、/cmds/wiz/to.lpc(1 处)。

call_out_info() 的每一项只有 3 个元素(2026-10-20,KB 06 §7.287)

驱动返回的是 ({ 物件, 函数, 延迟 }),不带 call_out 的参数,旧代码读第 4 项 info[i][3](callouts 指令、calloutd、cod 工具)一读就报 Array index out of bounds,指令从第一行起什么也打印不出来。改为 (sizeof(info[i]) > 3 ? info[i][3] : 0)(scripts/lpc_fix_callout_info.py)。改动:/cmds/arch/callouts.lpc(1 处)。

登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)

驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/adm/obj/simul_efun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。

sefun cat():缺文件时 write(0) 报错(2026-10-20,KB 06 §7.297)

void cat(string file) { write(read_file(file)); }:文件不存在(压缩包没带的欢迎/公告文件,或只有运行中的 mud 才会写的日志)时 read_file() 回 0,write(0) 在本驱动里报 Bad argument 1 to receive(),调用者(登录对话、cat 指令)被中断;老驱动什么也不打印。改成 write(read_file(file) || "");(scripts/lpc_fix_cat_unchecked.py)。改动:/adm/simul_efun/file.lpc。

翻页器 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。

存档里的乱码文件:restore_object() 报 Illegal file format(2026-10-20,KB 06 §7.316)

存档里有一批数据文件是乱码:开头是 ?adm、?inh、?clo、?obj(#/adm/… 的第一个字节被换成了 ?),后面是随机字节,没有任何驱动读得了。adm/daemons/chinesed 在预载时 restore() 读 data/e2c_dict.o,首次登录时起始房间的留言板 /clone/board/kedian_b 读 data/board/*_b.o;restore_object() 对不是文本的文件抛 Illegal file format,调用处的 catch()(KB 06 §7.255)保住了房间和精灵,但本库 master 的 error_handler() 把调用栈返回给驱动,每次启动和首次登录都在控制台打印一次或两次 错误讯息被拦截。现在共用的 feature/save.lpc 的 restore() 先读文件第一个字节,不是 # 就返回 0(读被 master 拒绝时 read_bytes() 返回 0,仍由 restore_object() 决定,和以前一样);留言板和字典从空开始,第一次 save() 写出合法文件,乱码文件原样留在树里。实测:启动和登录都没有报错,已保存的角色重新连线照常加载。

NPC 的 chat() 在 chat_msg 为空数组时下标越界(2026-10-21,KB 06 §7.334)

NPC 基类的 chat() 是 if (arrayp(msg = query(...))) { if (random(100) < chance) { rnd = random(sizeof(msg)); if (stringp(msg[rnd])) ...,不检查列表是否为空。本库现有的 NPC 文件里没有字面写成空 chat_msg 的(扫描只认 set("chat_msg", ({ })));守卫挡的是运行时才为空的列表和以后新增的 NPC。在 random(100) < chance 前面加 sizeof(msg) &&(scripts/lpc_fix_chat_empty.py;if (random(100) > chance) return 0; 的写法是 !sizeof(msg) ||)。改动:/inherit/char/npc.lpc(共 1 个文件)。

存档里的 .c 文件名改成 .lpc(2026-10-21,KB 03 §4.2 第 9 项)

存档里写着 "/d/city/npc/wei.c" 这样的文件名;extract 时源文件已经全部改成 .lpc,而驱动对带扩展名的路径只认字面,new() / load_object() / move() 都找不到它,调用方多半把失败当成「没有这个东西」悄悄跳过。本库 3 个存档共 6 处按 scripts/lpc_fix_save_dotc.py 改成 .lpc(只改 .lpc 存在而 .c 不存在的字符串;玩家存档、备份、留言板、巫师目录和文件索引缓存不动)。改动:/d/shaolin/npc/obj/kat.o(1 处)、/d/shaolin/obj/kat.o(1 处)、/d/shenlong/obj/lxlx.o(4 处)(共 3 个存档)。

巫师命令不带参数时对 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 与工作树对照,没有退步。

blockade / recover:data/keep 不存在,解封之后世界仍然锁着(2026-10-21,KB 06 §7.340)

管理员扫描里 blockade 报 Error copying file from /data/user/f/fluffos.o to /data/keep/fluffos, Error: No such file or directory,随后的 recover 在第一个 cp() 上死于 */data/keep/fluffos: lstat failed,来不及执行最后的 LOGIN_D->set_madlock(0):封锁解不开。data/keep/ 是运行时目录,git 克隆里没有空目录。blockade 在第一次写之前 if (file_size("/data/keep") != -2) mkdir("/data/keep");(mkdir() 失败答 0,不抛错),recover 对没有保存档的玩家 if (file_size(src) < 0) continue;,其余照常恢复、照常解锁(scripts/lpc_fix_keep_dir.py,hy5 上实测:没有保存档时 recover 答“时空解封完毕”,blockade 建出 data/keep 并保存,recover 取回并删除)。改动(2 个文件):/cmds/adm/recover.lpc、/cmds/arch/blockade.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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 1 条注册(1 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/inherit/misc/fboard.lpc(do_list)。最多的函数名:do_list 1 处。

对象路径里的 ..: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。 还剩 1 个函数没有补(inquiry 1):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/d/huashan/npc/teller.lpc:tell_fortune。

(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)

inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 1 个文件里 1 行只含这个指针的行("算命": (: tell_fortune :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。

汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 17 处(2026-10-22,KB 06 §7.360)

原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字);各派掌门的辈分判断(name[0..1] == "澄")永远不成立。

本库 17 处(12 个文件):拼名字的赋值 9 处、与汉字常量比较 8 处。例:name[2..3] → name[1..1](/kungfu/class/shaolin/cheng.h:156);name[0..1] → name[0..0](/kungfu/class/shaolin/qing-fa.lpc:104);name[0..1] == "慧" → name[0..0] == "慧"(/kungfu/class/shaolin/cheng.h:140)。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,不要重跑。

黑木崖脚下的老者:init() 里留着调试语句,每个走进 /d/heimuya/road1 的人都触发运行时错误(2026-10-22,KB 06 §7.361)

d/heimuya/npc/laozhe.lpc 的 init() 在 ::init() 之后有 what = new(__DIR__ "obj/yancao"); sprintf("%s aaa\n", what);:调试遗留,%s 收字符串或数字、不收对象,于是 (s)printf(): Incorrect argument to type %s. (arg: 0) 每次有人进入乱石坡都抛一次,后面的 call_out("greeting", ...) 永远不执行,老者不再搭话(还每次白克隆一个烟草)。两行都注释掉(同系的 haiyang2 / hy / hy5 / cctx / fqyy2 早就注释了)。证明:scripts/lpc_reach_census.py xiakexing2017 --load --walk(一个普通 NPC 访客走进每个加载成功的房间):HEAD 336/337,改后 337/337。

长度检查按字节写,旁边的提示按汉字写:2 处(2026-10-22,KB 06 §7.362)

老档的 strlen() 数字节(一个汉字 2 字节),转成 UTF-8 后它数字符:提示写「必须是 2 到 6 个中文字」的检查还是 strlen(name) < 4 || strlen(name) > 12,于是两三个字的房名、帮会名、群名、聊天室名、称号被提示自己说允许的那句话拒绝,上限却是提示的两倍;留言板的标题检查 i < 4 || i > 24 || i % 2 还会拒绝所有奇数个字的标题(post 你好吗)。本库 2 处(1 个文件):if ((strlen(name) < 2) || (strlen(name) > 12)) {(/adm/daemons/logind.lpc:513)。scripts/lpc_fix_strlen_gate.py 以提示为准:条件里每个 strlen 门槛都恰是提示所说数字的两倍才折半,存长度的变量(i = strlen(arg);)的门槛和 || i % 2 奇偶项、其后循环的 i % 2 == 0 && 一并处理;提示里有英文 / 字母 / 字节的不动。证明(scripts/lpcc_eval.py,bxsj 的留言板,求值对象继承 NPC 并重写 edit() 记录调用,command("post 标题")):HEAD 拒绝「你好」和「你好吗」、放行 23 个字的标题;改后接受前两个、拒绝第三个;shenmo 的 named.lpc invalid_new_name("张三"):HEAD 回 名字只能是2-4个中文字。,改后回 0。抽样 24 + 15 个库的 HEAD / 工作树加载对比 0 退步。

按字节写的汉字循环、茶馆猜诗和同盟频道标题(2026-10-23,KB 06 §7.363)

老档按字节做「一个汉字」:while (i--) if (i % 2 == 0 && !is_chinese(name[i..<0])) 只检查偶数下标,奇数位上的拉丁字母(张a)会过;茶馆猜诗 mixup() 用 strlen(str)/2 再 j=j+j、str[k..k+1] 交换两字节;泥潭系 channeld 的 switch (strlen(fname)) case 4/6/8/10 和 strlen(family)==4/6 按字节长度给同盟 / 门派名加空格。本驱动的 strlen() 和区间都按字符算。scripts/lpc_fix_byte_char.py:去掉奇偶项、把区间改成 name[i..i],按字符重写 mixup() 并把 strlen(first)>=14 折成 7,频道标题的 case / 门派长度改成字符数(对照 aoxiangtianji)。本库 汉字循环 / is_chinese 区间 1 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。

血条 / 表情标题按字节算显示宽度(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(一格两个空格)。本库 4 处(2 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。

doc/build devil 丢分号/粘连 inherit(2026-10-27,KB 04 §6.25 Also)

doc/build/devil.lpc(建档示例倭寇)被格式化弄坏:inherit NPC void create() 粘在一起,语句之间的 ; 全丢,还有嵌套坏掉的 set("con", set("con", 10)。按原意重写成带 setup() 的完好 NPC(原 /obj/blade、/obj/cloth 在多数库不存在,示例里去掉携带以免 create 炸)。证明:HEAD 加载失败;改后加载成功。

jmqshenmu 截断 init(2026-10-27,KB 04 §6.25 Also)

d/mingjiao/jmqshenmu 在 void init() { 处文件结束。补上 } 使房间可加载(本库未收齐 climb/jmqdating 时不做内容补写)。证明:HEAD 失败;改后加载。

深度功能测试(2026-10-27,§6.25 one-arg message_vision)

message_vision("…") 补第二参(me / TP / this_player())。