The Smiling, Proud Wanderer II (Xiao Ao Jiang Hu II)

✅ 可玩

笑傲江湖 II

xajh2

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

▶ 开始游玩 · Play Now

游戏登录画面自称"虚拟人生",属于"XO mudlib"引擎家族(源自 TMI-2/ES2,由 Falcon 于1996年重写),与本项目另收录的《笑傲江湖迷你版》《XO最终版1.2》同源但独立演化、开站更晚的一个站点快照(站内公告显示自2003年4月1日开站);虽然打着《笑傲江湖》的旗号,继承自共同 XO 世界模板的门派体系其实是金庸多部小说的大杂烩——《倚天屠龙记》里的少林、武当、峨嵋、昆仑、崆峒、华山六大派,与《天龙八部》大理段氏皇宫、神农帮、无量剑派并存,此外还有丐帮及其对立的情报机构"一品堂";角色成长分拳脚、轻功、剑术、刀法等相互独立的修为体系。新建角色对同一 IP 地址限流以防刷屏,退出游戏(quit)需先玩到10点经验才能存档,均为刻意的反滥用设计而非缺陷。

English

Branded at login as "Virtual Life," this is a member of the "XO mudlib" engine family (descended from TMI-2/ES2, rewritten by "Falcon" in 1996) and a later, independently-evolved snapshot of the same bloodline as this collection's Xiao Ao Jiang Hu XO and XO Final 1.2 — in-game notices show it opened April 1, 2003. Despite the Smiling, Proud Wanderer branding, the actual sect roster inherited from the shared XO world template is a broad Jin Yong multi-novel mashup: the Six Great Sects of Heaven Sword and Dragon Saber (Shaolin, Wudang, Emei, Kunlun, Kongdong, Huashan) sit alongside Demi-Gods and Semi-Devils' Dali/Duan-clan palace, the Shennong Gang, and the Wuliang Sword sect, plus the Beggars' Sect and its rival intelligence service, the Yipin Hall. Character growth runs along separate fist/palm, qinggong, sword, and blade skill tracks. New-account creation is IP-rate-limited to curb spam, and quitting before earning 10 experience points forfeits the save — both deliberate anti-flood design, not bugs.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/xajh2/

管理员账号 / Admin account

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

本地运行

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

游戏端口:40068

NOTES · 移植与修复记录

xajh2 — 笑傲江湖 (XO/TMI-2/ES2 lineage, NOT the nitan family)

Archive: 笑傲江湖II.rar. Port: 40068. Status: done (boots clean, full registration with a real Chinese name works end-to-end, and post-login commands look/score both produce real output).

Lineage check (done first, per instructions) — related to xo/xo_final, NOT nitan

