Immortal Companions' Bond: Zhejiang University Edition

✅ 可玩

仙侣情缘·浙大版

xlqyzdb

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

▶ 开始游玩 · Play Now

仙侣情缘·浙大版由 bugbug 与 alading 在"缥缈水云间"站点基础上二次开发,是一个西游记题材的仙侠世界:从长安城的南城客栈开始冒险,逐步领悟"道行境界""武学境界""法力修为"等修行体系,整体基调偏神话/仙侠而非纯武侠,人物成长围绕"西天取经""大闹天宫"等西游记经典桥段展开,注册流程还有一道"是否为中小学生"的年龄限制问答(回答 no 才能继续),新角色可分配体格/根骨/悟性/灵性四项天赋属性,或直接接受系统默认值;经逐字节比对确认,本档案与本项目另收录的"仙侣情缘(早期测试版)"(xlqy_early)、"新仙侣情缘之飘渺纪元"(xlqy_new2007)公共路径下 84%-86% 的文件完全相同,其实是同一位作者笔下同一个世界观的三个开发快照,而非仅仅共享引擎;与更早的 2001 年"知秋站"存档(xianlvqiyuan)则是更远的同源近亲,文件层面的重合度只有约 10%,与那份存档自身更贴近原始代码的情况相符。

English

Developed by bugbug and alading as a second-generation build on top of the "Misty Waters" server, this is a Journey-to-the-West-themed immortal/mortal romance world: adventures begin at a Chang'an south-city inn, and characters progress through named cultivation stages (Taoist attainment, martial-arts mastery, magical-power cultivation) toward a scripture-seeking, havoc-in-heaven-style arc, with an age-restriction question gating registration. A byte-level file comparison found this shares 84-86% of its common-path files with this collection's "Immortal Companions' Bond (Early Test Build)" (xlqy_early) and "New Immortal Companions' Bond: Age of Mist" (xlqy_new2007) — genuinely the same authored game world across three development snapshots, not just a shared engine. It is a more distant relative of the 2001 "Zhiqiu-station" snapshot also archived here (xianlvqiyuan) — same overall setting and engine lineage, but only about 10% file-level overlap, consistent with that archive's own earlier, closer-to-original codebase.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/xlqyzdb/

管理员账号 / Admin account

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

本地运行

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

游戏端口:40033。

NOTES · 移植与修复记录

xlqyzdb — 仙侣情缘 "浙大版" (ZJU fork)

Archive: 仙侣情缘浙大版.rar. Port: 40033. Status: done (boots clean with a trimmed preload list, full registration flow verified end-to-end including a real Chinese name).

What this is

