The Quest of Oriental Chivalry

✅ 可玩

侠客行 Ⅰ (The Quest of Oriental Chivalry)

xkx2001

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

▶ 开始游玩 · Play Now

由 "XiaKeXing Gaming Group" 制作的武侠 MUD,登录画面自称 1996-2003 年出品,属于早期"ES II"引擎家族的一支,与本项目中的 `rzrmud`(大唐西游)、`bmxkx2001`、`xuanjianlu`(玄剑录)等同属一个更古老的技术谱系,但游戏世界内容各自独立(发行版本层面,`d/taihu/gumu/houtang.lpc` 等场景文件头带"Cracked by Roath"署名,和 `bmxkx2001`/`jym`/`xkm` 是同一批流通版本,`xuanjianlu` 则属于"Cracked by Kafei"的另一批流通版本,与 `xkx2000zxb` 同批,两批文件头署名不同但核心引擎同源)。游戏以"侠客岛"为背景:新角色先在岛上沙滩苏醒,由"赏善使"张三或"罚恶使"李四迎接并带往登记处办理入岛手续(用 `register <邮箱>` 完成注册,系统随机生成新密码并断开连接,是一种老式的防小号验证机制),之后才正式踏入江湖,探索少林、武当、华山、丐帮、星宿派等经典金庸门派地图,体验完整的门派拜师、内功修炼、江湖任务与帮派系统。

English

Produced by the "XiaKeXing Gaming Group," with a login screen claiming a 1996-2003 pedigree. Part of an early branch of the ES II engine family, sharing a more distant common technical ancestry with this project's rzrmud (Great Tang Journey West), bmxkx2001, and xuanjianlu (Record of the Mysterious Sword), though each has an independent game world — at the release level, scene files such as d/taihu/gumu/houtang.lpc carry the "Cracked by Roath" header shared with bmxkx2001/jym/xkm, while xuanjianlu belongs to a separate "Cracked by Kafei" batch (shared with xkx2000zxb); the two batches differ in header credit but share the same core engine. Set on "Xiake Island," new characters wake on the island's beach and are greeted by the "Rewarding Envoy" Zhang San or the "Punishing Envoy" Li Si, who guide them to registration before they set out into the jianghu proper. The world covers classic Jin Yong sects — Shaolin, Wudang, Huashan, the Beggars' Sect, the Xiuzhong Sect, and more — with a full apprenticeship, inner-cultivation, quest, and clan system; new arrivals must first follow a welcoming NPC to the island's registry and complete `register <email>`, a vintage anti-multi-accounting mechanism from the era.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/xkx2001/

管理员账号 / Admin account

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

本地运行

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

游戏端口:40021。

NOTES · 移植与修复记录

xkx2001 — 侠客行 Ⅰ (Xiakexing/The Quest of Oriental Chivalry, 2001 test lib)

Archive: xkx2001测试用老lib.zip. Port: 40021. Status: done (boots clean, connects, plays through login into "create new character?" confirmation with zero crashes).

What this is