Despite sharing the "笑傲江湖" (Laughing in the Wind) title with nitan6 (archive #22), this is a completely different codebase — confirmed by evidence, not assumed either way:

Conclusion: same lineage family as xo/xo_final (TMI-2/ES2/Falcon "XO mudlib"), a distinct build/snapshot — not a duplicate, not nitan-family. xo's §4 fix was ported directly (proven fix, same file shape); the §15/ §15a-c/§15v nitan-specific fixes do not apply here at all.

What this is (per the archive's own README, 说明.txt)

Actual game name is 笑傲江湖 (config.xo's name field says "笑傲江湖 迷你版" — a stale copy-paste leftover from the mini-edition config, not this build's real name, same class of leftover documented for dtsl2 in TODO.md). Per 说明.txt: official address xo.27net.net:4000, default wizard account locker/000000 (present in secure/etc/wizlist). Used the archive's own working title "笑傲江湖II" for the slug/config name since that's what distinguishes it from xo/xo_final in this project's naming.

Fixes applied (with why)

1. AGENTS.md §15h — is_chinese/IsLegalName GBK byte-range bug (proactive, confirmed present here too): - secure/sefun/chinese.lpc's is_chinese(str): strlen(str)>=2 && str[0]>160 && str[0]<255 (GBK lead-byte check) → strlen(str)>=1 && str[0]>=0x4e00 && str[0]<=0x9fff (CJK codepoint range, UTF-8 native). - system/daemon/band.lpc's IsLegalName(name): length bound i<4 || i>10 (4-10 *bytes* = 2-5 GBK chars, matching the message "只能是 2 到 5 个中文字") → i<2 || i>5 (2-5 *characters*); and the per-character loop's i % 2 == 0 && !is_chinese(name[i..<0]) (byte lead-position gate) → dropped the i % 2 == 0 && entirely (every UTF-8 index is already one full character). - Verified end-to-end: sent the real Chinese name 秦风 through registration and it was accepted, advancing to the password prompt — not just "reached the prompt".

2. AGENTS.md §4 — master.lpc lazy-security-daemon reentrancy guard (ported directly from xo's proven fix, same file shape — secure/daemon/master.lpc's valid_write/valid_read already wrapped load_object(SECURITY_D) in catch() but had no re-entrancy guard and no find_object() fast-path check first): added private nosave int loading_security_d; and the same find_object-first / guarded-load / degrade-to-default-on-reentry shape used in libs/xo/work/secure/daemon/master.lpc.

3. New bug (not previously cataloged): combatd.lpc's switch/case with every real case commented out, leaving a bare default: — this driver requires at least one real case statement in a switch (error: need case statements in switch/case, not just default:). Since every branch except default was already dead code, replaced the whole switch with the default branch's statement directly (no restored content, just removed the now-meaningless switch wrapper). This is a preloaded daemon (combatd is in etc/preload), so this was a genuine boot-blocking compile error, not a dormant one.

4. New bug (not previously cataloged, high-impact): this driver build has __OLD_ED__ defined, so the "new" ed_start/ed_cmd/ query_ed_mode efuns this codebase's editor feature was written against simply don't exist at all (core.spec's #ifdef OLD_ED / #else split — only one side is ever compiled in). This broke TWO files: - system/feature/user/editor.lpc (the edit()/in-game-editor feature, inherited straight into /clone/user/user.lpc, the player body class itself) — failed to compile at all, which silently made make_body() return a broken/uncompilable object during registration with zero visible error (same failure shape as AGENTS.md §15b's "player-body class object itself failing to compile", new root cause). Fixed by rewriting edit() to call the OLD_ED ed(file, exit_fn_name, restricted) efun instead, with a new editor_exit_fn() callback replacing the old manual query_ed_mode()==-1 completion-detection. Confirmed via ~/src/fluffos/src/comm.cc that under __OLD_ED__ the driver's own C-level input loop (if (ip->ed_buffer) { ed_cmd(...); goto exit; }) intercepts every line directly while editing, bypassing input_to()/process_input()/write_prompt() entirely — so the old manual per-keystroke ed_cmd() forwarding in process_input() was unreachable dead code under this driver, not something to reimplement; simplified to a harmless passthrough. - system/feature/user/message.lpc's write_prompt() had the same query_ed_mode() dependency for customizing the in-editor prompt — same root cause, same fix rationale (dead code under __OLD_ED__, since print_prompt() in comm.cc also short-circuits straight to ip->prompt for ip->ed_buffer-active connections, skipping APPLY_WRITE_PROMPT). Removed the branch. - This is likely to recur on other libs — worth a new AGENTS.md catalog entry (§15af-shaped): any lib using ed_start/ed_cmd/ query_ed_mode needs this same fix, and the tell is a compile error in whatever file is inherited into the player body class, which then manifests as registration silently failing right after character creation with no error shown anywhere.

5. New bug (not previously cataloged, the hardest one to diagnose in this pass): the /log/nosave/ directory was entirely missing from the shipped archive (classic "RAR/zip drops empty dirs", same class as several prior libs) — used by log_file() targets like PPL_USAGE_LOG/WIZ_USAGE_LOG/COMMAND_LIMIT_LOG (all "nosave/..."-prefixed in include/globals.h). An uncaught write_file() error to that missing path, inside secure/sefun/file.lpc's log_file() simul_efun, silently aborted the entire get_gender() → log_login() → init_new_player() → enter_world() call chain right after gender selection — with zero visible error anywhere: not in debug.log, not shown to the connecting player. Bisected with temporary efun::write() checkpoints (removed after diagnosis, per AGENTS.md's established §8c/§15d technique) down to the exact statement, then discovered the *actual* error WAS being logged — just to the mudlib's own /log/runtime/secure file (via master.lpc's custom error_handler() apply), not debug.log. It was never shown to the player because error_handler() only calls message("error", ...) when this_player() is truthy, and at that specific point in the pre-exec() registration flow it apparently wasn't. New diagnostic lesson, worth cataloging: when a registration/input_to flow silently stalls at one exact step with *nothing* in debug.log and nothing shown to the player, check the mudlib's own log/runtime/* and log/catch files (if it has a custom error_handler() master apply) before assuming there's no error at all — the mudlib's own error logging can be strictly more complete than what reaches the terminal. Fix: created work/log/nosave/ (mkdir), and wrapped log_file()'s write_file() call in catch() as defense-in-depth (§15e-style graceful degradation — a failed bookkeeping log write should never be allowed to abort character creation).

6. §15i-shaped bug (comment eats the following code), recurring in two sibling files: system/std/char/task.lpc and system/std/char/special_quest.lpc both had // 现在先共用一个quest库 if ( random( class_score ) < 50 ) — the Chinese comment and the real if statement are on the *same physical line*, so // swallows the entire if, leaving a dangling else if/else chain with no matching if (error: syntax error, unexpected L_ELSE). Found via the exact same distinctive comment text in both files (one shared bug, not two) — split the comment onto its own line in both.

7. clone/user/immortal.lpc: string array arrCmd = explode(cmd,";")TYPE array NAME is not valid array-declaration syntax on this driver (distinct from AGENTS.md §15f's *bare* array — this is a *different* two-word "TYPE array" spelling, presumably a synonym this codebase's original target driver accepted). Fixed to string *arrCmd. This is the wizard-only multi-command (;-separated) batching feature in the wizard player-body class.

8. system/daemon/poisond.lpc: called set()/restore() (F_SAVE/F_DBASE methods) but never inherited either — a genuine pre-existing omission (every sibling daemon, e.g. emoted.lpc, does inherit F_SAVE;). Added inherit F_SAVE;. Not fully fixed even after this — lpcc still reports a (silent, no explicit error text) failure to load, most likely restore_object() erroring on nonexistent save data at create() time. Not pursued further: poisond is not in etc/preload and nothing else in the lib call_others into it — confirmed dead/unreachable code, so this doesn't affect boot or the verified registration/gameplay path.

Confirmed NOT needed (checked by reading the actual source)

New bug classes found here, worth AGENTS.md entries (flagged for the

main session to catalog)

1. A switch with every real case label commented out, leaving a bare default:, is a hard compile error on this driver (combatd.lpc). 2. __OLD_ED__ vs the "new" ed_start/ed_cmd/query_ed_mode API — this driver build only has ONE of the two editor APIs compiled in (__OLD_ED__ is defined), so any lib written against the other one fails to compile wherever it's used; if that's inherited into the player body class, registration silently breaks right after gender/ character creation with zero visible error. Likely to recur. 3. An uncaught write_file()/log_file() error to a missing directory during a pre-exec() input_to callback chain can abort the whole chain with zero visible error in debug.log or to the player — the mudlib's own custom error_handler() DOES log it, but only to its own log/runtime/<X> file, gated on this_player() for player visibility. New diagnostic lesson: check the mudlib's own runtime/catch log files, not just debug.log, when a flow silently stalls. 4. TYPE array NAME (two words, not AGENTS.md §15f's bare array) is ANOTHER old-MudOS-dialect array-declaration spelling this driver doesn't support — found extensively (12+ instances) in a dead legacy file (system/daemon/xoban.lpc, superseded by band.lpc per its own header comment) and once in a live file (immortal.lpc, fixed). 5. §15i's "comment eats following code" pattern recurred in a new shape: the comment and the swallowed code are on the *same* physical line (not the comment eating the *next* line) — found identically in two sibling files via one shared distinctive comment string.

Interactive test result (full transcript outcome)

Registration flow, in ONE continuous mudclient-style connection: newqinfeng (English id) → y (confirm) → 秦风 (real Chinese name — accepted, not rejected) → 123456/123456 (password + confirm) → [email protected] (email) → m (gender) → lands in the game world (welcome banner "温故知新" shown).

Post-login commands (§15ae standing policy, tested after registration completed, not just at the prompt):

This satisfies AGENTS.md §15ae's standing policy: registration was verified with a REAL Chinese name in one continuous connection, AND at least one ordinary post-login command was confirmed to produce real output (not the generic fail message) after registration completed.

lpcc sweep

4344 files: 4242 pass / 102 fail (97.65%) before the additional fixes below; verified 3 of the 4 newly-fixed files now individually PASS (task.lpc, special_quest.lpc, immortal.lpc; poisond.lpc still fails for the unrelated reason noted above) — full sweep not re-run a second time given the remaining failures are already fully triaged below and the net gain would be ~3-4 files out of 102.

Failure categories (triaged, not blindly re-swept):

Directories created (archive shipped without them — zip/rar drops

empty dirs)

Re-verification pass (2026-07-23)

Clean boot re-confirmed (zero fatal errors). First interactive attempt hit AGENTS.md §15j head-on: a new registration sent from a SECOND, separate mudclient.py connection (after an earlier same-session attempt with an invalid id had already ticked BAN_D->IsTimeAllowed()'s per-IP throttle) produced literally zero server response to newlogind.lpc's die() has its diagnostic write() commented out, so the rejection is silent, indistinguishable at a glance from a hang/crash. Confirmed via a raw socket probe (send new, wait 8s, zero bytes back) and a clean debug.log (no errors at all) that the driver itself was healthy — this is the documented throttle behavior, not a regression. Fixed by doing the entire registration + look/score/quit sequence in one single continuous mudclient.py connection (id xoahmount, name 秦山), per §15j's own recommended workaround, which worked cleanly on the first try: real Chinese name accepted, entered the world (hit the same known, previously-documented /d/place/newbie/start missing-room content gap, gracefully degrading to void exactly as before), score rendered the full character sheet, and quit showed the real "10-point save gate" text. Zero genuine runtime errors in debug.log. No code changes needed — this pass was a clean re-confirmation.

Port / process hygiene

Port 40068 (next free after 40066-40067, reserved for archives #72-73). Driver was started/stopped many times during diagnosis (each source edit requires a fresh process — LPC files don't hot-reload); every kill used the exact PID from that launch's own $!/ps -ef output, never a broad pkill pattern (multiple *other* agents' driver processes were confirmed running concurrently throughout this session, e.g. for shenzhou, xajhzcjh, xajhxo — none were touched). No driver process left running at the end of this pass.

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

WASM-enablement pass (2026-07-24)

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

1. Loopback always allowedsystem/daemon/band.lpc: added IsLocalSite(site) helper (127.0.0.1 / leading 127. / empty/non-string / malformed non-dotted-quad = WASM garbage), and short-circuited: - IsBannedSite() (called from logind.lpc StartLogon() line ~59) → local always returns 0 (not banned); - IsMultiLogin() (called at world entry for peaceful residents, logind.lpc line ~506) → local always returns 0; - IsTimeAllowed() (the 3-minute per-IP new-registration throttle whose rejection path is a SILENT disconnect, logind.lpc line ~142) → local always returns 1 (allowed). Verified live: two fresh registrations from 127.0.0.1 within 3 minutes both succeeded. 2. Uptime startup gate — the #ifdef LOGIN_DELAY startup-grace block in StartLogon() is dead (LOGIN_DELAY is defined nowhere); the uptime() < 10 check at line ~92 is purely cosmetic banner text ("刚启动" vs "已经运行了"), not a gate. Nothing to bypass. 3. KEPT (game design): the "must play 10 minutes before first save" quit/save gate (cmds/comm/quit.lpc / save.lpc, CAN_SAVE_LIMIT_TIME) — wizardp is exempt, so fluffos saves normally. 4. Admin account seeded — id fluffos, pw Mud@2026, name 浮浮, registered via the real flow (new → id → y → Chinese name → password x2 → email → gender m). Granted (admin) via secure/etc/wizlist. Verified update /system/daemon/band recompiles successfully.

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

(work/data/log/f/fluffos is a runtime per-char log, matched by the repo's **/log gitignore pattern — leave ignored.)

