info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
《终极地狱完整版》(游戏内横幅:EMPEROR OF ULTRA HELL(BLOOD & MAGIC) FROM 2002.06.2)同样是本项目"地狱"/Doing 血统六件套的一员——与 `hell`、`zjdy2008wzb`、`zjdyaryl`、`zjdyzj`、`zjmudhell` 共享同一套少林、武当、峨嵋、丐帮、"屠龙"、"血刀"门派地图,逐字节比对显示整个家族地图相似度约 67-76%,本作与 `zjdy2008wzb` 最接近,与结成更紧密一组的 `zjdyaryl`、`zjdyzj` 相比则更疏远一些,属于同一份原作世界的又一个快照而非独立设计;本版的构建/同步精灵 `versiond.lpc`(2194 行)比 `zjdy2008wzb` 那份完整得多,且出厂时就带有 `log/nosave/` 目录、存档权限判断也已写对,因此没有撞上 `zjdy2008wzb` 那份"每个新连线都被直接断线"的致命问题。
English
Ultimate Hell: Complete Edition (in-game banner: 'Emperor of Ultra Hell (Blood & Magic), from 2002.06.2'). Part of the same six-way 'Hell'/Doing-lineage map family as this collection's hell, zjdy2008wzb, zjdyaryl, zjdyzj, and zjmudhell (the original archive was itself named hell) — its adm/single/master.lpc, logind.lpc and named.lpc are structurally near-identical to zjdy2008wzb's, and a byte-level, line-ending-normalized room-tree comparison found the shared map is roughly 67-76% identical across the family (closest to zjdy2008wzb, more diverged from the tighter zjdyaryl/zjdyzj cluster), covering the same Shaolin/Wudang/Emei/Beggars'-Sect/'Dragon Slaying'/'Blood Blade' zones. This build's own versiond.lpc (2,194 lines) is a substantially more complete build/sync daemon than zjdy2008wzb's. It includes 43 bulletin boards spread across its sect and city zones.
README
内容亮点
- 和
zjdy2008wzb是同一个"地狱"(原始档案名就叫hell)家族的 另一个版本,游戏内横幅换成了"EMPEROR OF ULTRA HELL",地图与门派 布局(华山、武当、少林、丐帮、屠龙、血刀等)高度重合。 - 这份版本的
versiond.lpc(2194 行)远比zjdy2008wzb那份完整, 是一个更成熟的构建/同步精灵实现,但同样因为直连 socket 而需要在 WASM 下掏空。 - 不同于
zjdy2008wzb,这份档案本来就有log/nosave/目录,没有 撞上那个会让所有连线直接断线的致命 bug;securityd.lpc的存档 权限判断也已经是正确写法,不需要修。 - 全档案 43 处留言板都有 §7.86 的致命形状(
inherit BULLETIN_BOARD又多余地对自己replace_program),是这个 bug 形状第五个以上确 认命中的独立血统家族,已全部修复并 live 验证发帖成功。
本次修复的关键 bug
1. 经典 §8.1 GBK 字节区间 is_chinese()(str[i] 字节对判
断,161/255/176/248 阈值 + i%2 奇偶判断)——改成逐码点的
0x4e00-0x9fff;check_legal_name() 对应的下限从 2 改成
1,maxlen 没有减半的上限也一并改成 maxlen/2。
2. master.lpc(adm/single/master.lpc)的
valid_read()/valid_write() 都缺少标准的
user == this_object() 短路判断——都补上了。
3. 姓名组合最短长度检查(get_name() 里的
strlen(fname)<4,字节数没减半)和 named.lpc 的
invalid_new_name() 滑窗查重逻辑(下限 2→1,切片
name[i..i+3]/name[i..i+5]→name[i..i+1]/
name[i..i+2],循环上限 l-4→l-2,判断门槛
i+6<=l→i+3<=l),都按 hell 家族已经确认的减半模式修
了——修好前,单字姓氏和任何两字全名都会被误判"太短"。
4. §7.50 is_killing(object) 与 is_killing(string id) 类型
不匹配,修好了 11 处呼叫点(几个门派 suck/tan/zhua/
hua/huolang 档案、clone/user/user.lpc、
d/city/npc/guidao.lpc、cmds/std/ansuan.lpc、
cmds/std/surrender.lpc)。
5. versiond.lpc(2194 行,比 zjdy2008wzb 那份完整很多的
构建/同步版本精灵,但属于同一类 socket 精灵 bug)——通过
logind.lpc logon() 里直接、没有保护的
VERSION_D->is_version_ok()/query_temp() 呼叫触发,所以即
使不在 adm/etc/preload 里,每次连线都会被编译——按
AGENTS.md §7.52 掏空了 in_server()、connect_server()、
clear_syn_info()(只去掉 socket_close 循环)、
send_command()、send_client_pending_msg()、
syn_finish()(只去掉 socket_close)、
in_listen_callback()、in_write_callback()、
in_close_callback()、cmd_close()、send_pending_msg()、
send_result()、remove_connection()(只去掉
socket_close)里所有真正碰 socket 的呼叫;确认掏空后档案里
已经没有任何裸的 socket_*() efun 呼叫,大括号也配平
(145==145)。
adm/daemons/ftpd.lpc(117 处 socket 呼叫)和
adm/daemons/network/dns_master.lpc(5 处)都已经在
adm/etc/preload 里被注释掉,剩下的呼叫者也都是受保护的巫师指
令,属于完全休眠,本次没有改动。
另外确认了几处从共同的 hell 源码继承下来、已经是正确写法、不
需要修的地方:securityd.lpc 的 save_object valid_write()
判断已经正确比较 query_save_file()+__SAVE_EXTENSION__;
SECURITY_D 宏和 adm/etc/preload 里的路径本来就是对的,
adm/daemons/securityd 也已经在列表最前面;log/nosave/ 目录
本来就存在(不像 zjdy2008wzb 那份缺目录导致每个连线都被断开的
致命 bug,这份档案没有这个问题)。
启动过程和退出时会反复出现一个 Bad argument 4 to EFUN message()
运行时错误(几个精灵的 create()/sys_info() 经由 channeld.lpc
呼叫 simul_efun.lpc 的 message() 时第四个参数传了 int(0));
WASM 阶段曾判定为"不影响功能的驱动签名怪癖"予以保留,深度功能
测试(§10.7)发现这个判断是错的——它会把每一个新角色永久卡死在
人物创建流程里,详见下方"深度功能测试"一节。
深度功能测试(§10.7)修复的 bug
- §7.88(AGENTS.md 本次新增)message() 包装函数漏标
varargs, 缺失参数被静默填成int(0)后传给efun::message()直接崩 溃——WASM 阶段那个"不影响功能"的判断是错的:新角色创建流程"选 品质"一步(在"桃源石屋"对 NPC 花铁干out离开)的check_leave()里一次command("chat ...")频道广播会触发这个 崩溃,而且崩溃发生在房间放行判断"之前",导致玩家永远无法离开这 个房间、后续的"投胎做人"流程完全走不到。每一个新角色都会撞上, 反复输入out只会反复重现同一个崩溃。已修复adm/simul_efun/message.lpc的message(),补上varargs并给 缺失的exclude参数加上|| ({})兜底。live 验证:修复前out必现崩溃、角色永远卡在原地;修复后同样的房间/NPC/指令一次成功, 完整走完"忘忧池洗天赋 → born 中原人氏"流程,第一次真正进入游戏 世界。 - §7.89(AGENTS.md 本次新增)自带
runtime_config.h索引编号和 驱动实际编号不一致,巫师登录时崩溃——用巫师账号(fluffos)登 录时,密码验证通过后连线彻底卡死,任何指令(包括look)都没 有任何回应。根源是get_config(__MUD_PORT__)因为这份档案自带 的include/runtime_config.h编号和驱动实际编号对不上,静默取到 驱动内部另一个字符串类型的配置槽位,导致adm/daemons/network/messaged.lpc的socket_bind()收到一个字 符串端口号而报错崩溃,把巫师登录流程从中间截断。已把include/runtime_config.h换成驱动自带的权威版本(旧版备份为runtime_config.h.orig_zjdywzb),三处符号差异分别处理:__SAVE_BINARIES_DIR__别名成__MUD_LIB_DIR__,__ADDR_SERVER_IP__(cmds/arch/config.lpc一行纯展示文字)直 接删除,__PORT__不需要别名(这个驱动本身把它当编译期预定义常 量)。修复后巫师登录、look、goto全部恢复正常。 - §7.86 留言板
post崩溃:全档案 43 处致命形状全部修复,用 巫师账号在"客店留言板"live 验证发帖成功。 - §7.5/§8.9/§8.3a/§8.3b 均不适用:ACL 已在 WASM 阶段修好;食 物/饮水初始化是无条件固定值;指令表健康,注册后所有指令正常响 应。
- 战斗测试:与南阳城"小贩"多回合拳脚攻防,命中/闪避/擦伤判定 正常,血量低时角色自动逃跑到相邻场景(设计内的求生保护机制), 全程无崩溃。
- 死亡/复活流程本次未能完整走通,记录为已知限制而非确认 bug: 巫师
smash秒杀指令未能让测试角色真正死亡/复活,怀疑与combatd.lpcplayer_escape()里基于is_want_kill()的"逃过 一劫"分支有关,但未用call指令直接实测验证(巫师账号的call指令被SECURITY_D->valid_grant(me,"(arch)")拒绝, "(admin)" 权限似乎不自动满足 "(arch)" 授权,原因未深入排查);DEATH_ROOM->start_death()这个呼叫指向的函数在全档案里也没有 任何定义。两者都留给以后有需要时再深挖。 - 本次未测试:门派拜师、商店购物、真实死斗到死亡。
管理员账号 / Admin account
- ID:
fluffos - 密码 / Password:
Mud@2026(管理密码Mud@2026Adm) - 权限 / Level:
(admin),通过/adm/etc/wizlist授予(原档 案里已有的doing (admin)保留未动),登录后自动显示"目前权 限:(admin)"确认生效。
警告:对外公开架设前请务必修改此密码。
本地运行
cd libs/zjdywzb
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40109。
NOTES · 移植与修复记录
WASM 修复摘要(迁移自 meta.json 的 group_note)
终极地狱完整版(游戏内横幅:EMPEROR OF ULTRA HELL(BLOOD & MAGIC) FROM 2002.06.2),和本次会话已经处理过的 zjdy2008wzb 同属一个 Doing 血统 'hell' 家族——master.lpc/logind.lpc/named.lpc 结构几乎一致(原始压缩包内部文件夹字面就叫 'hell')。WASM 修复了:(1)经典的 §8.1 GBK 字节区间 is_chinese()(用 161/255/176/248 阈值和 i%2 奇偶判断做 str[i] 字节对检查)重写成逐码点 0x4e00-0x9fff 检查;check_legal_name() 对应的界限(最小值 2→1,没有减半的 maxlen → maxlen/2)也做了相应修复。(2)master.lpc(adm/single/master.lpc)的 valid_read()/valid_write() 缺少标准的 'user == this_object()' 保护——两处都已加上。(3)get_name() 里姓+名合并的最小长度检查(strlen(fname)<4,字节数翻倍)和 named.lpc 的 invalid_new_name() 滑动窗口近似名字去重(最小值 2→1,窗口切片 name[i..i+3]/name[i..i+5] → name[i..i+1]/name[i..i+2],循环界限 l-4 → l-2,闸门 i+6<=l → i+3<=l)都修复成减半后的逐码点界限,和 hell 家族的先例一致——不修的话,单字姓氏和任何两字全名都会被拒绝为'太短'。(4)§7.50 类的 is_killing(object) 对 is_killing(string id) 修复了 11 处呼叫点(kungfu/skill/*.lpc 的 suck/tan/zhua/hua/huolang 档案、clone/user/user.lpc、d/city/npc/guidao.lpc、cmds/std/ansuan.lpc、cmds/std/surrender.lpc)。(5)versiond.lpc(2194 行,比 zjdy2008wzb 那份更大更完整的构建同步版本精灵,但属于同一类 socket 精灵 bug)通过 logind.lpc 在 logon() 里直接、未加保护的 VERSION_D->is_version_ok()/query_temp() 呼叫在每一次连线时都能触达,即使它不在 adm/etc/preload 里——按照 AGENTS.md §7.52 掏空了 in_server()、connect_server()、clear_syn_info()(只掏空 socket_close 循环)、send_command()、send_client_pending_msg()、syn_finish()(只掏空 socket_close)、in_listen_callback()、in_write_callback()、in_close_callback()、cmd_close()、send_pending_msg()、send_result()、remove_connection()(只掏空 socket_close)里所有真正的 socket_*() 呼叫点;已确认没有残留的原始 socket_*() efun 呼叫,且大括号配平(145==145)。adm/daemons/ftpd.lpc(117 处 socket 呼叫)和 adm/daemons/network/dns_master.lpc(5 处 socket 呼叫)都处于休眠状态(在 adm/etc/preload 里被注释掉,只有带保护的巫师指令呼叫者)——保持原样,符合 AGENTS.md §7.52 的指引。另外确认了从共享的 hell 源码带过来的一些既有修复本来就是对的:securityd.lpc 的 save_object valid_write() 检查本来就对照 query_save_file()+__SAVE_EXTENSION__,而且 SECURITY_D/preload 路径本来就正确,adm/daemons/securityd 已经排在预加载列表最前面。log/nosave/ 目录本来就存在(和 zjdy2008wzb 不同,这份档案没有撞上那个阻断启动的目录缺失 bug)。管理员账号播种:fluffos (admin) 加入 adm/etc/wizlist(和压缩包原有的 'doing (admin)' 条目并存)。注册流程在格式化前后都完整验证过(英文 id→y 确认→中文姓→中文名→管理员密码+确认→登录密码+确认→角色类型菜单(1-5,用的 5)→性别→进入世外桃源),管理员权限已通过'目前权限:(admin)'确认。这份档案的提示符会显示实时时钟(符合 AGENTS.md §8.3 第 1 条)——这里用 --idle 1.0 就够了,不像 zjdy2008wzb 需要 0.3。有一处转档之前就存在、非致命的'Bad argument 4 to EFUN message()'运行时错误在预加载期间和 quit 期间会反复出现(几个精灵的 create()/sys_info() 路径通过 channeld.lpc 呼叫 simul_efun.lpc 的 message(),传了一个整数 0 作为第 4 个参数)——不会阻挡启动、注册、或管理员权限验证;作为一个转档之前就存在的驱动 message() 签名怪癖保留,不在这次 WASM 修复的范围内。LPC 格式化工具对全部 7080 个档案运行(写入 7025 个,27 个转档之前就存在的错误,28 个未改动)。没有 :: 父类呼叫拆分命中,没有 case 标签带尾随注释的候选,没有 CJK 重新加空格/转义损坏命中。
深度功能测试(§10.7,2026-08-05)
这份档案和手足档案 zjdy2008wzb 都还没做过 §10.7 深挖,这次直接从
AGENTS.md 的已知 bug 类别清单出发逐项排查,而不是 sibling-check(因
为没有已深挖的手足可以参考)。
- §7.88(本次新增)"非致命" message() 签名怪癖其实会把新手卡死 在角色创建流程里,是全新角色永远无法通过的硬性 soft-lock:WASM 阶段的笔记把
Bad argument 4 to EFUN message()记成"转档之前就存 在、不影响启动/注册/管理员验证的驱动签名怪癖",予以保留。这次实 测发现它不是无害噪音——adm/simul_efun/message.lpc的message()包装函数声明了 4 个必填参数,直接透传给efun::message(),但同一 份档案里好几处呼叫只传了 3 个参数,缺的第 4 个参数被驱动静默填成int(0),efun::message()直接拒绝这种类型不对的调用而抛错。新 角色创建流程里"选品质"一步(在"桃源石屋"对 NPC 花铁干out离开) 的check_leave()恰好先呼叫了command("chat ..."),这个 chat 频道广播触发了同样的崩溃——而且这次崩溃发生在check_leave()内 部、valid_leave()判断房间是否放行"之前",导致整条呼叫链被中断: 房间不放行离开(角色永远卡在桃源石屋),紧跟着那行本该执行的me->set("character", "阴险奸诈")也永远不会跑。也就是说每一 个新角色在人物创建的强制步骤上都会被卡死,反复输入out只会反 复触发同一个崩溃,是比"注册后指令表是空的"更隐蔽的一种"注册流 程看起来正常,实际完全走不下去"的 bug(因为这条崩溃夹在一大串正 常的注册提示文字中间)。修复:把message()包装函数声明成varargs,缺失的exclude参数补上|| ({})兜底(和同一个文件 里tell_room()已经用的写法一致)。修复前反复out必现同一崩 溃、角色永远留在桃源石屋;修复并重启驱动后,同样的房间、同样的 NPC、同样的out指令一次成功,顺利走完"忘忧池洗天赋→born <地名>"全部剩余创建流程,第一次真正进入游戏世界。已更新 AGENTS.md,新增 §7.88。 - §7.89(本次新增)自带的 runtime_config.h 索引编号和驱动实际编 号不一致,导致 get_config() 静默取到类型不对的另一个配置槽位, 巫师账号登录时崩溃:用巫师身份(
fluffos,已经在 wizlist 里) 登录时,密码验证通过后连线立刻卡死——look等任何指令都不再有 任何回应(连"什么?"都没有)。log/debug.log显示logind.lpc的check_ok()(只有巫师登录才会走到)触发 加载/adm/daemons/network/messaged,其create()→startup_udp()→socket_bind(socket_id, my_port)报错*Bad argument 2 to socket_bind() Expected: int Got: "10"——my_port = LOCAL_PORT() + MESSAGE_PORT本该是纯整数运算,但LOCAL_PORT()依赖的get_config(__MUD_PORT__)里__MUD_PORT__是CFG_INT(0),而这份档案自带的include/runtime_config.h用的 是自己从零开始算的BASE_CONFIG_STR/BASE_CONFIG_INT编号,和这 个驱动版本真实的内部配置槽位编号对不上(用~/src/fluffos/src/include/runtime_config.h逐条 diff 确认,和之 前ds386发现的"自带 runtime_config.h 编号错位"是同一类问题, 但这次真的撞上了一次会崩溃的类型错配,而不只是取到空值/错误 值)——get_config(14)实际读到的是驱动内部第 14 号槽位的真实内 容(一个字符串),导致my_port变成字符串"10"而不是数字端 口号,socket_bind()直接拒绝这种类型的第二参数并抛错,把check_ok()从中间截断,登录流程永远无法完成。修复:直接把这份 档案的include/runtime_config.h换成驱动自带的权威版本(旧版备 份为runtime_config.h.orig_zjdywzb),diff 出新旧版本符号差异后 发现三处旧符号在别处仍被引用:__SAVE_BINARIES_DIR__(只在已经 按 §7.52 掏空过的同步精灵里使用)→ 别名成__MUD_LIB_DIR__;__ADDR_SERVER_IP__(cmds/arch/config.lpc里一行纯展示用的巫师config指令输出,这个驱动版本压根没有 addr_server 这个概念)→ 直接删除这一行而不是硬造一个假别名;__PORT__(ftpdconf.h/versiond.lpc引用)不需要任何别名——这个驱动本身就把它当编译期 预定义常量(add_predefine("__PORT__", ...)),重新#define反 而会报"Illegal to redefine a predefined value"编译错误。修复并重 启驱动后,fluffos巫师账号登录、look、goto全部恢复正常, 权限正确显示(admin)。已更新 AGENTS.md,新增 §7.89。 - §7.86 留言板
post崩溃同样适用,是第五个以上独立血统家族命 中此形状:全档案 43 处inherit BULLETIN_BOARD+ 多余replace_program(BULLETIN_BOARD)致命形状,全部删除多余调用(未 发现 sje 那种运行时代码生成模板变体)。修复并重启后,用巫师账号 在"客店留言板"实测发帖,post board全流程顺利完成("留言完毕"),look board确认帖子正确显示作者与时间戳。 - §7.5/§8.9/§8.3a/§8.3b 均不适用:ACL 已在 WASM 阶段修好;
enter_world()的食物/饮水初始化是无条件固定值,没有年龄判断包 装;command_hook()/指令表健康,注册后look/score/kill/goto全部正常响应。 - 战斗测试:新角色在南阳城对小贩发起
kill,双方多回合拳脚 攻防判定正常(命中/闪避/擦伤消息、体力下降提示),血量降到危险 阈值时角色自动逃跑到相邻的"大驿道"(这份档案自带的求生保护机 制,属于设计行为,不是 bug),过程全程无崩溃。 - 死亡/复活测试未能完整走通,记录为本次已知限制:尝试用巫师
smash指令秒杀测试角色(ob->receive_damage("qi",1,me); ob->die();),代码读到die()应该无条件把玩家送进DEATH_ROOM(/d/death/gate)并触发复活流程,但实测多次smash后角色始终原地满血站着,未观察到任何死亡/复活画面。走读combatd.lpc的player_escape()发现其中有一条基于is_want_kill()/want_kill意图标记的"逃过一劫"随机分支,怀疑smash这种非常规击杀路径没有正确设置该标记,导致这条保护分支 被意外命中——但没有用call指令直接实测验证(巫师账号的call指令被SECURITY_D->valid_grant(me,"(arch)")拒绝,"(admin)"权 限在这份档案里似乎不自动满足"(arch)"授权,也没有深入排查这一 点)。这是内容/设计层面的不确定性,不是本次确认的编程 bug,留给 以后有需要时再深挖;DEATH_ROOM->start_death(me)这个呼叫指向的 函数在全档案里也没有任何定义(->呼叫不存在的函数在 LPC 里静 默返回 0,不会报错),是否有意如此(依赖me->move(DEATH_ROOM)之后房间自身的 NPC/机制接手)也一并留待验证。 - 测试方法论上的两个新发现,已写入 AGENTS.md §10.2:(1) 本地
telnet客户端经由 tmux 传输部分中文字符(如"者""考")时会被传 输层损坏,is_chinese()误判为"不是中文",换成mudclient.py原始 socket 连线后同样的字符完全正常——不是驱动/mudlib bug,是 测试工具本身的传输问题;(2) 这份档案的提示符带实时时钟,用mudclient.py --idle 1.0(等于时钟跳动间隔)会和每秒一次的时钟 刷新赛跑,导致后续指令永远发不出去(wash/born/score全部 石沉大海),改用--idle 0.5后立即解决。
§7.100 房间基类 replace_program() 扫尾修复(2026-08-19)
ROOM 宏(/inherit/room/room,见 include/globals.h)在 2,310 处
房间文件的 create() 里紧跟 inherit ROOM; 之后又多余地调用了一次
replace_program(ROOM);——和 AGENTS.md §7.100 记录的 jhfy3/其它
姊妹档案同一形状的休眠 bug:这个多余调用给对象打上永久的"pending
replace"标记,该对象一旦在生命周期里绑定任何闭包(call_out、
edit()、自定义 input_to、房间外的 set(..., (: ... :)) 等)就
会崩溃。用已验证过的 fix_710_room.py(二进制模式,只删除内容严
格等于 replace_program(ROOM); 的独立行)扫过 work/,删除 2,309
处;剩余 1 处是房间生成工具 clone/misc/roommaker.lpc 里的
str += "...replace_program(ROOM);..." 字符串拼接变体,手工改成
str += "\n\tsetup();\n}\n";(同一工具另有一处 heredoc 模板已被脚
本正常扫到)。另有 21 处是转档之前就已经被注释掉的 // 行,原样保
留。work/data/ 子目录下没有真实 .lpc 源码被脚本误跳过(唯一的
work/data/room/moman/*.lpc 4 个文件本身没有这个 bug)。git diff
--stat 确认 2309 个文件各删 1 行 + roommaker.lpc 1 处字符串编辑,
与脚本自报数字吻合。修复后真实驱动干净启动(零新增编译错误、端口
正常监听、debug.log 里没有任何"cannot replace"/"cannot bind"行),
巫师账号 fluffos/Mud@2026 实测走了 2 个曾经被注释触发过这个 bug
的房间(d/city/library.lpc、d/mingjiao/shanlu3.lpc)以及世外桃
源出生点,look/goto/quit 全部正常,无回归。
§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): 5 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-08-20)
上一轮遗留的"多次 smash 都杀不死角色"谜团这次彻底查清楚了,真相
和上一轮的猜测(combatd.lpc 的 player_escape()/want_kill 逃脱
分支意外拦截)完全无关——用临时 tell_object() 插桩逐行追踪
cmds/arch/smash.lpc 的 main() 才发现真正原因:
- 根因:
is_admin()是一层独立于巫师身份的更高权限门槛,smash/call/clone/summon/grant全部共用同一套!me->is_admin()⟹switch(SECURITY_D->query_site_privilege(cmd))⟹ 默认分支return notify_fail(...)(返回 0)的写法。clone/user/user.lpc第 62 行is_admin()的定义是admin_flag == 1222 || getuid() == "pkyou",和 wizlist 里的(admin)身份完全是两回 事——注释原文写得很清楚:"判断是否具有管理权限:如果是版本发布 站点或是通过 PASSWD 命令设置过,则具有管理权限。具有该权限的巫 师可是使用诸如 clone、call、log、smash、copyskill等命令。"set_admin()(把admin_flag设成 1222 的唯一途径)要求is_root(previous_object()),而全档案没有任何地方呼叫set_admin()——也就是说这份档案里除了硬编码的uid=="pkyou"后 门,压根没有任何在游戏内可达的合法途径把admin_flag置为 1222。site_privilege是个空 mapping(只能通过巫师指令set_site_privilege()写入,没人写过),所以smash/call里switch永远落到default:分支,main()静默return 0。 command_hook()(feature/command.lpc第 71-85 行)把"命令 main() 返回 0"通用地当成"这个动词不是命令,接着试试当作 emote":find_command(verb)命中且call_other返回假值后, 会依次 fallback 到EMOTE_D->do_emote()。data/emoted.o里刚好 存在一个名字也叫"smash"的表情动作("$P高举双手大喝:去死 吧!从天上降下一道闪电将$n化作一堆灰烬。")——这正是上一轮和这 一轮反复看到的"看起来像是把目标劈死了,但角色毫发无伤"的假象来 源:admin 指令smash因为权限门槛静默失败后,动词又被通用 fallback 机制当成了同名表情来执行,纯粹是场面话,根本没有调用receive_damage()/die()。用look <target>复查过,目标确实 "气血充盈,并没有受伤"。这不是 bug(smash/call/clone/summon/grant的门槛写法、以及"动词 fallback 到同名 emote"都 是一致、故意的设计),只是两层设计叠加出了一个极具迷惑性的假 象。用插桩确认后已经把smash.lpc恢复成原样(git checkout), 没有对这份档案做任何"修复"这层权限门槛的改动。- 顺带查清楚了
player_escape()到底是什么:只保护"玩家杀玩家" 的场景,和smash/NPC 杀死玩家完全无关。player_escape(killer, ob)一开始就要求playerp(ob)(受害者是玩家)且playerp(killer) && killer->is_want_kill(ob->query("id"))(凶手 也是玩家,且明确设过"想杀死你"意图标记)——smash走的是receive_damage()+die()直接调用,从不呼叫want_kill(),就算smash真的执行到底也不会触发这条逃脱分支;NPC 杀死玩家时killer不是playerp,同样永远不会命中。顺便观察到一处可疑但 按项目规则不动的地方:player_escape()顶部"今天是否已经用过一 次逃脱名额"的节流检查读的是顶层属性combat/which_day/total_count,但全文件唯一写入这两个键的地方是winner_reward()里完全不同的嵌套路径combat/today["which_day"]/combat/today["total_count"]——这两 组键名从未被真正写过,节流检查恒为假,逃脱分支理论上会对每一次 符合条件的玩家杀玩家意图击杀都无限次生效。这看起来像一处真正的 路径不匹配,但它只影响游戏平衡(PK 杀不死人),不产生任何编译 错误/运行时崩溃/debug.log 错误/驱动拒绝,按 AGENTS.md 的判定标 准属于"无错误信号=设计,不动"的范畴,本次不做任何改动,只记录在 这里供以后需要时参考。 - 真正打通了一次完整的死亡流程,全程 debug.log 干净:把
fluffos(巫师账号,(admin)身份但没有is_admin()神权限) 用goto /d/guanwai/milin2送进d/guanwai/npc/dongbeihu.lpc(东北虎,attitude: aggressive,outdoors: guanwai房间进 入即主动攻击)的地盘,纯粹靠真实战斗多回合打到死——依次看到 "你的眼前一黑,接著什么也不知道了...."(昏迷)→"你扑在地上挣扎 了几下,腿一伸,口中喷出几口鲜血,死了!"(和combatd.lpc第 1200-1201 行announce(me,"dead")的文案逐字一致,证实真的走到 了这条分支而不是表情假动作)→"【谣言四起】某人:听说浮浮莫名其 妙地死了。"(全服讣告)→角色被送进鬼门关 - /d/death/gate(DEATH_ROOM),房间里站着"冥府地藏王殿前 白无常(white gargoyle)"。全程debug.log没有产生任何新的运行时错误(唯一出 现的运行时错误是和这次测试完全无关、独立于任务任务精灵心跳复现 的*Inherit chain too deep: > 60,见下)。 DEATH_ROOM->start_death(me)未定义确认无害,是本次会话第 4 个证实同一良性形状的档案:feature/damage.lpc第 397 行DEATH_ROOM->start_death(me);在全档案里确实找不到任何start_death的定义,->呼叫不存在的函数静默返回 0;实测死亡 全程没有任何相关报错,紧随其后的me->move(DEATH_ROOM)和房间 自身的白无常 NPC(d/death/npc/wgargoyle.lpc)完整接管了后续流 程,和本次会话已经在 dtsl2/mhxy/tianxia 三份档案验证过的结论一 致——这是同一份共享血统里带过来的良性死代码,不是 bug。death_stage()的重入防护逐支路读代码确认写得对,但受限于巫 师排除逻辑+目标区域太远,没能用真人角色走完整段对话:d/death/npc/wgargoyle.lpc(以及血统一致的bgargoyle.lpc)的init()里有if (previous_object()->query_temp ("death_stage_active")) return;防重入闸门,death_stage()本体三条出口分别是:(1)!ob || !present(ob)时清掉death_stage_active再return;(2) 对话没讲完时"不清掉闸门、call_out续下一句再return"——这不是遗漏,是故意让闸门在整 段对话进行期间保持"占用"状态;(3) 对话讲完时先清掉闸门再reincarnate(),随后落入move(REVIVE_ROOM)走到函数末尾正常 结束。三条路径的收尾都对,没有 §7.112 那种"某个退出分支忘记清 闸门"的漏洞。没能用真人角色实测这整套对话的原因是:init()里wizardp(previous_object())直接短路跳过整套流程(巫师角色的鬼 魂永远不会触发白无常对话,这也是刚才测试fluffos死亡后鬼魂在鬼门关干等、白无常没有主动搭话的原因),必须换一个非巫师角 色才能真正触发;本次新注册了测试角色pkyou(关外人氏出生), 但从出生点/d/guanwai/kedian走到最近的野兽出没点(milin2/famu/heifengkou)实测超过 14 跳,中途按房间exits字段脚 本化寻路时踩进了一个不相关的死胡同(xuedi1↔xuedi2↔xuedi3↔ chuanchang雪地循环,和目标区域并不连通),而summon/goto等巫师快速传送指令对目标是"玩家"时同样卡在is_admin()门槛 上(summon.lpc第 42 行同款switch结构),没有合法的巫师 途径把pkyou快速送到猛兽出没的房间。鉴于逐行读过的三条退出 分支逻辑清晰无歧义,且这正是本次会话第 3 次在不同档案里核实到 的同一段血统代码(death_stage()的写法和 §7.112 已核实过的其 它档案一致),判断为设计正确、不需要进一步修复,但如实记录"非 巫师完整对话流程"这一小块没有做到真人实测,留给以后有需要时再 验证。- 顺带发现一个超出本次任务范围、和 smash/死亡无关的独立运行时 错误,先记录不修:驱动心跳期间反复出现
执行时段错误: *Inherit chain too deep: > 60,触发源是adm/daemons/quest/ shen.lpc的heart_beat()/start_quest()链路生成kungfu/class/generate/questnpc/shennpc(还观察到一次/d/quanzhou),这些运行时动态生成的继承链条超过了驱动inherit chain size(60,来自config.fluffos)的上限。这是一 个真正的运行时错误(有明确的 debug.log 报错签名),但和本次任务 的死亡/smash 调查完全无关,超出本次任务范围,留给以后专门排查 任务系统时处理,本次不动。 - 快速核对清单结果(均已确认,未发现新问题):§7.90(eval- cost)、§7.111(
standard_trace(),adm/single/master.lpc定义 正常)、§7.108 均无异常;§7.112(death_stage()重入防护)在wgargoyle.lpc/bgargoyle.lpc两处都写对,见上;§7.79 (addn()2 参数)全档案 0 处残留;§7.100(ROOM基类replace_program())全档案 21 处命中,逐一确认全部是转档之前 就已注释掉的//行,和 2026-08-19 那次扫尾记录完全吻合,没有 新的活跃实例;§7.30(feature/skill.lpc未初始化 mapping 存取 器)5 处存取器全部已是mapp(x) ? x : ([])写法;combatd.lpc没有bounce相关代码,4 份姊妹档案命中的除零 bug 在这份档案不 适用。
深度功能测试(2026-09-04,round three,shop + 拜师)
新角度:扬州醉仙楼购物 + 丐帮李斧头拜师。上一次深挖是 2026-08-20
死亡/复活(smash/death_stage),NOTES 全文没有 buy/商店/
apprentice 实测。sibling zjdyaryl 同日刚走过同一条路径;
zjdy2008wzb 2026-08-19 已经 buy jitui + bai li futou。
本轮没有新的 programming bug。enter_world() 已经是
ob->set("registered", user->query("registered"))(从 user 抄到
login,和 xkxz2 Bug 2 方向相反)。work/log/nosave/ 目录本来就在,
clone 这条路没碰到 ntii 那种缺目录崩溃——但 clone 本身对
(admin) 巫师不可用:is_admin() 是 admin_flag == 1222 ||
getuid() == "pkyou",和 wizlist (admin) 是两回事(2026-08-20
NOTES 已记录,不改)。env/prompt 是 "time",live clock,idle
0.45。
实测过程
管理员 fluffos / Mud@2026 / 浮浮。第一输入是「您的英文名字」,
没有 BIG5 提示。端口 40109。原先 registered:0、qi:1(上次死亡
测试留下的)、startroom 世外桃源。recover 后走完 register
[email protected] → decide → west/out(阴险奸诈)→ wash
→ born 扬州人氏,落地扬州客店。born 后弹出游戏规则 pager
(ENTER/q)。
clone /clone/money/gold 被拒:「你不能复制物品。」(is_admin()
门槛,设计如此)。布衣当铺估价一文不值。改用巫师 update 加载一
个一次性 /clone/misc/_tmp_pay.lpc(create() 里 new 黄金然后
move 给 this_player();测完已删,未提交),i 出现一两黄金。
goto /d/city/zuixianlou,list 包子五十文 / 烤鸡腿八十文。扬州
店小二没有丐帮穷叫化踢人。buy jitui 成功:「你从店小二那里买下
了一根烤鸡腿。」i 剩九十九两白银 + 二十文铜钱(10000−80=9920)。
goto /d/gaibang/underhs(舍身崖下),李斧头 id li /
kungfu/class/gaibang/li-futou.lpc,只收男性。apprentice li 一次
成功:「恭喜您成为丐帮的第二十代弟子。」score「你是扬州人氏,
天性阴险奸诈,师父是李斧头。」title「丐帮第二十代传人」。
第一次 save 撞上 30 秒冷却(born 刚写过 last_save)。丢
socket 后重连「重新连线完毕」,再 save「档案储存完毕」。此时
user.o registered:1 + family_name 丐帮 / master_name 李斧头;
login.o 仍是 registered:0(do_decide 不写 link_ob)。
重启驱动强迫走 enter_world()。重连落地客店而不是世外桃源,没有
「您还没有注册」,score 仍是丐帮二十代 / 李斧头 / 扬州人氏,银
子铜钱还在。烤鸡腿是 F_FOOD,不进 autoload,重启后不在身上——
不是存档 bug。
live debug.log 是 libs/zjdywzb/log/debug.log(第一轮 Boot Time
Fri Sep 4 03:39:22,persist 轮 03:41:00),无 error: / Too deep
recursion。mudlib work/log/log 只有开机/指令编译警告。心跳期
Inherit chain too deep(2026-08-20 已记录、quest/shen.lpc)本轮
短会话没有再出现,不修。管理员存档未提交。
深度功能测试(§10.7,2026-10-04)— negative range ends
scripts/lpc_fix_negative_ranges.py: 4 line(s) in 1 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)— 房间列出的物件缺档时房间照常加载
房间基类 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-06)— 与 zhonghua2 同源的三处编译警告
include/npc/indian.lpc与masia.lpc共用全局名ma_sname/ma_pname;两个头文件被同一个守护进程(npcd等)一起包含时,后包含的印度名表覆盖马来名表,generate_ma_name()生成的是印度名字。印度表改名in_sname/in_pname(KB 04 §6.10)。cmds/std/team/swear.lpc:结义失败提示被多余的;截成两句,fail只剩"……可惜现在有人不在,$N",后半句"的提议只好作罢。"丢了;接回去。
验证:改动的文件和包含 npc/indian 的守护进程在 HEAD 与工作树分别加载(scripts/lpc_listcheck.sh,新进程),无回退。
深度功能测试(§10.7,2026-10-07)— 编译警告整理
scripts/lpc_warnings.py zjdywzb --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 528 个 .lpc 和若干 .h。HEAD 与工作树分别加载(新进程):527 -> 527 通过,无回退;改过的头文件的 153 个包含者 152 -> 152 通过。
手工:kungfu/skill/huifeng-jian/mie.lpc(回风剑「灭剑」)的出招描述 msg = HIC "…幻出死亡的色彩,剑尖渐\n"; 后面两行("渐逼近$n…""影化为一剑直刺$n前胸…")被多余的 ; 截成单独的语句,玩家只看到半句;接回去(scripts/lpc_join_cut_strings.py 现在也认带颜色宏的字符串)。adm/daemons/natured.lpc 的空语句 lt[LT_MON];、adm/daemons/combatd.lpc 的空语句 your_temp["guarding"]; 删去。
启动时警告 49 -> 0;新进程登录(新人物确认)正常。
- 2026-10-12:
clone/misc/swmuding.lpc(神木王鼎)自己声明了一张mapping insects虫表,与继承的clone/misc/muding.lpc的insects同名;读表的是木鼎的代码,读到的是木鼎自己的表,神木王鼎的虫表从来没用上(Redeclaration of global variable 'insects')。改名sw_insects,create()里insects = sw_insects;交给木鼎(nitan170911、wxddym、zjdy 系同修)。HEAD 与工作树各载入一次,regress 0。
编译警告收尾(2026-10-13)
手工清零;改动的 3 个对象在 HEAD 与工作树各载入一次,regress 0。
- 醉仙楼
d/city/zuixianlou3.lpc同时继承ROOM和F_DEALER,驱动只用dealer的reset(),房间的objects不再刷新;写void reset() { room::reset(); dealer::reset(); }(同 xkxc98sj)。 clone/megazine/obj/duka_3b.lpc(赌球卡 B)赔率变量声明两次,代码用后一份;删去前一行,赔率不变。- 书库
d/city/library.lpc自己把STORY_DIR定义成/doc/story/,覆盖globals.h里指向故事守护进程的同名宏;先#undef再定义,行为不变。
负数伤害当作消耗(2026-10-15,KB 06 §7.241)
receive_damage("neili", -52)、receive_damage("qi", -12) 之类把 add() 的负数写进了 receive_damage():本库 feature/damage.lpc 对负数伤害报错(或归零),练功/运功时报 F_DAMAGE: 伤害值为负值 或消耗失效。已改为正数伤害,内力/精力类改为 add(type, -N)(scripts/lpc_fix_damage_calls.py):/kungfu/skill/duanyun-fu、/kungfu/skill/feifeng-whip。
转码吃掉的引号(2026-10-15,KB 03)
原档在右引号前有一个孤立的 GBK 前导字节,转 UTF-8 时连引号一起丢了(如 return name + HIM "发作身亡了;),字符串一直延续到后面,文件编译失败:/inherit/condition/damo。补回引号(scripts/lpc_fix_eaten_quote.py)。其中 /inherit/condition/damo.lpc 不能编译时,所有 damo_*(达摩)中毒状态都载入失败。
find_player() 只认在世界里的身体(2026-10-15,KB 07 §8.26)
simul_efun 的 filter_player() 加上 environment(ob):注册中途断线留下的、不在任何房间的身体不再被当成玩家,两个以上时 find_player() 不会再报 The player(<id>) has existed in the world.(wllz3 实测过的问题)。本库若注册流程不会留下带玩家 uid 的身体,这一改动不改变行为。
技能名拼错(2026-10-15,KB 06 §7.249)
query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):choushui-zhang→chousui-zhang(chousui-zhang);cuhixiao-jifa→chuixiao-jifa(xiaoao-jianghu);lenyue-shengong→lengyue-shengong(lengyue-shengong);matrial-cognize→martial-cognize(qiankun-danuoyi/nuoyi);taiji-shengon→taiji-shengong(taiji-shengong);xuxiang-zhi→wuxiang-zhi(wuxiang-zhi/wuse);yaowang-mishu→yaowang-miaoshu(yaowang-miaoshu)。
技能名拼错:技能文件里拼错自己的名字(2026-10-15,KB 06 §7.249)
技能自己的文件(或它的绝招目录)里,query_skill() 读的是差一个字母的自己的名字,永远得 0:“基本功夫要高过本技能”之类的限制从不生效,按等级取招、计算伤害的那一项一直是 0。改为本技能的名字(scripts/lpc_fix_skill_typo.py 的 self 判据):shicheng-liulang→shicheng-liulong(shicheng-liulong);xuanyuan-arror→xuanyuan-arrow(xuanyuan-arrow)。
柔云步法「柔云纵」有时报错(2026-10-15,KB 06 §7.251)
rouyun-steps/zong 随机把人送到三个房间之一:精、精力已经扣了,“乘云而去了”也说了,送不过去时人留在战斗里并报错。第三个是巫师的测试房 /u/deaner/test1,库里没有;改为 /d/city/guangchang(hy、jym 等库原本就用这个)。
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,而不是得到拒绝。1 处(1 个文件)改为 dbase 路径查询 query("family/master_name"):有门派时值相同,没有时得 0;原有的判断照常生效(scripts/lpc_fix_family_index.py)。给映射赋值的写法未动。
修罗刀「索命」不存在(2026-10-15,KB 02 §3)
原档案里这个绝招放在一个名叫 xiuluo-dao/jueming.c 的目录里(jueming.c/suoming.c),改名 .lpc 之后成了 jueming.lpc/suoming.c:游戏里没有 suoming,而 perform jueming 会把这个目录当作虚拟对象去载入,报 Inherit chain too deep。同系五个库都是 xiuluo-dao/suoming.lpc。文件原样移到 xiuluo-dao/suoming.lpc(保留本库自己的旧版本:用 sword 技能计算,没有 living() 判断),现在能用。
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 个对象设了本库没有文件的武功(1 个名字,最多的是 freezing-force)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:1 个里 1 个通过。(scripts/lpc_guard_set_skill.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 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 11 处、8 个文件:changquana→changquan(4 处)、tiaji-jian→taiji-jian(2 处)、taiji-shenfong→taiji-shengong(1 处)、yan-fly→yanfly(1 处)、lingboweibu→lingbo-weibu(1 处)、jiahua-neigong→jiaohua-neigong(1 处)、jinggang-zhi→jingang-zhi(1 处)。(scripts/lpc_fix_skill_names.py)
标准整理轮(2026-10-19,scripts/toolkit_fix.sh)
本库在新驱动下重扫,机械修复:2 个函数补上 varargs(调用处少传参数)。修后与 HEAD 逐个物件对比载入,没有退步。
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 03 §4.6)
转换时丢掉的分号、逗号、引号或文件末尾的 },共 2 个文件,修前 HEAD 载入失败。别的库里同路径的完好副本只比这里少几个标点,按它补回(括号里是那个库):/d/baituo/npc/obj/bowl.lpc(cctx)、/d/baituo/obj/bowl.lpc(cctx)。修后与 HEAD 逐个对比载入:这 2 个物件全部载入,没有退步(scripts/lpc_restore_from_twin.py、lpc_fix_eaten_quote.py --comma、lpc_fix_eof_braces.py)。
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 处)。
member_array('/', str) 改为 strsrch(str, "/"):字符字面量的 member_array 按字节搜索(2026-10-20,KB 06 §7.283)
member_array('/', str) 里的 '/' 是整数,驱动按 UTF-8 字节搜索,返回字节偏移(带第三个参数时起点也是字节);最新的 FluffOS(923cdbc0)在字符串里有非 ASCII 文字时,对每个调用点警告一次(Warning: member_array(int, string) searches BYTES)。而 str[0..idx-1] 按字符切,中文字符串会在错误的位置截断。改为 strsrch(str, "/")(找不到同样返回 -1,按字符计数;scripts/lpc_fix_member_array_char.py,JSON 编码器等少数几处手改)。改动:/adm/daemons/vrm_server.lpc(1 处)、/cmds/arch/sameip.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)。
目录读出后的后缀截取宽度(2026-10-20,KB 06 §7.296)
.c 改名成 .lpc 之后,get_dir("*.lpc") 读出的文件名还按两个字符截取($1[0..<3]):baguadao.lpc 变成 baguadao.l,用它拼出的路径加载不到对象(故事、战场、事件、帮助、自造物品数据、召唤列表等)。改成 [0..<5](scripts/lpc_fix_getdir_strip.py)。改动:/adm/daemons/storyd.lpc(1 处)。
任务 NPC 的一条因果链:virtuald 递归、chinese.lpc 从未编译、两个基类档案里没有(2026-10-20,KB 06 §7.327 / §7.328,KB 04 §6.15)
扫描里 drink、put、quest、refuse 这些无关指令后面都跟着同一条控制台错误:Inherit chain too deep: > 60 when trying to load '/kungfu/class/generate/questnpc'(shennpc 同样),出自任务守护的心跳,每隔几秒一次,调用栈是 60 层 master::compile_object() 和 virtuald::compile_object() 交替。
1. adm/daemons/virtuald.lpc 的 if (file_size(name + ".lpc")):文件不存在时 file_size() 是 -1,条件为真,于是对一个不存在的路径调 name->query_virtual_room(),驱动加载失败,又回头问 master 的 compile_object(),一直递归到深度上限。改为 >= 0(zhongjidiyu 早已这样改过)。
2. 递归消失以后露出的是:clone/quest/judge.lpc、search.lpc、shen.lpc 要 new(CLASS_D("generate") + "/questnpc") 和 /shennpc,档案里没有这两个基类。同族的 zjdy2008wzb、zjdyaryl 带着,它们的 clone/quest/judge.lpc、shen.lpc 与本库逐字节相同,所以从 zjdy2008wzb 原样收入这两个文件。
3. 这两个基类继承的 generate/chinese.lpc 在新驱动里从来没有编译过:三处 set_skill("parry", "taiji-jian")(另两处是 damo-jian、huashan-sword),应当是 map_skill("parry", ...);同一个基类也挡着 killed、receiver 和 d/room/roomnpc/shouwei(住宅守卫)。
4. 顺手清了 receiver.lpc 里没用的局部变量 exp 和 shouwei.lpc 的 obn。
实测:重启后登录并停留约三分钟,任务守护不再产生任何控制台错误(修改前每隔几秒一条);加载检查:依赖这些类的 9 个对象,HEAD 通过 4 个,tree 通过 9 个,回归 0。
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() 都找不到它,调用方多半把失败当成「没有这个东西」悄悄跳过。本库 1 个存档共 1 处按 scripts/lpc_fix_save_dotc.py 改成 .lpc(只改 .lpc 存在而 .c 不存在的字符串;玩家存档、备份、留言板、巫师目录和文件索引缓存不动)。改动:/data/dbased.o(1 处)(共 1 个存档)。
注册到不存在函数的 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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 5 条注册(4 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/adm/npc/referee.lpc(do_halt)、/clone/megazine/obj/maikaren.lpc(do_drop)、/d/tianlongsi/munitang.lpc(do_exit)、/inherit/misc/fboard.lpc(do_list)。最多的函数名:do_halt 2 处、do_drop 1 处、do_exit 1 处、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。 还剩 2 个函数没有补(inquiry 2):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/d/meizhuang/npc/danqing-sheng.lpc:ask_me、/d/xiakedao/npc/shipotian.lpc:ask_weiwang。
读不到的留言板:"/clone/board/x"->foo(); 在 create() 里报错(2026-10-22,KB 06 §7.357)
xkx 系的房间在 setup() 之后写 "/clone/board/gaibang_r"->foo(); 来加载留言板(foo() 本来就没有定义,只为了触发加载)。这个库没有这个文件,call_other() 报 *call_other() couldn't find object '/clone/board/gaibang_r',对象留在内存里、load_object() 却抛错:玩家第一次走进房间就看到错误,写在 create() 中间的还会切掉后半段。scripts/lpc_fix_missing_foo.py 把 1 条单独成行的语句(/clone/board/yipin_b;/d/lingzhou/yipindayuan.lpc)注释掉并写明原因(别的房间里同样的语句本来就是作者自己注释掉的)。证明和 45 个库的整体加载检查见 KB 06 §7.357。
(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)
inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 2 个文件里 3 行只含这个指针的行("令牌": (: ask_me :), "威望": (: ask_weiwang :), "江湖威望": (: ask_weiwang :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。
汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 15 处(2026-10-22,KB 06 §7.360)
原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字);各派掌门的辈分判断(name[0..1] == "澄")永远不成立;NPC 的称呼和提示里取到两个字而不是一个字。
本库 15 处(10 个文件):拼名字的赋值 8 处、与汉字常量比较 6 处、to_chinese()[..] 1 处。例:name[0..1] → name[0..0](/kungfu/class/emei/tobebonze.h:39);name[2..3] → name[1..1](/kungfu/class/shaolin/cheng.h:164);chk[0..1] == "你" → chk[0..0] == "你"(/adm/npc/ganjiang.lpc:542)。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,不要重跑。
长度检查按字节写,旁边的提示按汉字写: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) < 4) || (strlen(name) > 12)) {(/adm/npc/luban.lpc:1764)。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)。本库 channeld 同盟 / 门派标题 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(一格两个空格)。本库 2 处(1 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。
死巫师出口 gold/dock、sunrise、mei(2026-10-23,KB 06 §7.370)
/d/wanjiegu/road4 东向 /u/g/gold/dock、峨眉寿星居南向 /u/xinyue/sunrise_room、玩家卧室通往 /u/mei/… 均无对应房间。scripts/lpc_fix_drop_dead_u_exits.py 删除这些出口行,并去掉同方向的 create_door(否则 create_door 无出口会炸)。本库 1 处。证明:zxty08nxgbb 的 road4 HEAD 有东向,改后没有;mei_room1 可加载。
中文名 / 验证码仍按 GBK 取「两字节一字」(2026-10-23,KB 06 §7.373)
pname = pname[(random(sizeof(pname)) & 0xFFFE)..<1]; pname = pname[0..1]; 在按字符计下标时会取出两个汉字。scripts/lpc_fix_pname_slice.py 去掉掩码并把收尾改成 [0..0](pname/pname2/sec_name/str/str1)。本库 4 处。证明:nt1 生成名长度 2–4;HEAD 常见 3–5。
yuanwai 孤儿 call_out announce_success(2026-10-27,KB 06 §7.349 Also)
d/changan/npc/yuanwai.lpc(及 aoxiangtianji 的 d/city 副本)在 check_daughter 里 call_out("announce_success", …),但本文件从未定义该函数;同系也无实现。到点会 *Undefined function*。cry_daughter 已完成认亲与赠书,删除这对 remove_call_out/call_out。证明:扫描 HIT call_out→announce_success 且无定义;改后残渣 0。
深度功能测试(2026-10-27,§6.25 zhongling tell_object 引号错位)
d/wanjiegu/npc/zhongling(及同路径副本)的 tell_object("who, 钟灵脱下一双绣花鞋。\n") 把对象参数写进了字符串;改为 tell_object(who, "…")。证明:原文错位;改后加载,语料残渣 0。
深度功能测试(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… 行。