A modified fork of "仙侣情缘"/XLQY by bugbug & alading at 缥缈水云间 (ZJU — Zhejiang University), circa 2003-4-5, based on a branch closer to xlqy_new2007 (archive #26, chinese.c byte-identical) than to xianlvqiyuan's (archive #38) 2001 original — though logind.c/ master.c/securityd.c/convertd.c all differ from both, so this needed its own diagnostic pass. adm/obj/{master,simul_efun} layout. Archive required nested extraction (.rar → xlqy.tar.gz → tar xzf). ~9,206 raw files, 7,952 after .c→.lpc rename.

Fixes applied

1. Standard §15h: is_chinese() single-char CJK rewrite; check_legal_name() bound < 2 || > 12 → < 1 || > 6, removed the i%2==0 gate (name[i..<0] slice was already clean). 2. §15o insurance: added get_include_path() to master.lpc. 3. §8h recurrence: convertd.lpc's Greek-table stray-backslash typo, 45 occurrences, fixed with the standard sed pattern (no CRLF present). 4. §15g/k-adjacent hardening: adm/simul_efun/file.lpc's cat() hardened against a missing file (same fragile write(read_file(...)) pattern as xianlvqiyuan, though the BANNER file itself was already correctly lowercase in this archive — applied as insurance regardless). 5. A long boot-hang investigation, resolved pragmatically per explicit user direction ("DNS / inet daemon should be generally disabled" / "It's not our target fix this round"): the full boot (with the complete original adm/etc/preload, 24 entries) took an extremely long time — observed via instrumented waits showing the driver spending the overwhelming majority of wall-clock time blocked (e.g. 13 seconds of accumulated CPU time over 9+ minutes of wall-clock), consistent with one or more blocking/slowly-timing-out network calls scattered through preload and early object loading. Initially suspected and partially fixed adm/daemons/network/dns_master (an intermud DNS/mudlist daemon whose create() calls resolve() + socket_create()/socket_bind() to bootstrap a cross-mud database from a hardcoded remote server) — commented out its networking calls and removed it from preload. This did NOT fully resolve the slowness on its own (a full-preload retry still took many minutes), and rather than continue open-ended bisection across the other ~20 preload daemons, applied the pragmatic fix directed by the user: trimmed adm/etc/preload down to only the entries needed for the registration flow itself (securityd, band, virtuald, logind, cmd_d, chinesed, convertd) and excluded the rest (emoted, aliasd, fingerd, channeld, monitord, natured, weapond, rankd, combatd, miscd, spelld, storyd, choosed, dns_master, locationd, feizeid, auto_cleard — combat/quest/ location/social systems, not needed to test registration). With this trimmed list the driver booted in well under 15 seconds. This is a deliberate scope reduction for testing purposes, not a full restoration of the original mudlib's feature set — if this lib is ever run for real play rather than registration-flow verification, the excluded daemons should be re-added one at a time and checked individually for their own preload-time cost/blocking behavior.

New standing policy (see AGENTS.md)

Per explicit user direction, proactively check every future lib's adm/etc/preload for a DNS/intermud/network daemon and exclude it BEFORE the first boot attempt, rather than waiting to discover a boot hang first.

Interactive test result — full registration flow

With the trimmed preload list, verified the complete registration path in one continuous connection (same flow shape as xianlvqiyuan): gb → no (not a student) → new → xlqzda (English name) → 秦风 (real Chinese name) → accepted, proceeds to "请设定您的密码:".

lpcc sweep

7,952 files, 7,824 pass / 128 fail (98.4%). Failure tail is the usual shape (missing globals/inherits, a handful of syntax typos) — not triaged individually per AGENTS.md §6b/§13. Memory stayed healthy throughout (~14-15GB free).

Re-verification pass (2026-07-23)

Extended the interactive test past the password prompt through full registration (id xlqzdgz, name 秦江), gift confirmation, look, score, and quit — all completed correctly with real content (entered the game world at 南城客栈/"South City Inn" with real NPCs, score rendered the full character sheet, quit showed the real flavor-text quit sequence).

Investigated but not fixed — the "default error message" anomaly is real, reproducible, host-load-dependent, and non-blocking. The transient 你发现事情不大对了,但是又说不上来。(config's default error message) noted as a one-off, non-reproduced anomaly in the sibling lib xianlvqiyuan's NOTES.md reproduces HERE consistently but with a highly variable count per connection (observed 0, 3, and up to 18+ occurrences across repeated identical test runs against the same unmodified code). Instrumented adm/daemons/logind.lpc's enter_world() with temporary write() markers (removed after diagnosis, per the project's established §8c/§15d technique): confirmed the errors are scattered across several *different*, functionally unrelated calls in the same stretch of code (new MAILBOX_OB creation, "/adm/daemons/ipd"->seek_ip_address(), CHANNEL_D->do_channel(), UPDATE_D->check_user()) rather than one single repeated bad call — inconsistent with a single deterministic bug in any one of them. Also confirmed via a temporary instrumented master.lpc's error_handler() (writing standard_trace()'s full text to a scratch file, removed after diagnosis) that the underlying real error text never gets captured — not in debug.log (checked extensively, zero matches even with markers correlating the exact moment), not in the instrumented scratch file either (it was never even created, meaning error_handler()'s own write_file() produced no output during the very runs where the friendly message WAS shown to the player — inconsistent with a straightforward reproducible LPC-level error). Combined with the count varying run-to-run against byte-identical code, this points to a host-load/timing-sensitive artifact (this sweep runs many sibling agents' driver processes concurrently on a shared, resource-constrained host) rather than a deterministic mudlib bug — plausibly related to eval_cost/timer edge cases under CPU contention (see AGENTS.md's set_eval() gotcha for a similar class of timing-sensitive symptom). Not chased further: it never prevented registration, look, score, or quit from completing correctly in any of the ~6 test runs performed (worst case, extra noise the player has to scroll past), and matches the sibling lib's own precedent of being noted-but-not-investigated. Flagging here in more detail than the original one-line note in case a future pass sees it escalate to something that actually blocks a flow — this pass didn't observe that.

Driver rebuild / formatter / WASM pass (2026-07-23)

WASM-enablement pass (2026-07-24)

Standard four-change pass (AGENTS.md §1.3b/§1.3e/§1.5). Gates patched:

1. Loopback always allowed — adm/daemons/band.lpc: added a shared is_local_site(site) helper (true for 127.0.0.1, leading 127., empty/non-string, or malformed non-dotted-quad IPs = WASM garbage) and short-circuited is_banned(), is_strict_banned(), and create_char_banned() with it (all three take an IP string; covers the two is_strict_banned login gates in logind.lpc logon()/ encoding() and the create_char_banned/is_banned guest-jail check in enter_world()). 2. IP-format checks bypassed for loopback — adm/daemons/logind.lpc encoding() (~line 190): the !ip_name destruct and the every-char-must-be-digit-or-dot loop over query_ip_number() (both would kill any WASM connection with a garbage IP) now only run when band->is_local_site() is false. 3. Anti-flood throttles exempt loopback — logind.lpc logon(): (a) the 5-second same-IP reconnect throttle (last_ip + time()+5) and (b) the logon_cnt > 10 per-IP concurrent-connection cap now skip local connections (new is_local flag); (c) the MAX_LOGIN (=5) per-IP multi-login cap in get_id() (~line 360) skips local connections too. 4. Uptime startup gate — none exists (the uptime() use in encoding() only ages out the newid registration-in-progress temp map every 300s); nothing to bypass. The per-id newid in-memory throttle ("已经有人在注册这个id了", cleared every 5 min / on restart) was left as-is — it is per-id not per-IP and only affects re-registering the same id twice within 5 minutes. 5. Admin account seeded — id fluffos, pw Mud@2026, name 浮浮, registered via the real flow (gb → no → new → id → Chinese name → password x2 → email → gender m, then the 西游记-style gift screen at first world entry: 9 accept → y confirm). Granted (admin) via adm/etc/wizlist. Verified: banner shows (admin), wizard update /adm/daemons/band recompiles successfully, character finalized (no_gift flag cleared, lands in 南城客栈).

Save files (untracked, NOT gitignored — orchestrator must git add):

Retest: fresh normal registration (id qinfzd, 秦风) works end-to-end (gift screen → world → score renders → quit clean); fluffos login + wizard command works; debug.log has no new errors. One pre-existing, unrelated error surfaced once in a transcript: lazy-loading adm/daemons/emoted hits restore_object(): Illegal mapping format on the archive's own corrupt /data/emoted seed save (§7.7 class; emoted is excluded from the trimmed preload) — cosmetic, command still dispatched, not introduced by this pass. Test char saves removed. Note: id length limit is 3-8 chars (fluffos = 7 fits).

Fail-closed loopback retrofit (2026-07-24)

Security correction, applied retroactively. adm/daemons/band.lpc's shared is_local_site() helper (item 1 above) originally treated an empty/non-string/malformed IP as "local" (fail-open) — a stopgap for a since-fixed WASM driver bug. Tightened to fail-closed:

int is_local_site(string site) {
  if (!stringp(site)) return 0;
  if (site == "127.0.0.1" || site == "::1") return 1;
  if (strlen(site) >= 4 && site[0..3] == "127.") return 1;
  return 0;
}

An unparseable/empty IP now falls through to the normal (non-exempt) path everywhere this helper is consulted — is_banned(), is_strict_banned(), create_char_banned() in band.lpc, and every logind.lpc call site that gates on it (the IP-format check in encoding(), the anti-flood throttles in logon()/get_id()). No other call site needed a separate edit since they all delegate to this one helper. Retested: fresh registration (id xlqztwo, name 秦岭峰) still completes end-to-end via loopback (gift accept → world → look/score render correctly → quit); fluffos login + update /adm/daemons/band still succeeds (重新编译 /adm/daemons/band.lpc:成功!). Zero new runtime errors. Test char saves (xlqzgat/xlqzone/xlqztwo) removed after verification.

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

浙大分支的 XLQY。状态已从过时的 limited 修正——这份档案自己的 README 和 group_note 里从未记录过任何缺陷说明,本轮重新测试也没有发现:XLQY 家族里 xianlvqiyuan 的手足档案。管理员登录干净正常:GB/BIG5 选择→未成年人门槛(否)→id+密码→'您的系统权限目前是:(admin)'。

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

深度功能测试 / Deep functional test (2026-08-07)

第一次完整游玩测试(原生驱动 build)。测试角色 id zdbtest,中文名 云梦仙。本轮 WASM 未重新验证:emsdk 工具链下载硬编码指向 storage.googleapis.com,本次会话的出口代理策略性拒绝该域名(403,用 curl $HTTPS_PROXY/__agentproxy/status 确认是策略拒绝而非临时故障), 本地无法构建 WASM 驱动。

发现并修复:注册流程遗留的 printf("%O", ob) 调试输出(AGENTS.md §7.34 已知模式的又一实例)

adm/daemons/logind.lpc 中文名确认成功分支有一行未加注释的 printf("%O\n", ob);(紧接在 ob->set("name", arg); 之前)。同一档案 里还存在 loggind.lpc、login.lpc 两份带同样调试输出的旧文件,但通过 include/globals.h 的 #define LOGIN_D "/adm/daemons/logind" 确认它们 未被任何地方引用,是死文件,未做改动。按 §7.34 既定修法直接删除 logind.lpc 里的这一行。修复前注册第一个测试账号时确认能看到裸露的 对象路径夹在提示语之间;修复后(本轮实际注册)未再出现。§9 格式化 自检通过({"total":1,...,"unchanged":1,"errors":0})。

发现并修复:emoted 守护进程未捕获的 restore() 抛错,导致该守护进程永久残废(AGENTS.md §7.87 已知模式的又一实例,触发原因不同)

`` 执行时段错误:*restore_object(): Illegal mapping format while restoring emote. 程序:/feature/save.lpc 第 18 行 物件:/adm/daemons/emoted 呼叫来自:/feature/command.lpc 的 command_hook() 第 85 行,... 呼叫来自:/adm/daemons/emoted.lpc 的 create() 第 48 行,... ``

``lpc void create() { if (!restore() && !mapp(emote)) emote = ([]); } ` 当 restore() 抛出(而不是返回假值)时,if 语句的 fallback 赋值永远不会执行,emote 全局变量从此在整个进程生命周期里都是 0。 但这次的触发原因和 §7.87 原始实例不同:那次是 maximum read file size(300000)小于存档实际大小(328298 字节);这里 data/emoted.o 只有 262239 字节,本档案的 maximum read file size 同样是 300000,明显没超限——debug.log 报的是Illegal mapping format, 说明这份存档本身的映射语法就是驱动的 restore_object() 解析不了的 真实损坏数据(AGENTS.md §7.7 记录的"损坏存档可以合法地导致 restore() 失败"的另一种损坏形状,不是资源限制)。因为 create() 只在进程内第一次引用该对象时跑一次,且这次触发点是玩家 一次普通的移动指令(不是显式的 emote 指令),debug.log 里也确实 只留下这一条记录,此后同一进程内的 emote 相关调用(smile 等)不 会再重复报错,但 emote 会一直是 0,任何后续 do_emote() 路径 (包括 NPC 自身触发的社交行为)本应像 §7.87 里 xyj20032 那样以 *Value being indexed is zero.` 崩溃——本轮只是恰好没有再触碰到那条 代码路径就先被这次会话中断了,但代码本身的脆弱性和 §7.87 完全一致, 按同样标准视为需要修复的程序 bug。

``lpc void create() { catch(restore()); if (!mapp(emote)) emote = ([]); } ``

测试内容与结果

§7.100 sweep (2026-08-19)

Fixed the corpus-wide inherit ROOM; ... replace_program(ROOM); redundant-replace bug (AGENTS.md §7.100). 231 live occurrences deleted: 230 via scripted sweep (fix_710_room.py), plus 1 hand-fixed roommaker-tool template (obj/roommaker.lpc, simple string-builder variant). 3 already-commented-out instances left untouched. No real .lpc source found under work/data/. Verified via build-debug driver boot: clean compile, zero new "cannot replace"/"cannot bind" debug.log lines; confirmed serving via raw-socket connect on port 40033.

§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): 3 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.