"ES II mudlib" lineage (comment header in adm/single/master.c: "for ES II mudlib... modified by Xiang for XKX"), same family as rzrmud (archive #23, "大唐西游") — shares the exact same master.c function set (connect/epilog/preload/log_error/valid_*/standard_trace/ error_handler) almost verbatim, both apparently descended from the same "XKX"(侠客行)-authored base. adm/single/master.c + adm/single/simul_efun.c layout, same as bxsj/bxsj1 (archives #4/#5) — but NOT the same lineage as those (checked: no log_error()/ CHANNEL_D reentrancy call, no lazy-load-in-valid_read pattern, 2-arg valid_override but no allow-list issue hit in testing). MudOS v22b25, "XiaKeXing Gaming Group 1996-2003".

Fixes applied

None — booted clean with zero fixes on the first attempt (mechanical convert_lib.sh pipeline only), same as rzrmud. Before starting, checked this lib's master.c against the known bxsj-lineage bugs (§4 CHANNEL_D reentrancy, §14 valid_override 3-arg, §8c this_player()-override footgun) since the adm/single/ path layout matches bxsj/bxsj1 — none of those specific bugs are present in this master.c despite the similar directory layout, confirming layout similarity alone doesn't mean shared bugs; the actual master.c *content* here is closer to rzrmud's.

Known content gap (not fixed — see AGENTS.md §13)

lpcc sweep: 8124 files, 6884 pass / 1240 fail (84.7%). Largest cluster (52 failures, one root cause per AGENTS.md §8g): inherit/misc/ bboard.lpc (and its .bad.lpc backup copy) calls EDITOR_D->add(...)/ EDITOR_D->get_file_num(...) — a whole daemon (EDITOR_D, apparently an "anthology/featured-posts editor" service for bulletin boards) that doesn't exist anywhere in this archive at all, not a typo or missing #define. Genuine missing content (an entire daemon file), affecting only the bulletin-board "compile into archive" feature — not fixed, out of scope. The remaining ~1188 failures are the usual long tail (illegal- character/encoding edge cases, syntax typos in individual files, #include-only fragments failing standalone per §6b) — not triaged individually.

Interactive test result

Connects, ASCII banner + mudlist status table render correctly (Chinese intact), GB/BIG5 charset prompt works, username length/alphabetic validation works correctly (rejected both a too-long and an all-Chinese attempt before accepting a valid one), and a valid new username reaches the "create new character?" (y/n) confirmation prompt with zero crashes. Did not create a full character or play further (out of scope).

Re-verification pass (QA sweep, later session)

Earlier passes never tested past the "create new character?" confirmation prompt / the password-setup step, so the full registration -> post-login command flow (per the project's standing §15ae policy) had never actually been exercised here. Re-tested end-to-end this pass and found two real, fixed bugs, plus clarified one thing that looked exactly like a bug but wasn't:

1. §15s — adm/simul_efun/message.lpc's tell_room() passed a raw, unset int 0 as message()'s 4th ("exclude") argument whenever called in its common 2-arg form. This driver's message() rejects that shape (Bad argument 4 to EFUN message() ... Got: int(0)), and since tell_room() fires from a room's own create()/reset()/ make_inventory() chain (an NPC's arrival announcement) the very first time ANY room is lazily compiled, the uncaught error aborted the in-progress move() call that placed a brand-new character into their starting room -- leaving the character with no environment at all (look showed "你的四周灰蒙蒙地一片,什么也没有", environment(me) was 0, every subsequent command touching environment() crashed). Fixed with the standard pattern: exclude || ({}). Re-verified: after the fix, move() into the starting room completes cleanly and look renders the real room every time. 2. §15af — master.lpc's log_error() called wizardp(this_player(1)) unconditionally, including during PRELOAD when no player is connected (this_player(1) is 0) -- wizardp() calls into SECURITY_D via call_other, which can lazily load_object() the security daemon; doing so while the driver is still mid-compile of some unrelated file throws Object cannot be loaded during compilation. (seen 23 times on a clean boot, all during preload, all non-fatal here but pure log noise plus latent-crash risk of the kind documented for shenzhou). Also fixed the same function's "Warning:" vs the driver's real lowercase "warning:" case-sensitivity bug while touching this line (the filter never actually matched anything before). Fixed by guarding the whole check behind this_player(1) && first. Re-verified: a clean boot now shows zero 执行时段错误 lines at all (was 23). 3. Missing /log/nosave/ directory (§15ah-shaped): securityd.lpc's own denial-logging (log_file("/nosave/WRITE_LOG", ...)) and message.lpc's read/write audit log both target this subdirectory, which the raw archive never shipped. Created libs/xkx2001/work/log/nosave/ proactively. 4. Not a bug -- clarified a red herring that looked exactly like a silent-command-dispatch bug (§15ae's symptom shape) but has a totally different, legitimate cause: every brand-new (non-wizard) character lands on the "侠客岛" landing beach (d/xiakedao/shatan1.lpc), whose own init() calls add_action("block_cmd","",1) -- a DELIBERATE, hand-written onboarding gate that blocks every command except quit, goto, suicide, follow, tell, say, reply, look until the player follows the greeter NPC (auto-drags them after ~15s regardless) to the "侠客岛挂名处" (registration desk) and types register <email> -- which the mudlib code implements as generating and returning a brand-new system password and disconnecting the session (an authentic 1990s-era anti-multi-accounting mechanic, not a crash). block_cmd() returns 1 (block) with no message at all for anything outside that whitelist, so score/inventory/help/who/ etc. silently doing nothing at this stage is completely expected in-game behavior, not a driver-compat regression -- confirmed by reconnecting with the new password afterward and finding look/ score/quit all work perfectly in the real starting room. Spent real diagnostic effort here (temporary efun::write_file() tracing in process_input()/command_hook()/find_command(), all removed afterward) before finding shatan1.lpc's block_cmd() -- worth flagging in AGENTS.md as a new variant of the "commands silently do nothing after registration" family (distinct from §15ae's private- command-hook cause and §15aj's missing-environment cause): a deliberate, narrow post-registration command whitelist gate with a silent (no-notify_fail) block response.

Full clean re-verification (fresh boot, after all fixes): registration with real Chinese names 秦云(female)/秦伟(male), through the follow-NPC -> auto-escort -> register <email> -> new-password -> reconnect -> look/score/quit flow, zero 执行时段错误 in debug.log across the entire session.

Post-hoc fix: UTF8-native is_chinese/registration (AGENTS.md §15h)

Applied in a later batch pass across the whole project: is_chinese/is_chinese2 in the shared chinese.lpc simul_efun fragment used GBK byte-range checks that silently never match real Chinese text once strings are UTF-8 (this driver's str[i] returns a Unicode codepoint, not a GBK byte). This broke character registration specifically -- any real Chinese name was rejected. Fixed the range check to test the CJK Unicode block instead, and halved the GBK-byte-calibrated length bounds in check_legal_name to match. See AGENTS.md §15h for the full writeup; confirmed via a real interactive registration test (Chinese surname + given name reaching the next prompt).

Driver-rebuild retest + LPC reformat + WASM pass (this session)

WASM-enablement pass (loopback-allow + admin seed)

Applied the standard WASM-first changes (AGENTS.md §1.3b/§1.3e/§1.5):

1. Loopback always allowed through ban gates — this lineage gates on query_ip_name() (hostname), not the IP: - adm/daemons/band.lpc: added is_local_ip(string site) helper (empty/non-string, localhost, 127.* => local) and short-circuited is_banned() to return 0 for local sites. - adm/daemons/regband.lpc is_banned() (the registration-ban gate called from logind.lpc before new-character creation): same inline local short-circuit. (regband is also matched against email patterns; the local check only matches localhost-shaped strings so email matching is unaffected.) 2. Uptime startup gate: none in this lib. 3. Anti-flood throttles: none per-IP (MAX_USERS is a global cap — left intact). SECURITY_D->valid_wiz_login() requires a wiz_sites entry for every wizard — fluffos is seeded with pattern .* so this gate passes from anywhere including WASM garbage IPs; gate itself left intact. 4. Admin account seeded — id fluffos, display name 浮浮 (male). Registered through the REAL flow including this lib's quirks: initial password at creation, landing on the 侠客岛 beach, escort to 侠客岛挂名处, register [email protected] which ASSIGNS A RANDOM password (was zfbod) and disconnects. Then granted (admin) by editing data/securityd.o (the lineage's ACL save data — NOT the adm/etc/wizlist.h file, which this securityd only uses as a fallback when the save is absent): added "fluffos":"(admin)" to wiz_status and "fluffos":".*" to wiz_sites (required by valid_wiz_login). After restart, logged in and used the game's own passwd command to change the random password to the standard Mud@2026. Verified: 目前权限:(admin), update /adm/daemons/band recompiled OK, wizard-view look shows file paths. (goto on a non-room daemon object is a no-op in this lib; update is the canonical ACL check.) Save files: work/data/user/f/fluffos.o + work/data/login/f/fluffos.o (untracked, NOT gitignored — orchestrator must git add), plus the modified work/data/securityd.o (already tracked).

Retest: fresh registration (fluffos itself) through beach → 挂名处 → register → random password → re-login all worked natively; fluffos re-login with Mud@2026 + update verified; debug.log free of runtime errors.

深度功能测试 / Deep functional test (round two)

First genuinely hands-on *playthrough* pass (all prior passes above verified registration/admin-login/WASM boot, never actual post-登记 gameplay: skill learning, sparring, sect contact, or a real disconnect/reconnect). Read doc/help/newbie.dec99 and doc/help/intro in full first — both correctly describe this lib's mechanics (fight is the safe, non-lethal sparring form; kill/hit are real; bai/xue for apprenticing/learning; wimpy for auto-flee) even though the specific starting geography they describe (扬州客店 as the general hub) turned out to belong to the shared "ES2 newbie doc" template, not this lib's actual map — this archive's real starting zone is entirely the self-contained "侠客岛" (Xiake Island) tutorial/quest zone (d/xiakedao/, ~100 rooms), reached via the beach → escort → 挂名处 → register flow already described above. Native driver (~/src/fluffos/build-debug/src/driver config.fluffos), scripts/ mudclient.py throughout. Found and fixed one serious bug (a real driver segfault, not just a caught LPC error — the first of that severity found in this project's round-two pass) plus hardened the same function against a second, non-fatal failure mode found while re-verifying the first fix.

Test characters left behind (small save files, kept as playthrough evidence, matching this project's standing convention):

Bug 1 (NEW bug class): a mudlib logic bug (duplicate call_out scheduling from a missing return) segfaults the entire driver process, not just an LPC-level caught error

File:line: d/xiakedao/npc/longx.lpc, move_next(), ~line 188-194.

``lpc // BEFORE: me->move(places[count]); remove_call_out("move_next"); call_out("move_next", 10, me, count); } count = count + 1; remove_call_out("move_next"); call_out("move_next", 10, me, count); return; } // AFTER: me->move(places[count]); remove_call_out("move_next"); call_out("move_next", 10, me, count); return; // <-- was missing; this is what let the branch fall // through into the unconditional reschedule below, // double-scheduling "move_next" on the same object. } count = count + 1; remove_call_out("move_next"); call_out("move_next", 10, me, count); return; } ``

``lpc // BEFORE: command(commands[count]); // AFTER: catch(command(commands[count])); `` Re-verified: the identical "Illegal to move" line recurred a second time during the stress test above (same room, same step) and this time did not abort the guide's schedule — it simply fell into the ordinary "拉起你的手" recovery branch like any other separation, which is the intended degraded behavior.

Bug 2 candidate investigated, NOT confirmed as a real bug (documented so the next pass doesn't re-chase it)

While repeatedly registering/re-registering the same test id in quick, overlapping succession early in this pass (deliberately messy, back-to-back connections, several left to time out mid character- creation), one register → immediate-reconnect sequence surfaced the "您要将另一个连线中的相同人物赶出去,取而代之吗?" (duplicate-login) prompt where a clean netdead-reconnect was expected — looked exactly like regid.lpc's register_char()'s destruct(body) (on the very object that is itself the current interactive session) not actually freeing the id in time for a fast subsequent reconnect. Investigated at length (traced enter_world()/reconnect()/find_body() in logind.lpc, net_dead()/reconnect() in clone/user/user.lpc and clone/user/login.lpc). Could not reproduce it under a controlled, single-character, non-overlapping test (cetest: register → new password → immediate reconnect, repeated cleanly multiple times, always a clean netdead-reconnect, never the duplicate-login prompt). Current conclusion: the one observed instance was almost certainly an artifact of the deliberately chaotic overlapping-connections test pattern used earlier in this pass (multiple abandoned in-flight LOGIN_OB registrations for the same id) rather than a reproducible bug in the real register/reconnect path — recorded here, not filed as a bug, so a future pass doesn't need to re-investigate from scratch.

Also verified working, no bugs found

Explicitly NOT verified live (honest gaps, not silently skipped)

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

侠客行 XKX 基础版。

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

§7.112 跨库扫描修复(无常 NPC 重连触发重复轮回链)

§7.100 房间基类 replace_program() 扫尾修复(2026-08-19)

ROOM 宏(同一份 include/globals.h,重复定义两次,后一个 /inherit/room/room 生效,宏名不受影响)在本档案 2,210 处房间文件 的 create() 里紧跟 inherit ROOM; 之后又多余调用了一次 replace_program(ROOM);——AGENTS.md §7.100 记录的同一个休眠 bug, 和姊妹档案 bmxkx2001(同一份 clone/misc/roommaker.lpc 工具、 同一批受影响房间)逐字节一致。用 fix_710_room.py 扫过 work/, 删除 2,209 处标准形状;clone/misc/roommaker.lpc 剩下 1 处字符串 拼接变体,手工改成 str += "\n\tsetup();\n}\n";。修复后 work/ 下 0 处存活残留,278 处转档之前已注释掉的 // 行原样保留, work/data/ 下没有真实 .lpc 源码命中。git diff --stat 显示 2209 个文件净删 2210 行,与脚本自报数字 + 1 处手工编辑吻合。

驱动干净启动(零新增编译错误、端口正常监听、debug.log 无任何 "cannot replace"/"cannot bind"行),巫师账号 fluffos/Mud@2026 登录后确认"目前权限:(admin)",look/goto 走读了 2 个曾经命中 过这个 bug 的房间(d/zhongnan/zoudao4.lpc、 kungfu/class/baituo/btyard.lpc,与 bmxkx2001 完全相同)均正 常,quit 干净退出。登录存档的时间戳增量已用 git checkout HEAD -- 撤销,未落入提交。

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

Deep functional test (round two), standing cross-cutting pattern audit (this session)

The earlier "深度功能测试 / Deep functional test (round two)" section above (commit 148389b7af4) already did the full hands-on playthrough this project's §10.7 methodology calls for (registration, organic skill learning, safe sparring, sect-contact attempt, quit/reconnect, disconnect handling, admin re-verification) and found/fixed the longx.lpc double-call_out driver segfault. This pass did not repeat that playthrough (redoing it would just duplicate already-solid, still-current evidence); instead it audited this lib specifically against every standing cross-cutting bug-pattern class documented in AGENTS.md as of 2026-08-27 (§7.121/§7.122/§7.123/§7.124/§7.126/§7.129 through §7.135, §8.3a, plus the wimpy/env-wimpy dbase-key check), since most of those entries post-date the July pass and had never been checked against this specific lib. Native driver (~/src/fluffos/build-debug/src/driver config.fluffos), scripts/mudclient.py, admin account fluffos/Mud@2026.

Bug found and fixed: §8.3a private-demotion, THREE more instances in this lib (new confirmed instances of an existing AGENTS.md class)

This exact "ES II → XKX" lineage is independently on record in AGENTS.md §8.3a as having this bug in xuanjianlu, zjdyaryl, revive and (via a from-scratch discovery) demonangel — always in the same two file/function shapes (feature/action.lpc's eval_function(), std|inherit/item/combined.lpc's destruct_me()). xkx2001 turned out to carry the identical, previously-unchecked bug in all three places it can occur in this archive:

1. feature/action.lpc:70 — private void eval_function(function fun) { evaluate(fun); }, the sole callback target of start_call_out() (the shared "temporary condition recovery call_out" primitive; grep found 66 files calling start_call_out(), almost entirely kungfu/skill/*.lpc buff/DoT/ timed-status effects). feature/action.lpc is inherited into every character via inherit/char/char.lpc's inherit F_ACTION;, so eval_function becomes DECL_HIDDEN once inherited and every start_call_out()-scheduled callback across all 66 files would silently fail to fire (apply() with insufficient permission ... needs: private, has: hidden) the moment it actually fired, well after the triggering action itself looked completely normal — exactly the failure mode AGENTS.md's write-up describes. 2. inherit/item/combined.lpc:19 — private void destruct_me() { destruct(this_object()); }, the sole callback target of set_amount()'s v==0 branch (::move(VOID_OB); call_out("destruct_me", 1);). inherit/item/money.lpc (the base of every gold/silver/coin object in the game) inherits COMBINED_ITEM and does not define its own destruct_me, so this is the exact "money spent to exactly 0 orphans a permanent VOID_OB clone" shape called out for xuanjianlu — every time any player's coin/silver/ gold stack hits exactly 0 (an everyday shopping event), the spent currency object would move to VOID_OB and then leak there forever instead of actually destructing. 3. d/xueshan/inherit/liquid_content.lpc:26 — byte-identical private void destruct_me() / set_amount() shape, inherited by d/xueshan/obj/suyou.lpc (a liquid-container item) with no local override — same leak class, smaller blast radius (one item file).

Fix: dropped private from all three (kept every other qualifier; none of the three needed nomask since nothing else in the codebase overrides them). Mechanical, one-line-per-file, behavior-preserving — matches the standard remedy already established for this exact bug class.

Live-verified, both distinct call_out target shapes, via the seeded admin account:

Sweep methodology for future sibling checks: grepped every private\s+(?:nomask\s+)?(?:varargs\s+)?TYPE NAME( declaration in the whole archive against call_out("NAME"/add_action("NAME" occurring in the *same* file, then hand-filtered the ~50 raw hits down to real instances by checking whether that file is ever inherited by a *different* file (the DECL_HIDDEN-on-inherit bug only bites across an inheritance boundary — a private function whose only caller is the same standalone leaf/clone object that declares it, e.g. dozens of go_home/remove_effect/destruct_me-in-their-own-weapon-file patterns found in this same grep, is unaffected and was correctly left alone). feature/command.lpc's command_hook itself was independently re-checked and is not private here (nomask int command_hook) — already correct, unlike the sibling libs where that specific function needed the fix.

Everything else audited this pass: confirmed clean, no fix needed

Live verification summary

Clean boot (native driver, zero compile errors/warnings-as-errors, zero debug.log fatal/segfault lines across the whole session). Logged in as fluffos/Mud@2026 (目前权限:(admin) confirmed), landed correctly at the 沙滩 starting room. Ran the two live repros above (eval_function via start_call_out, destruct_me via a coin spent to 0) with debug.log clean throughout, then quit cleanly. No test-character saves were created this pass (all testing used the existing seeded admin account plus throwaway new()-cloned test objects that self-destructed or were never saved); no additional save-file cleanup needed beyond reverting the admin account's own login-timestamp/hunger-tick save drift from this session's testing (git checkout HEAD -- on work/data/{login,user}/f/fluffos.o) and removing the disposable work/tmp/tmp_eval.lpc eval scratch file.

AGENTS.md §7.19 fix: enable_player() reentrancy from init()

feature/command.lpc's enable_player() (wrapper around enable_commands()) was reachable from an NPC's init(): the shared inherit/char/char.lpc setup() (called from every character's create()) itself calls enable_player(), and d/zhongnan/npc/killer.lpc redundantly calls setup() again from inside its own init() (on top of the setup() its create() already made) -- same shape as the originally-documented mhxy zhangmen.lpc case. enable_commands() is only safe to call from create(): calling it again on an object already living() makes the driver re-invoke that same object's init() as a side effect, which recurses back into enable_player() on the same call stack until "Too deep recursion" aborts the boot on a room's first-ever visit. Fixed with a true reentrancy flag (in_enable_player_now), NOT a living() guard (which would break legitimate re-enables from revive() in feature/damage.lpc and wakeup()/wakeup2() in cmds/std/sleep.lpc, both confirmed to re-invoke enable_player() on this lib while the object is still living()). Verified via lpcc --batch single-file compile check (PASS). Part of the corpus-wide §7.19 sweep (Batch E).

深度功能测试(2026-09-04,round three,shop + 拜师)

新角度:扬州醉仙楼购物 + 丐帮左全拜师。round two 诚实记录「Shop list/buy at an actual merchant: not completed」(侠客岛起始区没有商店)以及「Full sect enrollment 未完成」(只测了凌逍按辈分拒收)。巫师 goto 可以直接 离开岛屿。

实测过程

管理员 fluffos / Mud@2026 / 浮浮。第一输入是 Do you want to use BIG5 code?(y/n),回 n 用 GB。落地侠客岛沙滩 /d/xiakedao/shatan。 score 「【天帝】普通百姓」。log_error() 已有 arning: 闸门,本轮 屏幕上没有编译警告倾泻。fluffos 的 wiz_sites 是 .*。

goto /d/city/zuixianlou,list 烤鸡腿四十文铜板 / 牛皮酒袋五十文 / 包子三十文。F_DEALER 里丐帮穷叫化拒绝购买整段是注释掉的,仍按家族惯例 先买再拜。clone /clone/money/gold 后 buy jitui 成功(「你从店小二那 里买下了一根烤鸡腿」)。i 剩六十文铜钱 + 九十九两白银(10000−40 = 9960),黄金条目消失,找零数学正确。

goto /d/gaibang/inhole,左全源码是 kungfu/class/gaibang/zuo-quan.lpc, apprentice zuo 一次成功:左全收徒,score 「丐帮一袋弟子」、师父左全、 恭喜成为第二十代弟子。cmds/usr/save.lpc 真正调用两个 save()。 user.o 立刻带上 family_name":"丐帮" / master_name":"左全" / generation":20。断线后再连(「重新连线完毕」),称谓/师傅/银子/鸡腿 还在。左全只收男性。

本轮没有新的 programming bug。live debug.log 是 libs/xkx2001/log/debug.log(Boot Time Fri Sep 4 02:46:07 2026),无 error: / Too deep recursion。管理员存档未提交。

深度功能测试(§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)— compile-warning and load-failure pass

scripts/lpc_warnings.py xkx2001 (8123 .lpc): 3380 distinct diagnostics and 154 files that did not load at the start, 160 diagnostics and 120 files at the end (8003 load). 1159 files changed. The 160 left are 151 errors in the dead files listed below and 9 warnings in the same places (8 in four non-loading copies of tomb.lpc, 1 artifact). A load check of every file at HEAD and in the working tree (700 files per VM boot): 7965 PASS -> 7999 PASS, 0 files that loaded before fail now, 34 more load. A live pair (HEAD vs working tree, same admin save, 36 commands): HEAD printed 44 compile or runtime error blocks for the rooms below, the working tree none.

Bulk (scan tools, KB 04 §6.10). 2174 unused locals in 955 files (the object me = this_player(), ob, ... boilerplate; scripts/lpc_audit_removed_locals.py listed 44 hits, all of them names declared again in an inner block or parameters of another function, none a global that a removed local used to shadow); 215 short() diamonds from one duplicate in feature/food.lpc (the driver already used name.lpc's); 64 prototypes whose return type disagreed with the definition; 49 header macros that clash with another header (#undef in front of the second #define: include/config.h MUDLIST_BAK x8, globals.h ROOM, daemons.h, ftpdconf.h, harbor.lpc SHIP ...); 36 redundant leaf inherits; 44 files with a clean_up() diamond (feature/sserver.lpc and NPC/ROOM/SKILL/ITEM both inherit F_CLEAN_UP): 42 leaves name the winner (lpc_name_winner.py; d/zhongnan/dajiaochang.lpc below its #include), d/city/{n,s}proom.lpc and d/zhongnan/beidou.h drop their own redundant inherit F_CLEAN_UP; (the 165 perform files that inherit only F_SSERVER need sserver.lpc's inherit, so the base keeps it); 56 nosave functions made protected, 15 un-nosaved; 85 unknown escapes; 63 varargs; 62 bare return; -> return 0;; 10 #pragma lines (save_binary, optimize all); DEBUG(...) is a variadic macro this driver does not have, so every call was a statement ("text", n);: genmap.lpc, traverser.lpc and kungfu/skill/linji-zhuang/youming.lpc get a real varargs void DEBUG(string fmt, mixed arg) {}.

Typos the no-side-effects list exposed (each read, then fixed to what the line plainly meant).

Rooms and objects that never loaded, repaired (live-checked).

Left alone, with the reason (120 files that do not load).

The same ~100 files are dead in bmxkx2001, jym, shenzhou, xkm, xkx2000zxb and xuanjianlu; the repairs above are ported to them with lpc_twin_port.py.

深度功能测试(§10.7,2026-10-05)— paths named in another case, the peak-view rooms

The ten tea items d/city/npc/tea/*.C (the stock of d/city/npc/c_waiter.lpc: __DIR__ "tea/longjing", ...) were stored with an upper-case extension and never loaded: scripts/lpc_case_paths.py --apply xkx2001 renamed them to .lpc (KB 03 §4.5; the renames went into the xkm commit by accident of staging, see git log --follow); the tool found nothing else to rename in this lib. d/wudang/taoyuan/tyroad4-7.lpc included their header through __DIR_"feng.h" (one trailing underscore, Bad #include directive) and now #include "/d/wudang/feng.h" (the sweep commit of 40 libs). Rescan after both: 152 distinct diagnostics, 8017 files compile and 116 do not (160 / 8003 / 120 before); scripts/lpc_check_files.py: the ten tea items, c_waiter and the four rooms load.

深度功能测试(§10.7,2026-10-05)— room base survives a raising NPC

scripts/lpc_resilient_room.py --apply: make_inventory() of inherit/room/room.lpc loads each inventory object as if (catch(ob = new(file)) || !objectp(ob)) return 0;. new() of a file that is not there returns 0 (the room just lacks that NPC), but new() of a file whose create() raises (a skill file for set_skill() this archive never had, a carry_object() item that is not there, a header that never shipped) raised inside the room and the room did not load at all. The wrong-case renames of this lib (the section above) make the NPCs behind them reachable for the first time, so a room that holds one with such a content gap would have gone from "NPC missing" to "room missing"; now it loads without that NPC, as it did before. The room base compiles and loads with the change (HEAD against the working tree). KB 03 §4.5.

深度功能测试(§10.7,2026-10-11):dns_master 空 if

深度功能测试(§10.7,2026-10-11):dns_master 空 if

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

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

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

另有 1 个文件(绝招、状态、物品、NPC 等,不限于 kungfu/skill)来自 gin/kee/sen/force 体系,同样按 kee→qi、gin/sen→jing、force→neili 改名(scripts/lpc_port_es_props.py --any-file)。

内力/上限当作伤害种类(补)(2026-10-15)

victim->receive_wound("force", n, me)(兵器吸内力)、receive_damage("max_qi", random(100))(走火入魔扣上限)等本库不接受的种类,1 处改为 add(type, -N)。

message_vision(msg, environment(me), me)(2026-10-15,KB 06 §7.242)

把房间当成了动作者:message_vision() 取房间的 name() 得 0,replace_string(msg, "$N", 0) 报错。在状态(中毒)里,这个错误发生在 apply_condition(name, duration - 1) 之前,状态永不结束,心跳里后面的恢复也每次被跳过。改为 message_vision(msg, me):/kungfu/condition/laugh、/kungfu/condition/sanpoison、/kungfu/condition/xianglu-du。

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。6 处补上 if (!mapp(fam)) fam = ([ ]);(刚 recruit_apprentice() 之后的除外)。

空手使用兵器绝招(2026-10-15,KB 06 §7.246)

perform 指令允许直接点名招式(如 perform stick.ban),这些绝招读了 me->query_temp("weapon") 却从不检查,空手时 weapon->name() 报错(有的已先扣了内力)。在赋值后补 if (!objectp(weapon)) return notify_fail("你必须手持兵器才能使用这一招。\n");(scripts/lpc_fix_perform_weapon.py):dagou-bang/ban、dagou-bang/chan、dagou-bang/sansha、damo-jian/weituo、huashan-jianfa/feilong、jinshe-zhuifa/huayu、jinshe-zhuifa/tuwu、liangyi-dao/sanshen、mo-bang/mo、quanzhen-jian/lianhuan、riyue-bian/chanrao、taiji-jian/tongshou 等 16 个。

把经验、属性、内力当技能读(2026-10-15,KB 06 §7.247)

经验、属性、内力上限不是技能,xkx/ES 的 query_skill() 对它们只返回 0(风云的还会去载入同名技能文件而报错)。改为 query("...")(scripts/lpc_fix_pool_as_skill.py):驯马的 /d/dali/npc/mafu(成功率少了福缘一项)。

临济庄 remove 没有运功时报错(2026-10-15)

linji-zhuang/remove 直接 switch (lj_exert["name"]),lj_exert 是 query_temp("lj_exert");没有正在运用的临济庄功夫时它是 0,exert remove 每次都报 Value being indexed is zero。补上 if (!mapp(lj_exert)) return notify_fail(...)。本库的临济庄各 exert 都不设置 lj_exert,所以这个 exert 以前每次都报错,现在只会提示没有在运功。

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

query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):xiaowu-xiang→xiaowuxiang(liuyang-zhang、liuyang-zhang/qufu、liuyang-zhang/zhongfu、ruyi-dao、ruyi-dao/lianhuan、tianyu-qijian 等 9 个文件);shenhuo-xuanming→shenghuo-xuanming(/d/kunlun/skill/dafeng-yunfei、dafeng-yunfei、xiaoyao-zhangfa);quanzhen-jianfa→quanzhen-jian(dugu-jiujian);xiatian-gong→xiantian-gong(/d/zhongnan/skill/chunyang-quan)。

嵩山剑法「蓄力」没穿布衣时报错(2026-10-15)

songshan-jian/xuli 用 cloth = me->query_temp("armor/cloth") 取衣服,随后直接 cloth->query("material") 判断要不要显示“衣袖鼓起”的描写;身上没有布衣时(裸身或只穿铠甲)这里报错,而此时 apply/speed 已经扣掉、复原的 call_out 还没排上,身法就一直少了一截。两处判断前补 objectp(cloth) &&。

技能名拼错:技能文件里拼错自己的名字(2026-10-15,KB 06 §7.249)

技能自己的文件(或它的绝招目录)里,query_skill() 读的是差一个字母的自己的名字,永远得 0:“基本功夫要高过本技能”之类的限制从不生效,按等级取招、计算伤害的那一项一直是 0。改为本技能的名字(scripts/lpc_fix_skill_typo.py 的 self 判据):baitao-xuangong→bitao-xuangong(bitao-xuangong)。

地图数据库 mapdb 的空档防护(2026-10-15,KB 06 §7.250)

genmap 第一次载入时 if (!restore()) save(); 会在地图建好之前写出只有文件头的 data/npc/map.o;mapdb 读它成功后 map 为 0,所有查询都对 0 取下标(nitan170911、shenzhou 等 10 个库就带着这样的文件)。没有 map.o 时 query_random_room_path() 又对空数组取下标。mapdb(/clone/obj/mapdb.lpc)加一个 restore():读档后 map 不是映射就重置为 ([]);地图为空时 query_random_room_path() 返回 0。

随机取房间后不判断就过滤(2026-10-15,KB 06 §7.250)

/d/city/npc/ftb_zhu.lpc、/d/qilian/obj/jinhe.lpc 从 mapdb 随机取一个房间后直接 strsrch(room_path, ...) 排除区域;地图还没生成时取回的是 0,strsrch(0, ...) 报错,护镖等任务就此中断。条件前补 !stringp(room_path) ||,取不到房间时走原有的重试和“cannot find target room”提示。

临济庄「幽冥桩」的两处笔误(2026-10-15,KB 06 §7.245、§7.250)

linji-zhuang/youming:killer->querry(...)、me->querry(...) 调用的是不存在的函数,永远得 0,恶人比值只按 PK 数算,改为 query;visit_room() 先拿 range 作判断、后从参数里取 range,判断时它还是 0,效果遇到没载入的房间就停,两行取值挪到前面。

银索金铃「点穴」对空手的对手报错(2026-10-15,KB 06 §7.246)

yinsuo-jinling/dian 失手时三种描写之一是“真气贯注”加对手兵器的名字,对手空手时 ob 为 0,三次失手有一次报错。改为 (objectp(ob) ? ob->query("name") : "全身")(泥潭一系原本就写作“真气贯注全身”)。

npc_setup 的浮点截断警告(2026-10-15,KB 04 §6.10)

/clone/obj/npc_setup.lpc:level = pow(10 * exp, 0.333334); 把浮点数隐式截成整数,编译时警告;改为 to_int(...),结果不变。

用兵器的绝招空手施展时报错(内联写法)(2026-10-15,KB 06 §7.246)

绝招不存兵器变量,直接写 (me->query_temp("weapon"))->query("name")(或在拒绝判断里写 (weapon = me->query_temp("weapon"))->query("skill_type")),之前又没检查兵器:空手 perform 时报 Bad argument 1 to EFUN call_other(),内力等往往已经扣掉。在第一次用到它的那条语句前补上“你必须手持兵器才能使用这一招”的拒绝(scripts/lpc_fix_perform_weapon.py):duanyun-bian/duanyun、feiyu-bian/chanrao、yunu-jianfa/suxin。

移进不存在的巫师房间(2026-10-15,KB 06 §7.251)

用完的物品、要“离开”的 NPC、掷在空中的兵器被移进巫师的工作室(/u/aln/、/u/ryu/、/u/wsky/),本库没有这些房间。本库的 move() 用 call_other(dest, "???") 载入目的地,找不到文件就报 call_other() couldn't find object,后面的语句都不执行:药(人参、狐狸皮、虎骨,clone/drug/cb_renshen、clone/drug/hugu、clone/drug/hulipi)吃下后药效已经生效,随后报错:call_out("destroy", 31) 没有排上,药一直留在身上,药效过去后可以再吃;逍遥三笑散(d/xingxiu/npc/obj/sanxiao)弹出之后报错,毒药留在身上,可以一再使用;疯魔杖法的掷杖绝招(kungfu/skill/fengmo-zhang/feizhang、kungfu/skill/fengmo-zhang/fengmo):内力、精力已扣,杖已卸下,随后报错;接回杖的 check_fight 没有排上,fengmo 标记不再清除,重新登录之前不能再用;天山杖法「飞杖」(kungfu/skill/tianshan-zhang/feizhang):杖“撞到山石上断成无数截”之后报错,杖仍留在身上。7 个文件改为移进本库的虚空房间 VOID_OB(/clone/misc/void),与原作把它们“停放”在巫师房间的用意相同。

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

25 个文件、35 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/lingjiu/obj/qingyi、/d/lingjiu/obj/doupeng、/d/shenlong/obj/cloth)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 0 个,改后 25 个(25 个原来载入不了),没有退步。

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 载入不了,刷出它的房间也少一个人。本库有 4 个对象设了本库没有文件的武功(4 个名字,最多的是 stuff、huashan-zhangfa、jingding-zhang)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:4 个里 4 个通过。(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 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 19 处、16 个文件:lingji-zhuang→linji-zhuang(7 处)、dashouyin→dashou-yin(5 处)、pudu-chang→pudu-zhang(2 处)、stuff→staff(1 处)、huagu-mainzhang→huagu-mianzhang(1 处)、banrou-zhang→banruo-zhang(1 处)、jingding-zhang→jinding-zhang(1 处)、bahuangh-gong→bahuang-gong(1 处)。(scripts/lpc_fix_skill_names.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() 里 6 个函数开头补上 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 文件。

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") && ...):/d/gaibang/ml.h(1 处)。

比较链的人工修正(2026-10-20,KB 06 §7.282)

d/hangzhou/huagang1.lpc:挖泥的判断 arg != "泥" || arg != "mud" 永远为真,随便挖什么词都挖出湿泥,后面的“你要挖什么?”走不到;本意是参数是“泥”或 mud 时才挖,改成 == 和 ||。

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,共 19 处)。改动:/d/city/jiuguan2.lpc(2 处)、/d/city/npc/mj.lpc(3 处)、/d/city/npc/mjxiaosi.lpc(3 处)、/d/city/wumiao.lpc(1 处)、/d/city/zuixianlou.lpc(1 处)、/d/heimuya/jiudian.lpc(1 处)、/d/kunlun/luanshiduibak.lpc(2 处)、/d/quanzhou/qzkedian.lpc(1 处)、/d/quanzhou/wglou1.lpc(1 处)、/d/xiakedao/no_pk_room.lpc(1 处)、/kungfu/skill/kurong-changong/liaoshang.lpc(3 处)。

登录密码超过 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)。

翻页器 start_more(read_file(...)):文件缺失时 explode(0)(2026-10-20,KB 06 §7.300)

东方故事系的翻页器 void start_more(string msg) { ... explode(msg, "\n") ... } 被这样调用:start_more(read_file(HELP_DIR + "help/topics"))、start_more(read_file(NEWS))。压缩包没带的文件使 read_file() 回 0,explode(0, ...) 在本驱动里报 Bad argument 1 to explode()(不带参数的 help 指令就是这样出错的),老驱动把 0 当空文本。在 start_more 开头加了 if (!stringp(msg)) return;(scripts/lpc_fix_start_more.py)。改动:/feature/more.lpc。

巫师命令不带参数时对 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/wiz/test.lpc(present)。加载检查 HEAD 与工作树对照,没有退步。

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

计时器或输入提示给驱动的函数名和文件里定义的差一个字母;驱动对不存在的函数静默不理,所以对应的计时从来没有触发过。调用和配套的 remove_call_out() 改成已定义的拼法:药炉:burning2 / burning3 → 文件里定义的 buring2 / buring3;do_destroy → 文件里定义的 do_destory。改动:/d/huanghe/npc/tc_wang.lpc、/d/xingxiu/npc/obj/yaolu.lpc、/d/xingxiu/obj/yaolu.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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 14 条注册(9 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/beijing/obj/kuanglu.lpc(do_look)、/d/kunlun/npc/mingjiao_npc.lpc(do_qingjiao)、/d/quanzhou/npc/gongping-zi.lpc(do_bihua、do_halt)、/d/shenlong/obj/sg_mianzhao.lpc(do_open)、/d/xiakedao/obj/car.lpc(do_hire)、/d/xingxiu/npc/feng.lpc(do_ketou)、/d/xingxiu/obj/sanxiao.lpc(throw_ob2)、/inherit/misc/fboard.lpc(do_list)、/kungfu/class/mingjiao/mingjiao_npc.lpc(do_qingjiao)。最多的函数名:do_bihua 3 处、do_qingjiao 2 处、do_halt 2 处、do_hire 2 处、do_look 1 处。

注册到不存在函数的 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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 11 条注册(2 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/xingxiu/xueling/exit1.lpc(do_ling)、/kungfu/class/gaibang/zhulin3.lpc(do_bake、do_cut、do_kao、do_remove等)。最多的函数名:do_cut 2 处、do_remove 2 处、do_wash 2 处、do_wrap 2 处、do_ling 1 处。

基类的 add_action 登记:处理函数只在后代里定义,基类自己被加载时报错(2026-10-21,KB 06 §7.349)

房间、NPC、公告板这类基类,init() 里登记的 add_action("函数", "动词") 的函数只由继承它的后代定义;基类对象自己被直接加载(房间可以走进去、NPC 可以克隆)时,访客输入这个动词,驱动报 *Function for verb 'x' not found.(第一轮扫描把这些当成“有后代定义,是活的”放过了,scripts/lpc_verify_handlers.py 在基类对象上跑 function_exists() 才看出来)。登记前加 if (function_exists("函数", this_object(), 1))(第三个参数 1:默认 function_exists() 把 protected / private 函数当作没有定义),后代照常登记。共 6 处、1 个文件(scripts/lpc_fix_dead_add_action.py --guard):/d/bwdh/kantai.lpc(do_abandon、do_practice、do_study)。

对象路径里的 ..:new(__DIR__ "../obj/x") 报 Read access denied(2026-10-22,KB 06 §7.351)

__DIR__ 是当前文件所在目录(带结尾斜杠),__DIR__ "../obj/x" 就是 /d/zhiye/npc/../obj/x;驱动的 legal_path() 不接受带 .. 的路径,new() / load_object() 直接抛 *Read access denied.(实测,find_object() 对它答 0)。走库自己的 carry_object() / 房间 objects 表的路径,库的基类会先规范化,能用;直接把路径交给 new() / clone_object() / load_object() / move() 的不行,调用到那一步才报错。改成规范化后的绝对路径(scripts/lpc_fix_dotdot_dir.py --direct):/d/wudang/npc/xiaocui.lpc(__DIR__ "../obj/nuerxiang" → "/d/wudang/obj/nuerxiang")。

(: 函数 :) 指针指向对象自己没有定义的函数(2026-10-22,KB 06 §7.355)

item_desc / inquiry / chat_msg_combat 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:编译不报错,用到时驱动报 *Undefined lfun pointer called: 函数名(look 某物、ask NPC about 话题 直接出错,战斗 / 闲聊里的指针在 NPC 的 chat() 里出错,这一拍心跳的后半段被跳过)。scripts/lpc_scan_callout_targets.py --kinds=lfun。 还剩 12 个函数没有补(inquiry 6, chat_msg_combat 6):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/d/city/npc/hua_girl.lpc:ask_me、/d/emei/npc/guide.lpc:ask_job_begin、/d/emei/npc/guide.lpc:ask_job_finish、/d/huanghe/npc/tc_hu.lpc:ask_job、/d/huanghe/npc/tc_you.lpc:ask_join、/d/shaolin/npc/xuanci.lpc:ask_tiaoshui、/d/xiakedao/npc/huang.lpc:auto_perform、/d/xiakedao/npc/laozu.lpc:auto_perform、/kungfu/class/murong/baichuan.lpc:auto_perform、/kungfu/class/shenlong/pang.lpc:auto_perform 等另外 2 个。

读不到的留言板:"/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/pigroom_b;/d/city/duchang2.lpc)注释掉并写明原因(别的房间里同样的语句本来就是作者自己注释掉的)。证明和 45 个库的整体加载检查见 KB 06 §7.357。

log/ 下的日志子目录没有人建:写日志的 write_file() / log_file() 报错(2026-10-22,KB 06 §7.358)

这个库的代码往 log/ 的 2 个子目录写日志:log/beijing(d/beijing/helper.lpc)、log/test(clone/npc/shan.lpc)。压缩包、git clone 和站点包里都没有这些目录(本地启动没有写过它们,gen_keep_dirs.py 没记),write_file() 对不存在的目录抛 *Wrong permissions for opening file /log/dlog/money for append. "No such file or directory",调用它的函数在写日志那一行中断,后面的奖励、标志、步骤都没有执行。本库的 log_file() 是 write_file(LOG_DIR + file, text),目录不存在就抛同样的错误(实测)。把 log/beijing、log/test 记进 scripts/wasm_keep_dirs.txt(scripts/lpc_keep_log_dirs.py;站点和 pristine 树按它建空目录)。证明(scripts/lpcc_eval.py,在 /adm/daemons 这类目录下的求值对象里写 /log/zzdirprobe/...,新进程)见 KB 06 §7.358 的实测表。

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

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

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

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

本库 115 处(48 个文件):拼名字的赋值 89 处、与汉字常量比较 25 处、改名官的长度检查 1 处。例:name[0..01] → name[0..0](/clone/npc/qiu.lpc:287);name[4..5] → name[2..2](/clone/npc/qiu.lpc:287);name[0..3] → name[0..1](/d/city/npc/shiye.lpc:129)。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,不要重跑。

按字节写的汉字循环、茶馆猜诗和同盟频道标题(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 区间 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(一格两个空格)。本库 3 处(1 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。

段延庆 yanqing 空格污染无法加载(2026-10-27,KB 04 §6.25 Also)

kungfu/class/dali/yanqing.lpc:汉字与 dbase 键都被插空格(" id "、" family / master_id "、空格版 say),另有 query("PKS) 缺引号和嵌套 void init。无法就地修补;用同系已修好的 xkm 副本覆盖(bmxkx2001 / xkx2001 / xuanjianlu 三份原相同)。证明:HEAD 加载失败;改后加载成功。

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