Retest: fresh normal registration (id qinfxa, 秦风) works end-to-end, score renders, quit-with-nosave gate fires as designed; fluffos login + wizard update works; zero new errors in debug.log. Test char artifacts removed. Known pre-existing content gap unchanged: new players land in 太白楼 via the missing-/d/place/newbie/start fallback (documented in the earlier pass).

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

Security correction, applied retroactively. IsLocalSite() (item 1 above) originally treated an empty/non-string/malformed IP as "local" (fail-open) — a stopgap for a since-fixed WASM driver bug. Tightened to fail-closed:

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

IsBannedSite(), IsMultiLogin(), and IsTimeAllowed() all delegate to this one helper, so no other call site needed a separate edit; an unparseable/empty IP now falls through to the normal (non-exempt) path in all three. Retested: fresh registration (id xajhgate, name 秦岭峰) still completes end-to-end via loopback (score renders correctly, lands in 太白楼 per the pre-existing content gap above, quit's 10-point save gate fires as designed — no save file created, nothing to clean up); fluffos login + update /system/daemon/band still succeeds (重新编译 /system/daemon/band.lpc:成功!). Zero new runtime errors from this change (the pre-existing hui_quest.lpc syntax-error lines seen in debug.log are an unrelated, unchanged content bug in /d/menpai/shaolin/npc3/hui_quest.lpc, not touched by this pass and not on the login/registration path).

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

Full hands-on playthrough per AGENTS.md §10.7, one continuous mudclient.py session per major phase (multiple phases because prior findings required a fix + driver restart before continuing — each restart began a fresh phase, not a fresh probe). Read help/user/newbie first (行路篇/拜师学艺篇/交流篇 sections): confirmed the intended new-player loop is register → explore the local city → duilian/bihua for safe practice fights → qingjiao <师傅> <技能> to learn from an ordinary city NPC before ever committing to a sect.

Test character: id qinfeng, Chinese name 秦风, male, registered through the real flow (new → id → y → 秦风 → password ×2 → email → m). Landed via the /d/place/newbie/start fallback in either 高昌城中心 (Gaochang) or 小秦淮客寓 (Yangzhou, per select_start_room()'s random pick — see bug 3 below for why this now actually matters). Test character and its save files (data/user/q/qinfeng.o, data/login/q/qinfeng.o, data/log/q/qinfeng*) were removed after testing, per this project's usual convention (§1.5-seeded fluffos admin excepted). Restored one UNRELATED pre-existing tracked file at the same log path (data/log/q/qinfeng, part of the original shipped archive, coincidental id collision — verified via git log before touching it) after an overly-broad rm.

New bugs found and fixed

1. d/menpai/shaolin/npc3/hui_quest.lpc — swallowed-comment syntax error breaking the Shaolin quest-giver's compile (AGENTS.md §6.6). Line 183: // 现在先共用一个quest库 if ( random( class_score ) < 100 ) — the // comment swallows the if condition on the same physical line, leaving line 184's tag = ... unconditional and a dangling else if/else chain with no matching if (error: syntax error, unexpected L_ELSE). This file is inherited by d/menpai/shaolin/npc3/huijue.lpc — one of Shaolin's actual apprentice-recruiting master NPCs (attempt_apprentice()/ recruit_apprentice() both live there) — so the compile failure silently removed that specific recruiting master from the game entirely. Reproduced live: this compile error fired on the very first fresh registration in this session (via select_start_room()'s room-loading scan touching the Shaolin zone), confirmed in debug.log. Already spotted-but-explicitly-left-alone by the prior "fail-closed loopback retrofit" pass (2026-07-24, this same NOTES.md, "not touched by this pass"). Fix: split the comment onto its own line, restoring if (random(class_score) < 100) tag = .... Verified: lpcc now compiles both hui_quest.lpc and huijue.lpc clean (only the usual benign #pragma/nosave-class warnings), and a fresh boot produces zero hui_quest-related lines in debug.log (previously appeared on every single boot). Swept the whole lib for the same comment string and for the general "comment line immediately followed by a bare {" shape (Python scan of every .lpc file) — the only other 4 hits were legitimate (function signature already on the preceding line); no further instances.

2. system/skill/basic/kongshou.lpc — same bug class, breaking the default UNARMED COMBAT skill used by every character. Line 39: // 这个函数用来区别这种天生的技能与其他后天学习的技能int is_native_skill() followed by a bare { on the next line — identical shape to bug 1, just with the swallowed code being a whole function signature instead of an if. kongshou (inherit SKILL, query_xiuwei_type()"unarmed") is the base bare-hands combat skill every fresh character starts with (no weapon, no learned skill needed) — error: syntax error, unexpected '{' meant this file has never successfully compiled on this driver. Found live via checklist item 3 (safe-sparring mechanism): bihua <某人> (per help newbie's 拜师学艺篇 — combat that auto-stops at 20% HP/stamina, and the command itself tops up the opponent to full HP/force first if they're already above 20%, i.e. the "mirrors the attacker" stat-copy shape AGENTS.md §10.7 point 3 describes) against 老先生 (a peaceful, non-hostile teacher NPC at d/city/gaochang/npc/teacher.lpc, whose base-class accept_fight() accepts a challenge from a full-health player specifically because its attitude is unset — hits the default: branch, not the "friendly" refusal case). 老先生 accepted ("既然小兄弟赐教,老头子只好奉陪"), combat began, and every single combat round from then on threw *call_other() couldn't find object '/system/skill/basic/kongshou'! — repeating on EVERY subsequent heartbeat-driven combat tick, spamming the error even into unrelated commands (score, i) typed afterward while combat was still "active." This is a severe, universal bug: unarmed combat (the default state for literally every player without a weapon) never actually resolves correctly. Fix: split the comment/declaration onto separate lines, same shape as bug 1. Verified via lpcc: the file now compiles clean (previously a hard syntax error). Re-tested live post-fix (fresh driver restart, reconnected as 秦风): a follow-up bihua attempt against the same NPC no longer produced the error — confirmed via debug.log staying clean through the exchange. (The original NPC/room the first accepted-bihua repro used, 老先生 in Gaochang, was not re-reachable in the exact same accepted-combat shape after the character's startroom anchored to a different city on a later login — see bug 3 — so the positive re-verification used the same NPC's *declined*-bihua path plus a clean lpcc recompile as corroborating evidence rather than re-triggering a second full accepted exchange; documented honestly, not silently presented as re-proven identically.) Swept the lib for the same shape: dodge.lpc/yeshou.lpc/ horsedodge.lpc/wuqi.lpc (siblings sharing the same is_native_skill() doc-comment text) all have the declaration and opening brace on the same line — not affected.

3. system/daemon/logind.lpc's enter_world() — the long-documented missing-/d/place/newbie/start content gap ALSO silently skips user->save()/ob->save() for every brand-new character (new finding, matches AGENTS.md §7.14's exact guard pattern). Every prior pass on this lib (see the "Interactive test result", "Re-verification pass", and "Driver rebuild" sections above) correctly identified that /d/place/newbie/start doesn't exist in this archive and documented the symptom as "gracefully degrades to void, not fixed (would require fabricating missing content, out of scope)" — true as far as it went, but incomplete: user->move("/d/place/newbie/start") throws an uncaught *call_other() couldn't find object error that aborts the rest of enter_world() outright, including user->save() and ob->save() near its tail (system/daemon/logind.lpc lines ~657-658). Concretely, this means a fresh registration on this lib never wrote any save data to disk until the next periodic 15-minute autosave (AUTO_SAVE_INTERVAL = 900s, system/feature/user/autosave.lpc) or a net-dead force-quit (NET_DEAD_TIMEOUT = 900s, same value, system/feature/user/interactive.lpc) fired — both bypass the separate, intentional "must play 10 real minutes before manual quit saves" retention gate (CAN_SAVE_LIMIT_TIME = 1800s, cmds/comm/quit.lpc/include/options.h) because they route through later_quit()'s set_temp("valid_quit", 1) shortcut, which unconditionally calls me->save(). Practical consequence, confirmed live: any brand-new player who disconnects uncleanly (the single most common real-world disconnect mode) within their first ~15 minutes has NO save file at all yet — and system/daemon/logind.lpc's get_id() determines "does this id exist" purely from file_size(ob->query_save_file() + __SAVE_EXTENSION__) (line 166), never consulting find_body(arg) for a live/net-dead in-memory body (that check only happens later, in get_passwd(), which is unreachable without first passing the disk-existence gate) — so such a player is told "对不起,这个id还没有登录过,请用new来起用这个id" ("this id has never logged in, please use new") on any reconnect attempt before the 15-minute mark, even though their body is still genuinely alive server-side. This reads exactly like account/data loss to a real user. (This specific "login flow doesn't check live state before disk" shape is new — it doesn't quite match any existing §7.20/§7.21 entry, which are both about a *found* net-dead body's location not being restored; here the body is never *found* at all because the existence check short-circuits on disk state first. Possibly worth a new catalog entry if seen again on a sibling — see draft below.)

Fix, matching AGENTS.md §7.14's own prescribed pattern ("Guard the move with load_object()/fallback to START_ROOM"): wrap the newbie-room move in catch(), falling back to the same startroom value the two lines above it already validated via catch(load_object(startroom)):

if (newbiep(user)) {
  if (catch(user->move("/d/place/newbie/start")))
    user->move(startroom);
} else
  user->move(startroom);

Verified live end-to-end: fresh registration (fresh driver boot, id qinfeng, 秦风) landed in a REAL room (城中心, Gaochang — one of the 5 start_rooms candidates) instead of /clone/misc/void, and data/user/q/qinfeng.o / data/login/q/qinfeng.o existed on disk immediately after registration completed (previously: nothing on disk at all until 15 real minutes had passed). debug.log stayed clean (only the same, now-harmless /log/catch-routed notice that the newbie room is missing — no longer fatal to the rest of the flow). The underlying content gap itself (no /d/place/newbie/start in this archive) is unchanged and still out of scope (§7.14/§13 — an archive gap, not fabricated). Player-visible side effect worth noting: since startroom is only ever persisted via the OTHER fallback branch (catch(load_object(startroom)) failing) and never by the success path this fix exercises, a newbiep() player's landing room is picked fresh by select_start_room() on every FULL login (not full reconnects — a net-dead reconnect correctly resumes the same room the body never actually left, verified in the reconnect testing below) until whatever in-game action clears newbiep(). This is pre-existing behavior common to any of the 5 start_rooms entries, not something this fix changed or introduced — documented as an observation, not touched (game-flow question, not a crash).

Checklist coverage

Process hygiene

Driver booted/restarted 4 times during this pass (each source fix requires a fresh process — confirmed via readlink -f /proc/<pid>/cwd before every kill that the PID being killed was this lib's own driver, never another concurrent agent's; several other libs' drivers were confirmed running throughout, e.g. dtsl2, fy2qh, fy3dz, longyunmeng, shenmo, jqxz2008std, hymud — none touched). No driver process left running at the end of this pass. A background sleep+reconnect sequence intended for the real-wait net-dead check (item above) was armed then explicitly torn down mid-pass per a time-budget correction — no dangling background jobs left either.

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

同一个 XO 血统,2003 年快照。

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

§7.100 sub-threshold instance (2026-08-20)

Found during the §7.100 tail-sweep (below the original 166-lib survey's

=100-occurrence threshold, never checked). 2 live

replace_program(ROOM); occurrences in d/menpai/wuliang/west/shufang.lpc and d/menpai/wuliang/west/shufang1.lpc. No room-building tool exists in this lib. Fixed by deleting the redundant lines. Verified via a clean native driver boot (zero new debug.log errors, port listening, killed by exact PID after ~8s).

深度功能测试第四轮 / Round four (2026-08-20): the 3 explicitly-flagged §10.7-rule-6 gaps

Targeted pass closing the three gaps the round-two pass ("2026-07-25") explicitly left unverified-live. Native build-debug driver, own raw Python dual-socket telnet client (dual.py, adapted from scripts/mudclient.py to drive TWO simultaneous connections — needed because give requires the recipient to be genuinely interactive(), not just alive-but-netdead, so a player and an admin connection had to be open at once), each response inspected before sending the next command. Standard checklist re-confirmed first (see below) — all already clean/fixed by prior passes, no new checklist regressions.

Gap 1: reconnect persistence — RESOLVED, completed live

Test character qinlie/秦烈 (registered fresh this pass). After reaching a stable state (money via the sanctioned admin clone+summon+give pattern — see Gap 2 — landed qinlie at 太白楼 carrying 青布衣衫(worn)/百宝箱/两银子), issued a real quit at real playtime > CAN_SAVE_LIMIT_TIME (1800s past birthday, confirmed via query("birthday") vs wall clock first) — this is the FULL later_quit() path (call_out("later_quit", 30, me), not the "confirm without saving" gate), which fired after the real 30-second delay, dropped non-autoload items with on-screen confirmation ("你丢下 一个百宝箱。你丢下一套青布衣衫。"), then the standard farewell text and a genuine destruct(me) — confirmed via find_body/call qinlie->... both returning "找不到指定的物件" immediately after. Waited a real ~3 additional minutes (character fully absent from memory the entire time — not a netdead body, an actual absence, so this is an unambiguous disk-only restore test) before reconnecting. Restored state matched: score showed identical stats/attributes/skill levels/death count (3 deaths, matching pre-quit); i showed the silver money intact (AutoLoad in the raw .o save file confirmed /clone/money/silver survived with its exact amount) — this is the only inventory item that is expected to survive a quit, per later_quit()'s own query_autoload()-gated drop logic, verified correct. Two adjacent findings, both confirmed NOT bugs (no error signature, fully graceful, consistent across repeated reproduction):

A separate accidental discovery while investigating a stalled qinshen reconnect (see the §7.108 fix below) is the REAL bug found this pass.

Gap 2: shop purchase — RESOLVED, completed live, correct

Followed the sanctioned admin pattern this project's other round-four passes established (see mhxy's NOTES): as admin (fluffos, pre-seeded per the 2026-07-24 WASM-enablement pass), clone /clone/money/gold (this lib's own /clone/money/gold.lpcvalue 10000), then summon qinshen/give gold to qinshen to bring a fresh, ordinarily-registered test character (qinshen/秦深, id qinshen, pw Test2026) into the same room and hand over real currency — but this lib has an extra wrinkle worth documenting for future passes: give to a non-wizardp recipient silently routes through a different, stricter accept path when the recipient is netdead (!interactive(who)) — cmds/verb/give.lpc's do_generate block calls who->accept_object(ppl, obj) (the quest-turn-in handler, system/std/char/master.lpc), which almost always declines for an ordinary gift, producing the exact same "好象不大愿意接收你的物品。" text a genuine env/refuse_give opt-out would show — easy to misdiagnose as the wrong bug. Fix for testing purposes (not a code change, a methodology note): the recipient must be genuinely interactive() — i.e., actually connected, not merely alive-but- netdead — for a plain give to succeed. Wrote a small dual-socket Python driver (dual.py) to hold the player connection open with periodic blank keepalives while a separate admin connection acted on them in the same room. Completed purchase: list san at 小三 (酒保, 太白楼) showed 女儿红(nver hong):一两银子; buy nver hong from san succeeded (the plain alias nver alone does NOT match — vendor.lpc's buy_object() matches the object's full id() string, which for this item is the two-word "nver hong", not the shorter aliases also registered via set_name). Verified via query_money() and i before/after: exact price deduction with correct auto-change (paid with 1 gold = 10000, price 1000, correct remainder = 9000, redistributed by feature/finance.lpc's pay_money() into 0 gold + 9 silver + 0 coin — confirmed both the existing gold object's amount getting adjusted and a brand-new silver object being created for the change, matching pay_money()'s own logic read from source) — both the price deduction and the item receipt (女儿红(Nver hong) in i) verified correct.

Gap 3: combat/death progression — RESOLVED, real death/respawn cycle, ONE REAL BUG FOUND AND FIXED

Per the task's dtsl2/mhxy precedent question: checked cmds/verb/kill.lpc first — its only gate at all for an NPC target (!userp(obj) branch) is obj->refuse_killing(ppl), an NPC-defined opt-out (many NPCs have it, many don't; undefined-function call_other returns 0/allow by default) — no combat_exp or other PK-only gate applies to NPC targets at all, confirming the precedent holds here too. kill (not bihua) worked immediately.

Found a genuinely killable target: d/city/gaochang/npc/asan.lpc (combat_exp 50000, set_hp gin/kee/sen 500/700/700) has refuse_killing() (a protected quest NPC, not usable), but its sibling d/city/gaochang/bosipu.lpc's npc/shangren2.lpc (波斯商人 阿里, combat_exp 50000, no refuse_killing override) does not. Test character qinlie/秦烈 (age 14, fresh, 0 combat_exp) walked there for real (center→west→west→north) and kill shangren — real, unmodified combat resolved over a few rounds ("你的眼前一黑,接着什么也不知道 了……" → "你满身血迹,再也支持不住,一头撞倒在地上") into a genuine death, moved to 黄泉路 (d/place/death/yellow1). debug.log stayed completely clean (0 new lines) through the entire combat/death/ reincarnation sequence, this pass's entire session.

Walked the full underworld chain for real (yellow1→north→yellow2(孟婆's chat_msg ambient lines look identical to её her real death_msg stage-0 line and briefly caused a misdiagnosis — the REAL tea_give() sequence completed correctly and gave 孟婆茶)→north→gate(白无常/黑无常)→north→difu1 (牛头/马面)→north→difu2→north→dadian/阎罗大殿, where d/place/death/npc/chacha.lpc (查察司判官) runs the actual reincarnation dialogue.

Real bug found: chacha.lpc's death_stage() permanently soft-locks any ghost who still has ANY item (including the 孟婆茶 mengpo tea EVERY ghost receives automatically two rooms earlier) at stage 4.

void death_stage(object ob, int stage) {
  ...
  else {
    obs = deep_inventory(ob);
    if (sizeof(obs)) {
      command("hmm");
      command("say 不过阴间的东西是不能带到阳间的,你先要把你身上的东西放下来。");
      return;                              // <-- never clears the guard
    } else
      tell_object(ob, death_msg[stage]);
  }
  ...

init() guards re-entry with ob->query_temp("death_stage_active") — every OTHER exit path in this file (the early !ob || !present(ob) branch, and the normal end-of-sequence path after reincarnate()) correctly clears this flag, but the item-check branch does not. Since nothing else in the whole codebase ever calls death_stage() except init() (itself blocked by the still-set flag), a ghost who reaches stage 4 still carrying items — which is EVERY ghost, since die() never auto-drops inventory and every ghost is handed a 孟婆茶 two rooms before reaching 查察司 — gets stuck in ghost form permanently: re-entering the room after dropping items is a no-op forever, with zero error anywhere (debug.log stays clean the whole time — this is a silent, crash-free soft-lock, not a crash). Live-reproduced twice (once via natural gameplay — stalled for real, query_temp confirmed the flag stuck at 1 with no error and no pending callouts entry for death_stage at all; once via a controlled repro with an admin-given item after the fix, confirming the exact same stall shape). Fix: added ob->delete_temp("death_stage_active"); right before the existing return; in that branch, matching the pattern every other exit path in the file already uses. Verified end-to-end, twice: (1) direct repro — cleared the stuck flag via admin, re-entered with an item still carried, confirmed the NEW code now clears the flag itself (query_temp → 0, not stuck at 1) when hitting the item-check message; (2) full natural recovery — dropped the item, re-entered the room with zero admin intervention, and the complete 5-stage dialogue played out correctly this time, ending in a real reincarnate() + move to a live room (d/city/gaochang/center). lpcc-equivalent live update + destroy-and-respawn-the-clone (dest, call .../dadian->reset()) used to get the fix into the ALREADY-LOADED live NPC instance without a full driver restart — confirmed via callouts listing that the fresh clone (chacha#160) picked up the new updated bytecode where the stale clone (chacha#119, still running pre-fix code even after a successful file-level update) had not.

Post-reincarnation state verified correct: score showed the death count incremented (3, after 2-3 real deaths across this pass's testing) and all stats restored to full (heal_up()/reincarnate()'s eff_*=max_* reset), no lingering ghost flags.

§7.108 gap found and fixed: interactive.lpc's reconnect() missing enable_commands()

Found by accident while investigating why a long-idle qinshen reconnect left every command producing "什么?你想干嘛?"/showed "对 不起,由于你没有环境,转移到void..." — this is AGENTS.md's own cataloged §7.108 ("kick out an already-connected duplicate login" reconnect path), which a corpus-wide sweep already fixed on 162 libs — xajh2 was evidently missed by that sweep (grep -n enable_commands system/feature/user/interactive.lpc → zero hits, confirming the gap). system/daemon/logind.lpc's confirm_relogin() has the exact vulnerable shape (exec(old_link, user); destruct(old_link); then reconnect(ob, user)), and system/feature/user/interactive.lpc's character-class reconnect() never called enable_commands() — only set_heart_beat(1)/set_temp("netdead",0)/remove_call_out. Fix: added enable_commands(); as the first line of reconnect(), matching AGENTS.md §7.108's exact proven remedy. Verified live, twice: (1) reproduced the exact "kick out" sequence pre-fix is implied by the qinshen symptom (not independently re-confirmed broken pre-fix, since the fix was applied before a clean isolated repro was run — see honesty note below); (2) post-fix, reproduced the full canonical §7.108 repro live (connection A holds qinlie open and interactive, connection B logs in as the same id, gets the "另一个连线中的相同人物" kick-out prompt, answers y) — look/score both produced full, correct output immediately after "重新连线返回。", not the generic fail message. Honesty note: the qinshen anomaly that led to finding this (commands failing AND environment loss AND find_player()/call unable to resolve the object even though find_body()'s internal fallback still could) was not independently root-caused beyond identifying it as this same gap's family — it may be a compound symptom of §7.108 interacting with this lib's long-idle netdead path rather than a byte-for-byte match to the catalogued symptom; not chased further given the canonical repro above already gives an unambiguous, clean positive/negative comparison.

Checklist re-confirmation (all already clean/fixed by prior passes — no regressions found)

Process hygiene

Driver restarted once mid-pass (to pick up the interactive.lpc fix cleanly, since it affects the live player-body class shared by every connected character — safer than a live update for a base class already in active use by test characters). Every kill used the exact PID (804454, then 813660), confirmed via readlink -f /proc/<pid>/cwd before each kill. No other libs' drivers were touched. Test character save files (qinlie, qinshen — login/user/log, all three dirs) removed after testing per this lib's established convention; fluffos admin save-timestamp churn (data/login/f/, data/user/f/) left as-is per this project's standard practice for the seeded admin account.