info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
笑傲江湖(最终版)是以金庸小说《笑傲江湖》为背景的武侠 MUD,属于 TMI-2/ES2(Falcon 系)引擎家族,在"笑傲江湖迷你版"(xo)几乎逐文件沿用的地图和无量派设定基础上大幅扩充而成,作者自称"自玩测试站",扩出了 10 座城市、16 大门派(少林、武当、峨嵋、昆仑、丐帮,以及慕容世家等跨作品客串门派)的完整世界;新玩家从"新手培训站"开始,逐步学习基本指令、领取新手礼包,再踏入江湖闯荡,比迷你版更照顾新手的引导设计;新角色开局 10 分钟内还不能存档退出,此时执行 `quit` 并二次确认不会保存进度,而是直接销毁角色——一个刻意防止刷号的设计。
English
A wuxia MUD set in Jin Yong's "The Smiling, Proud Wanderer," greatly expanded from this collection's Mini Edition (xo) into a much larger world of 10 cities and 16 sects (Shaolin, Wudang, Emei, Kunlun, the Beggars' Sect, and more, alongside crossover sects like the Murong clan). The author describes it as their own private test server. New players start at a dedicated newbie training station that walks them through basic commands and hands out a starter kit before they head out into the jianghu, and characters can't save-and-quit during their first 10 minutes of play -- quitting early destroys the character on confirmation rather than saving it, a deliberate anti-account-farming design.
README
内容亮点
- 详见上方简介:10 城 16 派的扩充地图、新手培训站引导、开局防刷号销号机制。
在线试玩
https://mudlibs.fluffos.info/xo_final/
管理员账号 / Admin account
- id:
fluffos - 密码 / password:
Mud@2026 - 中文名 / display name: 浮浮(Fluffos)
- 权限 / level:
(admin)—— 本 lib 的最高巫师权限(通过/secure/etc/wizlist授予,SECURITY_D->get_status()据此判定)。
警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。
本地运行
cd libs/xo_final
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40024。
NOTES · 移植与修复记录
xo_final — 笑傲江湖最终版1.2 (The Smiling, Proud Wanderer — "Final" Edition)
Archive: xo最终版1.2.rar. Port: 40024. Status: done (boots clean,
full registration flow verified end-to-end including a real Chinese name).
What this is
Same TMI-2/ES2 (Falcon) lineage as xo (#28), but the full, un-trimmed
"final" build — ~7,174 .c files vs xo's 1395 "mini edition". Same
secure/daemon/{master,sefun} layout. A homebrew test-station banner
("自玩测试站... 本lib是在mini xo基础之上,全新设计开发而成") confirms this
is a fork built on top of the mini edition.
Fixes applied
1. AGENTS.md §4 (lazy security-daemon load): applied proactively to
secure/daemon/master.lpc's valid_write/valid_read, same shape as
xo.
2. AGENTS.md §15h (GBK byte-range Chinese detection): secure/sefun/
chinese.lpc's is_chinese, system/daemon/band.lpc's IsLegalName,
and system/daemon/logind.lpc's length checks all fixed to the CJK
codepoint range / halved character-count bounds, applied proactively
from the catalog before first boot.
3. New: "comment eats next line" typo, found twice — a Chinese-text
comment line with no leading whitespace runs // right into the start
of the *next* physical line's code, silently deleting a function
declaration and leaving a dangling {:
- system/skill/basic/kongshou.lpc: // ...int is_native_skill()\n{
- d/menpai/shaolin/npc3/hui_quest.lpc: // ...if ( random(...) < 100 )
Both fixed by moving the comment to its own line above the code.
4. Lossy GBK→UTF8 conversion corruption: convert_lib.sh's iconv -c
pass silently drops genuinely-invalid byte sequences in some raw .c
files, occasionally eating a string literal's closing quote (breaking
syntax) and sometimes leaving behind invisible Unicode Private-Use-Area
characters (, ) that defeat exact-string Edit calls.
Surveyed the whole tree with iconv -f GBK -t UTF-8 against the RAW
pre-conversion files to find every instance (33 total flagged,
most cosmetic/non-fatal); confirmed and fixed the ~8 in system/skill
that actually broke compilation, reconstructing each closing quote
from context (sibling "action"/"lian"/"zhaoshi" fields' patterns):
mohe-zhi.lpc, yuanyang-dao.lpc, taizu-quan.lpc (two spots),
dagou-bangfa.lpc, dugu-jiujian.lpc, baye-zhui.lpc. The two with
hidden PUA characters needed Python line-index read/write instead of
the Edit tool's exact-substring match.
Interactive test result — full registration flow
Verified the complete registration path in one continuous connection (critical per the user's explicit instruction not to stop at "reaches a prompt"):
1. new → 欢迎光临笑傲江湖3 / English-name prompt.
2. English id (myxoidfull) → passes IsLegalID, reaches the
"确定吗(y/n)?" confirmation.
3. y → reaches the Chinese-name prompt.
4. Real Chinese name 赵云 → accepted (no rejection message),
proceeds straight to "请设定您的密码:" — this is the actual proof the
§15h fix works, not just that the prompt renders.
Also confirmed (as expected, not a bug): empty input at the English-name
prompt triggers a polite disconnect; BAN_D->IsTimeAllowed's anti-flood
throttle (3 real minutes between new registrations from the same IP,
enforced via an in-memory NewIps mapping in band.lpc, cleared by
restarting the driver) rejects a second new attempt from the same
source within the window with no output at all (die()'s error
message is commented out at logind.lpc — this is intentional silence,
not a swallowed error; don't mistake it for a bug when retesting).
lpcc sweep
Memory warning (new data point for AGENTS.md §6b): this ~7,174-file
lib drove the host to ~214MB free / 18.8GB RSS on the lpcc process
after ~12 minutes despite being far smaller than the previously-documented
"tens of thousands of files" nitan-family threshold — file count alone
does not predict memory blowup risk. Monitor free -h/process RSS on
*any* sweep, not just presumed-large ones, and kill proactively if
pressure gets severe. Reached a clean boot (zero compile errors in the
boot log) after the fixes above.
2026-07-23: driver rebuild retest + LPC formatter + WASM check
- Formatter: ran
tools/lpc-syntax'sformat-corpus.mjsover all 7174.lpcfiles inwork/; 7154 written, 9 already-conformant, 11 errors (files it refused to touch, expected/fine on legacy code). Confirmed the__OLD_ED__-compat rewrite insystem/feature/user/ editor.lpcand the (disabled)private/nosavecompatibility shim ininclude/globals.hboth survived the reformat unchanged in substance. - Native retest: rebuilt
~/src/fluffos/build-debug/src/driverbooted clean (zero fataldebug.logerrors). Ran a full end-to-end registration + play session in one continuous connection with a real Chinese name (赵云): English idzhaoyun→ confirm → Chinese name → password → email → gender all completed, dropped into the newbie training area (新手培训站);lookshowed the correct room,scoreproduced a correct full character sheet,quitcorrectly enforced the "10 real minutes before saving" gate. One operational note (not a bug): this lib'sBAN_D->IsTimeAllowedanti-flood throttle (documented in the original pass) meant a secondnewattempt shortly after a first one from this same host got silently ignored — resolved by restarting the driver (clears the in-memory throttle map) rather than waiting out the real-time window. - WASM: booted cleanly (only the expected non-fatal
socket_create/socket_bind/socket_connectpreload warnings from the missingsocketspackage). Full registration + play flow completed successfully end-to-end under WASM — real Chinese name 赵云乙, English id/confirm/Chinese name/password/email/gender all completed, dropped into 新手培训站 exactly like the native run;lookandquitboth worked correctly (10-minute save gate correctly triggered its confirmation prompt). Notably, this lib's ownenter_world()/registration chain — despite being near-identical in shape to itsxo(#28) sibling's — does not exhibit the world-entry hangxoshows under the identical WASM harness (seexo's own NOTES.md). Verdict: fully playable under WASM, no issues found.
WASM-enablement pass (loopback-allow / gate bypass / admin seed)
Standard WASM-first pass per AGENTS.md §1.3b/e and §1.5. Gates patched
(all in system/daemon/band.lpc, the live BAN_D — xoban.lpc is
orphaned/unreferenced, left untouched):
- Added
IsLoopbackSite(string)helper (loopback / empty / malformed-IP → local). IsBannedSite()(~line 126) — loopback short-circuitreturn 0;.IsMultiLogin()(~line 214) — loopback exempt (return 0;= allowed).IsTimeAllowed()(~line 244, the 3-minute per-IP new-registration throttle whose rejection used to look like a silent stall) — loopback exempt (return 1;= allowed). Verified: two back-to-backnewregistrations from 127.0.0.1 both succeeded.logind.lpc'sLOGIN_DELAYstartup gate is compiled out (macro never defined) — noted, no change.uptime() < 10there is cosmetic banner text, kept. The 10-minute new-account quit-retention prompt is a content timer, kept per policy.
Admin account seeded: id fluffos, pw Mud@2026, name 浮浮, granted
(admin) via fluffos (admin) appended to secure/etc/wizlist.
Verified: real registration (new/fluffos/y/浮浮/pw×2/email/m → landed in
新手培训站, auto-save fired), relogin as fluffos →
update /system/daemon/band → 成功, score correct. Retest: two fresh
normal registrations (秦风 m, 秦岚 f) end-to-end; test char saves
removed; zero new debug.log errors. Note: this lib's email prompt
actually validates format — [email protected] is rejected, use a normal-looking
address. Save file: work/data/user/f/fluffos.o (NOT gitignored —
normal add, no force-add needed).
Fail-closed retrofit for the loopback-allow gate (2026-07-24)
The loopback helper above was originally written matching the
project-wide convention at patch time (AGENTS.md §1.3b), which also
treated an empty/non-string/malformed query_ip_number() result as
loopback, defensively, because the WASM driver used to return garbage
there. That underlying driver bug is now fixed (fluffos commits
e33bb5da "fix: query_ip_number() returned uninitialized garbage under
WASM" and 007bb863 "feat: synthetic resolve() on WASM instead of
raising an LPC error", both 2026-07-23; the locally-built
build-debug/build-wasm binaries already postdate both commits), so
treating unparseable IPs as trusted is a fail-open gap with no remaining
justification. Retrofitted to fail-closed: loopback is now strictly
ip == "127.0.0.1" || ip == "::1" || ip[0..3] == "127." (with a
stringp() guard before the slice) — a malformed/empty IP now falls
through to the NORMAL gate instead of being treated as local. Retested
after tightening: fresh driver boot clean, fluffos loopback login and
its wizard update command both still work; zero new debug.log errors.
WASM 修复摘要(迁移自 meta.json 的 group_note)
同一血统,完整的'final'构建版本。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 13 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
深度功能测试 / Deep functional test (2026-08-07)
第一次真正的完整游玩测试(此前几轮都只验证了注册/look/score/quit
和管理员登录,没有实战、门派、持久化)。测试角色 id xofdeep,中文名
云梦仙,拜入峨嵋派后法号"静仙"。本轮 WASM 未重新验证:emsdk 工具链下
载硬编码指向 storage.googleapis.com,本次会话的出口代理策略性拒绝该
域名(403,已用 curl $HTTPS_PROXY/__agentproxy/status 确认是策略拒
绝而非临时故障),本地无法构建 WASM 驱动。
本档案体量巨大(~7,174 个 .lpc 文件),原生 debug(ASAN/UBSAN)驱动
下几乎每条指令第一次触发某个尚未编译过的文件时都会有数秒到十几秒的
延迟(不是死锁,只是懒编译开销大),测试过程中多次原样等待而非误判
为卡死。
发现并修复:toptend.lpc 的排行榜数据文件缺失时,每一次玩家登录都会崩溃(AGENTS.md §7.54 已知模式的又一实例,触发原因是错误的 file_size() 判断值而非完全没有判断)
- 症状:
debug.log(首次触发时,修复前)记录了一条完整的登录期崩 溃:
``
*Bad argument 1 to explode()
Expected: string Got: 0.
函数调用堆栈:
get_passwd() /system/daemon/logind.lpc:235
enter_world() /system/daemon/logind.lpc:607
topten_checkplayer() /system/daemon/toptend.lpc:40
topten_add() /system/daemon/toptend.lpc:354
`
logind.lpc 的 enter_world()——每一次玩家真正进入游戏世界都会调用
——无条件调用 toptend_checkplayer(),后者又为年龄/杀人数/内功/拳
法/轻功/剑法/刀法/短兵刃/长兵刃/经验/钱财/总纲这 12 项排行榜各调用
一次 topten_add()`。
- 根因:
topten_add()(第 344 行)用
``lpc
if (file_size(f_name) == 0) {
... // 写一份全新的排行榜文件
}
//主要程序!
...
str = read_file(f_name);
astr = explode(str, "\n");
`
只处理了"文件存在但大小为 0"的情况,没有覆盖file_size()返回 -1
(文件根本不存在)的情况——而 libs/*/work/data/topten/ 这整个目
录在项目的 .gitignore 里被标记为"可重新生成,不纳入版本控制"
(# Lib similarity/lineage analysis data -- regenerable via ...
一节)。也就是说这不是这份归档特有的缺陷,而是任何一次真正的全
新 git clone 都会必然触发:file_size()==-1(不等于 0)→ 落
入下面的"主要程序"分支 → read_file() 对不存在的文件返回整数
0(不是空字符串)→ explode(0, "\n") 抛出上面的错误。同一文件
里另外两处功能类似的读取(topten_query() 第 125 行、topten_del()
第 243 行)都正确地写的是 file_size(f_name) == -1`,证明这是那一
处判断值写错了,不是设计上刻意如此。
- 修复:把第 344 行的判断从
== 0改为<= 0,同时覆盖"文件不 存在"(-1)和"文件存在但是空"(0)两种情况——两种情况原本要走的 都是同一个"写一份全新排行榜"的回退分支,改动后不影响任何"文件已 有正常数据"的路径。§9格式化自检通过 ({"total":1,...,"unchanged":1,"errors":0}),3 处格式化盲点检查 干净。 - 验证:修复前用刚重启的驱动新建/重连测试角色,
debug.log每次 都稳定复现上面的崩溃(enter_world()是任何一次真正登录都会走的 路径,不需要特殊操作触发)。修复后重启驱动、清空的debug.log里,无论是新注册还是重新登录已有角色,全程零次Bad argument/explode相关错误。因为该驱动本身有"mudlib error handler"接管未捕获异常并继续执行的兜底机制,这个崩溃此前并不会中 断登录流程本身(角色仍能正常进入游戏),但会在每一次登录都往debug.log里写入一条完整的崩溃现场,而且意味着排行榜功能对一个 全新归档来说从第一次运行起就是坏的。已加入 AGENTS.md §7.54 作为 该模式的又一实例(同文件已有的姊妹判断用的是正确的== -1,是很 好的"检测正确写法就在同一个文件里"的例子)。
观察但未修改:库存列表中某个物品的 ANSI 颜色码首字节(ESC,0x1B)在发送给玩家的字节流里神秘缺失,怀疑是驱动层问题,未能定位根因
- 症状:新角色起始物品"百宝箱(Box)"在
i(inventory)指令的输 出里,颜色码HIG(ESC+"[1;32m")的 ESC 字节完全缺失,玩家实际 看到的是裸露的字面文字[1;32m百宝箱,而不是绿色高亮的"百宝箱"。 用绕开 tmux/nc 终端渲染的原始 Python socket 抓包核实过:这不是终端 渲染问题,服务器实际发送出去的字节流里确实少了那一个 ESC (0x1B) 字节——\r\n后紧跟的恰好是inventory.lpc自己硬编码的两个空格 前缀,然后直接是[1;32m的文本字节,中间没有任何 ESC。同一条i输出里,另一件装备物品的颜色码(HIM,ESC+"[1;35m")完全正 常,ESC 字节俱全;这件"百宝箱"自身的收尾重置码NOR(ESC+"[2;37;0m")的 ESC 字节也完整——只有这一处开头的HIG丢 了 ESC。 - 已排除的可能原因(逐层核实过源码/字节,均未发现问题): 1.
clone/misc/box.lpc源码本身(set_name(HIG "百宝箱" NOR, ...)) ——用od -c核实过字节完全正确,HIG/ESC宏也是正常引用。 2.include/ansi.h里ESC/HIG宏定义——同样od -c核实过,ESC内含真实的\033控制字节。 3.inventory.lpc的渲染逻辑(" " + $1->short())——纯字符串拼 接,没有做任何可能吞字节的处理。 4.system/feature/base/name.lpc的short()(box.lpc实际继承链上真正生效的那个)——只对id("box"→"Box")调用了capitalize(),从不touch颜色化的 name 字符串本身。 5.logind.lpc:522-523把百宝箱发给玩家的方式是普通的new("/clone/misc/box"); mb->move(user);,不经过command()/ 输入解析,不会碰到驱动里那个专门为"防止玩家在聊天里注入 ANSI 转义码"设计的、默认开启的输入端 ESC 过滤逻辑(comm.cc的on_user_input(),配置项no ansi/strip before process input)——而且那个过滤逻辑是把 ESC 替换成一个空格字符,不是整 字节消失;实测字节里\r\n后只有两个空格(正好等于inventory.lpc自己那个硬编码前缀的长度),不是三个,从字节 计数上也确认不是这条路径。 6. 编译期的字符串常量折叠(compiler/internal/ grammar_rules_exprs.cc里"str1"+"str2"字面量在编译期合并 那一段代码,NODE_STRING分支)——追踪了ScratchString/store_prog_string()/findstring()/alloc_new_shared_string()(base/internal/stralloc.cc)整条链路,这些都是基于strlen()/strcmp()的传统"NUL 结尾共享字符串"实现;我们这段 被拼接的字符串里没有任何嵌入的 NUL 字节,所以于理不应该被这条 链路的任何一环误判/截断。 同一条崩溃现场的调试信息(前一条 bug 修复前抓到的debug.logcrash trace,打印函数调用栈里各个参数对象的可读名字时)也显示了完 全一样的缺陷(/clone/misc/box#5 ("[1;32m百宝箱[2;37;0m")),说明 这不是"仅在i指令这一条输出路径上偶发",而是这个对象的name属性本身,无论从哪条打印路径读取,都已经缺了这个字节——更倾向于是 运行时字符串存储层面的问题,而不是某一处具体的输出格式化代码的问 题。 - 为什么没有继续深挖:排查已经超出了这份 mudlib 归档本身能覆盖 的范围——上面所有排除项都指向 fluffos 驱动(
~/src/fluffos,本项 目里近百份档案共用的同一份驱动代码)的 C++ 运行时内部,而不是这份 归档自己的.lpc源码或配置。要继续定位真正的根因,需要给驱动加 实际的运行时调试(断点/打印)并重新编译,这已经不是"读源码就能确 认"的范畴,而且即便确认了,修复也应该落在~/src/fluffos里并对 这个项目共用同一驱动的全部档案做回归验证,不是这一次单库深潜任务 的范围。如实记录为一个已充分排查、但未能定位根因的观察,代码未作 任何改动。
测试内容与结果
- 注册:
new→ 英文名xofdeep→ 确认y→ 中文名(云梦仙)→ 密码 ×2 → 邮箱 → 性别(f)→ 顺利进入起始房间〖新手培训站〗,help newbie(真正的新手指南入口是help start→newbie,help new只 是更新日志)、help menpai均正常显示门派列表。 - 门派/技能——组织路线(拜师):少林派(
d/menpai/shaolin)的两 个候选拜师对象(法堂执事慧觉、僧人虚音)都以"女施主不要跟贫僧开 这种玩笑"拒绝了女性角色——这是少林(僧人门派)刻意设计的性别限 制,不是 bug(少林派 NPC 的accept_teach/对话逻辑本身可以正常 compile 运行,只是业务逻辑拒绝)。改为峨嵋派(d/menpai/emei,历 史原型即为女性门派)——apprentice miejue shitai(灭绝师太,峨嵋 派第三代掌门人)一次成功,获得法号"静仙"、门派/师承/generation字段全部正确写入并在后续score、真实驱动重启后都保持一致。 - 技能——请教(qingjiao):
qingjiao lao cunzhang jiben-quanfa两词形式因为 NPC 别名"lao cunzhang"本身带空格、被sscanf("%s %s", master, skill)贪婪匹配成master="lao"(不存 在的单词,present() 找不到)而失败——但qingjiao jiben-quanfa from lao cunzhang的from语法(sscanf("%s from %s", skill, master),master部分是贪婪匹配到结尾)正常工作,是命令本身就 提供的两种写法之一,帮助文档里也写了两种格式都存在,不是 bug。 - 安全陪练/对练(duilian):
duilian miejue shitai——真实的、非 致命的对练交锋,命中会有轻微划伤但不会真的重伤/死亡;持续对练一 段时间后角色因为体能耗尽而"眼前一黑,接着什么也不知道了……"(昏迷) ,hp面板正确区分了"方格=消耗程度(体能条清空但很快恢复)"和"百 分数=内伤外伤程度(这里仍是 100%,代表没有真正受伤)"——帮助文本 (help hp)明确写了这个设计,不是 bug。system/feature/char/ damage.lpc的unconcious()会正确排一个 30+秒的call_out("revive", ...),实测约 1 分钟后角色自动苏醒、恢复行动能力,不是永久卡死。 - 持久化——异常驱动重启后重连:会话过程中容器/驱动两次意外重启 (与本次深潜无关的环境因素),其中一次重启发生在角色刚好处于"对 练昏迷"状态时存档——
disable_type属性的"<昏迷不醒>"字符串确 实原样保存进了存档文件,但重连登录时角色可以正常行动(score等 指令立即可用),并没有卡死:disable_type只在!living(who)(当 前真的还在昏迷状态)时才会被读取用于展示,登录流程本身走的是enable_player()重新挂载所有指令,跟这个残留属性值无关——是稳健 的设计,不是 bug。门派/师承信息在两次异常重启后都完整保留。 - 持久化——正常
quit后重连:quit正常触发存档提示("保存玩 家档案中…"),重连后score显示的门派、法号、天赋数值与 quit 前 完全一致;固定回到起始房间〖新手培训站〗而不是 quit 时所在房间 ——与本项目其它已测试档案的既定设计一致,不是位置持久化 bug。 - 管理员账号:
fluffos/Mud@2026登录成功(Wiz motd 正确显 示),update /system/daemon/toptend(本轮修复的文件)热更新成功 ("重新编译 ... 成功!"),确认写 ACL 正常。 - 观察但未处理(超出"仅修程序 bug"范围):
mygiftd.lpc里定义 的新手引导任务链(mygift指令,"了解如何开始玩游戏"等共 90+ 项 任务)依赖的"条件标识"(如newbie_mygift/help、newbie_mygift/ map……)在全档案里几乎从未被任何代码实际置位——仅有的两处相关引 用(maze/forest/out.lpc、system/daemon/zhoutiand.lpc)都是被 注释掉的MYGIFT_D->check_mygift(...)调用。也就是说这条任务链 第一步之后大概率永远无法自然达成、拿不到后续奖励。这更像是一个未 完工的内容功能(需要决定在哪些指令里补上check_mygift()调用, 这是内容/设计判断,不是"明显写错的一行代码"),如实记录为观察, 未作改动。
§7.100 扫描修复(ROOM 基类多余 replace_program())
#define ROOM STD_DIR "room/room"(STD_DIR = /system/std/,宏
名本身仍是字面量 ROOM,源码里的调用不受拼接影响):删除 615 处
多余的、独立成行的 replace_program(ROOM);(保留 inherit
ROOM;),全部由脚本自动删除。本库没有任何在游戏内建造房间的工具
(roommaker/rmmaker 等名称均未找到),因此没有"工厂"侧需要修
复。修复后全库仅剩 11 处历史遗留的 //-注释掉实例,均确认无害、
未改动。已用 build-debug 驱动干净启动验证(0 个新增编译错误,端
口 40024 正常监听,debug.log 无新增 "cannot replace"/"cannot
bind" 行);未做完整 §10.7 深度游玩测试。
深度功能测试第二轮 / round-four §10.7 (2026-08-20):真实战斗致死与重生
补全上一轮(2026-08-07)明确标注为"未测试"的缺口——真实战斗导致死
亡、进入地府、转世重生的完整闭环。角色 id xodeath,中文名死测生。
测试方法
system/std/char/character.lpc 的 heart_beat()(第 106 行)确认
了本库的死亡触发模型与本 session 已测过的 xyxy2/xajhxo 同宗:
eff_kee/eff_sen/eff_gin(不可自然恢复的"内伤"值,只被
receive_wound() 扣减)任意一项 < 0 时直接 die();kee/sen/
gin(可恢复的"体力"值)< 0 则只是 unconcious()(安全,30+ 秒后
revive())——这正是 duilian 陪练"不会造成伤害"的设计基础
(duilian.lpc 帮助文本明确写"对练不会造成伤害",它走的是完全独
立的 duilian_ob() 逻辑,根本不调用 receive_wound())。
用管理员账号 fluffos(本库已有的 (admin) 账号)通过
call xodeath->set("eff_kee",3) 把测试角色的"内伤"值预先调到接近
归零(正常新手默认 max_kee=165),再 call
xodeath->move("/d/place/newbie/lianwu_chang") 把角色传送到新手练
武场——这里除了安全的 duilian 指令外,也有可以真实 kill 的普通
NPC「陪练」(d/place/newbie/npc/pei_lian.lpc,非"陪练"专属安全对
象,未设 no_die/no_fight)。对 kill peilian 发起真实战斗(kill.
lpc 会调用双方 kill_ob(),NPC 真实反击),几回合后一次「陪练」
的真实命中(receive_wound)让 eff_kee 转负,下一次 heart_beat
触发真死:「你满身血迹,再也支持不住,一头撞倒在地上,腿蹬了几
下,眼见是活不成了。」——移动到 DEATH_ROOM
(/d/place/death/yellow1,黄泉路)。
之后角色(无携带物品,i 显示"你身上没有任何东西")按黄泉路系列
房间一路 north 走完全程:黄泉路→黄泉路(2)→鬼门关→酆都地府(1)→
酆都地府(2)→阎罗大殿,在阎罗大殿遇到"查察司判官"(即
d/place/death/npc/chacha.lpc)。该 NPC 的 init()→check_rein()
(新角色 age<=15 自动满足条件)→death_stage() 五段式对话状态
机在几次移动的间隔时间里自动跑完,最终 reincarnate() 清空 ghost
标记、把角色传送到三个随机复活城市之一(本次是魁星阁/扬州),
score 确认状态从"孤魂"变回"平民",且"你经历过一次死亡"的死亡计
数正确写入存档;quit/重新登录后死亡计数、门派/属性状态在真实的
保存-加载周期后依然一致。全程 debug.log 保持完全空白,零错误。
§7.112 死亡状态机重入守卫——现场验证,确认干净
按本 session 在 xajh2/xajhxo 上两次发现并修复的模式(
death_stage() 某个出口分支没清 death_stage_active 重入守卫,
把幽灵永久卡住),逐分支核对了本库仅有的两个同类状态机:
d/place/death/npc/chacha.lpc的death_stage(ob, stage):三个 出口——!ob||!present(ob)(清后 return)、stage==4 且身上还有 物品(清后 return,提示"你先要把你身上的东西放下来")、以及最终 完成分支(清后reincarnate())——全部正确清了death_stage_active,没有任何遗漏分支。d/place/death/npc/mengpo.lpc的tea_give(ob, stage):同样两 个出口(!ob||!present(ob)和最终发放"孟婆汤"完成分支)都正确 清了守卫。
两个文件都是干净的,不是本库的实例——这次实测(含真实经过两个状 态机)也没有触发任何卡死。
标准检查清单快速过一遍(均确认已修好/本来就干净,不再重新推导)
- §7.90(eval-cost):
config.fluffos的maximum evaluation cost已是 5000000000(远高于默认),本轮整段真实游玩(含跨文件 懒编译触发)零 eval-cost 相关 debug.log 错误。 - §7.100(
ROOM多余replace_program()):grep -rn '^\s*replace_program(ROOM)' work命中 0(历史 615 处已在上一节 修复;//-注释掉的 11 处历史实例未受影响)。 - §7.111(
standard_trace()/error_handler()对err["object"]为 0 的防护):secure/daemon/master.lpc的standard_trace()用%O格式化err["object"](对 0 安全),error_handler()调用file_name(err["object"])前已有if (err["object"])判空,本来就是安全写法。 - §7.112:见上一节,现场验证干净。
- §7.79(bare
addn()/addn_temp()2 参数):已在 AGENTS.md 该节的排查阶段确认本库有真实的本地/继承addn(system/feature/char/skill.lpc的set_wugong系是走正常函数 查找,不经过 simul_efun shim),bare 调用从未出问题,不在受影响 的 6 个库名单内。 - §7.108(
reconnect()应调用enable_commands()):system/daemon/logind.lpc的reconnect()→user->reconnect()→system/daemon/interactived.lpc的reconnect(who)→who->setup()(character.lpc的setup()内部调用enable_player())——链路完整;本轮测试角色多次真实断线重连 (quit/重登录),每次重连后指令都立即可用,未观察到 §7.108 症 状。 - §7.30(
mapping存取器返回未初始化int 0):system/feature/char/skill.lpc的query_jibie_map()等六个存 取器本身确实是裸return m_jibie;(无mapp(x)?x:([])三元防 护),但逐一核对了全库所有调用点(cmds/comm/{skills,display, hide,enable,disable}.lpc、system/daemon/{skilld,npcd,combatd, betrayd,giftd}.lpc)——凡是玩家指令可达的路径全部在用之前用sizeof()/mapp()做了防护;仅存的两处未防护索引 (combatd.lpc:1311的m_jibie[skill]、npcd.lpc:838的tell_skills()里keys(skills))在代码结构上只有当角色已经 学会并启用过至少一门武功时才可能触发——而那正好是set_wugong()→set_jibie()已经把m_jibie初始化成真实 mapping 的前提条件,二者互斥,理论上无法触发真实崩溃。本轮用全 新零武功角色实测score/skills/真实战斗全程零 debug.log 报 错,验证了这个分析——按项目"只修有错误信号的 bug"的红线,未作 改动。 combatd.lpc的bounce除零模式:grep -rln bounce work/system/daemon/*.lpc命中 0,本库没有这个变体。
结论
本轮唯一明确指派的缺口(真实战斗→死亡→地府→转世重生的完整闭环)
已完整走通,全程零 debug.log 错误,未发现新 bug;标准检查清单
七项全部确认干净/已修复。测试角色存档(xodeath)事后已清理,管
理员 fluffos 及背景 NPC 存档因驱动运行产生的正常增量已随本次提
交一并保存。
深度功能测试(§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)— here-document terminators
scripts/lpc_fix_heredoc_terminator.py --all --apply: 1 here-document block(s) repaired (1 with the terminator glued to the last text line, 0 with it indented), so End of file in text block no longer hides the room or object behind them: d/city/dali/huating.lpc. The terminator must start its line (the lexer's own test, KB 04 §6.10); a glued one lost its newline in the GBK -> UTF-8 conversion together with a half character, which stays out.
深度功能测试(§10.7,2026-10-07)— 兵器的 actions 函数指针
set("actions", (: WEAPON_D, "query_action" :));:旧 MudOS 把 (: 字符串, "函数" :) 当作 call_other 函数指针,本驱动把它当作表达式函数指针,求值只得到字符串 "query_action"。没有对应兵器技能时 feature/attack.lpc 直接用兵器的 actions,COMBAT_D 拿到的不是 mapping,reset_action() 后仍不是,记一条 bad action 就不出招——拿着这些兵器打不出攻击。改成别的兵器早已用的 (: call_other, WEAPON_D, "query_action" :)(KB 04 §6.10):d/menpai/wuliang/west/npc/obj/flower.h。改动的文件在 HEAD 与工作树分别加载(新进程),无回退。
深度功能测试(§10.7,2026-10-07)— 编译警告整理
scripts/lpc_warnings.py xo_final --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 178 个 .lpc 和 10 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):172 -> 172 通过,无回退;改过的头文件的 296 个包含者 295 -> 295 通过。
启动时警告 25 -> 0;新进程登录(输入名字)正常。
深度功能测试(§10.7,2026-10-08)— .lpc 后缀的定宽切片
.c 改名 .lpc 时只改了字符串没改切片宽度:f[<2..] == ".lpc" 拿两个字符比四个字符,永远不成立(!= 永远成立),按后缀筛文件、去后缀的代码因此从不运行或截成 x.l(KB 06 §7.238)。scripts/lpc_lpc_suffix_slice.py --apply 改了 2 处比较、0 处去后缀(如 secure/daemon/httpd.lpc);改动的文件 HEAD 与工作树分别加载(新进程),无回退。
深度功能测试(§10.7,2026-10-10)— 编译警告整理
scripts/lpc_warnings.py xo_final --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 3 个 .lpc(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):3 -> 3 通过,无回退。启动时警告 0 -> 0;新进程登录(输入名字)正常。
- 2026-10-10:编译警告第二轮(按驱动报告逐条修):按位 & 改逻辑运算 0 处,返回类型/原型 2 处,NPC+F_UNIQUE 的 init() 0 个,带初始化的未用局部变量 1 个,只在 #if 分支用的声明移进分支 0 个文件;改动 3 个文件,HEAD 与工作树各载入一次无回退(3 objects; HEAD PASS 3, tree PASS 3; regress 0; newly loading 0),这些文件的警告 3 -> 0。
深度功能测试(§10.7,2026-10-11):“无副作用表达式”警告复查
d/menpai/huashan/npc/dugu.lpc:两个只剩注释的if (special) { }、if (money) { }挪进注释(行为不变)。载入无回退。
编译警告收尾(2026-10-13)
scripts/toolkit_fix.sh 一轮去掉 2 处多余继承(d/pig/pigroom.lpc 等),模板与迷宫两类照 nt6 的改法搬来,其余手工,能编译的文件警告清零。改动的 23 个对象在 HEAD 与工作树各载入一次:HEAD PASS 23,tree PASS 23,regress 0。
- 华山风清扬
d/menpai/huashan/npc/feng.lpc自己写了int check_skill(string),用来挑他肯教的武功,可这个名字正好盖住system/feature/char/skill.lpc的check_skill()——基类每次set_jibie()/set_jilei()都靠它向KUNGFU_D检查武功名。于是风清扬身上的武功名从不被检查,给他设任何非剑法武功时还会说一句“我只懂剑法”。他的那个改名feng_check_skill(4 处)。 clone/user/templet.lpc(存档autoload转换器)继承F_DBASE后又自己声明dbase,存盘写出两条dbase,玩家下次载入可能读到空的那条(KB 06 §7.233,同 xo);删去自己的那个。- 六件随身物品(结婚戒指
clone/toy/marry/ring.lpc、丐帮布袋、青城hong、无量peijian、一品堂qiankunjian/riyuedao)继承了玩家用的F_AUTOLOAD(class Item *query_autoload()存一身装备),又自己定义int query_autoload()表示“我随身带走”。物品用不到F_AUTOLOAD的任何东西,AUTOLOAD_D读的是物品自己的 int;去掉那一行继承。 template/weapon/{blade,sword,whip}.lpc、template/gem/{gem,skull}.lpc把模板数据存进与TEMPLATE_OB同名的变量再用set_*()交给基类,maze/forest/maze.lpc的unique_rooms同理:叶子的变量改名my_*/forest_unique_rooms(同 nt6),行为不变。accept_object()同时来自master.lpc和npc.lpc(后者只return 0;)的五个 NPC、d/place/player/combine_unarmed.lpc的七个函数:写明用驱动原先选的那份(scripts/lpc_name_winner.py)。d/maze/newbie/npc/muren.lpc的duilian_ob()改void,与attack.lpc一致;d/pig/pig.h的PIG_D先#undef再指向同目录的守护进程;草稿system/feature/todo/poison.lpc的列表poison改名poisons(同 xo)。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。9 个文件 10 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断);x op= 浮点先把浮点转成整数再运算(系数小于 1 时 x 直接变 0),改为x = to_int(x op ...):/cmds/app/explore.lpc(power +=)、/cmds/skill/xiulian.lpc(jilei_gain +=)、/secure/daemon/userd.lpc(x +=)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。 - 2026-10-14:1 个存档文件(留言板 1 个;
data/board/cob.o)还是原档案的 GBK 编码,没有转成 UTF-8,restore_object()报Invalid utf8 string后对象以空数据启动(表情守护进程没有表情、留言板没有帖子、玩家无法载入)。scripts/lpc_save_encoding.py逐个按 GB18030 解码,结果按存档格式解析通过才写回(KB 03 §4)。
- 2026-10-14:2 个文件的函数声明为
int(或void)却返回字符串、对象(int query_seal() { return "…"; }),旧驱动照样把值交给调用者;改为实际返回的类型(scripts/lpc_fix_void_return.py)(KB 04 §6.1、§6.3)。新进程比对 HEAD 与工作树:2 个对象,新增载入 0,回退 0。
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。5 处补上 if (!mapp(fam)) fam = ([ ]);(刚 recruit_apprentice() 之后的除外)。
NPC 带着本库没有的物品,载入失败(2026-10-15,KB 06 §7.73)
12 个文件、15 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/kungfu/class/gaibang/obj/blackcloth、/d/city/lanzhou/clone/obj/cloth1、/d/city/dali/npc/obj/yifu3)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 12 个,改后 12 个(0 个原来载入不了),没有退步。
tuzhong1 里的全角逗号(2026-10-18,KB 03 §4.6)
d/zuzhi/yipin_tang/peacock/xuanwu_tang/npc/tuzhong1.lpc 的 set_name("徒众", ({ "tu zhong" , "tuzhong" })) 里一个逗号是全角的 ,(Illegal character 0xef),NPC 载入不了。换成半角 ,。载入:通过。
通向不存在房间的出口,按对面房间的回头出口改正(2026-10-18,KB 06 §7.257)
房间出口 "west": __DIR__"xxx" 指向的文件本库没有,玩家走过去就被卡住(笔误、改过名的房间,或者没带进档案的编号房间)。对面的房间存在、并且它相反方向的出口正好指回这个房间,说明出口原本就是通向它:/d/zuzhi/yipin_tang/peacock/xuanwu_tang/midao 的 down:yandong1.lpc → yandong。改动后每个出口都能解析到本库的文件(scripts/lpc_fix_dead_exits.py;目标在运行时本来就能载入的库——虚拟房间——不动)。
被注释吞掉的 inherit / include(2026-10-19,KB 06 §7.274)
/d/city/yangzhou/npc/obj/guo.lpc:#include <ansi.h>。这些指令接在上一行 // 注释的末尾(档案里丢了换行),成了注释的一部分,驱动看不到:缺 inherit 的技能报 Undefined function offensive_target,缺 #include <weapon.h> 的兵器报语法错误。scripts/lpc_fix_swallowed_directive.py 把换行放回去。载入检查 1 个文件,1 个原来编译不过、现在载入,没有退步。
补回丢失的标点(2026-10-19,KB 03 §4.6)
转换时丢掉的分号、逗号、引号或文件末尾的 },共 4 个文件,修前 HEAD 载入失败。别的库里同路径的完好副本只比这里少几个标点,按它补回(括号里是那个库):/d/zuzhi/yipin_tang/dixia/shuangqi.lpc(xajhxo)、/d/zuzhi/yipin_tang/dixia/suni.lpc(xajhxo)。一行文字在逗号前丢了结尾的引号(下一行是同一个表的下一项),补上:/d/menpai/wuliang/npc/zuo.lpc、/d/menpai/wuliang/west/npc/xin_shuangqing.lpc。修后与 HEAD 逐个对比载入:这 4 个物件全部载入,没有退步(scripts/lpc_restore_from_twin.py、lpc_fix_eaten_quote.py --comma、lpc_fix_eof_braces.py)。
补回标点后新载入的物件里的未使用局部变量(2026-10-19,KB 04 §6.10)
之前载入失败、补回丢失的标点后才载入的物件里还有未使用的局部变量警告,共 1 个文件:/d/menpai/wuliang/west/npc/xin_shuangqing.lpc。删掉没有用到的声明;用 scripts/lpc_listcheck.sh 对比 HEAD 和改后,物件仍然载入,没有警告。
旧驱动的类型宽松:缺省参数、无类型形参、数组初始化、class 变量(2026-10-19,KB 04 §6.19、§6.20)
原来的 MudOS 把调用少传的参数补 0、把没写类型的形参当 mixed、不检查变量的声明类型与初始值,也允许变量叫 class;这个驱动在编译时全部拒绝,整个物件载入失败。修了 2 个文件:/d/zuzhi/yipin_tang/peacock/xuanwu_tang/develope/boat.lpc、/d/zuzhi/yipin_tang/xuanwu_tang/develope/boat.lpc(scripts/lpc_round_files.py 的修复轮:调用处补写 0、无类型形参补 mixed、string X = ({...}) 补星号、void 函数的返回值,以及 class 变量改名 pick_class)。逐个文件在 HEAD 和改后载入对比:这些文件里 0 个原来载入失败、现在载入,没有退步。
set_wugong() 对没有文件的技能不再中止物件(2026-10-19,KB 06 §7.256)
system/feature/char/skill.lpc 的 set_wugong() 经 set_jibie() → check_skill() 对没有技能文件的名字 error(),授了这种技能的 NPC 在 create() 里中止(本库缺 baidu-dao、lingshe-shenfa、pikong-zhang、panshe-dao、jumang-shenfa、qinglong-zhua、ny-bufa 等,共 11 个物件授了它们)。同文件的 set_xiuwei() 早就用 if (!valid_skill(skill)) 绕开这一情形,set_wugong() 开头加同样一行:if (!valid_skill(skill)) return 0;,物件照常载入,只缺这一项技能(缺的技能仍是内容缺口)。修后在这 11 个物件和 system/menpai/ 的 9 个物件上对比 HEAD 与工作树:载入 18 → 19(weishi2),没有回归,没有警告;system/menpai/autoresetd.lpc 仍失败(读 /etc/roomreset,存档里没有这个文件)。
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/verb/mazemap.lpc(1 处)。
if (x = 常量):条件里写成赋值的比较(2026-10-20,KB 06 §7.290)
if (arg = "button" || arg = "按钮")、if (!arg || arg = "") return 0;、else if (check = 0 && tempB != ""):把 == 写成了 =,条件的值就是那个常量,分支对所有非零数和任何字符串(连 "" 都算)永远成立、对 0 永远不成立,变量还被覆盖;驱动对此没有任何警告。每一处 = 改成 ==(scripts/lpc_fix_cond_assign.py,共 1 处)。改动:/d/city/yangzhou/shufeng_guan.lpc(1 处)。
登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)
驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/secure/daemon/sefun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。
join、accept 对档案里没有的擂台房间调用函数(2026-10-20,KB 06 §7.270 / §7.288)
cmds/verb/join.lpc 和 accept.lpc 开头是 object tai = load_object("/d/place/biwu/biwutai"); if (!tai->query_temp(...))。比武大会的擂台房间 /d/place/biwu/biwutai 不在档案里(d/place/biwu/ 下的报名处、大厅、等候室和各门派擂台房都在,唯独缺它),load_object() 答 0,于是直接敲 join 或 accept 就是 Bad argument 1 to call_other()(本库的错误处理把它写进 /log/runtime/cmds,并对玩家说“运行时发生错误”)。两个命令现在先测 objectp(tai),房间不存在就按原来的话回答“现在不是报名/比武时间”。比武大会本身(vote ... start、巫师命令 biwu、reward_d 的 record())依赖同一个房间,仍然不工作,那是档案缺的内容,没有碰。实测(xo_final):join 答“现在不是报名时间,你不能去报名。”,accept 答“现在不是比武时间,你不能接收邀请。”,控制台没有错误;加载检查 2/2,回归 0。
closed 守护的心跳每秒调一次不存在的 versiond(2026-10-20,KB 01 §1.3(c))
closed.lpc 的 heart_beat() 以 if (! VERSION_D->is_version_ok()) return; 开头,本库没有 /adm/daemons/versiond:有人在闭关时,控制台每秒一条 call_other() couldn't find object '/adm/daemons/versiond',闭关的后续处理(load_all_users())也永远走不到。改成 if (find_object(VERSION_D) && !VERSION_D->is_version_ok())(KB 01 的写法:没有版本守护就当版本正常)。加载检查 OK。
巫师命令不带参数时对 0 调用 efun(2026-10-21,KB 06 §7.336)
巫师(管理员)把命令敲成不带参数时,驱动把 0 传给字符串参数;下面的命令第一句就把这个 0 交给 efun,运行时报 Bad argument 1 to ...,这条指令从来没有用过。用 scripts/lpcc_bare.py 不登录、在一次 lpcc 求值里以 0 调用 main() 逐个确认后,由 scripts/lpc_fix_bare_arg.py 在第一处使用参数的语句前加守卫:文件自带 int help(object me) 的,守卫是 if (!arg) return help(me);(help() 定义在 main() 之后的 1 个文件补了原型 int help(object me);,否则编译报 Undefined function help);没有 help() 的是 return notify_fail("…\n");。改动(3 个文件,括号里是接住 0 的 efun):/cmds/admin/empty.lpc(find_player)、/cmds/admin/testnpc.lpc(sscanf)、/cmds/imm/hbless.lpc(find_player)。加载检查 HEAD 与工作树对照,没有退步。
注册到不存在函数的 add_action,第二轮:宏路径父类和静态解析不了的对象(2026-10-21,KB 06 §7.349)
第一轮的静态解析读不了经宏路径继承父类的对象(#define ROOM STD_DIR "room/room" 这类定义的正文以另一个宏开头)和它解析不了的继承,这些对象当作 UNSURE 没有扫;第二轮修了解析器,重扫并对这些对象在运行时问 function_exists()。add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 17 条注册(11 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/city/yangzhou/yuelao_ci.lpc(do_divorce)、/d/city/yangzhou/yuelao_ci_bak.lpc(do_divorce)、/d/city/yangzhou/yuelao_ci_bak_xli.lpc(do_divorce)、/d/menpai/huashan/npc/xiaoer.lpc(do_buy、do_list)、/d/menpai/quanzhen/npc/seller.lpc(do_buy、do_list)、/d/menpai/wudang/npc/xiaoer.lpc(do_buy、do_list)、/d/menpai/xuedao/npc/xiaoer2.lpc(do_buy)、/d/place/newbie/npc/lao_tou.lpc(do_vendor_list)、/d/zuzhi/yipin_tang/peacock/xuanwu_tang/xuanya1.lpc(do_droop、do_pull)、/d/zuzhi/yipin_tang/xuanwu_tang/xuanya1.lpc(do_droop、do_pull) 等共 11 个文件。最多的函数名:do_buy 4 处、do_divorce 3 处、do_list 3 处、do_droop 2 处、do_pull 2 处。
(: 函数 :) 指针指向对象自己没有定义的函数(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/menpai/shaolin/npc/xuwen.lpc:ask_armor2、/d/menpai/shaolin/npc/xuwen.lpc:ask_armor3。
(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)
inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 1 个文件里 2 行只含这个指针的行("僧鞋": (: ask_armor2 :), "绑腿": (: ask_armor3 :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。
写死的 "/路径"->函数() 指向库里没有的文件:改成现存的同名文件(2026-10-22,KB 06 §7.359)
2 处语句级的 call_other 字面量路径,库里没有这个文件(区域改了名 / 大小写不对 / 差一个字母 / 文件挪了地方):"/cmds/arch/xplor"->QueryLevel()(/system/daemon/biwud.lpc)→ "/cmds/app/xplor"。call_other() 对不存在的对象抛 couldn't find object,所在函数当场中断。改成库里现存、并且定义了被调用函数的文件(scripts/lpc_fix_call_path.py;巫师目录、备份目录、名字不同的兄弟文件都不算候选)。
汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 8 处(2026-10-22,KB 06 §7.360)
原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字)。
本库 8 处(5 个文件):拼名字的赋值 8 处。例:name[0..1] → name[0..0](/d/menpai/emei/npc/tobebonze.h:41);name[4..5] → name[2..2](/d/menpai/quanzhen/npc/chu.h:80);name[2..3] → name[1..1](/d/menpai/quanzhen/npc/qing.h:119)。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 处(2 个文件):} else if (strlen(msg) > 16) {(/d/city/yangzhou/pplroomd.lpc:89);} else if (strlen(msg) > 40) //若长度在八个汉字以外,重新输入。(/d/place/player/newroom.lpc:86)。scripts/lpc_fix_strlen_gate.py 以提示为准:条件里每个 strlen 门槛都恰是提示所说数字的两倍才折半,存长度的变量(i = strlen(arg);)的门槛和 || i % 2 奇偶项、其后循环的 i % 2 == 0 && 一并处理;提示里有英文 / 字母 / 字节的不动。证明(scripts/lpcc_eval.py,bxsj 的留言板,求值对象继承 NPC 并重写 edit() 记录调用,command("post 标题")):HEAD 拒绝「你好」和「你好吗」、放行 23 个字的标题;改后接受前两个、拒绝第三个;shenmo 的 named.lpc invalid_new_name("张三"):HEAD 回 名字只能是2-4个中文字。,改后回 0。抽样 24 + 15 个库的 HEAD / 工作树加载对比 0 退步。
按字节写的汉字循环、茶馆猜诗和同盟频道标题(2026-10-23,KB 06 §7.363)
老档按字节做「一个汉字」:while (i--) if (i % 2 == 0 && !is_chinese(name[i..<0])) 只检查偶数下标,奇数位上的拉丁字母(张a)会过;茶馆猜诗 mixup() 用 strlen(str)/2 再 j=j+j、str[k..k+1] 交换两字节;泥潭系 channeld 的 switch (strlen(fname)) case 4/6/8/10 和 strlen(family)==4/6 按字节长度给同盟 / 门派名加空格。本驱动的 strlen() 和区间都按字符算。scripts/lpc_fix_byte_char.py:去掉奇偶项、把区间改成 name[i..i],按字符重写 mixup() 并把 strlen(first)>=14 折成 7,频道标题的 case / 门派长度改成字符数(对照 aoxiangtianji)。本库 汉字循环 / is_chinese 区间 2 个文件。证明(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 是两字符牌码,未改。
estate ROOM_HEAD 宏引号 / #inherit / dir / retrun(2026-10-27,KB 04 §6.25 Also)
system/daemon/estate 的 ROOM_HEAD/EXIT_STRING 宏里裸双引号打断 #define,模板写成 #inherit AREA,CreateRoom 用未定义的 dir,AddItem 写 retrun 且参数 class Location 本库无此类型。改为转义引号、inherit AREA、out_dir、return、mixed location。证明:HEAD 失败;改后加载。
深度功能测试(2026-10-27,§6.25 tell_object 字符串目标)
pplroomd:tell_object("ob", …)→tell_object(ob, …)。
深度功能测试(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… 行。