深度功能测试补测:真实 PVE 战斗 + 商店买卖(2026-08-24)

补齐上一轮标记为"未覆盖"的两项(真实野外战斗、buy 商店购买), 使用管理员账号 fluffos。

真实 PVE 战斗

从起始房间〖南城客栈〗沿 west/south/south/south/west/northwest/ west/south 到〖民居〗(d/city/minju3.lpc),kill rat 攻击非陪练 性质的〖大老鼠(Rat)〗。战斗完整回合制展开(拳法/擒拿/腿法攻防轮流 描述、命中/闪避判定、体力状态提示逐级下降),管理员角色自身未受 训练、命中率极低,最终被大老鼠击杀——你死了 → 传送到〖阴阳界〗 死亡之门(d/death/gate.lpc)→ 判官崔珏对话 → 自动"命不该死"判定 → 复活并传送到〖荒郊小店〗,随身携带的不值钱物品掉落一件(预期行 为)。整个死亡→复活流程正确完成,未见任何崩溃或卡死;debug.log 干净(唯一相关记录是下方 emoted 的已知一次性提示,见下)。结论: 真实 PVE 战斗机制(含死亡与复活)功能正常,无程序 bug。

商店买卖(buy/sell,董记当铺 d/city/dangpu.lpc)

路径:起始房间 west → west(朱雀大街 → 董记当铺)。用 clone 指令(管理员权限)复制一把〖铜锤〗(value 500)验证 sell/buy 的金额与库存计算:

结论:sell/buy 双向金额与库存计算均正确,无程序 bug。

发现并修复的真实程序 bug:log_file() 未加 assure_file() 保护(AGENTS.md §7.11 已知模式的又一实例)

`` 执行时段错误:*Wrong permissions for opening file /log/nosave/CLONE for append. "No such file or directory" 程序:/adm/obj/simul_efun.lpc 第 17 行 呼叫来自:/cmds/imm/clone.lpc 的 main() 第 56 行 呼叫来自:/adm/obj/simul_efun.lpc 的 log_file() 第 17 行 ``

```lpc void assure_file(string file);

void log_file(string file, string text) { assure_file(LOG_DIR + file); write_file(LOG_DIR + file, text); } `` (文件:adm/simul_efun/file.lpc`。)

