info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
《指尖MUD》从嵌套压缩包中提取而来(`zjmud.7z` 内的 `指间mud服务器+手机版客户端.zip`,内部文件夹名为 `hell`),核心系统档案(`master.lpc`/`logind.lpc` 等)为配合自定义手机客户端协议被大幅重写、注册流程和 master 文件哈希都与其他版本不同,但地图内容仍属于本项目"地狱"/Doing 血统六件套家族(与 `hell`、`zjdy2008wzb`、`zjdyaryl`、`zjdywzb`、`zjdyzj` 同源)——逐字节比对显示与 `zjdyzj` 相似度高达 83%,与 `zjdyaryl` 约 79%,与 `zjdy2008wzb` 约 76%,与 `hell`、`zjdywzb` 则更疏远、约 67%,说明这是把同一套门派/城市江湖世界整体搬到一套完全独立开发的手机客户端引擎上,而不是简单的引擎内衍生版本;本作和 [shujian3](../shujian3/) 同属一个手机 App 协议、同一套自定义 `logind.lpc` 血统,但密码学挑战是真正校验的(真实的 `crypt(ZJKEY, ...)` 握手,没有任何万能旁路字符串),是这批档案里手机协议实现最严谨的一个;不过核心系统重写也带来代价——留言板发帖、玩家商店、邮件等旧系统专属功能在重写时没有被移植过来,深度测试确认这些功能在这套引擎里本来就不存在,并非隐藏的 bug。
English
Extracted from a nested archive (zjmud.7z, containing a folder named 'hell'). Its core system files (master.lpc, logind.lpc, and others) were heavily rewritten to support a custom mobile-phone client protocol — different master-file hash, different registration flow — but the map itself belongs to the same six-way 'Hell'/Doing-lineage family as this collection's hell, zjdy2008wzb, zjdyaryl, zjdywzb, and zjdyzj: a byte-level, line-ending-normalized comparison found its /d room tree 83% identical to zjdyzj, 79% to zjdyaryl, 76% to zjdy2008wzb, and 67% to both hell and zjdywzb — the entire sect-and-city world ported onto an independently developed mobile client engine rather than a simple in-engine derivative. It shares the same mobile-app protocol and custom logind.lpc lineage as shujian3, and unlike shujian3 its password field is genuinely cryptographically verified (a real crypt(ZJKEY, ...) challenge with no bypass string) — the most carefully-implemented of this family's mobile protocols. The system rewrite came at a cost: legacy features tied to the old core (bulletin-board posting, player-run shops, mail) were never carried over to the new engine and simply don't exist here.
README
内容亮点
- 地图和"地狱"/Doing 家族(
hell/zjdy2008wzb/zjdywzb)逐字节 相同,说明这份档案是把"地狱"的门派世界搬到了一套完全独立开发的 手机客户端引擎上,而不是简单的引擎内衍生版本。 - 自定义手机 App 协议里的密码学挑战是真实校验的:客户端必须正确计 算
crypt(ZJKEY, str[2..3])才能通过握手,账号密码字段也要求真 实的crypt()密文,和shujian3的"密文字段目前未做真正校验" 形成对比——这是这批档案里安全性设计最认真的一个手机协议实现。 - 地图内容逐字节相同,但核心系统被完全重写这一事实也带来一个副作用: 旧系统专属的功能(留言板
post、玩家可用的商店/买卖/邮件指令)在 重写时没有被移植过来——不是这些子系统里藏着 bug,是它们在这份手机 App 引擎里根本不存在,深度功能测试(§10.7,见 NOTES.md)确认过。
注册流程(手机 App 协议)
1. 连线后驱动打印一次 ver1.0,<str> 握手横幅,input_to("jiance", ...)
等待客户端回应 crypt(ZJKEY, str[2..3])(str 本身是
crypt(ZJKEY, "zj"),一个真实的、需要正确计算的密码学挑战——和
shujian3 不同,这里没有任何万能旁路字符串)。
2. 一次性发送 账号║密码║密文║email——只认字面 ║
分隔符,不会做逗号自动替换;密文字段是真实校验的
crypt(ZJKEY,账号)+crypt(ZJKEY,密码),同样没有旁路。
3. 新账号则紧接着发送 性别║图片║中文昵称,例如 m║img1║指剑。
本次修复的关键 bug
adm/daemons/logind.lpc:crypt(ZJKEY, 0)在这个驱动上不是原版的 确定性 DES-crypt,而是每次都生成一个全新的随机$6$SHA-512 盐值,导致这个客户端挑战/应答握手在数学上永远无法通过(参见 AGENTS.md §7.14,zjdyzj的同类问题)。已改为显式的 旧式 2 字符盐值crypt(ZJKEY, "zj"),恢复确定性。adm/simul_efun/chinese.lpc的is_chinese():沿用旧版 GBK 字节区间判断(str[i]<176||str[i]>=248等),在这个驱动上strlen()按字符计数、str[i]是 Unicode 码点而非原始字节,导致 真实的中文名字永远无法通过检测。已改为码点区间判断 (0x4e00-0x9fff)。clone/user/user.lpc的accept_kill():is_killing(ob)传入了一个物件而is_killing()期望的是字符串 id(AGENTS.md §7.50,这是第三个独立发现同一个 bug 的血统),导致整个玩家身体类 无法编译,注册流程卡死。已改为is_killing(ob->query("id"))。- 三个纯 socket 功能的守护进程在 WASM 下无法编译,按"禁用整个文件 的入口点"的方式清空为 no-op(§7.52):
- adm/daemons/versiond.lpc(~2200 行的版本同步守护进程):
in_server()/connect_server()/send_command()/
send_client_pending_msg()/syn_finish()/in_listen_callback()/
in_write_callback()/in_close_callback()/cmd_close()/
send_pending_msg()/send_result()/clear_syn_info() 内的
socket_close() 分支,共 12 处——这个文件不是"多功能大文件"的
例外情况(is_version_ok()/is_release_server() 等非 socket
函数被 questd.lpc 等广泛调用),但整个文件因为散落各处的
socket_* 调用无法编译,实际上在运行时导致
collect_all_quest_information() 崩溃——之前的记录把这个当作
"非致命错误,保持原样"是不准确的。
- adm/daemons/payd.lpc(HTTP 充值回调服务器)。
- adm/daemons/network/dns_master.lpc(跨服 intermud UDP 层)。
深度功能测试补充修复(2026-08-08,§10.7)
adm/simul_efun/file.lpc的log_file()一直是裸的write_file(), 没有调用同一份文件里紧邻定义的assure_file()(AGENTS.md §7.11); 每一条新连线最先执行的clone/user/login.lpc::logon()就会调用它 写/log/nosave/logon。此前的记录说"创建了缺失的 /log/nosave 目 录"只是治标:那个目录从未被 git 追踪(.gitignore把整个libs/*/work/log/当运行时状态排除),只是恰好还没在某次 checkout 中丢失过。已在log_file()里补上assure_file(LOG_DIR + file);, 并在文件顶部加一行assure_file()前向声明(否则整个simul_efun/master编译失败)——live 验证:临时清空work/log/nosave/后重启驱动,新连线不再卡死,目录被自动重建。efun::message()的 0 值exclude参数问题:核实后确认adm/simul_efun/message.lpc的message()函数体本身就有if (!exclude) exclude = ({});防护,属于彻底根治,不是像zjdywzb/yhwhpublicfi(AGENTS.md §7.88)那样"包装函数漏标varargs、部分调用点参数不足"的破损形状——这份代码从一开始就没 有这个漏洞,此前的 WASM 笔记"已修复"这个说法是准确的。
管理员账号 / Admin account
- id:
fluffos - 密码 / password:
Mud@2026 - 权限 / level:
(admin)
管理员名单存储在纯文本文件 adm/etc/wizlist 里;账号本身通过正常注
册流程创建。
警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。
本地运行
cd libs/zjmudhell
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40204。
NOTES · 移植与修复记录
WASM 修复摘要(迁移自 meta.json 的 group_note)
从 zjmud.7z(指间mud服务器+手机版客户端.zip)里嵌套的一个 zip 中提取,内部文件夹字面命名为 'hell'——和已有的 hell/zjdywzb 世纪家族无关,master 哈希不同。修复了 efun::message() 因 exc_target=0 被拒绝的问题;创建了缺失的 /log/nosave 目录。同样是 shujian3 血统的手机 app 协议和自定义 logind.lpc。需要完整的 WASM 修复:(1)crypt(ZJKEY, 0) 客户端握手的非确定性问题(AGENTS.md §7.14)——已修复为 crypt(ZJKEY, "zj"),和 zjdyzj 一样;和 shujian3 不同,这份档案里 get_user() 中基于 crypt 的密文检查没有被注释掉,也没有逗号到 ║ 的便利替换,所以注册需要字面的 ║ 分隔符和真实的 id/密码密文(crypt(ZJKEY,id)+crypt(ZJKEY,密码));(2)经典的 §8.1 GBK 字节区间 is_chinese() 检查修复成 CJK 码点区间;(3)clone/user/user.lpc 的 accept_kill() 里 is_killing() 物件对字符串参数不匹配(§7.50,这是第三个撞上这个 bug 的血统);(4)按 §7.52 掏空了三个纯 socket 精灵(versiond.lpc——create/in_server/connect_server/send_command 等一共 12 个函式,不像之前那条笔记说的那样"保持原样",因为它实际上在运行时破坏了 questd.lpc 的 collect_all_quest_information();payd.lpc 的 HTTP 支付回呼;adm/daemons/network/dns_master.lpc 的 intermud UDP 层)。管理员(fluffos/Mud@2026)通过真实注册加 adm/etc/wizlist 播种。score 对刚创建的角色正确地被一个'born'标记挡住(游戏设计如此,不是 bug)——改用 look+quit 验证。
深度功能测试(§10.7,2026-08-08)
本次先按 AGENTS.md §11 通读了 zjmudhell 自己的 README("核心系统改写、
地图原样保留"血统关系)以及同一份手机 App 协议血统的 shujian3
README/NOTES.md,逐条对照其已知 bug(§7.68 复活软锁、commandd.lpc
.c 后缀死循环、securityd.lpc valid_read() 误伤等),但不假设
移植——每一条都在 zjmudhell 自己的代码里单独核实。
/log/nosave目录问题:不是"已创建但可能丢失",是代码本身缺失 防护,本次已根治(AGENTS.md §7.11 新增确认实例)。adm/simul_efun/ file.lpc的log_file(string file, string text)一直是裸的write_file(LOG_DIR + file, text),没有调用同一份文件里两个函数之 后就定义的assure_file()(这份代码库里channeld.lpc/examined.lpc/versiond.lpc/securityd.lpc/多个adm/npc/*.lpc/cmds/arch/{punish,restore}.lpc都已经在正确使用这个 helper)。clone/user/login.lpc的logon()——每一条新连线最先执行的函 数,版本握手横幅打印之前——会无条件调用log_file("nosave/logon", ...)。当前work/log/nosave/目录之所 以还在磁盘上,只是因为上一轮 WASM 排查时意外创建过、后续会话一直 没有清空过——它从来没有被 git 追踪(.gitignore把整个libs/*/work/log/当成驱动每次启动都会重建的运行时状态排除掉,和debug.log一个待遇),所以这个隐患一直没有真正暴露。live 复现 确认:把work/log/nosave/临时改名挪走,用未修复的代码重启驱 动、发起一条全新连线——连版本握手横幅ver1.0,...都没有打印出 来,连线直接卡死。修复(在write_file()前加assure_file(LOG_DIR + file);)后,同样的"目录不存在 + 全新驱动 进程"场景下连线正常完成,log/nosave/被自动重新建出来(用ls/git status核实过,时间戳是刚刚,内容只有这次会话自己写 的两个文件)。修复过程踩到和xajhxo(§7.11 原文)完全一样的坑:assure_file()在这份文件里是定义在log_file()之后的,不 加前向声明整个simul_efun/master编译直接失败(No program in object '/adm/single/simul_efun'!,硬启动中止,不是警告)——补一行void assure_file(string file);前向声明后编译恢复干净。已把这个 新确认实例记入 AGENTS.md §7.11(这是继夕阳再现/XYZX 血统家族之后 第二个独立撞上"同一份文件里 log_file() 没调用紧邻的 assure_file()" 这个具体形状的、完全不相关的代码库家族)。
efun::message()的 0 值 exclude 参数问题:确认是彻底根治,不 是"只补了一个症状"。adm/simul_efun/message.lpc的message(mixed arg, string message, mixed target, mixed exclude)本身不是varargs,但函数体第一行就是if (!exclude) exclude = ({}); efun::message(arg, message, target, exclude);——也就是说这个 wrapper 早就把"被驱动静默填成int(0)的缺失第 4 参数"在传给efun::message()之前转换成了空数组,和tell_room()(同一文件里)已有的|| ({})写法是同一个思路,只 是换了个位置实现。这和zjdywzb/yhwhpublicfi(AGENTS.md §7.88) "4 个必填参数、内部调用点只传 3 个"的破损形状不同——zjmudhell这份代码从一开始就没有这个漏洞。live 验证:完整走过"世外桃 源 →east(进入"光明磊落"竹屋,触发陆天抒NPC 对话)→out(进入阎罗殿)"这条注册仪式路径,这条路径上多处message_vision()/message()调用全部正常,没有任何Bad argument 4 to EFUN message()报错。上一轮 WASM 笔记"修复了 efun::message() 因 exc_target=0 被拒绝的问题"这句话本身是准确的 ——本次只是把"为什么准确、准确到什么程度"钉死到具体代码行。
- §7.50/§8.3a:已在此前的跨库扫描中修复过,本次重新核实仍然生效。
clone/user/user.lpc的accept_kill()用的是is_killing(ob->query("id"))(字符串 id,不是物件),feature/ command.lpc的command_hook声明是nomask int command_hook(...)(没有private)。两处都已经是修好之后的形状,角色创建全程没有 卡在这两个已知坑上。
- §7.86(留言板
post崩溃):不适用——这份档案压根没有留言板系 统。全档案搜索BULLETIN_BOARD/BBS_BOARD/inherit.*board/post.lpc均无命中,cmds/目录下也没有任何注册"post"动词的 文件;和hell/zjdywzb等血统家族地图内容逐字节相同、但核心系 统被完全重写这一血统关系一致——留言板属于旧系统的功能,这份手机 App 引擎重写时没有移植过来,不是"已修复",是"从未存在",两者要区 分清楚。
- §7.68(死亡/复活 present() 硬中止软锁):代码形状命中,但按撤回 说明的两个前提条件核实后确认不适用,未做任何修改。
d/death/npc/ {wgargoyle,bgargoyle}.lpc的death_stage()确实是if (!ob || !present(ob, environment())) return;这个撤回前的原 始形状(和shujian3共享同一套gate.lpc/鬼门关血统,shujian3自己的这个修复后来也被撤销了,参见shujian3README 的更正说明, 所以不能拿它当"已验证适用"的先例)。按 §7.68 撤回说明要求的两个前 提条件逐一核实:(1)本档案的feature/move.lpc/feature/ command.lpc里完全没有is_ghost()检查——鬼魂在这份代码里没有 被禁止移动;(2)没有找到任何"强制移动任意玩家、不检查是否是鬼魂" 的脚本 NPC(shujian3/bmxkx2001那种"游导 NPC"角色,全档案搜索is_ghost命中的文件里没有类似的强制move()逻辑)。两个前提都 不成立,说明"鬼魂离场就放弃复活流程,等下次进入房间的init()重新触发"更可能是这份代码里合理的、大多数鬼魂本来就能自行游荡的 设计,不是 bug——按撤回说明的要求,未应用重试式修复。
- 实际死亡机制和
hell/zjdywzb系的鬼门关不完全一样,值得记录:feature/damage.lpc里真正常见的战斗后果是unconcious()(气/精 归零、disable_player()、随机 30-130 秒后call_out("revive", ...)自动苏醒),和真正调用die()/移动进鬼门关的永久死亡是两条不同 的路径——score的死亡记录字段只在真正die()时才会变化。用管 理员账号kill ouyang(欧阳克,/kungfu/class/ouyang/ouyangke, 一个门派级 NPC,明显不是给新手打的)挑起战斗,两回合内被打到"气" 归零、"你的眼前一黑,接著什么也不知道了...."(unconcious()的 标准文本),符合预期;断线重连后如果气/精仍然是负值,logind.lpc的reconnect()(第 364-365 行)会再次调用unconcious()提醒角 色仍在昏迷中——这是有意的一致性检查,不是本次要修的 bug,只是记录 下来供以后遇到类似"重连后又打印一次昏迷提示"时不必重新排查。没有 在这次会话里把角色真正打死(欧阳克显然是刻意放在新手城里、不该被 新手攻击的强力 NPC,按 §7.90 附近"游戏设计/难度,不是 bug"的既有 原则不去动它),因此没有触发真正的鬼门关复活流程;d/death/gate. lpc/wgargoyle.lpc/bgargoyle.lpc已做静态代码审查(见上一条), 未做端到端真人复活的 live 验证。
- §7.90(eval cost 上限):
config.fluffos是这个项目最常见的700000默认值,本次测试没有触发过。完整会话(注册、5+ 个从未 访问过的房间移动、门派 NPC 战斗、断线重连、quit)之后grep -c "cost limit reached" log/debug.log和当次boot*.log都是 0——这份档案目前的内容体量没有顶到默认上限,未做调整。
- 手机 App 协议的实测细节:驱动打印
ver1.0,<str>后需要客户端 回应crypt(ZJKEY, str[2..3])(ZJKEY硬编码在include/zjmud.h里,str本身是固定盐值crypt(ZJKEY, "zj")所以整条握手串在这 个驱动构建上是完全确定性的,可以离线用 Pythoncrypt.crypt()算 出来复用);随后一次性发送账号║密码║密文║email(密文=crypt(ZJKEY,账号)+crypt(ZJKEY,密码),真实校验,没有旁路);新账 号紧接着发送性别║图片║中文昵称。进入游戏后角色创建走的是 "世外桃源(选品质方向)→ 阎罗殿:pianshu <类型>(先天偏属)→wash(忘忧池随机洗四维)→born <地名>(转生出生点)",和shujian3/zjdywzb是同一套仪式骨架,具体房间/NPC 名字不同。管 理员账号fluffos/Mud@2026通过这条真实注册流程创建,目前权限: (admin)立即生效(adm/etc/wizlist里的名单已经生效,账号文件本 身是这次会话新注册出来的,验证密码可用)。
- 确认一次 AGENTS.md §10.2 记录过的 telnet 客户端 CJK 字节损坏问 题,非本档案 bug:通过
scripts/tmux_mud.sh(本地 telnet 进程) 发送kill 李阿婆时,telnet 本地直接掉进了自己的telnet>转义 命令提示符(?Invalid command),从未真正发给驱动;同一条指令换 成scripts/mudclient.py(裸 socket)发送(改用拼音别名kill ouyang,欧阳克的set_name()里注册的别名之一)立即正 常触发战斗。另外这份档案的角色状态栏(012开头的一整行 HP/气/ 食/水状态)大约每秒钟推送一次,属于 §8.3 第 1 条"实时时钟提示符" 的同类情况——用mudclient.py时要用--idle 0.5或更低,tmux_ mud.sh因为是固定等待时长而不是"等到安静",不受这个影响。
- 这份档案没有留言板(见上)、也没有可用玩家指令的商店/买卖/邮件 系统:
cmds/目录下没有mail/buy/list一类的动词文件,feature/dealer.lpc(NPC 商人逻辑)存在但没有任何add_action()暴露给玩家指令——这些功能很可能只通过手机客户端自己的结构化菜单 协议(连线时看到的06b12:...这类按钮提示)触发,raw telnet/ socket 测试触及不到,不代表功能缺失,只是这次测试方法的覆盖边界,
§7.52 追加实例(2026-08-18):adm/daemons/network/messaged.lpc 的 socket_bind() 崩溃
作为 hell 那份档案(AGENTS.md §7.52)发现的同一个 bug 的跨库排查
一环,确认这份档案(和 hell 属于同一个 shujian3/"Doing" 手机
App 血统家族,goto.lpc 也共用完全相同的
MESSAGE_D->find_user(arg) 兜底逻辑)现场复现了完全一致的崩
溃:巫师账号第一次执行 goto(触发 messaged.lpc 首次懒编译)
时,执行时段错误:*Bad argument 2 to socket_bind(); Expected: int
Got: "10".——根因和 hell 完全相同(LOCAL_PORT() 的 (int) 转
型对 get_config() 实际返回的非数字值在运行时不起作用)。这份档
案自己早前的 WASM 修复记录里提到过"按 §7.52 掏空了三个纯 socket
精灵"(versiond.lpc、payd.lpc、dns_master.lpc),但
messaged.lpc(第四个碰 UDP socket 的精灵)当时被漏掉了——和
hell 自己的 versiond.lpc-修过-但-messaged.lpc-漏掉是完全相
同的疏漏模式。修复:startup_udp() 掏空成 return 0,
send_udp() 补上 !socket_id 保护。现场验证:重启全新驱动进
程,goto 干净执行,debug.log 里不再出现任何
socket_bind/Bad argument 记录。
跨库排查结论(截至本次):这个 bug 在 hell/zjmudhell 这两
个同源手机 App 血统档案上都 100% 复现;但在完全不相关的血统
aoxiangtianji(西游记题材)上用同样的方法(call/eval 直接触
发 create())没有复现——get_config(__MUD_PORT__) 在那份档
案上正确返回了数字端口。这说明这不是一个对所有携带这段代码的档案
都必然触发的 bug,可能和具体的血统/配置有关,不能不加验证就对
AGENTS.md §7.52 列出的其余 14 个档案批量套用同一个修复——已经把这
个结论更新回 AGENTS.md §7.52 本身,留给下一轮继续逐个排查。
记录以供以后有更完整客户端模拟能力时补测。
AGENTS.md §7.100 fix (2026-08-19): redundant replace_program(ROOM) landmine
Same corpus-wide bug as the batch-1-6 sweep (ROOM macro
"/inherit/room/room" from include/globals.h). Deleted 2,315 live
standalone replace_program(ROOM); lines under work/ via
fix_710_room.py, plus hand-fixed the room-building tool's
string-builder template (work/clone/misc/roommaker.lpc). 3 real
.lpc files under work/data/ checked — none had the bug pattern.
Remaining matches after the fix are all pre-existing //-commented.
Verified: clean build-debug boot (zero new compile errors, zero
"cannot replace"/"cannot bind" in debug.log; log/ directory didn't
exist for this lib and had to be created before the driver would
boot). Live admin login through this lib's custom 指间MUD
crypt()-challenge protocol (same ZJKEY/handshake as sibling
zjdyzj) as fluffos/Mud@2026 — entered 客店, look/quit both
worked cleanly. Incidental data/{login,user}/f/fluffos.o save drift
from the login test was reverted via git checkout HEAD before
committing.
§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-21):真正的战斗/死亡/复活、商店、拜师
上一轮(2026-08-08)只走完了注册仪式(世外桃源→east→out),从未真正
玩过游戏本体。本轮用管理员账号 fluffos(现场重连,账号早前登记)
补测了战斗死亡复活、商店购买、拜师三项,外加一遍标准检查清单快速核
实。全程用 /home/sunyc/src/fluffos/build-debug/src/driver 加载
config.fluffos,work/log/debug.log 全程零 Error(只有大量正常
的懒编译期 Warning,不是运行时报错)。
- 战斗/死亡/复活:完整走通,未发现新 bug。用管理员
clone /d/guanwai/npc/wolf在/d/guanwai/famu1(伐木场,符合 wolf.lpcinit()里outdoors+/d/guanwai/%*s的自动索敌条件)现场复 制了一只野狼,kill wolf触发真实生死战(不是安全的 spar/fight, 印证了本次会话在其它档案上反复验证过的规律:kill指令对 NPC 目 标没有任何安全闸门)。中途因为默认wimpy阈值触发自动逃跑,wimpy 0关闭后重新交战。追踪了完整的伤害链路:feature/damage.lpc的heart_beat()检查qi<0||jing<0时,如 果living(me)仍为真(角色还没被打晕)就调用unconcious()(设qi/jing精确为 0,disable_player()关闭指令能力,living()随之变假,call_out("revive", ...)排队苏醒);由于kill.lpc对 NPC 目标会双向调用kill_ob()(me->kill_ob(obj)且obj->kill_ob(me)),令野狼真正"想杀死"角色(is_killing生 效),combatd.lpc的脱离战斗判定(第 733 行)要求双方都不is_killing对方才会脱战,所以野狼在角色昏迷后不会停手,继续攻 击把qi打成负值——下一次heart_beat()时living(me)已经是 假,触发真正的die()。现场日志:你的眼前一黑,接著什么也不知 道了....后紧接着你扑在地上挣扎了几下,腿一伸,口中喷出几口鲜 血,死了!,角色被移动到DEATH_ROOM(/d/death/gate.lpc,"鬼 门关"),白无常(d/death/npc/wgargoyle.lpc)的death_stage()五段对话按 5 秒间隔完整播放(是ghost分支),最后reincarnate()+move("/d/city/guangchang")把角色送回泥潭广 场,之后qi/jing随心跳自然恢复。§7.112 死亡阶段重入护栏 现场验证:wgargoyle.lpc/bgargoyle.lpc的death_stage()每 条return路径(不存在/离场、非鬼魂反杀、最终转生)都正确清了death_stage_active临时标记,只有"还在等下一段对话"的中间分支 不清(本来就该保留,等这次call_out链跑完再清),这次是本档案 第一次真正触发这段代码,行为和静态审查时的预期一致,没有发现遗 漏分支。全程debug.log零新增报错。 - 商店购买:确认此前笔记记录的"无可用买卖指令"结论依然成立,不 是本次要修的 bug。
feature/dealer.lpc里do_buy(string arg)函数本身逻辑完整(价格计算、vendor_goods校验、INPUTTXT二次 确认购买数量等都写得很完整),但全档案里没有任何地方真正调用它 ——唯一的调用点是adm/npc/youxun.lpc自己重载后再::do_buy()转发,属于该 NPC 自己内部的特例,不是通用绑定。逐一确认了三层可 能的绑定方式全部缺失:(1)cmds/目录下没有buy.lpc/list.lpc/sell.lpc,也没有对应的.alias文件;(2)feature/command.lpc的command_hook()只通过find_command(verb)精确匹配cmds/里的真实文件,没有"扫描房 间内 NPC 是否认识这个动词"的兜底逻辑;(3)现场对着真实商人 NPC李阿婆(d/city/npc/liapo.lpc,有vendor_goods)连续尝试list/buy 1 苹果/buy apple,全部只得到标准的"什么?"(未知 指令),不是报错,也没有触发任何 debug.log 记录——这正是这份档案 "核心系统改写、商店 UI 从未移植"血统关系的预期行为,没有错误签 名,按项目既定原则不算 bug,维持上一轮"手机客户端菜单协议触及不 到"的结论不变。 - 拜师:完整走通,正确分配门派/称号,未发现新 bug。用管理员
clone /kungfu/class/gaibang/he-bj(丐帮七袋弟子"何不净")现场 复制到角色所在房间,bai he触发真实拜师流程:cmds/skill/ apprentice.lpc(通过cmds/skill/bai.alias绑定到bai动词, 确认过这个绑定机制真实存在)核实双方状态后调用ob->attempt_apprentice(me);he-bj.lpc的permit_recruit()(定义在gaibang.h,判断没有背叛/没有其他门派)和自身的combat_exp > 120000上限检查(新角色combat_exp=0,满足)都 通过,执行command("recruit " + id),recruit_apprentice()正确写入family = { family_name: "丐帮", generation: 20, master_id/master_name: 何不净, title: 七袋弟子 }(师父是七袋, 角色排在第 20 代)。score复核:丐帮第二十代传人、师父:何 不净。,字段全部正确。过程中顺带确认了permit_recruit()/attempt_apprentice()分层设计在这份代码里是完整可用的(不是像d/city/npc/baibian.lpc那个"模仿玩家技能"的对练 NPC那样,虽然 调了create_family()但没有inherit F_MASTER、没有定义attempt_apprentice(),那个是死代码,不适合用来测试拜师——已经 在排查过程中确认,未使用它)。全程debug.log零新增报错。 - 标准检查清单快速核实(本次全部确认已修复/不适用,未发现新问 题):§7.111(
master.lpc的standard_trace()第 230 行error["object"] ? file_name(error["object"]) : "0"已有三元防 护);§7.108(clone/user/user.lpc的reconnect()第一行就是enable_commands());§7.30(feature/skill.lpc5 个 accessor 的mapp(x) ? x : ([])防护逐行核实存在);§7.79(全档案addn(零命中,不适用);§7.100(此前已修复,本次未复查,沿用 之前记录);本次会话额外指定核对的四个"已在其它档案全部关闭"的 corpus-wide 坑,逐一确认代码形状本身不存在,不是漏查而是真的不 适用:adm/daemons/combatd.lpc无bounce相关代码、全档案没有chacha.lpc、adm/daemons/natured.lpc没有if (!userp(ob[i])) destruct(ob[i])这个具体写法、cmds/std/go.lpc没有sizeof(exit[arg])这个具体写法(它用的 是!mapp(exits)/undefinedp(exit[arg])检查,形状不同,不受 影响)。
AGENTS.md §8.3a variant fix (2026-08-27): feature/action.lpc::eval_function() and inherit/item/combined.lpc::destruct_me() wrongly private
Same targeted follow-up as libs/hell (see that lib's NOTES.md entry
of the same date for the full writeup) — libs/revive's own §8.3a
deep-test finding explicitly named zjmudhell as a sibling in the
hell/"Doing" lineage still carrying this bug. Confirmed present here,
byte-identical file layout and content to hell:
feature/action.lpc::start_call_out()'scall_out("eval_function", ...)was blocked by aprivate-declaredeval_function(), inherited into the player body viainherit/char/char.lpc->clone/user/ user.lpc— silently no-ops every delayed-callback effect/sleep/combat mechanism usingstart_call_out().inherit/item/combined.lpc::set_amount(0)'scall_out("destruct_me", 0)was blocked the same way, affecting everyCOMBINED_ITEMdescendant (money, thrown weapons, medicine powders, etc.).
Fix: dropped private from both declarations (function bodies
unchanged), matching the established revive/hell/§8.3a pattern.
Verified live: native build-debug driver, port 40204, using this
lib's custom mobile-app crypt-challenge login protocol (ZJKEY-based
handshake per this lib's own earlier §7.14 fix notes) to log in as the
existing fluffos/Mud@2026 admin account. goto'd to the
sleep_room /d/guanwai/xiuxishi and ran sleep: with the fix in
place, wakeup() fired correctly within its 12 + random(10) second
delay window ("你迷迷糊糊的睁开双眼,爬了起来。"), confirming
start_call_out()/eval_function() actually dispatches post-fix.
(Not independently re-tested with private restored on this lib —
hell's identical before/after test already demonstrated the failure
mode live, and this lib's source was byte-identical before the fix.)
debug.log clean of any 执行时段错误/Bad argument post-fix.
Incidental data/user/f/fluffos.o save drift from the test login
reverted via git checkout before committing.
深度功能测试(§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 zjmudhell --fix 加 scripts/lpc_fix_no_effect.py --apply,再用 scripts/lpc_nitan_family_fixes.py zjmudhell(adm/daemons/natured.lpc 的空语句 lt[LT_MON];),共改动 533 个 .lpc 和若干 .h。HEAD 与工作树分别加载(新进程):531 -> 531 通过,无回退;改过的头文件的 139 个包含者 138 -> 138 通过。
手工:28 处 Illegal to declare nosave function(adm/single/master.lpc、feature/treemap.lpc、adm/daemons/questd.lpc、dbased.lpc、network/cmwhod.lpc、cmds/arch/data.lpc 和四个卖吃喝的房间的 create_food/water/wine)。include/globals.h 有 #define nosave nosave,nosave 成了宏,扫描工具认不出这个关键字,于是没自动改;照 KB 04 §6.10 的做法手工改成 protected。这些函数都只被本对象(call_out、sort_array 的回调)或驱动程序(crash())调用,没有别的对象 -> 调它们。
启动时警告 71 -> 0。新进程登录画面正常;普通 telnet 输入名字后答"客户端非法"——本库用指间 MUD 的客户端握手(KB 01 §1.7),HEAD 也一样。
深度功能测试(§10.7,2026-10-11):“无副作用表达式”警告复查
cmds/wiz/who1.lpc:输出数组建好前一行孤立的分隔线字符串(与 wxddym 同码),删去,行为不变。载入无回退。
深度功能测试(§10.7,2026-10-12):编译警告收尾
能编译的文件里的警告 3 -> 0(lpc_warnings.py zjmudhell 重扫:7240 个文件可编译,剩下的诊断都在本来就编译不过的文件里)。与 zjdyzj 同码:zjdyzj 当天的修复(提交 15d40f6ba6a)改的 3 个文件在这里都是 token 级孪生,用 lpc_twin_port.py adopt 搬过来;ZJ 登录协议代码没有动。
真问题: clone/misc/swmuding.lpc 神木王鼎自己声明了一张 13 种毒虫的 mapping insects,可读表的是基类 clone/misc/muding.lpc 的函数,它们读的是基类那份普通木鼎表,叶子的表从来没用上。改名 sw_insects,在 create() 里赋给基类的 insects。
其余: feature/food.lpc、feature/liquid.lpc 的 do_effect() 改为 varargs int do_effect(object me) 并 return 1;,与 eat/drink 读取返回值的重载(d/guanwai/obj/wan.lpc、d/xiakedao/obj/pigpart.h)同型。
验证:改动文件、所有继承 F_FOOD/F_LIQUID 的物品、包含 pigpart.h 的文件、eat/drink 命令共 206 个对象,HEAD 与工作树各用新进程载入:203 -> 203 通过,regress 0;删除声明审计 0 项待看。206 个对象载入无回退。
负数伤害当作消耗(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_*(达摩)中毒状态都载入失败。
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。2 处补上 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):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)。
技能名拼错:技能文件里拼错自己的名字(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)。给映射赋值的写法未动。
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 载入不了,刷出它的房间也少一个人。本库目前没有对象设了缺失的武功;三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。(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 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 12 处、9 个文件: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 处)、zhoangnan-zhi→zhongnan-zhi(1 处)、jinggang-zhi→jingang-zhi(1 处)。(scripts/lpc_fix_skill_names.py)
区域物件的零碎修正(2026-10-19,KB 06 §7.267–§7.271)
feature/dbase.lpc的 ZJ 出口提示在目的地不存在时不再中断set("exits/…")(catch住data->query("short"))
逐个物件与 HEAD 对比载入,没有退步。
标准整理轮(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)。
旧驱动的类型宽松:缺省参数、无类型形参、数组初始化、class 变量(2026-10-19,KB 04 §6.19、§6.20)
原来的 MudOS 把调用少传的参数补 0、把没写类型的形参当 mixed、不检查变量的声明类型与初始值,也允许变量叫 class;这个驱动在编译时全部拒绝,整个物件载入失败。修了 1 个文件:/kungfu/class/generate/girl.lpc(scripts/lpc_round_files.py 的修复轮:调用处补写 0、无类型形参补 mixed、string X = ({...}) 补星号、void 函数的返回值,以及 class 变量改名 pick_class)。逐个文件在 HEAD 和改后载入对比:这些文件里 1 个原来载入失败、现在载入,没有退步。
从 work/ 里删掉 tar 的 PaxHeader/ 记录(2026-10-19,KB 02 §2.2)
压缩包的制作者把 tar 的 pax 扩展头记录(49 path = ...、20 ctime = ...,每个不超过 262 字节)当成文件打进了包里,放在 PaxHeader/ 目录(有的嵌套两层)。转换时 .c 结尾的被改名成 .lpc,在扫描里成了编译不过的“物件”。这些是元数据,不是 mudlib 内容:2 个文件从 work/ 删除(raw/ 里仍然有)。
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 编码器等少数几处手改)。改动:/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 处)、/adm/single/importer.lpc(1 处)。
get_config(__MUD_PORT__) 经旧版 runtime_config.h 得到字符串(2026-10-20,KB 06 §7.302)
本库的 include/runtime_config.h 是老 MudOS 的编号(整数设置从 15 起,本驱动从 256 起),get_config(__MUD_PORT__) 读到的是另一项设置(字符串),(int) 又只是编译期标注,于是 socket_bind(fd, LOCAL_PORT()) 报 Bad argument 2 to socket_bind(),版本守护进程 port = get_config(__MUD_PORT__) + VERSION_PORT 是字符串相加。头文件自己的注释写明 get_config(__MUD_PORT__) == __PORT__,所以代码里的这个调用(连同外面的 (int) / to_int())改成了驱动的预定义 __PORT__(scripts/lpc_fix_mud_port.py)。改动:/include/getconfig.h(1 处)。
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 个文件)。
注册到不存在函数的 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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 7 条注册(4 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/adm/npc/referee.lpc(do_halt)、/d/beijing/hg_w.lpc(do_body)、/d/city/npc/angelus.lpc(do_level_up、do_up_skills、do_up_tianfu)、/d/tulong/yitian/npc/obj/ling2.lpc(do_du)。最多的函数名:do_halt 2 处、do_body 1 处、do_level_up 1 处、do_up_skills 1 处、do_up_tianfu 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.354)
MACRO->函数() 调用的函数在这个库的守护进程里不存在:call_other() 对不存在的函数不报错、只返回 0,所以这些功能(时间显示、昼夜事件、名字检查、价格文字……)一直没起作用。同一血统另一个库的同名守护进程里有这些函数,原文照抄到本库守护进程末尾(scripts/lpc_port_function.py;调用处传的参数个数、函数体用到的函数 / 全局变量 / 宏 / 字符串键都在本库里有,已核对;守护进程在 HEAD 和工作区都能加载,每个函数用固定参数各调用一次:HEAD 返回 0,现在返回正常的值):/adm/daemons/chinesed.lpc ← hhsj:find_skill()(1 处调用)。
读不到的留言板:"/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/honghua_b;/d/hangzhou/liuhed.lpc)注释掉并写明原因(别的房间里同样的语句本来就是作者自己注释掉的)。证明和 45 个库的整体加载检查见 KB 06 §7.357。
汉字按字节切片的老写法没有换成按字符:改法名 "澄" + 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:40);name[2..3] → name[1..1](/kungfu/class/shaolin/cheng.h:164);chk[0..1] == "你" → chk[0..0] == "你"(/adm/npc/ganjiang.lpc:532)。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:1735)。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 两字同盟名得到 【 兄 弟 】。关键对象加载通过。
中文名 / 验证码仍按 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.10 未知 pragma save_binary/optimize)
删除本库中 FluffOS 不认的 #pragma save_binary / #pragma optimize… 行。