Fingertip MUD

✅ 可玩

指尖MUD

zjmudhell

🔑 fluffos / Mud@2026 更新 66a3bd8 2026-10-28 源码 下载 ZIP

▶ 开始游玩 · Play Now

《指尖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

内容亮点

注册流程(手机 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/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)

管理员账号 / Admin account

管理员名单存储在纯文本文件 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 自己的代码里单独核实。

§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,不是运行时报错)。

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:

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 同源的三处编译警告

验证:改动的文件和包含 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):“无副作用表达式”警告复查

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

逐个物件与 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… 行。