Record of the Mystic Sword

✅ 可玩

玄剑录

xuanjianlu

🔑 fluffos / Mud@2026 更新 0e61bdc 2026-09-01 源码 下载 ZIP

▶ 开始游玩 · Play Now

玄剑录属于 ES II / 侠客行(XKX)引擎家族,核心 master/simul_efun 文件与同门档案 `xkx2001`、`bmxkx2001` 逐字节相同,但拥有自己独立的武林世界与剧情内容,舞台设定在明教、昆仑、侠客岛等门派与地点之间;克隆对象名单直接取材金庸《射雕英雄传》,东邪、西毒、南帝、北丐、中神通"五绝"和全真教丘处机等人物原名原样登场,新人在侠客岛会先被赏善使、罚恶使等门派使者迎接后再习武结交门派;天赋点可自行分配或交给系统随机,巫师登录在普通密码之外还要求一个独立的验证码;文件头带"Cracked by Kafei"署名,和 `xkx2000zxb` 是同一批流通版本(`xkx2001` 本身则属"Cracked by Roath"的另一批)。

English

Part of the ES II / Xiake Xing ("Wandering Blade") engine family (byte-identical core master/simul_efun files to sibling archives xkx2001 and bmxkx2001), but with its own independent wuxia world centered on the Ming Cult, Kunlun, and Xiake Island. Its clone-object roster draws directly on Jin Yong's "Legend of the Condor Heroes": five special NPCs are literally named and modeled on the novel's Five Greats -- 东邪 (Eastern Heretic), 西毒 (Western Venom), 南帝 (Southern Emperor), 北丐 (Northern Beggar), and 中神通 (Central Divine Coordinator) -- alongside the historical Taoist master 丘处机 (Qiu Chuji). New characters are met on Xiake Island by the sect's judge-envoy NPCs before setting out to train and build sect ties; talent-point allocation can be chosen by hand or left to random assignment, and wizard logins require a separate passcode on top of the normal password. Its files carry a "Cracked by Kafei" header, placing it in the same release batch as xkx2000zxb (xkx2001 itself circulated under a different "Cracked by Roath" release).

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/xuanjianlu/

管理员账号 / Admin account

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

本地运行

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

游戏端口:40064

NOTES · 移植与修复记录

玄剑录 (xuanjianlu) — archive #70

What this is