已知的一次性无害提示(非新 bug,确认此前修复仍然有效)

south/list 等指令在每个驱动进程生命周期内第一次触发 /adm/daemons/emoted 惰性加载时,仍然出现"错误讯息被拦截:"开头的 restore_object(): Illegal mapping format 提示(本轮死亡战斗与商 店测试各触发一次,分属两个不同的驱动进程)。这与 2026-08-07 深度 功能测试记录的 §7.87 修复(emoted.lpc 的 catch(restore()))预 期行为完全一致——该提示不写入 debug.log(仅出现在玩家屏幕上,与 此前记录的"底层真实错误文本从未被捕获"现象一致),且未阻断任何后 续指令。确认修复仍然有效,非新问题。

AGENTS.md §7.19 sweep (2026-09-01): enable_player() reentrancy from init()

Same corpus-wide bug class as mhxy/wuhanzhan (AGENTS.md §7.19): this lib's feature/command.lpc enable_player() wrapper (around the raw enable_commands() efun) is reachable from an NPC's init() via a redundant create()-then-init()-calls-setup() (or reset_me() calling setup()) chain -- confirmed via a body-aware static scan of every init() in this lib: 43 NPC/item files call setup() directly or via reset_me() from init() (e.g. d/zhangmen.lpc, d/nanhai/npc/zhangmen.lpc, d/xueshan/npc/zhangmen.lpc -- the same zhangmen.lpc family already documented as the original live-reproduced crash on mhxy), after create() already called setup() once (which already made the object living()). Calling enable_commands() a second time on an already-living() object makes the driver re-invoke that object's own init() as a side effect, which re-enters this same chain while the original call is still on the stack -- genuine reentrancy, crashing with "Too deep recursion" (most likely on an NPC's first-ever preload/compile).

