info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
由 "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
内容亮点
- 以"侠客岛"为背景的武侠文字 MUD:新角色先在侠客岛的沙滩上苏醒,由 岛上的"赏善使"张三或"罚恶使"李四迎接并带往登记处办理入岛手续,之后 才正式踏入江湖。
- 游戏世界涵盖少林、武当、华山、丐帮、星宿派等经典金庸门派地图,包含 完整的门派拜师、内功修炼、江湖任务与帮派系统。
- 新手接引流程颇具特色:登岛后只允许极少数指令,需跟随迎宾 NPC 走到 "侠客岛挂名处"用
register <邮箱>完成登记,系统会生成一个随机新 密码并断开连接——这是老式防小号验证机制,用新密码重连即可继续。
在线试玩
https://mudlibs.fluffos.info/xkx2001/
管理员账号 / Admin account
- id:
fluffos - 密码 / password:
Mud@2026(已通过游戏内passwd指令将注册流程随机分配的密码改为该标准密码。) - 中文名 / display name: 浮浮
- 权限 / level:
(admin)—— 本 lib 最高权限。授权数据存放在data/securityd.o(wiz_status+wiz_sites,后者必须有.*之类的 登录地址模式,否则巫师账号无法登录)。
警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码(游戏内 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)
- LPC formatter applied (
tools/lpc-syntax, allwork/*.lpc): 8,074 files reformatted, 46 unchanged, 9 refused (self-check failures on messy legacy code, expected). Confirmed the §15stell_room()fix inadm/simul_efun/message.lpcsurvived reformatting intact. - Native re-test against the freshly rebuilt driver (
~/src/fluffos/build-debug/src/driver, rebuilt from latest upstream master): boots clean, zeroFATAL/SIGSEGV/执行时段错误indebug.log. Full registration verified end-to-end with real Chinese names, including this lib's distinctive onboarding mechanic: id/name/ password/gift/email/gender -> lands on the "侠客岛" beach -> greeter NPCfollow-escorts the player (auto-drags after a delay) to the "侠客岛挂名处" (registration desk) ->register <email>generates a brand-new system password and disconnects -> reconnect with the new id+password ->look/score/quitall correct in the real starting room. Verified with idqretkxr/real name 秦云归来 (male), new passwordychifissued byregister, reconnect succeeded and reached the beach's continuation room with correct character sheet. - WASM build tested (
~/src/fluffos/build-wasm/srcviascripts/wasm_client.js): boots cleanly (only expected non-fatal preload warnings, e.g.Macro 'ROOM' redefinedand no-sockets- package notices). Registration succeeded end-to-end under WASM, including the full beach -> follow -> register-desk -> new-password flow — real Chinese name 秦云网四 (idqfwaskc),registercorrectly issued a new password (slrze) and disconnected, exactly matching native behavior. Contrary to this project's general expectation for this lib (this lib's own login/ban checks —BAN_D->is_banned(query_ip_name(ob))andREGBAN_D->is_banned(query_ip_name(ob))inlogind.lpc— usequery_ip_name(), notquery_ip_number(), and neither gated the registration path in this pass's testing), this lib's own registration flow was NOT observed to be blocked by the documentedquery_ip_number()-format WASM limitation in the portion tested. The one step not further exercised under WASM: reconnecting with theregister-issued new password to complete the loop, sincewasm_client.jsboots a fresh in-memory filesystem per invocation and has no built-in way to reconnect to an already-disconnected session within one process (a harness limitation, not a mudlib or driver bug) — the native re-test above already fully covers that final step. The generalquery_ip_number()WASM limitation described in AGENTS.md remains real and does affect other libs in this batch (seexyzxfk/xyzxfy2's NOTES.md, whoseBAN_Dchecks usequery_ip_number()directly) — it just doesn't reach this particular lib's tested registration path.
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):
- id
qinxia, 秦霞 (female), passwordgzctc(system-issued byregister, original self-chosen password wasXia@2026but the in-gameregisterstep always overwrites it with a random one — see the onboarding flow documented earlier in this file). Main playthrough character: completed the full 沙滩 → escort → 挂名处 →register→ new-password reconnect flow; learned 基本内功 (force) to level 1 via the organic teacher-NPC path (蓝衣弟子/diziat 瀑布, a non-sect basic-skills trainer reached vianorthupfrom 望海亭); fought a real, safefight dizisparring match to conclusion (气 dropped visibly, match ended cleanly with "承让", no death risk, confirming this lib's own safe-sparring mechanism —inherit/char/ npc.lpc'saccept_fight()— works exactly asdoc/help/newbie.dec99describes); received a free 短剑 from 黄衣大汉 (han) viaask han about 武器; attemptedbai lingxiao(凌逍, a real 华山 sect member reachable directly on the island,create_familyconfirmed) and was correctly declined with "要拜师,你得去拜我师父" (game design: a generation-14 disciple can't take his own apprentices, redirects to his own master) — confirms the sect-join code path is live and reachable from the island itself, not just via off-island travel. Note: theforceskill progress above did not survive the driver crash described below (unsaved at the time) — re-verified the teacher-NPC learning mechanism works but did not re-grind it back to level 1 after the restart, not worth the tool-time. - id
cetest, 测试 (female), passwordkbzeh— a disposable second character used specifically to rule out a suspected registration- reconnect race (see Bug 2 below): repeated register → reconnect cycles with careful, non-overlapping connections all completed cleanly with no duplicate-login prompt. - id
linxue, 林雪 (female), passwordnybhj— used specifically to reproduce and re-verify Bug 1 (the crash) end to end with a real player, deliberately left un-followed through the full escort route (d/xiakedao/npc/longx.lpc'smove_next(), all 9 scripted steps: 沙滩 zone → 小路 → 迎宾厅 → 瀑布 area → 甬道 → 大山洞) long enough for the guide NPC to force-drag her (check_follow's count>2 branch) and for the fixed retry path to run repeatedly for several real minutes with zero crashes (see Bug 1's verification below).
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.
- Symptom: the driver process itself died —
Connection refusedon a subsequent connect attempt, no LPC-level error visible to any player, the crash only discoverable viadebug.log's C++ backtrace and the process actually being gone fromps. This happened spontaneously during an idle real-time wait (~2.5 minutes) while two test characters (an admin account and a fresh player) were sitting, one of them still mid-escort by this lib's own island-guide NPC (d/xiakedao/npc/longx.lpc, randomly named "龙一".."龙九" per instance — the exact same NPC that escorts literally every brand-new character from 迎宾厅 toward the island's interior after registration — see this file's own onboarding-flow section above).debug.log's crash backtrace bottoms out inside the driver's own C++call_out()implementation (src/packages/core/call_out.cc:209,while (ob->shadowing) { ob = ob->shadowing; }) — a null/danglingobject_t*dereference consistent withcall_out()being invoked for an already-freed object. - Root cause:
move_next()walks the guide NPC through a fixed script of movement commands (commands[]/places[], 9 steps) via a self-reschedulingcall_out("move_next", 10, me, count). When the escorted player becomes separated from the guide mid-step (!present(me, environment(long))— happens routinely: some steps in the script are non-directional verbs likeclimb tree/jump fallthat this driver's automatic leader/follow co-movement doesn't propagate to followers, so the two can legitimately end up in different rooms even with both still fully connected), the recovery branch explicitly re-teleports the player and reschedulescall_out("move_next", 10, me, count)with the current, unchangedcount(a retry of the same step) — but the code had noreturnafter that reschedule, so execution fell straight through into the function's own unconditional tail, which reschedulescall_out("move_next", 10, me, count+1)a second time on the same object in the same invocation. Every time this branch fires, one MORE duplicate/orphaned "move_next"call_outentry accumulates against the same NPC object; if the object is laterdestruct()ed (e.g. the ordinary "guest gave up and wandered off" cleanup path, lines 163-169) while one of these un-cancelled duplicates is still pending, the driver's own C++ call_out bookkeeping is left holding a scheduled callback against a now-freed object — which segfaults the whole process, not just the one LPC call, the next time that duplicate fires. - Why every earlier verification layer missed this: it requires (1) a real player actually being escorted by this NPC (only happens post-registration, past every smoke test's usual stopping point), (2) the player becoming separated from the guide at least once (routine during real play, essentially never during a scripted
--sendsequence that never idles mid-route), and (3) enough real wall-clock time (multiple 10scall_outcycles) for the duplicate-pileup + destruct race to actually collide — a boot-log watch or alook/score/quitsmoke test never lingers anywhere near this NPC long enough to hit it. Found purely by accident during this pass's required real-time wait for the net-dead/reconnect check below (two characters were left idle mid-escort while waiting) — exactly the kind of thing §10.7's "wait a real amount of time" step exists to catch. - Fix: add the missing
returnso the retry-reschedule path can never fall through into the unconditional one:
``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;
}
``
- Verified live, twice: (1) restarted the driver clean, waited a real ~2.5-3 minute idle window with two characters mid-escort by this exact NPC (reproducing the same conditions as the original crash) — the process stayed up the whole time,
ps/ssconfirmed it was still listening,debug.logstayed free of new fatal/segfault lines. (2) A second, harder stress test: deliberately walked a fresh character (linxue) into 迎宾厅 and left the connection open, un-followed, for the guide's full 9-step scripted route (several real minutes) — the!presentrecovery branch fired repeatedly (visible as "龙七不知从哪变了出来,拉起你的手边走边埋怨道" printing many times in a row near the route's end), i.e. the exact vulnerable branch ran dozens of times back-to-back under the fix, and the driver never crashed.update /d/xiakedao/npc/longx(as the seeded admin account) recompiled the fixed file with zero errors afterward. - Residual, lower-severity issue found while stress-testing the fix (not a crash, not separately fixed as a distinct bug, folded into the same edit below): at the route's very last step (
places[8]=/d/xiakedao/dadong, a room whose only exit issouth), the retry branch can loop indefinitely (every 10s, "拉起你的手边走边埋怨道..." repeating forever) instead of ever reaching the intended "请在这里稍候, 岛主一会便到" completion line — the guide NPC just never finishes greeting that specific player, though it also never hurts anything (no error, no leak beyond the one harmless retrycall_out). Not root-caused further given time budget; noted here rather than silently dropped. - A second, related failure mode found and fixed while investigating:
move_next()'scommand(commands[count])call (the line right before the buggy branch above) can itself throw an uncaught driver-level error — reproduced live, twice, with the exact same lib-shipped message both times:*Illegal to move or destruct an object (/d/xiakedao/yongdao3) defining actions from a verb function(north) ... which returns zero(this driver's own safety net against destructing/moving an object still on a verb-function call stack — a pre-existing quirk of this specific room transition, not introduced by anything in this pass; not further root-caused). Before Bug 1's fix, this was moot (masked by the crash). After Bug 1's fix, this alone would still silently abortmove_next()before it ever reaches the reschedule logic — leaving that guide NPC instance permanently stuck with no pendingcall_outat all (harmless, but a content regression: that NPC just stops working for that player). Wrapped the call incatch()so a failed scripted step degrades to the same "guest got left behind, drag them back" recovery path instead of derailing the whole function:
``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.
- Lineage check:
libs/bmxkx2001/work/d/xiakedao/npc/ longx.lpcis a byte-for-byte identical copy of the pre-fix file (confirmed viadiff) — same missingreturn, same vulnerable shape, almost certainly equally capable of segfaulting that lib's driver under the same real-time-wait-mid-escort conditions. Not fixed there (out of scope for this pass per the task instructions — flagged for the orchestrator/a future pass on that lib). This bug is a mudlib-source logic bug (missingreturn), not a driver-generic pattern by itself, but the *consequence* (an LPC-reachable path to a driver segfault viacall_outscheduled against a soon-to-be-freed object) is a driver-generic risk worth watching for anywhere a self-reschedulingcall_outchain has more than one reschedule call reachable from the same function body — see the draft AGENTS.md class in this task's final report.
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
- Net-dead (unclean) disconnect + reconnect, both prompt and after a real wait: reproduced constantly and incidentally throughout this pass (every
mudclient.pyinvocation in this session that didn't end in an explicitquitis itself an unclean disconnect) — every single reconnect, including several after multi-minute real waits and one across an actual driver crash/restart (which, expectedly, reverted to each character's last explicitly-*saved* location rather than their live in-memory one — normal for any crash, not a bug), correctly restored the player to their real in-game location withlook/scoreboth correct.clone/user/user.lpc'snet_dead()/reconnect()pair (destructs only the old *login* link object, rememberslink_ob/room vianosave/tempvars,reconnect()is actually wired up and called bylogind.lpc) is a sound, already- defensive implementation — this lib does not exhibit the AGENTS.md §7.20 void-parking class or the §7.21 stranded-mid-wizard class: the pre-registration character-creation wizard runs entirely on theLOGIN_OBconnection object itself (not a separate persistent player body), and that object's ownnet_dead()just destructs it after a 1-second grace timer (clone/user/login.lpc) — an unclean disconnect mid-creation simply abandons that attempt cleanly, with no permanent stranding, confirmed by reconnecting after several such abandoned attempts and always landing at a fresh您的英文名字:prompt. - Clean
quit+ real wall-clock-gap reconnect: state (location, stats, inventory) persisted correctly across a genuinequitand a later fresh login, for bothqinxiaandlinxue.debug.logstayed free oferror:/Too deep recursion/fatal lines after everyquitperformed in this pass. - Safe sparring (
fight): confirmed working exactly as documented (seeqinxia's playthrough above). - Organic skill learning: confirmed working (
xue dizi force,chashowed the new skill). - Sect-join code path: confirmed reachable and functioning as designed (
bai lingxiao, correctly declined per generation rules). - Admin account (
fluffos/Mud@2026, seeded in an earlier pass): re-verified end to end this pass — login,look,update /d/xiakedao/npc/longx(recompiled the fix live), cleanquit, all correct;(admin)privilege intact.
Explicitly NOT verified live (honest gaps, not silently skipped)
- Shop
list/buyat an actual merchant: not completed. This lib's real starting zone (d/xiakedao/, the island) is a large, fully self-contained ~100-room tutorial/quest area with no shop anywhere inside it (confirmed by grep — noinherit ... SHOPin anyd/xiakedao/*.lpc); reaching a real shop requires leaving the island for the mainland, which (per this lib's own design) generally happens via a sect's own recruitment escort (e.g. following 凌逍 to actual 华山) rather than a direct exit — not completed within this pass's time budget. Did verify a related, simpler item-economy mechanic instead:ask han about 武器(黄衣大汉, an island NPC) hands out a free weapon, confirmed working. - Real (lethal) combat and death/respawn: not attempted. Only the safe
fightsparring form was exercised live;kill/hitand the death → 鬼门关 → 武庙 revival flow described indoc/help/newbie.dec99were not tested, given the time already spent on the crash investigation/fix/re-verification above. - Full sect enrollment (an actual
baiacceptance, not just the correctly-declined attempt at 凌逍): would require reaching one of the actual sect masters (岳不群/宁中则 for 华山, perdoc/help/intro) off-island — not attempted within this pass's budget. The mechanism itself (feature/apprentice.lpc'screate_family/recognize_apprentice/bai.lpc) was verified structurally and via the one live, correctly-declined attempt.
WASM 修复摘要(迁移自 meta.json 的 group_note)
侠客行 XKX 基础版。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 40 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
§7.112 跨库扫描修复(无常 NPC 重连触发重复轮回链)
d/death/npc/{wgargoyle,wgargoyle1,bgargoyle}.lpc的init()无条件调度death_stagecall_out 链(AGENTS.md §7.112):这三个文件原本都没有任何去重判定——enable_commands()会在同房间内向所有对象重播init(),玩家哪怕只是断线重连一次,也会在原有的death_stage/最终判官对话链之上再叠一条新链,导致重复的判官对白、重复扣血/转生等竞态错误。已仿照同族已修复库(dtsl/dtsl2/dtslmud/jym/xuanjianlu)的做法,给每个init()加上按受害者存的set_temp("death_stage_active", 1)/query_temp(...)门槛判定,并在death_stage()的每一个退出点(角色离场、黑无常"阳人退回"分支、链条走完转生)里delete_temp(...)清除标记。三个文件形状与xkm版本几乎一致(均无final_death_stage分支)。已用独立驱动干净编译+启动验证,未发现残留 save 数据变化。
§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:
eval_function:eval this_player()->start_call_out((: debug_message("XKX2001_EVAL_FUNCTION_TEST_OK\n") :), 1)returnedResult = 1(scheduled), and the marker stringXKX2001_EVAL_FUNCTION_TEST_OKappeared in the driver's own stdout ~1s later with zeroinsufficient permissionlines indebug.log— confirms the previously-private, now-publiceval_functionis reachable bycall_out()once inherited into a live player body.destruct_me: cloned/clone/money/coin, moved it onto the admin body, confirmed present viai;present("coin_money", this_player())->set_amount(0)immediately moved it out of inventory;sizeof(all_inventory(load_object("/clone/misc/void")))read1right after (the orphaned coin sitting inVOID_OBawaiting itscall_out) and0after a further 3-second real wait (the fixeddestruct_me()fired and actually destructed it) —debug.logstayed free ofinsufficient permissionthroughout. Did not separately re-run this exact repro againstliquid_content.lpc(identical code shape, already proven correct viacombined.lpc); confirmed only that the file still compiles clean post-fix.
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
- §7.122 (autoload/marker-item duplication on disconnect/reconnect): this lib's own mechanism (
feature/autoload.lpc'ssave_autoload()/restore_autoload(), ES2-lineage naming, not the TMI-2compute_autoload_array/load_autoload_objnaming that the original AGENTS.md §7.122 bug was found under, despitexkx2001being listed as anes1descendant worth checking) is not vulnerable to the TMI-2-style double-storage bug:feature/save.lpc'ssave()is a baresave_object(file)(the native efun, which never serializes inventory sub-objects at all — no redundant "baked into ordinary inventory data" copy exists for this lib the way it does in the TMI-2 lineage's own custom recursive save), and theautoloadvariable is synchronously zeroed (clean_up_autoload()) immediately after every single consumption, both on the save side (clone/user/user.lpc'ssave()) and the restore side (restore_autoload()'s own tail call) — so a stray re-entrantsetup()call (checked every call site:logind.lpc'senter_world(), and the admin-onlypromote/award/cloneusrcommands) either operates on a brand-new empty-inventory clone or findsautoloadalready back to0(pointerp(0)false, immediate return) and is a safe no-op either way.reconnect()(the real net-dead recovery path) never callssetup()at all. Traced every->restore()call site in the archive; none of them callrestore()on an already-live/online object (all target either a freshnew(...)dummy/LOGIN_OB clone or bail out early via apresent()/find_player()online-check first). wimpy/env-wimpydbase-key mismatch: re-verified clean —cmds/usr/wimpy.lpc(the player command) andinherit/char/char.lpcheart_beat()(the real consumer) both consistently use"env/wimpy". Noted, but NOT fixed as it's a narrow, single-NPC content quirk rather than the systemic pattern being checked for: two NPC files (d/city/npc/maskman.lpc,d/huashan/npc/maskman.lpc) callset("wimpy", 30)(wrong key, dead write, that one NPC just never auto-flees) — cosmetic/content-only, in scope boundary territory (could be an intentional "this masked-man boss fights to the death" design choice), left untouched per this project's programming-bugs only mandate.- §7.121 (float arithmetic in declared-
intfunction): no int-declared field/function anywhere in the money/finance path (feature/finance.lpc'scan_afford()/pay_money()) uses float math at all — pure integer arithmetic throughout. The only stray= 0.N;float-literal assignments found archive-wide (d/taohua/obj/taomu(-)?jian.lpc'sgain3) are to afloat-declared variable, not anintone — not an instance of this class (and any balance question about the clamp value itself is a content judgment, out of scope). - §7.123 (bare file-scope
IDENT = (...)statement): grepped for the shape archive-wide; every hit found is inside a function body (create()/reset()/etc.), never a genuine top-level/file-scope statement. - §7.124 (fraction-vs-percentage threshold mismatch):
env/wimpyand every other percentage-shaped threshold field checked use consistent 0-100 integer literals throughout; no0.NNfraction literal assigned to a percentage-typed field found. - §7.126 (stale pre-
.lpc.cextension in.osave data): this lib has no macro-placeholder/coordinate-AREA door-storage mechanism at all (file_path()-style runtime path reconstruction from save data doesn't exist in this archive) — not applicable. - §7.130 (unconditional liveness-check call after non-interactivity already detected):
inherit/char/char.lpc'sheart_beat()has an explicitif (!interactive(this_object())) return;guard strictly BEFORE its ownquery_idle()call a few lines later — correctly ordered, not the buggy shape. - §7.131 (
find_living/find_playerneedingset_living_nameregistration):feature/command.lpc(inherited into every character) callsset_living_name(query("id") || query("name"))for every character — registration is real and universal; not applicable. - §7.132 (
map()-over-mapping wrong-argument binding): nomap(mapping, function)call site exists anywhere in the archive (only unrelatedquery_skill_map()-named accessors and a coordinate-gridgenmap.lpc) — not applicable. - §7.133 (disconnect-notification apply never defined): this driver calls
net_dead()on the player object directly (not a master-levelremove_interactive()apply), andclone/user/user.lpcdefines a real, workingnet_dead()/reconnect()pair (already confirmed working in the July pass's "Also verified working" section above) — not applicable. - §7.134/§7.135 (uninitialized-array field / accessor missing a lazy-init guard sibling has): the concrete instances of this general shape in this archive (
feature/skill.lpc's 4 uninitialized-mapping accessors) were already found and fixed in the earlier §7.30 sweep (see above, commitfa23db1fa2e); no further un-guarded sibling-accessor gaps found on a fresh look at that file or its neighbors.
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).
if(power,18)power=18;x3 ind/zhongnan/beidou.h,if (power, 15) power = 15;incmds/std/beidou3.lpc: a comma for<. The comma expression is 18, which is true, so the Beidou formation's power was always 18 (15) whatever the team added up to; it ispower < 18now (the same line sits in 11 more libs, swept incd82039c63c).max1 / 2;inclone/drug/fengdong.lpcandd/zhongnan/caiyao.h: the halving for a victim under 10000 exp never happened,max1 /= 2;.seed == 1;ind/huashan/bian.lpc(the 1-in-100 chance for a stranger to find the Zixia book never applied) andskill == 30/60/100;ind/xingxiu/obj/tongbo.lpc(the three flattery tiers never applied: the bonus grew without bound with the number of earlier flatteries):=.tmp[i] == EOT + ".";inadm/daemons/network/mail_serv.lpc:=.if (ob);ind/qilian/npc/huoji.lpcwas a guard that guarded nothing,destruct(0)ran for a missing object:if (ob) destruct(ob);.for (times; times > 0; times--)(init clause with no effect) incmds/skill/{lian,practice}.lpc,d/shenlong/{jushi,sea,sea2}.lpc,d/taohua/shijian.lpc:for (; ...).if (current_water == 0) {}(d/xingxiu/shanjiao.lpc),neili == 0;(kungfu/condition/xianglu-du.lpc,neiliis never read afterwards), a-(x) * (x);left behind after-=(kungfu/skill/taiji-shengong/{heal,lifesave}.lpc), a stray"\n";after thereturn(d/changbai/ssmiao.lpc): deleted.#if PREF_TELL & SVC_TCPinsideif (...) { }left an emptyifwhen the option is off (adm/daemons/dns_master.lpcandnetwork/dns_master.lpc): the#ifmoved outside theif.(a & b)between two booleans (d/forest/npc/langren.lpc,d/quanzhou/npc/langren.lpc):&&.};after a function (7 files),!= =(kungfu/class/dali/yanqing.lpc, the file is a draft, see below).
Rooms and objects that never loaded, repaired (live-checked).
- Here-document terminators (
scripts/lpc_fix_heredoc_terminator.py):LONGindented ind/taihu/gumu/{cye,cyn,cyw, line,lins,linw}.lpc,d/beijing/zijin/shunzhme.lpc; glued to the last text line ind/dali/tianls9.lpc,d/beijing/kangqin/chufang.lpc,d/kunlun/mj_center.lpc(the raw bytes show a lone GBK lead byte before the newline, which the conversion dropped together with it;tianls9's last line ends in damaged characters that stay damaged). - Lost closing quote:
set("short", "棋室);ind/city/qiyuan{2,3,4}.lpc;d/shaolin/jiebei.lpc(...修好。}, the text。\n";is the onejymandshenzhoucarry);clone/npc/shan.lpc(...自强不息啊);,。"fromxuanjianlu);kungfu/class/shenlong/lu.lpc(the damaged reply你现在身上不是雄坡穑趺从掷...became你现在身上不是有雄黄么?, the sentencexuanjianluandjymcarry);d/huanghe/doc/set_bang.h(two answers end in unrecoverable characters; the strings are closed after帮主的景仰之情, nothing was written new:d/huanghe/npc/bangzhu.lpcloads again). - Strings broken by a raw newline:
clone/misc/sing.lpc(13 song headingsWHT "/ text; the file is#included byqupu.lpc),d/changbai/damk.lpc(...地对洞/"了出去。\n"). - Octal-escaped GBK bytes:
"\241\243"for。ind/shenlong/{jushi,sea,sea2}.lpc;sea.lpcandsea2.lpcalso lost the closing quote inside不由得走近一看,原来是(不傻米呓豢矗词), restored from the same message injushi.lpc. - Missing declarations or code:
d/huangshan/{jushi,penglai,wenshu,yixian1}.lpcdo_zuan()usedmewithout declaring it and never returned 1 (object me = this_player();,return 1;);d/xiakedao/shatan5.lpchas noinheritat all (its neighbourshatan4.lpc, edited by the same wizard on the same day, hasinherit __DIR__ "no_pk_room";-- restored the same way, a judgement call from the neighbours);d/shaolin/{obj,npc/obj}/putao.lpccall::init()with no parentinit(deleted);d/changbai/obj/sanqi.lpchas the}ofcreate()in front ofsetup();;d/quanzhou/yaopu/obj/huanglian.lpclacks a).
Left alone, with the reason (120 files that do not load).
- *Not code*:
d/em/garbage.lpc,d/hangzhou/hangzhou.lpc,d/kunlun/map_mingjiao.lpc,doc/help/map_mingjiao.lpc(ASCII art or binary),d/city/grt.lpc,kungfu/class/emei/zhou_log.lpc(pasted logs),d/shaolin/npc/obj/saoba.lpc,clone/food/dianxin.lpc(pasted player dumps),clone/obj/job.sav/*(binary). - *Drafts, copies and scratch nothing references*:
backup/andtemplate.lpcfiles,d/wudang/taoyuan/tyroad{4..7}.lpc(#include __DIR_"feng.h", the real rooms are ind/wudang/), four identicaltomb.lpc/tomb2.lpcpairs underd/em/andd/emei/(older drafts of the workingd/emei/tomb.lpc),d/kunlun/skill/bagua-dao.lpc,kungfu/class/dali/yanqing.lpc(a quoted line between every character, an unbalanced"),d/taihu/gumu/houtang.lpc(item_descstrings broken across lines),d/xiakedao/npc/temp.lpc. - *Half-written*:
d/heimuya/basket.lpccalls acheck_trigger()that was never written (and::reset()with no parentreset); the sibling ferryd/changbai/damk.lpchas the working version to copy from, which is new content, so it stays. - *Headers the archive never had*: the TMI-2 network daemons
adm/daemons/{socket.lpc,network/*}(uid.h,mailer.h,priv.h,post.h,net/socket_err.h; the preload line fordns_masteris commented out and no live code names these daemons),d/shaolin/npc/xuanci.lpc(job.h,xuan-ci.lpcis the working NPC),cmds/std/{xunbu,yunyong}.lpc,d/wizard/center.lpc,d/quanzhou/yaopu/obj/yaolu.lpc,d/xiakedao/npc/gaolao1.lpc,cmds/wiz/{map2,rstbkup}.lpc. - *Artifacts of compiling an
#included fragment on its own*:adm/simul_efun/object.lpc(Invalid simulated efunction overrideand the no-side-effects warning atefun::destruct(ob);;/adm/single/simul_efun.lpccompiles),clone/obj/ npc_setup.lpc.
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.
- 2026-10-10:
/clone/misc/roommaker.lpc生成新文件用的 here-document 模板(@ROOM_CODE/@OBJ_CODE)里的inherit行,被 93ca83e68f3 的重复继承清理当成代码删掉了(lpc_diamonds.py当时不识别 here-document),用它生成的房间/物品会因缺少继承而无法编译。已恢复该行;工具已修正。
深度功能测试(§10.7,2026-10-11):dns_master 空 if
adm/daemons/network/dns_master.lpcquery_services():if (! (mud_svc[mud]["tell"/"finger"] & SVC_KNOWN)) {里只有#if PREF_X & SVC_TCP … #endif,选项关闭时 if 体为空(“无副作用”警告);把#if/#endif挪到 if 外面(KB 04 §6.10,2026-10-07 那次扫描漏掉了! (带空格、大括号另起一行的写法)。载入无回退。
深度功能测试(§10.7,2026-10-11):dns_master 空 if
adm/daemons/network/dns_master.lpcquery_services():if (! (mud_svc[mud]["finger"/"tell"] & SVC_KNOWN))块里只有#if PREF_X & SVC_TCP … #endif,选项关闭时 if 为空(“无副作用”警告报在下一句);把#if/#endif挪到 if 外面(KB 04,2026-10-07 那次扫描漏了! (加括号换行这种写法)。载入无回退。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。37 个文件 41 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。
- 2026-10-14:1 个文件用了颜色宏(
HIW、NOR)却没有#include <ansi.h>(首个错误syntax error, unexpected L_STRING);开头补上;2 个文件的函数声明为int(或void)却返回字符串、对象(int query_seal() { return "…"; }),旧驱动照样把值交给调用者;改为实际返回的类型(scripts/lpc_fix_void_return.py)(KB 04 §6.1、§6.3)。新进程比对 HEAD 与工作树:3 个对象,新增载入 1,回退 0。
- 2026-10-14(更正):上一条把
auto_perform()也改成了mixed/string,其实那里返回值的类型是因为前面的错误(未定义的函数)才变成未知,不是真的返回字符串;恢复为int。
无门派玩家的 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)
下列房间的出口写的文件不存在,而对面恰好只有一个房间的出口指回本房间,所以按对面的房间改正(笔误、大小写、路径少了一级):
/d/beijing/east/wangfudajie3.lpc的southwest出口:/d/beijing/zijincheng/dongchangan→/d/beijing/zijin/dongchangan/d/huangshan/ciguang.lpc的south出口:wengquan→wenquan/d/huangshan/tiandu1.lpc的up出口:jiyubei→jiyunbei
改后房间照常载入(与 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… 行。