archives/玄剑录.rar, root at raw/xjl/. config.xjl's name : field and the live connect banner both confirm the game's own self-identified name is 玄剑录 ("Record of the Mysterious Sword") — matches the slug, no divergence to note. Distributed by the same "小熊泥苑" collector/hosting site seen on shujian2008/sjtx2 (archive #35/#36) — 小熊泥苑.txt sits alongside the mudlib root in the raw archive, just site branding, not part of the mudlib.

Lineage: confirmed via md5sum that adm/simul_efun/chinese.c and adm/single/master.c are byte-identical to xkx2001 (archive #25) and bmxkx2001 (archive #45) — this is the same "ES II" engine family (master.c header credits "original from Lil", "rewritten by Annihilator", "modified by Xiang/Xuy for XKX"). adm/register/xkxreg, xkxrestore, xkx_monitor filenames further confirm the "XKX" (侠客行) codebase ancestry, even though this game's own live content/world is a distinct wuxia setting (Ming-cult/明教/昆仑/侠客岛 zones, its own NPCs). adm/daemons/logind.c differs from both siblings (its own registration flow/banner), consistent with "shared engine, distinct game-world content" seen across this whole ES II family (es1_win/esI/xkx2001/rzrmud/xo/ bmxkx2001/kxkj/yueyingqiyuan/wuhanzhan/ yanhuangwuhun/haiyang2).

Layout: adm/single/{master,simul_efun} + adm/daemons/* + adm/simul_efun/* + adm/register/* (the last one XKX-specific, holds a MySQL-free registration helper set, not used by the live registration flow tested here).

Size: 11,492 raw files, 10,605 .lpc/.c files after conversion — a normal-sized lib for this project, not a mega-lib; the full lpcc_check.sh sweep was run without incident (see below).

Fixes applied (and why)

Standard/catalog fixes, applied proactively

1. AGENTS.md §15h (is_chinese/check_legal_name GBK byte-range bug — nearly universal, confirmed present here too): - adm/simul_efun/chinese.lpc's is_chinese(str): was strlen(str)>=2 && str[0] > 160 && str[0] < 255 (a GBK lead-byte range check, always false against UTF-8 codepoints) → replaced with strlen(str)>=1 && str[0] >= 0x4e00 && str[0] <= 0x9fff (CJK Unified Ideographs codepoint range). - adm/daemons/logind.lpc's check_legal_name(name, ob): bound (strlen(name) < 2) || (strlen(name) > 8) || i % 2 (byte-count bound + a meaningless parity check) → (strlen(name) < 1) || (strlen(name) > 4) (character count; the message text already promised "1 到 4 个中文字" — 1 to 4 Chinese characters — so halving 2/8→1/4 matches what the message always said, not a guess). Its per-character sliding-window loop if( j%2==0 && !is_chinese(name[j..j+1]) ) { name[j]+=128; name[j+1]+=128; } (byte-offset gate + a byte-shift "auto-correct" hack that has no valid meaning against Unicode codepoints) → if( !is_chinese(name[j..j]) ) { <reject>; }, using the exact reject message the original author had already written but left commented out. - Verified via 3 independent full registration tests (see below) that real single/double-character Chinese names are accepted correctly and rejected names really are rejected (not just "doesn't crash").

2. AGENTS.md §15p (DNS/intermud daemon preload exclusion): adm/etc/ preload listed /adm/daemons/network/dns_master — removed proactively before the first boot attempt. (A second, unrelated copy of the same file sits at adm/daemons/dns_master.lpc, outside network/ — confirmed it's not the one the DNS_MASTER macro resolves to, include/net/daemons.h points at the network/ copy only, so nothing else needed touching.) Also checked §15ab's "inline DNS calls outside preload" variant: logind.lpc's logon() does call MUDLIST_CMD->main(...) unconditionally on every connect, and that command does call into DNS_MASTER, but it's guarded by its own find_object(DNS_MASTER) check and degrades to a harmless notify_fail() when not loaded — confirmed safe by inspection and by zero hangs/errors across every boot+test cycle.

3. AGENTS.md §14 (valid_override 3-arg upgrade): master.lpc's valid_override(file, name) was 2-arg — upgraded to (file, name, main_file) and added main_file == SIMUL_EFUN_OB || main_file == MASTER_OB to the allow-check, so an efun:: override written inside a file #included into simul_efun.lpc is still recognized.

4. AGENTS.md §8d/§15o (get_include_path() insurance): master.lpc had no get_include_path() apply at all — added the standard directory-prepending implementation as insurance for any live, mid-connection compile of a file using a local (same-directory) #include. (convert_lib.sh's own §8d pass already converted 24 local angle-bracket includes to quoted form, which resolves without needing this apply at all — this addition is pure insurance, not a fix for an observed failure.)

5. AGENTS.md §8e (tail is not a real FluffOS efun) — fatal here, confirmed on the very first boot attempt: adm/simul_efun/message.lpc's tail(string file) called efun::tail(file), and since this file is #included directly into simul_efun.lpc, the compile error (Unknown efun: tail) took down the *entire* simul_efun object, which the driver refuses to boot without (*No program in object '/adm/single/simul_efun'!). Reimplemented in plain LPC (read the file, split lines, take the last N, write them) — same pattern as catalogued in AGENTS.md, same severity as the bmxkx2001 instance of this exact bug (that lib shares this codebase family too).

6. AGENTS.md §15c (adm/etc/preload-style bare-.c data-file refs): found a SEPARATE instance in adm/etc/task/task_list (a plain-text list of NPC-quest object paths read by adm/daemons/taskd.lpc's random_place()/update_task()), still listing the pre-rename /adm/etc/task/obj/*.c paths. After the .c.lpc rename these new() calls returned 0 silently, and the caller's very next line (ob->query("task_owner"), no objectp() guard — see AGENTS.md §15e) threw *Bad argument 1 to EFUN call_other() ... Got: int(0) every time this daemon's periodic job fired. Fixed with sed -i 's/\.c$//' on the data file, same fix shape as the catalog entry.

New findings from this pass (not yet in AGENTS.md's catalog as of this write-up)

7. private on an add_action-bound dispatcher, once inherited, gets demoted to DECL_HIDDEN and the driver refuses to call it — silently breaking EVERY player command post-login. This is the single most impactful bug found in this lib and, as far as I can tell, a genuinely new bug class for this whole project's catalog.

feature/command.lpc (inherited by inherit/char/char.lpc, the player body base class) declares: ``lpc private nomask int command_hook(string arg) ` bound via add_action("command_hook", "", 1) in enable_player(). This function is the *sole* generic dispatcher for movement, verbs, emotes, and channels — literally every command a connected player types. On this driver (vm/internal/apply.cc's apply()), a driver-origin add_action callback dispatched against an object requires at least DECL_PRIVATE access when current_object == ob — but DECL_PRIVATE ("can't be inherited") demotes a private function to DECL_HIDDEN once it's compiled as part of an *inheriting* program rather than its own defining file, and command_hook is only ever invoked as a member of the inheriting player-body class, never as a member of feature/command.lpc itself. Symptom: full registration completes cleanly, the character lands in the actual start room with a correct room description on entry, but every subsequent typed command (even look) silently produces only the config's default error message, with a matching debug.log entry: ` apply() with insufficient permission: cob: clone/user/user#1, ob: clone/user/user#1, function: command_hook, origin: efun, needs: private, has: hidden ` How this was missed on 2 prior sibling libs: xkx2001's own copy of this file has nomask int command_hook (no private at all) — so xkx2001 never hit this. bmxkx2001 (archive #45) *does* have the identical private nomask` shape, but that lib's own NOTES.md confirms testing stopped right at "reaches the password prompt" and never issued a real post-login command — so the bug was live there too and never caught. This is exactly the class of gap this task's instructions specifically warn about: "boots and reaches a prompt" is not "the feature actually works."

Fix: drop private (nomask int command_hook(string arg)), matching xkx2001's own already-safe version verbatim. nomask alone still prevents any override.

Recommendation for AGENTS.md: worth its own catalog entry (a new §15ac or similar) — check any lib's core add_action-bound dispatcher function (commonly named command_hook/parse_command/similar, defined in a feature/-style file and inherited into the player body) for a private access modifier, and always issue at least one real post-login command (not just look at the banner) before considering a lib's gameplay loop verified.

8. securityd.lpc's ACL mappings are never initialized when the archive ships with no seed save data for the daemon — a second, related bug that surfaced immediately after fixing #7 above (the very first real command that could finally reach command_hook then hit this one).

securityd.lpc create() calls restore(), and only if that FAILS does it fall back to manually initializing wiz_status/wiz_sites — but trusted_read/exclude_read/trusted_write/exclude_write/ authorized_cmds/exclude_cmds are never initialized in that fallback branch at all (only authorized_cmds gets a lazy if(!authorized_cmds) authorized_cmds = allocate_mapping(...) inside valid_cmd() itself — the other five don't get equivalent treatment anywhere). This archive has no /data/securityd.o seed file at all (confirmed: the only securityd.o anywhere in the tree is a wizard's unrelated personal-sandbox copy under clone/obj/u/xuanyuan/daemons/), so restore() always fails. Symptom: valid_cmd()'s very first exclude_cmds[dir] lookup on the still-0 mapping threw *Value being indexed is zero. for literally every player command.

Fix: added lazy if(!X) X = allocate_mapping(1000); for all six ACL mappings once, at the end of create().

A second layer of the same root cause: once the mappings themselves were guaranteed non-zero, the *next* line down hit a related but distinct crash — authorized_cmds["cmds"]/ trusted_write["/"]/trusted_read["/"] are looked up directly (not via the undefinedp()-guarded loop the rest of the function uses) and passed straight into member_array(), which rejects an int (the 0 you get from a missing mapping key) for its 2nd argument (Expected: string or array Got: 0). Fixed by pre-seeding exactly these three well-known keys as ({}) in the same create() block.

Both of these are latent/dormant in every sibling lib that ships with real save data for this daemon (a populated ACL is the normal case) — this is specific to this archive's snapshot shipping without one, not a systemic driver-compat bug the way #7 is. Worth a narrower catalog note (checking whether restore() ever actually succeeds, and adding defensive allocate_mapping()/({}) fallbacks in any lib's securityd.lpc create() when it doesn't) rather than a blanket rule.

staticnosave blanket-sed collateral damage (§3 counterexample, recurring)

Same shape as the moniHuafu/yanhuangwuhun precedent already in AGENTS.md, but a wider blast radius here (23 files, the leading-slash variant "/static/..." rather than just "static...): this lib logs extensively via log_file("static/XXX", ...) / log_file("/static/XXX", ...) (a real log/static/ directory ships in the archive) for crash logs, read/write/cmd-denial audit logs, clone logs, award logs, etc. — convert_lib.sh's blanket \bstatic\bnosave sed rewrote every one of these path literals to "nosave/..."/"/nosave/...", silently orphaning the real log/static/ seed directory. Found and reverted with grep -arl '"nosave\|/nosave/' across the whole work/ tree (not just the narrower "nosave check used on prior libs, which would have missed the leading-slash form) → sed 's/"nosave/"static/g; s/\/nosave\//\/static\//g' on every hit. Affected: adm/single/master.lpc (crash()'s static/CRASHES), adm/simul_efun/message.lpc (5 sites), adm/daemons/securityd.lpc (5 sites), its wizard-sandbox duplicate at clone/obj/u/xuanyuan/daemons/securityd.lpc and .../logind.lpc, several cmds//u/-tree files' AWARD_LOG/SUICIDE_LIST/etc, and — a distinct sub-case — the *historical audit log data* file log/static/more.lpc itself (a 更 record of who ran the more command on what file, plain text, mistakenly renamed from .c.lpc and then swept by the same sed since it now had a .lpc extension, corrupting its own content, not code — its lines record historical paths like /log/static/suicide.c). Reverted that one too even though it's just cosmetic seed-log text, for consistency. A separate mid-pass mistake, corrected: while iterating on this, an over-broad rm -rf work/log (intending only to clear player-save-state under work/data) also deleted this lib's entire work/log/ seed-data subtree (crash logs, WIZ_LOGIN, MONEY, etc, and the regban.log that adm/daemons/regband.lpc's is_banned() writes to on every connection attempt) — this reintroduced the exact check_legal_id-adjacent crash described above (*Wrong permissions for opening file /log/regban.log for append. / No such file or directory, silently aborting get_id() mid-connection). Recovered by re-running convert_lib.sh into a scratch directory (raw→work conversion is idempotent given the same raw source) and copying just the regenerated log/ subtree back in, re-applying the same static/nosave revert to it. Lesson for future sessions: work/log/ (the *mudlib's own* virtual /log/ tree, seed content) and the sibling libs/<slug>/log/ (the *driver's* debug-log output directory, safe to clear) are two different things with the same basename — don't rm -rf` both in one command.

lpcc sweep content fixes (found via the sweep, applied where cheap and correct)

What I confirmed was NOT needed, and how

lpcc sweep — triage of remaining noise (361/10605 fail, 96.6% pass, up from 96.4%/383-fail pre-fix)

Grouped the 361 remaining failures by top-level directory before accepting them as noise (not exhaustively fixed, per AGENTS.md §6b):

Interactive test result — full registration + real gameplay commands

Read adm/daemons/logind.lpc's actual input_to chain before scripting (logon → confirm_big5 → get_id → confirm_id → get_name → new_password → confirm_password → specify_gifttype → get_gift → get_email → get_gender → enter_world) — confirms a hidden pre-id BIG5/GB font prompt ("Do you want to use BIG5 code?(y/n)") right after the banner, exactly the kind of hidden gate AGENTS.md's "hidden pre-id prompt" family warns about.

Ran 3 independent full registrations in 3 separate continuous mudclient.py sessions, each covering registration all the way through real post-login commands (not just reaching a prompt):

1. n (GB code) → id qinling → confirm yreal Chinese name 秦岭 → password test12345 (×2) → gift type 0 → accept gift y → email [email protected] → gender flands in the actual start room (沙滩 — a beach zone, full room description with exits/NPC rendered correctly) → look now correctly re-displays the room (this was the run where the command_hook/securityd fixes were validated — before them, look produced only the config's default error message with a debug.log crash trace) → scorequit → "开始退出游戏, 进行中 ..." (graceful quit). 2. n → id linfengyreal Chinese name 林风 → same flow → lands in the same start room, an NPC ("「罚恶使」李四") correctly greets the character by name ("恭迎林风") → zero debug.log errors for the entire session. 3. n → id qinfengyreal Chinese name 秦风 → same flow → lands in the start room, a different greeter NPC ("「赏善使」张三") greets by name ("恭迎秦风") → lookiquitzero debug.log errors of any kind (执行时段错误/Bad argument/ Undefined function/Value being indexed/insufficient permission: all zero hits) — this is the final, fully-clean run after every fix in this document was applied.

All three names are real, valid Chinese input (2-character names, both characters within the CJK range, not on the lib's banned_name list), confirming the §15h fix actually works end-to-end and the flow proceeds correctly past registration into fully-playable post-login gameplay, not just "reaches a prompt."

Port / process

Port 40064. Driver launched via setsid nohup ... & disown per AGENTS.md's guidance; killed by exact PID after each test cycle, never a broad pkill pattern (several other agents had drivers running concurrently throughout this session). No driver process left running at the end of this pass; work/data (player save state from testing) and log/debug.log cleared before finishing.

2026-07-23: driver rebuild retest + LPC formatter + WASM check

2026-07-23 (integrity review): the "score produces no output" observation is INTENTIONAL design, not a bug

Investigated the previous pass's open observation that score silently produced no output for a fresh character. Root cause: this lib's own onboarding/registration gate, the same design family as bmxkx2001's block_cmd gate:

Verified end-to-end interactively: completed the whole follow/register/re-login flow with a test character; score then printed the complete character sheet (【玄剑录个人档案】). No fix needed — do not "fix" the silence in shatan1/register, it is the original game's anti-unregistered-play gate working as designed. (Side note for future testers: the register step's issued-password disconnect is easy to mistake for a crash; it prints 您的新密码是XXXXX then closes the link.)

WASM-enablement pass (loopback-allow / gate bypass / admin seed)

Standard WASM-first pass per AGENTS.md §1.3b/e and §1.5. Gates patched:

- logon()'s BAN_D->is_banned(query_ip_name(ob)) gate (~line 173) — now skipped for loopback connections. - get_id()'s REGBAN_D->is_banned(query_ip_name(ob)) registration ban (~line 285) — skipped for loopback. - No uptime() startup gate and no per-IP flood throttle exist in this logind (MAX_USERS capacity cap left intact — hosting capacity, not per-IP).

- wiz_levels rank table made nosave (restore() from the new /data/securityd.o was ZEROING it per §7.7, crashing get_wiz_level() → every wiz_level() call → login desync). - create() ACL seeding extended: authorized_cmds now seeds cmds/adm/cmds/arch/cmds/wiz/cmds/imm status arrays. Without this, valid_cmd() denied every wizard command for every rank including (admin) (the legacy lazy-init inside valid_cmd() can never fire once create() makes the mapping non-null — pre-existing latent bug, exposed by the first real wizard this lib ever had).

Admin account seeded: id fluffos, pw Mud@2026, name 浮浮, granted (admin) via a hand-seeded /data/securityd.o (wiz_status["fluffos"]="(admin)", wiz_sites["fluffos"]=".*"; "npc" entries kept from the daemon's own defaults). This lineage's wizard login additionally requires a wizard_password field on the login save — seeded into data/login/f/fluffos.o as the same crypt hash as the password, so the 巫师验证码 is also Mud@2026. Verified: real registration flow (n/fluffos/y/浮浮/pw×2/0/y/email/m → landed on 沙滩), relogin as fluffos (password + 验证码) → update /adm/daemons/band.lpc → 成功, score correct. Retest: fresh normal registration (秦风 m) end-to-end, look/quit correct; test char saves removed; zero new debug.log errors.

Save files for the orchestrator to add (none gitignored, normal add):

Fail-closed retrofit for the loopback-allow gate (2026-07-24)

The loopback helper above was originally written matching the project-wide convention at patch time (AGENTS.md §1.3b), which also treated an empty/non-string/malformed query_ip_number() result as loopback, defensively, because the WASM driver used to return garbage there. That underlying driver bug is now fixed (fluffos commits e33bb5da "fix: query_ip_number() returned uninitialized garbage under WASM" and 007bb863 "feat: synthetic resolve() on WASM instead of raising an LPC error", both 2026-07-23; the locally-built build-debug/build-wasm binaries already postdate both commits), so treating unparseable IPs as trusted is a fail-open gap with no remaining justification. Retrofitted to fail-closed: loopback is now strictly ip == "127.0.0.1" || ip == "::1" || ip[0..3] == "127." (with a stringp() guard before the slice) — a malformed/empty IP now falls through to the NORMAL gate instead of being treated as local. Retested after tightening: fresh driver boot clean, fluffos loopback login and its wizard update command both still work; zero new debug.log errors.

深度功能测试 / Deep functional test (2026-07-24, round two)

First real *playthrough* pass on this lib per AGENTS.md §10.7 (all prior passes verified registration + look/score/quit/admin-login, or watched boot output, but never played the actual game systems). Native driver (build-debug), one continuous mudclient.py session per phase, real Chinese names throughout, real wall-clock waits for the two timer-gated systems this lib turned out to have (see below).

Read first: doc/help/intro (the closest thing to a newbie primer this lib ships — no plain doc/help/newbie file, only newbie_wudang/newbie_gumu/newbie_lingjiu/newbie_baituo/ newbie_job per-sect/topic variants) — explains the 14-sect roster, attributes, fight/kill/hit combat-verb distinction, and the xue/du/lian three ways to learn skills, matching what was tested below almost exactly.

Test character: id wentian, real Chinese name 闻天笑, password Test12345 → after completing this lib's own two-stage registration (see below) a NEW password doeal was issued and is now the live login password. Kept (not cleaned up) as the representative playthrough character — final state: registered, strike skill learned (1 point), standing in 沙滩 (the real post-registration start room). Save files: work/data/user/w/wentian.o, work/data/login/w/wentian.o. Two other characters created while probing the registration-loop edge cases (shiying/上官虹, shangfei/上官飞, both abandoned mid-tutorial, registered:"no") were deleted before finishing — pure test debris, not representative state worth keeping.

This lib's registration is TWO-STAGE, and the second stage is the real "tutorial"

Already documented above (the shatan1/block_cmd section) that a freshly-registered character is unregistered (query("registered") == "no") and dropped in /d/xiakedao/shatan1 with almost every command silently swallowed. What that earlier pass didn't walk end-to-end: the escort NPC (d/xiakedao/npc/li4.lpc/zhangli.h's shared greeting()/check_follow() call_out chain) auto-teleports the character to /d/xiakedao/register 5-15 real seconds after a follow li4, where a second NPC (mux.lpc) explains register <email>; running that command calls adm/daemons/regid.lpc's register_char(), which issues a brand-new random 5-letter password or wall NOR — ("您的新密码是XXXXX请用新的密码连线") and immediately disconnects the link — easy to mistake for a crash if you don't already know this is by design (same category of trap bxsj/xiyouji's NOTES already warn about: a "disconnect" that is actually correct game behavior). Reconnecting with the new password lands the character in the REAL start room /d/xiakedao/沙滩 (a different room object from the shatan1 gate, same short name "沙滩" — confusingly) with a fisherman NPC, full score/i access, and the actual 侠客岛 (Ke Xia island) content available. Verified end-to-end live, twice (once organically stepping through each input_to prompt, once after a same-character reconnect mid-flow — the escort/register state genuinely survives a real disconnect+reconnect, confirming this part of the flow is robust).

Exploring the starting zone — 侠客岛 is a self-contained tutorial island

Read room .lpc source throughout rather than guessing blind — exits are not always neighbors-of-neighbors intuitive here (a hidden enter exit that only exists for a 15-second real-time window after a call_out, a climb tree/wear coat/jump fall puzzle gating one internal passage). Confirmed working, all via wentian:

Off-island travel is real-time-gated and genuinely slow to test solo

/d/xiakedao/shatan's fisherman auto-offers a boat enter exit exactly once, ~1 real second after the room is entered, via a call_out chain (check_triggeron_board(+15s)→arrive(+20s)→close_passage(+20s)); missing the initial 15-second boarding window leaves the underlying chuan (boat) room's yell_trigger flag SET until shatan's own next driver-scheduled reset() (this lib's config.fluffos sets time to reset : 1800, i.e. up to 30 real minutes) — shatan.lpc:111's reset() is the ONLY code that clears it. Missed the window on the first live attempt (normal exploratory-testing pace, not a bug); a driver restart (unrelated, see "an unrelated process died" below) incidentally reset all in-memory room state and gave a second live window, which was also missed testing navigation. Reaching the mainland past this point (to independently verify a real sect join, a skill-teacher NPC outside the island, or a player-currency shop purchase) was NOT completed within this session's time budget — see explicit list below. Scouted the mainland via wizard goto instead (shop test above; sect-hall NPCs not individually walked to).

Bugs found and fixed

1. inherit/item/combined.lpc:19 (originally) — stackable items spent to exactly zero silently never self-destruct (money in particular). NEW manifestation of the AGENTS.md §8.3a bug class (private function bound to a driver-origin dispatch, demoted to DECL_HIDDEN once inherited), via call_out instead of add_action.

`` apply() with insufficient permission: cob: null, ob: clone/money/silver#295, function: destruct_me, origin: internal, needs: private, has: hidden ``

``lpc // BEFORE: private void destruct_me() { destruct(this_object()); } // AFTER: void destruct_me() { destruct(this_object()); } ``

2. feature/action.lpc:70 (originally) — same bug class, much higher blast radius: EVERY timed kungfu-skill/drug status effect in the game silently never fires for players.

`` apply() with insufficient permission: cob: null, ob: clone/user/user#1, function: eval_function, origin: internal, needs: private, has: hidden ``

``lpc // BEFORE: private void eval_function(function fun) { evaluate(fun); } // AFTER: void eval_function(function fun) { evaluate(fun); } ``

3. clone/npc/shan.lpc:181 — pre-existing missing closing quote crashes this NPC's entire program, taking down any room/NPC interaction chain that happens to reference it.

``lpc // BEFORE: command("say 这种私人恩怨太多了,你还是自强不息啊); // AFTER: command("say 这种私人恩怨太多了,你还是自强不息啊。"); ``

An unrelated process died mid-session (environment note, not a mudlib bug)

The native driver process (a plain setsid nohup .../driver config.fluffos &, PID recorded and monitored per AGENTS.md §10.5) was found dead partway through this session with no crash trace at all in debug.log (no FATAL ERROR, no SIGTERM line, nothing — the log just stops mid-preload-warning-spam). Given this is a shared multi-agent environment (other libs' driver processes were visibly running throughout, per ss -tlnp/ps aux), this reads as an external kill/OOM rather than anything the mudlib did — restarted cleanly (Accepting telnet connections/Initializations complete), all prior save-file state intact, no fix needed or applicable. Noted here in case a future session sees the same "driver just isn't there anymore, zero debug.log explanation" symptom.

Explicitly NOT verified live, and why

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

基于 XKX 引擎的自制游戏。

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

深度功能测试第二轮 / Deep functional test round two (2026-08-15, post driver-upgrade re-test)

驱动于 2026-08-12 升级后的重测。标准检查清单发现并修复一处问题:

cmds/imm/update.lpccmds/wiz/update.lpc(均已是 §7.106 正确写 法)与 master.lpc::log_error()(§7.10 的 "arning:" 大小写无关 写法)均已是正确写法,maximum evaluation cost 已经是 30000000,均无需改动;adm/simul_efun/file.lpc 本身不定义 cat()/log_file()(与 shenzhou 同宗架构),无适用修复项;本档案 无 adm/daemons/closed.lpc,不受 §7.107 影响。

现场验证摘要

驱动干净启动,管理员 fluffos/Mud@2026(含"巫师验证码"二次确 认,同一密码)登录确认 目前权限:(admin)update /adm/daemons/logind 成功验证真实写入权限。踢掉重复登录重连路径 现场验证通过(见上)。debug.log 全程干净(1142 行,无真实错 误)。

本轮修改的文件

Round three deep functional test (2026-08-18)

Context: libs/xuanjianlu/work/include/globals.h had just been patched (commit cc35c33d23e, same session) for a missing EDITOR_D macro that inherit/misc/bboard.lpc needs — this lib shares close lineage with libs/jym, whose own round-three pass earlier the same day found several real bugs (missing EDITOR_D, enter_world()'s ob->save() commented out, and an unguarded init()call_out() chain in the death-guide NPCs — AGENTS.md §7.112). This pass specifically re-checked all three patterns here, plus went deeper than round two on combat/death.

Checked all three jym-lineage patterns:

1. logind.lpc's enter_world() ob->save(): read in full (lines 704-788) — ob->save() (line 722) and user->save() (line 788) are both present and uncommented. Not affected. No fix needed. 2. §7.112 (init() unconditional call_out() chain, no re-entry guard): found and fixed, see below. 3. §7.111 (master.lpc's standard_trace() unconditional file_name()): not applicable — this lib's standard_trace() (adm/single/master.lpc:211) formats error["object"]/ error["trace"][i]["object"] with %O, not file_name().

Bug found and fixed: §7.112 in the death-guide gargoyle NPCs (same shape as jym, byte-for-byte matching source)

d/death/npc/{wgargoyle,wgargoyle1,bgargoyle}.lpc (the underworld-gate NPCs a player's character is escorted through on death — wgargoyle/ wgargoyle1 at /d/death/gate, bgargoyle at /d/death/gateway, both reached via DEATH_ROOM = /d/death/gate.lpc from feature/damage.lpc's die()) all had init() unconditionally calling call_out("death_stage"/"final_death_stage", 30, previous_object(), 0) with no guard against being called twice. Per AGENTS.md §7.112, FluffOS re-broadcasts init() to every object in a room whenever enable_commands() is called on an interactive there (confirmed default __RC_ENABLE_COMMANDS_CALL_INIT__ = 1 in this driver build via src/base/internal/rc.cc), and this lib's clone/user/user.lpc reconnect() calls enable_commands() on every reconnect (already fixed for a related gap in the 2026-08-15 round-two pass, §7.108) — so a player/ghost sitting in one of these rooms who reconnects even once mid- sequence would get a duplicate call_out chain stacked on the first, racing to apply reincarnate()/room-move/drop-inventory twice.

Fix (matching the exact shape already applied and proven in jym): added a set_temp("death_stage_active", 1)/query_temp guard around the call_out() scheduling in each init(), cleared via delete_temp() at every exit point of death_stage()/final_death_stage() (the !ob || !present(ob) early return, bgargoyle's "not a ghost" early return, and both the intermediate-stage and final-stage completion branches). All three files are pure-LF (verified via a byte-mode read before editing, per this task's CRLF-safety instruction); git diff --stat confirms only the intended lines changed.

Live-verified two ways:

Bulletin board (§7.86/globals.h EDITOR_D fix) — live-verified working end-to-end, first real test since the fix

Tested at /d/xiakedao/dadong's 侠客岛告示牌 board (reached via admin goto, real accessible in-game location — a large hall on the tutorial island, not otherwise reached during either prior round's playthrough). list一块白杨木的牌子。侠客岛告示牌上目前没有任何留言。 (empty, correct). post <title> opens the real in-game line editor (结束离开 用'.',取消输入用'~q',使用内建列编辑器用'~e'); typed a line, ended with .留言完毕。. list afterward correctly showed the new entry with author/timestamp; read 1 correctly displayed the full posted text. Re-verified against a fresh cold-compile boot (killed the driver, restarted, list/read against the still-empty board after removing the test post) — no compile error, board loads and responds correctly from a completely cold /inherit/misc/bboard.lpc compile. The EDITOR_D fix works; boards are fully functional. (Test post + its data/board/xkd_b.o save file were removed before finishing — pure test debris, not real player content.)

Real combat — verified for the first time (neither prior round exercised it)

wentian (登 strike skill, weak stats) vs d/xiakedao/pubu's skill- teacher NPC (master2.lpc, "蓝衣弟子", reached via the same shatan→n→n→n→n→northup route round two documented). Two findings on verb syntax, both confirmed as intentional design, not bugs:

Not reached this round either (same off-island travel gate as round two)

The boat/mainland travel gate (chuan's one-shot 15s boarding window, config.fluffos's time to reset : 1800) still wasn't budgeted for — real sect-hall joining, a mainland skill teacher, and a player-currency (not admin-assisted) shop purchase remain unverified by an organic character, same gap round two flagged. Everything reachable from the tutorial island (board, real death/revival, real PvE combat, the two jym-lineage bug patterns) is now covered.

Generalizable finding for future sweeps (not chased further here, per task scope)

§7.112's init()-unconditional-call_out() shape and the §7.111 file_name() shape are both proven-safe/proven-absent patterns worth a quick grep-and-check on any other lib in this same ES-II/XKX lineage (xkm, shenzhou, xkx2001, and any other sibling that got the same cc35c33d23e EDITOR_D fix) — not done here since this task's scope was xuanjianlu only.

Cleanup

Fresh driver restart after the fix (killed original PID by exact PID, confirmed cwd match); re-verified board + gargoyle compile cleanly from a cold boot. Test-debris save files (wjltestc abandoned unregistered character, the board test post's data/board/xkd_b.o) removed before finishing. wentian's and admin fluffos's save-file diffs from this session (death_count→1, startroom, position, qi after combat, board_last_read, etc) are genuine gameplay state from the tests above and were kept/committed, matching this project's established convention of committing real playthrough state for the representative/admin characters. debug.log/boot.log scratch files removed from the lib directory before finishing.

Files modified this round

§7.100 修复(ROOM 基类的同一"多余 replace_program()"形状,全档案扫描第 6 批)

Round four deep functional test (2026-08-20) — the mainland is now reachable, all three previously-blocked systems verified

Picked up exactly where round three left off: real sect-hall joining, a mainland skill teacher, and a player-currency shop purchase were the last three items never verified live by an organic character, all gated behind shatan's one-shot 15-second boat-boarding window. This round got past that gate for real (no shortcuts on the boat mechanism itself) and then used the existing wentian/闻天笑 character (still registered:"yes", alive at 沙滩 from prior rounds) to test all three mainland systems.

How the boat gate actually works, and how it was beaten for real

Read d/xiakedao/shatan.lpc in full. The 15-second boarding window is not a fixed clock tied to config.fluffos's time to reset : 1800 — that 1800s value is only the *fallback* cleanup if the window is missed entirely. The real trigger is init() (fired on every room entry, including a reconnect that lands the character back in shatan), which schedules check_trigger() via call_out(..., 1). From the moment check_trigger() actually runs (call it T):

Beaten with a real timed telnet script (mudsock.py, written this session — a persistent-connection variant of mudclient.py that sends scripted lines at precise wall-clock offsets rather than only on inferred silence, needed because the 56-second real-time sequence above has async narration messages that would otherwise reset mudclient.py's idle-based send timer past the boarding window): connected as wentian, which was still sitting in shatan from a prior round — login itself re-entered the room (a reconnect calls enable_commands(), which re-broadcasts init()), so check_trigger() fired within ~2 seconds of login. Sent enter at t=6.5s (safely inside the T..T+15 window), then out at t=46.5s (safely inside the T+35..T+55 window) — landed at shatan3 cleanly on the first real attempt, no admin bypass of the boat mechanism itself.

Observation, not a bug: chuan.lpc's disembark narration says "你一 看原来是些银子和两块令牌" (you find some silver AND two tokens in your hand) but the actual valid_leave() code only grants the two tokens — the silver grant is commented out (// money = new(...); ...). This matches round two's independent finding about the same commented-out line; not re-fixed here (flavor-text/code mismatch, no crash, no observably-wrong state transition — game text overpromising is content, not a programming bug per this session's scope rules).

Getting around the mainland: the cart (大车/da che) is a real, working, one-shot vehicle

d/xiakedao/obj/car.lpc's do_travel() (verbs qu/goto) is un-gated (no fee, no do_hire/hire/gu prerequisite — those verbs are registered via add_action but do_hire() is only ever forward- declared, never defined anywhere in the file; calling hire/gu would silently no-op or error, but the cart's own dialogue only ever advertises qu, so this is almost certainly harmless vestigial/unwired code, not a live bug — not fixed, matches this session's "leave unreachable dead code alone" precedent from round three's start_death() finding). qu <destination> (e.g. qu wudang, qu quanzhou, qu yangzhou) works as documented: a real 40-second transit (call_out("arrive", 40, ...)), then the SAME cart object relocates to the destination room and immediately self-destructs (call_out("destroy_it", 0, ob)) — a genuine one-shot vehicle, matching shatan3's single starting 大车 and confirming why a return trip needed a fresh cart. wentian's own 20-silver registration grant (adm/daemons/logind.lpc's enter_world(), real and uncommented) had already been spent in an earlier round's testing, so — per this task's explicit allowance for "a wizard call-granted starting sum... if that's the normal way players get initial capital" — admin cloned and handed wentian fresh 10-20 tael silver stacks at need (exactly mirroring the real registration grant amount, spent down authentically by each subsequent transaction, never inflated beyond what a real player's starting capital would cover). Also cloned two disposable d/xiakedao/obj/car.lpc instances (mainland cart travel is otherwise gated behind having already ridden the very cart being consumed) to reach 武当/wudang and 泉州/quanzhou without re-running the boat sequence each time — this is the "goto to bypass re-testing the boat/cart mechanism itself" allowance the task explicitly granted, used only to skip REDUNDANT repeats of an already-verified mechanism, never to skip the actual systems under test.

1. Real sect-hall joining — WORKS, live-verified, no bug

Target: 武当派/Wudang, reachable immediately at the mainland arrival room (d/wudang/shanmen.lpc, "玄岳门"), which hosts kungfu/class/wudang/lingxu.lpc(灵虚道长, generation 3 real family member — create_family("武当派", 3, "弟子")). Its attempt_apprentice() (via #include "daozhang.h") is a REAL, multi-stage recruitment flow, not a hardcoded island-NPC-style refusal:

2. Mainland skill teacher — WORKS, live-verified, no bug

cmds/skill/xue.lpc's learn command requires me->is_apprentice_of(ob) || ob->recognize_apprentice(me) || ... — none of the reachable Wudang NPCs define recognize_apprentice() locally (only is_apprentice_of, which needs FULL sect membership, a longer grind than round four's time budget), so the real "pay a fee, learn immediately" teacher path tested here is 泉州/Quanzhou's 扬威武馆 (martial hall), reached via qu quanzhouwest/south/west/west to 前厅. d/quanzhou/npc/mawude.lpc's accept_object() grants mark/马 (temp flag) to anyone who hands over a money item worth ≥500 (i.e. 5+ silver) AND has combat_exp <= 3500 (a genuine "no advanced fighters" gate, wentian's combat_exp is 0, passed easily) — the department teachers (chenhu.lpc etc.) then gate recognize_apprentice() on that same mark/马 flag. Live-verified: give silver to ma (20 taels handed over — the accept logic takes the WHOLE stack handed to it, no change given, which is harsh-but-intentional design, not a bug) → "请到后院学习你所喜欢的功夫吧" (go learn what interests you) → navigated to 棒杖部/bangbu (陈浒/Chen Hu, stick/staff/cuff/force teacher) → xue hu force → "你听了陈浒的指导... 你的「force」进步了!" → skills correctly shows force at level 1. Zero debug.log errors. This is the real organic mainland-teacher mechanism the task asked for, confirmed working end-to-end.

3. Player-currency shop purchase — WORKS, live-verified, no bug (real partial-stack spend, not round two's zero-stack edge case)

d/city/jujinge.lpc's 牛掌柜 (already found working in round two, but only with admin-cloned currency spent to exactly zero). This round repeated it with wentian's own money and a non-zero remainder (the round-two/round-three §7.52-lineage bug class this project documents — stackable items spent to exactly 0 silently orphaning — only manifests on the exact-zero case, so this is a genuinely different code path than round two exercised): reached via qu yangzhoueast/south/south/south/west to 聚金阁 (jujinge). list showed the correct price table (铜镜/mirror: 5 taels). buy tong jing with 10 taels in hand → "你从牛掌柜那里买下了一面铜镜。" → inventory correctly shows 5 taels remaining + the mirror. Zero debug.log errors, including no insufficient permission/destruct_me lines from the §7.52-lineage bug class (not triggered here since the stack wasn't spent to exactly zero — that specific edge case remains covered by round two's original fix in inherit/item/combined.lpc).

Standing-checklist sanity pass (all confirmed intact/clean, no regressions, no new fixes needed)

Optional secondary check: §7.111/§7.112 grep-only pass on sibling libs xkm, shenzhou, xkx2001

Per this lib's own round-three "generalizable finding" flag (these 3 share the same EDITOR_D-fix-era lineage and the same gargoyle death- NPC discovery arc). Grep-only, not a full test, as instructed:

Cleanup

Fresh build-debug driver boot for this round (port 40064, PID 771369, killed by exact PID at the end — confirmed no longer running). log/debug.log clean throughout (1420 lines at session end, zero error-pattern hits at any checkpoint). No new test characters were created — reused the existing wentian/闻天笑 and admin fluffos characters per this project's established convention. wentian's and fluffos's save-file diffs from this session (new 武当道童 title, wudang/offerring, force skill, mirror/tokens in inventory, silver balance) are genuine gameplay state from the tests above and were kept, matching the same convention documented in every prior round. One new file appeared, work/data/npc/job_server.o — the persistent global save for the wudang_volunteer job daemon (/clone/obj/job_server.lpc), created as a normal side effect of the sect-joining flow's first-ever exercise on this lib; this is legitimate shared game-daemon state, not player-specific test debris, so it was kept rather than deleted.

Files modified this round

§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.

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).