feature/damage.lpc's revive() and cmds/std/sleep.lpc's wakeup() both call enable_player() again while the object is still living() (kept alive across the disabled interval by disable_player()'s own internal re-enable_commands()). This confirms a bare if (living(this_object())) return; guard would be the WRONG fix -- used the same true reentrancy-flag fix as mhxy instead: a nosave private int in_enable_player_now; set for the duration of the wrapper's body, guarding only genuine same-call-stack reentrancy while leaving every legitimate re-enable (revive/wakeup/disguise) unaffected. enable_player() had a single fall-through exit (no early returns), so one guard-at-top + one clear-at-bottom pair was sufficient. Verified via a single-file lpcc --batch compile check (PASS) -- not individually live-boot-tested.

深度功能测试(§10.7,2026-10-04)— negative range ends

scripts/lpc_fix_negative_ranges.py: 5 line(s) in 2 file(s) count a range from the end with < (x[a..-1] -> x[a..<1], x[-2..] -> x[<2..]; KB 06 §7.209). In this driver a negative constant end gave "" / ({ }) and a negative start the whole value, so each of these returned the wrong slice; the compile warning A negative constant as the second element of arr[x..y] is gone from the files that compile. Range lvalues (s[0..-1] = text, the prepend idiom) are left alone. A paired compile of the changed files at HEAD and in the working tree showed no new error.

深度功能测试(§10.7,2026-10-04)— undefined macro and simul_efun gaps

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

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

深度功能测试(§10.7,2026-10-05)— 任务表里的 .c 路径与任务守护进程的防护

scripts/lpc_dynamic_rows.py:d/obj/quest/dynamic_location、d/obj/quest/dynamic_quest 里 279 行写的是原档案的 .c 路径。转档时文件已改名 .lpc,而显式后缀按原样解析,load_object("/d/x.c")、new("/d/obj/quest/x.c") 返回 0 且不报错(KB 03 §4.2,KB 06 §7.220),所以 adm/daemons/questd.lpc 读到的每一行都加载不出来:随机任务和任务物品从未生成,在 create() 里直接对结果调用 tar->set() 的版本还会崩溃,连带加载它的 cron 守护进程和 give 的任务钩子。这些行已改为 .lpc;另有 4 行指向的文件两种后缀都不存在(档案缺内容),保持原样。 scripts/lpc_dynamic_quest_guard.py:1 处 spread_quest() 调用(init_dynamic_quest() 的循环里)包进 catch(),缺失的任务物品或编译不过的房间只跳过那一个任务,守护进程照常加载、其余任务照常生成。 验证:adm/daemons/questd.lpc和 cron 守护进程在新进程里加载(HEAD 一次,工作树三次,选房是随机的):HEAD 通过 2 个,工作树三次分别通过 2、2、2 个(共 2 个),无回退。

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

scripts/lpc_warnings.py xlqyzdb --fix 加 scripts/lpc_fix_no_effect.py --apply(daemon/skill/xuanyuan-archery.lpc 删去什么也不做的 (: throw_weapon :);),共改动 753 个 .lpc 和若干 .h。HEAD 与工作树分别加载(新进程):734 -> 734 通过,无回退;改过的头文件的 296 个包含者 292 -> 292 通过。 未动:adm/daemons/loggind.lpc 是 logind.lpc 的旧副本(else if { 语法错误,LOGIN_D 指的是 logind);cmds/arch/full.lpc 连写两个 else;d/city/npc/vikee.canoe.lpc 把 case 写成 cast——都是 HEAD 起就编译不过、没人引用的文件。

启动时警告 48 -> 0;新进程登录(编码选择、新人物提问)正常。

深度功能测试(§10.7,2026-10-12):vikee.canoe.lpc

编译警告收尾(2026-10-13)

scripts/toolkit_fix.sh 一轮去掉 7 处多余继承;搬来 xianlvqiyuan(8c3c85ca28e)的改法(逐 token 相同的照搬,其余手工):袈裟 jiasha.lpc 补 cloth::init() 让撕袈裟的 tear 生效,d/kaifeng/ground.h 表格守卫,d/city/npc/yg/yaoguai.lpc、yaoguai1.lpc 没人读的 yaoguai_type 删去,李靖、西王母 d/niulang/xiwangmu.lpc 的 die/unconcious 与玉兔的 kill_ob 改 void,alias.lpc 的 last_cmd_time、colors.h 防重。另:cmds/arch/make_npc.lpc 生成的文件里写 \#include、\},驱动对不认识的转义只留后一个字符,去掉反斜杠,生成内容不变;d/obj/drug1/punish.lpc、punish1.lpc 写明用驱动原先选的 clean_up()。能编译的文件警告清零;改动的 24 个对象在 HEAD 与工作树各载入一次,regress 0。

内力/精力当作伤害种类(2026-10-15,KB 06 §7.241 第二类)

本库 feature/damage.lpc 的 receive_damage() 不接受 jingli/neili/force 等种类,me->receive_damage("jingli", 30) 之类在练功、绝招、吸取时报 F_DAMAGE: 伤害种类错误。6 处改为 add(type, -N)(scripts/lpc_fix_damage_calls.py 第二步)。

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

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

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

1 个文件(技能、绝招、兵器、书籍、区域 NPC 等)来自侠客行体系(qi/jing/neili),而本库人物是 gin/kee/sen/force:receive_damage("qi", ...) 报 伤害种类错误,query("neili") 永远为 0。按 qi→kee、jing→sen(本库学习/读书指令消耗的就是它)、neili→force、max_neili→max_force、jiali→force_factor 改名(scripts/lpc_port_es_props.py --any-file)。

转码吃掉的引号(2026-10-15,KB 03)

原档在右引号前有一个孤立的 GBK 前导字节,转 UTF-8 时连引号一起丢了(如 return name + HIM "发作身亡了;),字符串一直延续到后面,文件编译失败:/d/obj/books-nonskill/hmeng014。补回引号(scripts/lpc_fix_eaten_quote.py)。

iori-sword 取招越界(2026-10-15,KB 06 §7.243)

同 xlqy_early:action[random(7)] → random(sizeof(action))。

技能名拼错(2026-10-15,KB 06 §7.249)

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

存档读不出来:字符串里的换行、空行(2026-10-15,KB 03 §4.4)

d/football/footballjdz.o、d/football/footballztj.o、d/moon/emoted.o、d/qujing/yizi.oo.o、data/doc/2001/content.Sep.o:字符串里有原样的换行(save_object() 存成 \r,restore_object() 逐行读,原样的换行让字符串收不了尾),或有空行(Illegal file format),读档的对象(留言板、NPC、玩家)从空白开始。换行改写成 \r、删去空行(scripts/lpc_save_encoding.py --from-raw),驱动核对全部能读回(scripts/lpc_save_restore_check.py)。

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

14 个文件、18 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()),多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/city/npc/obj/choupao、/d/city/npc/obj/bingfu、/d/city/npc/obj/sengpao)。本库 NPC 基类 std/char/npc.lpc 的 carry_object() 第一句是 if (file->query_unique()),对不存在的文件直接 call_other,驱动在这里报 call_other() couldn't find object,new() 来不及返回 0,所以只要 NPC 带着一件本库没有的物品就载入不了(调用处加判断对它无效)。在它前面补 if (!objectp(load_object(file))) return 0;(--base),缺的物品就跳过,NPC 照常载入;上面那些调用处同样改成带判断的块(基类以后改回去也不怕)。载入检查:改动的文件改前能载入 1 个,改后 14 个(13 个原来载入不了),没有退步;其余 1 个另有原因(多为 set_skill() 了本库没有的武功)。

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

老版 MudOS 给每个 LPC 对象预先定义了 SAVE_EXTENSION(".o");FluffOS 的 ChangeLog(COMPAT_BUSTER 一条)把它删了,改用 __SAVE_EXTENSION__。本库没有自己 #define 这个宏,用到它的文件一律 Undefined variable 'SAVE_EXTENSION',整个对象载入不了。改成 __SAVE_EXTENSION__(scripts/lpc_fix_predefined_macro.py):adm/daemons/network/netmail.lpc。netmail 是网络邮件守护进程(本库缺 mailer.h 等头文件,改后仍载入不了)。改后用 lpcc 逐个载入这些对象:0 个通过,1 个还有别的错误(adm/daemons/network/netmail:/adm/daemons/network/netmail.lpc:18: error: Cannot #include mailer.h)。

蓬莱三星的 ref() 是保留字(2026-10-18,KB 04 §6.14)

福星、禄星、寿星(d/penglai/npc/)里有一个函数叫 int ref (),call_out("ref", 20) 去调它;ref 在 FluffOS 里是关键字(引用),unexpected L_REF,三个 NPC 都载入不了。函数和 call_out 都改叫 reff(本系里 dtxyzjb、zitengzhan 早就这么改,call_out 却没跟着改):d/penglai/npc/luxing.lpc、d/penglai/npc/shouxing.lpc。改后载入:2/2 通过。

d/moon/obj/poem 两句诗丢了收尾的引号(2026-10-18,KB 03 §4.6)

string *poem 数组里 "不放玉花飞堕地,留在广寒宫阙。, 和 "把酒送春春不语,黄昏却下潇潇雨。, 的收尾引号没了(转码时跟半个汉字一起丢了),下一行的开引号把字符串合上,后面的诗句成了代码:Illegal character 0xe6,整个物件载入不了。补回 ",。载入:通过。

d/shendian/obj/niepan 的收尾引号变成了私用字符(2026-10-18,KB 03 §4.6)

set("long", HIR"……五种符号<U+E4D4>NOR); 里本该是 " 的位置是一个私用区字符(转码时半个汉字加引号被换成了它),Illegal character 0xee,整个物件载入不了。换回 "。载入:通过。

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

补回本库没有的基类文件(2026-10-18,KB 04 §6.15)

下面这些基类本库没有,继承它们的对象(房间、NPC、物品)整批载入不了(inherit XXX; 的宏没定义,或者 inherit "/std/…"; 的文件不在)。同系的库里有同名文件,两库有大量逐字节相同的文件(亲缘关系),所以从那边原样拷来,缺的宏补进全局头文件 include/globals.h:/std/weapon/unarmed(宏 UNARMED)←wuhanzhan,1 个对象继承它,抽样 1/1 载入。每个基类补回后单独载入、再抽样载入使用它的对象,基类载不进来或者一个用户也载不进来的就撤掉(本库撤掉了它们,不再补)。(scripts/lpc_restore_bases.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 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 19 处、19 个文件:dagonforce→dragonforce(6 处)、lahua-shou→lanhua-shou(5 处)、yange_blade→yange-blade(2 处)、dragon-step→dragonstep(2 处)、hellfire_whip→hellfire-whip(1 处)、moyunshou→moyun-shou(1 处)、changqaun→changquan(1 处)、cuixi-zhang→cuixin-zhang(1 处)。(scripts/lpc_fix_skill_names.py)

调用了父类没有的函数的对象(2026-10-19,KB 06 §7.259)

::create();、::init();、return ::buy_object(who, item); 这类调用写的是父类里有这个函数的版本;本库的父类没有,旧驱动把它当空操作(返回 0),现在的驱动在编译时就报 Unable to find the inherited function,整个对象载入不了。单独成句的调用删掉,return ::f(...) 改成 return 0;(void 函数里是 return;),与旧驱动的行为一致。共 1 处、1 个文件:::buy_object()(1 处)。只改了出错原因全是这一类的文件;还带着别的错误(多半是父类本身没了)的文件没动,等父类补回后再看。(scripts/lpc_fix_dangling_super.py)

技能基类丢了“预备武功”那一段(2026-10-19,KB 06 §7.262)

feature/skill.lpc 有 skill_map/map_skill(),却没有预备武功(prepare_skill)这一块:变量 skill_prepare、prepare_skill()、query_skill_prepared()、query_skill_prepare()。来自 xkx 系列的 NPC 在 create() 里调用 prepare_skill("cuff", ...),于是 Undefined function prepare_skill 整个 NPC 载入不了。从同系的 xkx100 把这一块原样补回(声明加在 skill_map 后面,函数加在文件末尾),受益 NPC 1 个:d/wuguan/npc/master。(scripts/lpc_transplant_function.py)

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

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

出口指向不存在的房间(2026-10-19,KB 06 §7.257)

下列房间的出口写的文件不存在,而对面恰好只有一个房间的出口指回本房间,所以按对面的房间改正(笔误、大小写、路径少了一级):

改后房间照常载入(与 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 第四轮)

/d/jz/npc/jiang.lpc:set("daoxing", 1500000) 丢了分号;补回后载入,没有警告。完好副本在 dtxyzjb,只有文件头的署名注释不同。

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

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

x == "a" || "b":后面的字符串常量永远为真(2026-10-19,KB 06 §7.281)

if (arg == "yuyou" || "yu you") 里的 "yu you" 是非空字符串,永远为真,整个条件对任何 arg 都成立;作者要的是每个字符串都和变量比较。具体是:驿站把任何输入都当成合法的去处。在每个字符串前补上变量和运算符(scripts/lpc_fix_literal_or.py;forrest2.lpc 是 dir == "north" && "south",手工改成 (dir == "north" || dir == "south") && ...):/std/room/yizhan.lpc(7 处)。

member_array('/', str) 改为 strsrch(str, "/"):字符字面量的 member_array 按字节搜索(2026-10-20,KB 06 §7.283)

member_array('/', str) 里的 '/' 是整数,驱动按 UTF-8 字节搜索,返回字节偏移(带第三个参数时起点也是字节);最新的 FluffOS(923cdbc0)在字符串里有非 ASCII 文字时,对每个调用点警告一次(Warning: member_array(int, string) searches BYTES)。而 str[0..idx-1] 按字符切,中文字符串会在错误的位置截断。改为 strsrch(str, "/")(找不到同样返回 -1,按字符计数;scripts/lpc_fix_member_array_char.py,JSON 编码器等少数几处手改)。改动:/adm/daemons/vrm_server.lpc(1 处)。

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

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

浩劫系统 verdict_time():领头玩家已经离线时 lead->name() 报错,戒严不解除(2026-10-20)

三十分钟后收场时 lead(接战书的玩家)若已退出游戏就是 0,lead->name() 在宣布战果的 message() 里报错,后面的 LOGIN_D->set_madlock(0) 不再执行,戒严一直保持,quit 指令因此对所有人关闭。改为 (lead ? lead->name() : "各派豪杰")。改动:/feature/disaster.lpc(2 处)。

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,共 8 处)。改动:/cmds/wiz/dns.lpc(1 处)、/d/city/mjlady3.lpc(3 处)、/d/shendian/babu/obj/longzhua.lpc(4 处)。

callouts:sizeof(info[i]) == 4 把每一行都挡掉了(2026-10-20,KB 06 §7.287)

驱动的 call_out_info() 每项只有 3 个元素,输出行前的守卫 if (sizeof(info[i]) == 4) 永远不成立,指令只打印表头。改为 >= 3。改动:/cmds/arch/callouts.lpc(1 处)、/cmds/wiz/callouts.lpc(1 处)。

socket / dumpsocket 指令:dump_socket_status() 驱动里没有(2026-10-20,KB 06 §7.291)

这些巫师指令 write(dump_socket_status());,MudOS 的 efun 在 FluffOS 里不存在,指令编译不过。在模拟 efun 文件里补了 dump_socket_status():用 socket_status() 排出同样的表(没有 sockets 包的构建,比如 WASM,只有表头;scripts/lpc_add_sefun.py dump_socket_status)。

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

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

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) ||)。改动:/std/char/npc.lpc(共 1 个文件)。

巫师命令不带参数时对 0 调用 efun(2026-10-21,KB 06 §7.336)

巫师(管理员)把命令敲成不带参数时,驱动把 0 传给字符串参数;下面的命令第一句就把这个 0 交给 efun,运行时报 Bad argument 1 to ...,这条指令从来没有用过。用 scripts/lpcc_bare.py 不登录、在一次 lpcc 求值里以 0 调用 main() 逐个确认后,由 scripts/lpc_fix_bare_arg.py 在第一处使用参数的语句前加守卫:文件自带 int help(object me) 的,守卫是 if (!arg) return help(me);(help() 定义在 main() 之后的 1 个文件补了原型 int help(object me);,否则编译报 Undefined function help)。改动(1 个文件,括号里是接住 0 的 efun):/cmds/adm/dumpprog.lpc(new)。加载检查 HEAD 与工作树对照,没有退步。

cron.lpc:emery() 用循环外的下标,第一次越界就让 cron 停摆(2026-10-21,KB 06 §7.345)

怪兽入侵事件 emery()(/adm/daemons/cron.lpc)末尾 for (m = 0; m < sizeof(user); m++) user[i]->set("kill_guaishou", 1);:i 是前面随机选的地区下标,循环变量 m 没用上,同一个玩家被标记 sizeof(user) 次,在线人数不到 i + 1 时下标越界(Array index out of bounds,yueyingqiyuan 管理员扫描里的控制台行;call /adm/daemons/cron->emery() 一个人在线时四次里三次抛错)。init_cron() 在 15 分钟的整点调用 emery(),之后才 call_out("init_cron", 60) 重新排自己:抛错之后重排没有执行,玩家自动存盘(autosave())、动态任务刷新(TASK_D->init_dynamic_quest())和后面所有事件在这次启动里都停了。改成 user[m](yueyingqiyuan 上实测:连续六次 emery() 都正常返回)。这个标记档案里没有任何地方读它(go.lpc 里的用法是注释掉的)。

full(cmds/arch):else 写了两遍,help() 里塞进了整个旧文件(2026-10-21,KB 04 §6.28)

/cmds/arch/full.lpc 编译不了(unexpected L_ELSE,修掉之后是 End of file in text block):main() 里 if (wizard) {...} else { if (!arg) ob = me; } else { ... } 多了一段 else,help() 的 @HELP 文本里贴着整份旧版文件,没有结束符。去掉第一段 else,帮助文字只留 指令格式 : full 和一句说明(兄弟库的 full 写着“这个指令让你加满精气神。”),补上 HELP、);、return 1;、}。

cast1:宏 SYNTAX 带着分号(2026-10-21,KB 04 §6.28)

#define SYNTAX "指令格式:cast <法术> [<参数>| on <目标>]\n"; 的 ; 属于宏展开,notify_fail(SYNTAX); 变成 notify_fail("..." ;);,整个指令编译不了(unexpected ';')。去掉宏里的分号,并删掉没用到的局部变量 dest(编译警告)。本库的 valid_kill(killer, victim, no_list) 要三个参数,调用只给两个,补了 , 0(和同库 ccast.lpc 一样,KB 04 §6.19)。

观礼台 d/city/under.lpc:wait_and_start() 里的 fan 没赋值,比武开不了(2026-10-21,KB 06 §7.346)

接受挑战(defend)两秒后 wait_and_start() 调用 fan->command("chat ...比武开始"),object fan; 声明了却没有 fan = load_object(__DIR__ "npc/fan");(dtxywzxzb 的同一份文件有),抛错之后两条 kill 和 call_out("finish_match", 300) 都不执行,比赛不开始。补上这一句(npc/fan.lpc、ground.lpc 本库都有)。

拼错的 call_out() / input_to() 目标(KB 06 §7.349,2026-10-21)

计时器或输入提示给驱动的函数名和文件里定义的差一个字母;驱动对不存在的函数静默不理,所以对应的计时从来没有触发过。调用和配套的 remove_call_out() 改成已定义的拼法:find_seal:disapprear → disappear(法术产生的封印十分钟后从不消失)。改动:/d/jjf/obj/find_seal.lpc。

注册到不存在函数的 add_action(2026-10-21,KB 06 §7.349)

add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 19 条注册(12 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/changan/pinqiting.lpc(do_pass、do_resign)、/d/city/npc/doctor.lpc(do_block)、/d/city/npc/duguoyin.lpc(do_vendor_list)、/d/eastway/npc/petboss.lpc(do_cost、do_list、do_upgrade)、/d/eastway/petboss.lpc(do_cost、do_list、do_upgrade)、/d/pansi/npc/qingxia.lpc(do_yes)、/d/qujing/jisaiguo/obj/luche.lpc(do_get、do_put)、/d/qujing/wudidong/cave2.lpc(do_push)、/d/zhuxian/npc/guard.lpc(do_bian)、/d/zhuxian/npc/guard1.lpc(do_bian) 等共 12 个文件。最多的函数名:do_bian 3 处、do_cost 2 处、do_list 2 处、do_upgrade 2 处、do_yes 2 处。没有动(读过):/d/wizz/stey/npc/rulai1.lpc 的 do_cancel(对象本身编译不过,登记留着,等对象能加载再处理)。

(: 函数 :) 指针指向对象自己没有定义的函数(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 1, chat_msg_combat 1):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/d/death/npc/yanluowang.lpc:ask_cancel、/d/xueshan/obj/hufabird.lpc:saysth。

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

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

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

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

本库 2 处(1 个文件):拼名字的赋值 2 处。例:name[0..1] → name[0..0](/d/nanhai/npc/tidu.lpc:127)。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,不要重跑。

长度检查按字节写,旁边的提示按汉字写:6 处(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 你好吗)。本库 6 处(3 个文件):if ((strlen(name) < 2) || (strlen(name) > 12)) {(/adm/daemons/loggind.lpc:863)。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 区间 9 个文件;茶馆猜诗 mixup 1 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。

血条 / 表情标题按字节算显示宽度(2026-10-23,KB 06 §7.365)

老档把汉字当两字节:bar_string[0..(val * N / max) * 2 - 1] 把「格数」换成字节下标,半格血条在本驱动(按字符算区间)上会整条画满;巫师表情 edemote 的 repeat_string("─", 13 - (strlen(emotion) / 2)) 也会少减汉字宽度。scripts/lpc_fix_display_pad.py:去掉 CJK 血条 / 空白条上的 * 2(jings/qis 赋值和 blank_string3[0..(23-a)] 的 23→11 一并改),表情标题去掉 / 2;ASCII 空格的 none_string / empty_string 仍保留 * 2(一格两个空格)。本库 14 处(7 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。

皇宫大殿北向 /u/fly 死出口(2026-10-23,KB 06 §7.367)

/d/huanggong/dadian 的 north 指向未随档的 /u/fly/doublesword/houdian。删除该出口。证明:HEAD 出口含 north,改后只剩南东西。

巫师区出口 faceoff / rence / niulang / yihua(2026-10-23,KB 06 §7.372)

/u/f/faceoff/、/u/r/rence/、/u/onskyer/niulang/、/u/skinys/yihua/ 对应区域已在 /open/world1/faceoff/bird/、/open/prayer/、/d/niulang/、/d/yihua/。scripts/lpc_fix_wizard_area_exits.py 在目标存在时改写,仍缺失的出口行删除。本库 4 处。证明:dreamofseven wood5 四向可加载;xlqy_new2007 天河东西向 niulang;kxkj kitchen7 → prayer。

家丁 CLOTH_OB()/WEAPON_OB() 调用式宏(2026-10-27,KB 04 §6.16 Also)

d/wuguan/npc/jiading.lpc(及 jiading1,xlqy 系在 d/dntg/hgs/wuguan/npc/)写 carry_object(CLOTH_OB("cloth")) / WEAPON_OB("changjian"):库中没有带参的 CLOTH_OB,WEAPON_OB 在 globals.h 只是裸路径 "/d/obj/weapon/",展开成 "/d/obj/weapon/"("changjian") 语法错误,NPC 无程序。同目录其它 NPC 用 __DIR__ "obj/cloth";剑在 /d/obj/weapon/sword/changjian。改成这两条(目标文件均在树内)。证明:HEAD Undefined function CLOTH_OB;改后 new() 成功且 inventory 为 2。

黑风山 pusa1 用未声明的 ob 且函数外残留 call_out(2026-10-27,KB 06 §7.348 Also)

d/qujing/heifeng/npc/pusa1.lpc 的 announce_success(object who) 写成 if (!ob->query("obstacle/hf"))(Undefined variable 'ob'),同目录 pusa.lpc 是 who->query("obstacle/hf") == "done";who->save(); } 之后还残留 call_out("die"...); return; },落在函数外(unexpected L_STRING),对象无程序。改成与 pusa 相同的门,并删掉残留语句。证明:HEAD 加载失败;改后加载成功。

雪山 obj/hufabird set_level 缺 }(2026-10-27,KB 04 §6.28 Also)

d/xueshan/obj/hufabird.lpc 的 set_level 在 setup(); 后少了一个 },后面的 int heal_up() / leave / invocation / die 都嵌进了 set_level(unexpected '('),对象无程序;同目录 npc/hufabird.lpc 正常关闭。顺带:messege_vision→message_vision,return : die()→return ::die(),以及无 saysth 的 (: saysth :) 按 7.355 注释掉。证明:HEAD 加载失败;改后加载成功。

洗衣盆 is_container 映射缺值编不过(2026-10-27,KB 04 §6.28 Also)

d/gao/obj/pen.lpc(或 d/qujing/gao/obj/pen)的 is_container() 写成 set("objects", ([ "/u/bula/gao/obj/kaoji" ]));:映射只有键没有值(unexpected ']'),对象无程序。同族 hy5 / xyj2000 已把该函数注释掉。改成标准的 return 1;。证明:HEAD 加载失败;改后加载成功。

月宫 quest/road 模板缺 index(2026-10-27,KB 06 §7.348 Also)

d/moon/quest/road.lpc(及误放在 npc/road.lpc 的副本)出口写 sprintf("%d", index ± 1),却没有像 road1…road10 那样声明 int index = N;(Undefined variable 'index'),对象无程序。补上 int index = 5;(中间段,东西出口都能落到已有房间)。证明:HEAD 加载失败;改后加载成功。

星君/罗汉模板缺 ID 宏(2026-10-27,KB 06 §7.348 Also)

d/dntg/sky/stars/star.lpc 与 d/nanhai/luohan/npc/luohan.lpc 用 title[ID] / first_name[ID],真正带号的副本是 #define ID N 再 #include 本文件;单独编译模板时 Undefined variable 'ID'。加 #ifndef ID / #define ID 0 / #endif,带号 include 仍用各自的 N。证明:HEAD 模板加载失败;改后模板与 stars/5、luohan5 均加载成功。

雷神 check 在 stop_fight 定义之前调用且 call_out 缺逗号(2026-10-27,KB 04 §6.28 Also)

d/qujing/start/24/1.lpc 的 check() 里调用 stop_fight(who),函数却写在后面(Undefined function stop_fight);call_out("lost", 1 who) 少了逗号(unexpected L_DEFINED_NAME)。把 stop_fight 挪到 check 之前,并改成 call_out("lost", 1, who)。证明:HEAD 加载失败;改后加载成功。

toy 旁缺 message.h(2026-10-27,KB 04 §6.23 Also)

obj/toy/*.lpc(及少数 obj/obj/toy、嵌套副本)写 #include "message.h",引号 include 只解析同目录;本 lib 的 d/obj/misc/message.h 有同内容副本,toy 目录没有。玩具对象无法编译(Cannot #include message.h)。从本 lib 的 d/obj/misc/message.h 复制到缺失的 toy 目录旁。证明:HEAD buwawa 加载失败;改后加载成功。

玉皇 yuhuang1 去引号无法加载(2026-10-27,KB 04 §6.25 Also)

d/dntg/sky/npc/yuhuang1.lpc:字符串与路径几乎全部丢掉引号(set_name(玉皇大帝,…)、carry_object(/d/obj/cloth/mangpao)、setup() 后无 ;),下一句 int promote_me() 成语法错误。同目录手足 yuhuang.lpc 完好;用本库 yuhuang 字节覆盖 yuhuang1。证明:HEAD 加载失败;改后加载成功。

悬空 ::init / unique::init(2026-10-27,KB 06 §7.259 Also)

kugu-1/2/3、kunxian-suo 写 ::init();,tianyi 写 unique::init();,而 F_UNIQUE / ITEM / feature/unique 没有 init;本驱动编译报 Unable to find the inherited function 'init'(tianyi 带 in file 'unique'),物件无法加载。老驱动把空父调用当返回 0。lpc_fix_dangling_super.py 删掉这些语句;cloth::init() 保留。证明:HEAD 加载失败;改后加载成功。

dami 误调 init_unarmed(2026-10-27,KB 04 §6.25 Also)

d/obj/food/dami.lpc 继承 ITEM+F_FOOD 却调用 init_unarmed(5)(并 #include <weapon.h>),未继承空手兵器基类,驱动报 Undefined function init_unarmed。手足 hymud 已注释该行。删掉该调用与无用的 weapon 头。证明:HEAD 加载失败;改后加载成功。

深度功能测试(2026-10-27,§6.25 tell_object 参数顺序)

tell_object("…", me/who) 参数颠倒(消息当对象);改为 tell_object(me/who, "…")(guest.lpc)。证明:残渣 0;抽样 guest/rukou/forest_c 加载。

深度功能测试(2026-10-27,§6.25 tell_object 缺 me(自动放弃/愿否))

一参 tell_object("…") 在已有 me 时改为 tell_object(me, "…")(city.lpc)。证明:抽样 city / xiaolongnv 加载。

深度功能测试(2026-10-27,§6.25 tell_object 路径当房间)

tell_object("/…", …) → tell_room(load_object("/…"), …)(去掉 .lpc)。

深度功能测试(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… 行。

深度功能测试(2026-10-27,§6.10 pusa1 die varargs)

d/qujing/heifeng/npc/pusa1:void die(object me) → varargs void die(object me),与角色基类一致。