Journey to the West: Glory Days

✅ 可玩

西游记[光辉岁月]

xiyouji2003

🔑 fluffos / Mud@2026 更新 85d6a35 2026-09-02 源码 下载 ZIP

▶ 开始游玩 · Play Now

一款 2003 年前后的"西游记"系列文字 MUD,与本项目中的 `xyj2000f`、`xiyouji`、`xiyouji2006`、`xiyouji450`、`mhxy` 等同属"ES II / 西游记"这一大家族——它与 `xyj2000f` 的核心引擎代码(`master.c`)几乎完全一致,是这条支线中较晚、内容更丰富的一个独立站点。

English

A circa-2003 entry in the Journey to the West series of text MUDs, part of the same "ES II / Journey to the West" family as this project's xyj2000f, xiyouji, xiyouji2006, xiyouji450, and mhxy — its core engine code (master.c) is nearly identical to xyj2000f, making it a later, more content-rich offshoot of that branch. Players set out from the South City Inn in Chang'an to face "eighty-one trials" across the three realms of heaven, earth, and the underworld, backed by a fairly developed system of sects, guilds, warfare, marriage, and Five Elements attributes. The inn's message board still preserves genuine historical developer announcements — quest-system revision notes, bug-fix notices, sect leaderboard rules — a firsthand record of how the site was run at the time.

README

内容亮点

深度功能测试新发现的 bug(详见 NOTES.md)

这份档案只打包了 d/city/(长安城)和 d/wiz/ 两个区域,原版 /d/obj/ 共享物品目录、/d/moon/(月宫)、/d/nanhai/(南海) 等区域整个缺失,导致约 35 个城内 NPC 一进入所在房间就触发驱动的通 用错误提示("系统局部错误,请向巫师汇报。")。逐个核实后:其中 linen(粗布衣)、choupao(绸袍)、sengyi(僧衣)、 sengxie(僧鞋)这几件衣物其实还在,只是被挪到了 d/city/obj/ 下——已把这 58 处硬编码的旧路径改正(新增 AGENTS.md §7.66)。另一 部分引用(如月宫罗衣、取经书、钢杖等)在整份档案里彻底找不到同名替 代文件,是真的没有被打包进来的内容缺口,如实记录未修复。

在线试玩

https://mudlibs.fluffos.info/xiyouji2003/

管理员账号 / Admin account

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

本地运行

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

游戏端口:40075

NOTES · 移植与修复记录

西游记2003.rar → xiyouji2003

Lineage: confirmed a genuine relative of BOTH xyj2000f (#15) and the 梦幻西游/mhxy family (#19/#56/#73) — but NOT a duplicate of any of them

Per the task's explicit instruction, all four already-processed Journey-to-the-West-themed siblings were read (libs/xyj2000f/NOTES.md, libs/mhxy/NOTES.md, libs/mhxyqd/NOTES.md, libs/shenmo/NOTES.md) and this archive's adm/obj/master.c, adm/simul_efun/chinese.c, adm/daemons/logind.c, adm/daemons/securityd.c, adm/daemons/named.c, adm/daemons/convertd.c were diffed/md5sum'd against the corresponding raw files of all four before any conversion work began.

Findings, by file (raw pre-conversion bytes, GBK-decoded for the text diffs):

Conclusion: this is a genuine, independently-evolved 2003-era snapshot of the SAME master.c/ES-II-derived "西游记" (Journey to the West) engine that xyj2000f (#15) also derives from — closer to #15 than to the mhxy/mhxyqd/shenmo branch, but not a duplicate of any of the four. Per the task's instruction to port proven fixes directly when related: xyj2000f's and mhxy's §8h convertd.lpc fix pattern (CRLF-aware sed) was ported directly and worked verbatim (same shape, same count). The §15h is_chinese/check_legal_name fix pattern was ALSO the right family of fix, but this archive's own is_chinese()/check_legal_name() implementations are a different (sliding-byte-pair) variant than either sibling's, so the concrete edits were derived fresh from THIS archive's actual code rather than pasted from a sibling (see below) — consistent with the project's repeated finding that shared lineage does not mean byte-identical bugs.

Status: DONE — boots clean, full registration + post-login-command flow verified twice in independent continuous connections

Re-verification pass (QA sweep, later session)

Re-tested the full flow end-to-end again this pass (still clean, zero 执行时段错误 in debug.log). Found and fixed two real bugs shared across this whole "西游记" sibling family:

1. §15s, tell_room()'s raw-int 0-as-4th-arg bug, re-examined and found to actually apply here after all. This file's own earlier pass explicitly noted the driver "tolerates a raw 0" in message()'s exclude slot and left it unfixed -- but a live crash of the exact same call shape was subsequently confirmed on sibling libs xkx2001 and xiyouji2006 in this same later session (bridge-room close_bridge()/room create() chains hitting Bad argument 4 to EFUN message() ... Got: int(0)). Ported the standard fix (exclude || ({})) here too, defensively, since the underlying call shape is identical even though this specific archive's boot/ registration path happened not to trigger it. Re-verified with a clean boot + fresh registration: zero regressions, zero new errors. 2. Stray pre-existing debug leftover printf("%O\n", ob); in TWO places in adm/daemons/logind.lpc (the Chinese-name-confirmation step and a related get_resp-style retry path) -- dumps a raw internal object reference straight to the connecting player right after their Chinese name is accepted, on every registration. Same shared-lineage leftover found (and removed) in siblings xiyouji/xiyouji2006/ xiyouji450. Removed both occurrences; re-verified with a fresh registration (qfmingyu/明玉): no stray object-reference text anywhere in the transcript, look/score both still correct, correct female-specific fields.

Conversion

scripts/convert_lib.sh libs/xiyouji2003/raw/xyj200341 libs/xiyouji2003/work: GB18030→UTF-8 (already_utf8=231, converted=3046, lossy=36, skipped_binary=140), .c.lpc rename (2599 files), literal ".c" reference fixes (307 refs fixed, 0 remaining), angle-bracket #include <x.c> fix (1 ref), local angle-bracket #include <x.h>"x.h" conversion (28 converted), staticnosave (83 files touched). No uppercase .C files found (find . -name "*.C" → 0, checked per §7 lessons from shenmo). No stragglers missed by the file-heuristic (file -b check on every .lpc/.h came back clean).

§3 counterexample reverted: the blanket staticnosave sed corrupted 17 files' log_file("static/XXX", ...) / set("nosave/marry", ...) -style string literals (a pre-existing log/static/ subdirectory naming convention — confirmed real seed data on disk: log/static/{CRASHES, LASTCRASH,LASTTIME} shipped in the raw archive) to "nosave/XXX". Reverted with a targeted sed -i 's/"nosave\//"static\//g' scoped to the 17 flagged files; verified 0 "nosave/ string-literal hits remain and all 17 "static/ literals are restored. Checked for the §15z variant (a #define nosave static/#define protected static compatibility shim) — none present, nothing to revert there.

Fixes applied (in order found, with why)

1. §15h, is_chinese() GBK sliding-byte-pair bug — TWO independent copies fixed (this archive's own is_chinese() variant differs from every sibling's, so both had to be re-derived from source, not copy-pasted): - adm/simul_efun/chinese.lpc's is_chinese(str): if( i < 2 || i % 2 != 0 ) return 0; while( i -= 2 ) { if( (str[i] < 129 || str[i+1] < '@') || ... ) return 0; } (a GBK-byte-pair sliding-window check assuming 2 bytes = 1 character) → rewritten to a straightforward per-character CJK Unicode codepoint range check (str[i] >= 0x4e00 && str[i] <= 0x9fff for every index). - adm/daemons/chinesed.c's separate is_chinese(str) (the one actually called by logind.lpc's check_legal_name via CHINESE_D->is_chinese(...)) had the SAME %2!=0-rejects-odd-length bug plus a pre-existing typo (for(i=0;...) if(!(str[0]>170 && str[0]<255)) — always checked str[0], never the loop variable str[i], harmless under the old convention since a 2-char-minimum check made this a no-op most of the time, but still wrong) — fixed to the same per-character CJK range check, using str[i] correctly. 2. §15h, check_legal_name() in adm/daemons/logind.lpc: two sub-fixes in the same function, confirmed by ACTUALLY testing registration through to completion (not just reading the code): - strlen(name) < 4 || strlen(name) > 8 (message text explicitly says "二到四个中文字" — 2 to 4 Chinese characters — the byte-doubled bound of 4-8 was calibrated for the old 2-bytes-per-character GBK convention) → halved to strlen(name) < 2 || strlen(name) > 4. - if( i%2==0 && !CHINESE_D->is_chinese(name[i..<0]) ) (the i%2==0 gate existed to land on alternating GBK lead-byte positions; the name[i..<0] slice is this archive's own idiosyncratic single-"character" extraction idiom under the old byte convention) → dropped i%2==0 entirely and changed the slice to name[i..i] (one real UTF-8 character), checking every position. 3. §15h, named.lpc's PATH() sharding macro AND sliding-window similar-name check (adm/daemons/named.lpc, this lineage's name↔id registry, inherit F_DBASE — confirmed real local set/query/delete, not the nitan-family bare-simul_efun bug): - #define PATH(name) (name[0..1] + "/" + name) ("first GBK character") → name[0..0] (first UTF-8 character). - invalid_new_name()'s similar-name detector: strlen(name) < 4 guard + for (i = 0; i <= l - 4; i++) { assure_map_name(name[i..i+3]); if (i+6<=l) assure_map_name(name[i..i+5]); ... } (4-byte/6-byte windows = 2-char/3-char windows under the old convention) → halved to strlen(name) < 2 guard + for (i = 0; i <= l - 2; i++) { assure_map_name(name[i..i+1]); if (i+3<=l) assure_map_name(name[i..i+2]); ... } (2-char/3-char windows directly). 4. §8h, adm/daemons/convertd.lpc's Greek-alphabet-table stray-trailing-backslash typo — same defect shape as xyj2000f/mhxy ("α\", should be "α",), 45 occurrences, same count as mhxy's copy. File has CRLF line endings (confirmed via file -b), so the CRLF-aware pattern from mhxy's precedent was used directly: s/\\"(,)?\r?$/"\1\r/. All 45 fixed, 0 remaining afterward. 5. NEW, most impactful bug in this pass — logind.lpc's intermud DNS-daemon "site verification" gate self-shutting-down the server on the very first connection (§15ab/§15p/§15u-adjacent): encoding()'s if(new_start==0) { if(!find_object(DNS_MASTER) || !check_ip_(...)) { ... shutdown(1); return; } new_start++; }. With dns_master correctly excluded from adm/etc/preload (§15p, applied proactively before first boot), find_object(DNS_MASTER) is unconditionally null on a fresh boot, so !find_object(DNS_MASTER) is unconditionally true — the driver called shutdown(1) on the FIRST player to type gb/select an encoding, taking down the entire process, before this fix was found (discovered on the very first interactive test attempt: check_ip_() itself is a hardcoded return 1; stub in adm/daemons/band.lpc, so this gate only ever existed to punish a missing/unreachable intermud daemon). Fixed by requiring find_object(DNS_MASTER) to be truthy before the whole condition can fire at all (if(find_object(DNS_MASTER) && !check_ip_(...)) {...}), matching the shape of every other DNS_MASTER-gated call in this same file (lines 156/159's total_players() already had this exact !find_object(DNS_MASTER) → return 0 early-out pattern; the encoding() gate just never got the same treatment). Worth adding to AGENTS.md's catalog as a distinct variant of §15ab: a DNS/intermud daemon excluded from preload per §15p can still be relied upon (incorrectly) as an ALWAYS-loaded precondition elsewhere in the same file, and when the consequence of it being absent is shutdown() rather than a crash/error, the failure mode is "the whole server dies silently on the first real connection" rather than a visible LPC error anywhere in debug.log. 6. NEW content gap on the mandatory registration path — /d/wiz/init ("choose your gift" staging room) does not exist anywhere in this archive (§13/§15e): enter_world()'s if(user->query("no_gift")) user->move("/d/wiz/init",1); branch fires for EVERY new character (confirm_gift() unconditionally sets no_gift=1 for all new players before calling enter_world()), but /d/wiz/init.lpc does not exist on disk anywhere in the archive (confirmed via find). move() to a nonexistent object silently left every new character with no environment at all, which then crashed feature/command.lpc's command_hook() on environment(me)->query("limit_cmds") (line 62, env was 0) — Bad argument 1 to EFUN call_other() ... Got: int(0) — for every single post-login command, look/score/quit all failing identically, discovered exactly per the standing §15ae policy of testing a real post-login command. Fixed with a guard matching the EXACT same fallback pattern already used a few lines below in the SAME function for a missing startroom: if (load_object("/d/wiz/init")) user->move("/d/wiz/init",1); else user->move(START_ROOM);. Verified fixed: after the fix, look correctly shows the real starting room (南城客栈/South City Inn) and score shows a full, correct character sheet. 7. §15w, master.lpc's log_error() broadcasting every compile WARNING (not just real errors) to the connected player — same shape as wuhanzhan/shenzhou/shenmo: if(this_player(1)) { if(wizardp(...)) efun::write("编译时段错误:"+message); else efun::write(get_config(__DEFAULT_ERROR_MESSAGE__)); } fired unconditionally for BOTH wizard and non-wizard connected players on ANY log_error() call, including harmless warnings (Illegal to declare nosave function, Unknown #pragma, ignored, etc — this archive's #pragma optimize/#pragma save_binary directives aren't recognized by this driver). Fixed by gating the broadcast on the message NOT containing the substring "warning:", matching the established fix pattern exactly; still writes everything to the log file unconditionally. 8. §14, valid_override() upgraded to the 3-arg form — the original 2-arg valid_override(file, name) only recognized file==SIMUL_EFUN_OB||file==MASTER_OB; added the main_file parameter and main_file==SIMUL_EFUN_OB||main_file==MASTER_OB too, so an efun:: override written inside a file #included into simul_efun/master (rather than being simul_efun.lpc/master.lpc itself) is still recognized. Applied proactively per the catalog's "free fix" guidance; never observed to matter in a real boot here either (same as most other libs where this was tried). 9. §8d/§15o, master.lpc missing get_include_path() — added the standard implementation (prepends the compiling file's own directory, falls back to :DEFAULT:). 1768 files use the local #include <x.h>-angle-bracket idiom in this lib; convert_lib.sh's automatic local-include-to-quote conversion handled 28 of them directly during the standard pass, and this master apply is insurance for the rest reached only mid-connection (matches the pattern documented for shenmo/mhxyqd). The real boot + registration test produced zero Cannot #include errors either way. 10. §15b, message_combatd restored as an alias to message_vision (adm/simul_efun/message.lpc) — same "called (obj/weapon/bow.lpc) but never defined anywhere" gap already catalogued for wmkj. Needed a varargs void message_vision(...) forward declaration ahead of the new message_combatd wrapper (§8b/§15aa: message_combatd is defined textually before message_vision in this file) to avoid an Undefined function message_vision compile error — confirmed with a targeted lpcc re-check on the 3 affected files (obj/weapon/qimeigun.lpc, obj/weapon/changqiang.lpc, obj/weapon/bow.lpc) after the fix, all now PASS. 11. New §8g-variant: CLUB/F_CLUB macros never added to include/weapon.h's otherwise-complete weapon-type table, even though /std/weapon/club.lpc and /std/weapon/_club.lpc both exist on disk. weapon.h defines ARCHERY/AXE/BLADE/BOW/DAGGER/ FORK/HAMMER/MACE/RAKE/SWORD/STAFF/STICK/THROWING/ WHIP/SPEAR/NEEDLE (plus their F_-prefixed feature-base counterparts) exhaustively but simply omitted CLUB/F_CLUB — a genuine oversight, not missing content (the target files are right there). Affected exactly 2 files (obj/weapon/qimeigun.lpc, obj/weapon/changqiang.lpc, both inherit CLUB;) — fixed by adding the two missing #defines following the exact same naming convention as every other weapon type in the file. Both files now compile clean. 12. §15ac, bare SAVE_EXTENSION instead of __SAVE_EXTENSION__ — found 2 files in the (non-critical, non-preloaded) intermud-mail subsystem (include/net/ftpdsupp.h, adm/daemons/network/netmail.lpc). Fixed via a blanket word-boundary replace in both files; cheap insurance even though this subsystem is out of scope for registration testing.

Confirmed NOT needed (verified by reading the actual source, not assumed from a sibling's precedent)

Registration flow (read from logind.lpc's actual input_to chain before testing, per standing policy)

logon() → banner + Select GB or BIG5 (gb/big5): (send gb) → encoding() → site stats display → 请您做出选择:① 进入(Enter) ② 离开(Exit) (send 1) → if_young() → intermud site list + 您的英文名字:(新玩家请键入 new 注册) (send new) → get_id() → registration-notice banner + 请您给自己取一个英文名字: (send a 3-8 lowercase-letter-only English id, e.g. qinling) → get_new_id()confirm_id() shows the Chinese-name banner + 请输入您要取的中文名字(二到四个中文字): (send a real 2-4 character Chinese name) → get_name() (validates via NAME_D->invalid_new_name() + check_legal_name(), no y/n confirmation step when a real name is typed) → 现在请设定您的密码: (send a ≥5-character password) → new_password()请再输入一次您的密码: (same password again) → confirm_password() → email-registration banner + 您的电子邮件地址: (send [email protected] form) → get_email()您要扮演男性(m)的角色或女性(f)的角色? (send m/f) → get_gender()make_body() + confirm_gift("n", ...) (automatic, unconditional — no further prompt) → enter_world() → welcome banner, "第 1 次连线" stats, Guanyin-bodhisattva reincarnation news flavor text, then the real starting room (南城客栈/South City Inn).

Registration + post-login-command test transcripts

Run 1 (id qinfeng, name 秦风, gender m) — run BEFORE the /d/wiz/init fix (item 6 above) was applied: registration completed successfully end-to-end (banner → gb → stats → new → English id → Chinese name accepted → password/confirm → email → gender → welcome message + reincarnation news flavor text, all rendered correctly), but every post-login command (look, score, quit) failed identically in debug.log with *Bad argument 1 to EFUN call_other() ... Got: int(0) rooted at feature/command.lpc:62 — this is the exact §15ae-class "registration works, every post-login command silently does nothing" signature the standing policy exists to catch, root-caused to the missing /d/wiz/init room (item 6) rather than a private-visibility bug this time. Player-visible symptom: three 系统局部错误,请向巫师汇报。 lines (the non-wizard branch of log_error()/error_handler()) instead of any real command output.

Run 2 (id qinling, name 秦岭, gender f) — run AFTER the fix: full registration completed identically, enter_world() placed the character in 南城客栈, look correctly displayed the real room description (店小二/inn staff greeting, board/vote-box/exits, matching the room's actual content), score displayed a complete, correct character sheet (你现在的头衔 : 【 少 女 】, gender-correct fields: 性别:[女性]/状态:[ 处 女 ], stats/attributes all populated), quit correctly triggered the "new account needs 30 minutes online before quit preserves it, confirm deletion? (y/n)" gate (a genuine, intentional gameplay safeguard, not a bug). Zero errors of any kind in debug.log across this entire connection.

Run 3 (id qinlan, name attempt 秦岭 → correctly rejected as already-registered by run 2 above — 对不起,这个名字和已注册用户 qinling 的名字重复了。, confirming the dup-name check works — then retried with name 林风, gender m): full registration + look + score both succeeded cleanly again in a SECOND independent continuous connection, landing in the same starting room, correct male-specific fields (性别:[男性]/状态:[ 童 男 ]/头衔 【 少 年 】). Zero errors in debug.log for this run either.

lpcc_check.sh sweep results

Ran twice: once before the message_combatd/CLUB fixes (items 10-11) to establish a baseline, once after.

Triage of the remaining 277 failures (by category, none on the boot/registration/post-login-command critical path — all confirmed via debug.log staying clean across 3 real interactive runs):

How to run

cd libs/xiyouji2003
~/src/fluffos/build-debug/src/driver config.fluffos
python3 ../../scripts/mudclient.py 127.0.0.1 40075 --timeout 30 --idle 1.5 \
  --send "gb" --send "1" --send "new" --send "<3-8 lowercase letters>" \
  --send "<2-4 real Chinese characters>" --send "<password>=5+" \
  --send "<same password>" --send "[email protected]" --send "m" \
  --send "look" --send "score" --send "quit"

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

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

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

1. Loopback always allowed through ban gatesadm/daemons/band.lpc: added is_local_ip(string ip) helper (127.*, empty/non-string, or non-dotted-quad => local) and short-circuited is_banned(), create_char_banned(), is_strict_banned() to return 0 for local IPs. (Shipped ban lists empty — defensive standard patch. The check_ip_() DNS-mirror gate was already neutralized in an earlier pass.) 2. Uptime startup gate: none in this lib. 3. Anti-flood throttles exempt loopbackadm/daemons/logind.lpc: (a) logon()'s per-IP concurrent-connection cap (logon_cnt > 8, was ~L186) now skipped for local IPs; (b) the #ifdef MAX_LOGIN per-IP multi-login cap in get_id() (was ~L491, allow_multi_login) now skipped for local IPs. KEPT: the new-account 30-minute quit-retention window in cmds/usr/quit.lpc (game design; wizards are exempt from it anyway). 4. Admin account seeded — id fluffos, password Mud@2026, 浮浮 (male), via the real flow (gb1 进入 → new → id → name → password ×2 → email → gender). Granted (admin) via adm/etc/wizlist (fluffos (admin); file previously lacked a trailing newline — rewritten with one so the appended line parses). NOTE this lineage forces wizards to set a separate 巫师专用密码 on first wizard login (must contain upper+lower+other chars, differ from the normal password): set to Wiz@2026. So fluffos login is: normal password Mud@2026, then wizard password Wiz@2026. Verified after restart: 目前权限:主管天神(admin), update /adm/daemons/band → 文件顺利更新成功, goto worked, quit clean (wizardp exempt from the new-account deletion prompt). Save files: work/data/user/f/fluffos.o + work/data/login/f/fluffos.o (untracked, NOT gitignored — orchestrator must git add).

Retest: fresh registration (fluffos itself) reached 南城客栈 with look correct; fluffos wizard re-login verified; debug.log free of runtime errors.

深度功能测试(第二轮,2026-08-03)

之前只做过注册流程的浅层烟雾测试(fluffos 管理员账号那一轮),本 轮做了完整的 §10.7 深度功能测试:新注册一个真实中文名角色(秦风游 / id qindive),进入起始房间南城客栈后,走遍朱雀大街东西两段、 长安鼓楼(中心广场)、南安大道等约十个城内房间,和店小二、疥顶小 僧等 NPC 对话,读了留言板上大量保留下来的历史开发者公告(任务修 改说明、bug 修复公告、系统更新通知),查看 score(属性面板正 常,食物/饮水槽满,没有 §8.9 那个坏 age 检查的问题),确认 quit/ reconnect 都正常工作。

主要发现并修复:一个覆盖全城约 35 个 NPC 档案的路径失效 bug(新增 AGENTS.md §7.66)。 移动进入朱雀大街东段(zhuque-e1)时,客户端 每次都收到一条毫无细节的通用提示"系统局部错误,请向巫师汇报。"—— 这是驱动 config.fluffos 里 default error message 配置项的内容, 真正的报错细节只在 debug.log 里:*call_other() couldn't find object '/d/obj/cloth/sengyi',来自疥顶小僧(jieding.lpc)create() 里的 carry_object("/d/obj/cloth/sengyi")->wear()。往下查发现:这份档 案的 d/ 目录下只有 city/wiz/ 两个子目录——d/obj/ d/moon/ d/nanhai/ d/lingtai/ 等原版目录整个都不存在于这份快 照里。但"僧衣"(sengyi)"僧鞋"(sengxie)这两件衣物本身其实还在, 只是被挪到了 d/city/obj/ 下(用 set_name() 核对确认是同一件东 西,不是巧合同名)。进一步全库搜索发现,同样的坏路径模式还出现在 另外 ~35 个 NPC 档案里,其中 linen(粗布衣)和 choupao(绸袍) 这两种衣物也同样在 d/city/obj/ 下找得到对应文件——于是用二进制 安全的批量替换(保留每个档案原本的 CRLF/LF 混合换行,参照 AGENTS.md §10.4 的教训)把这 58 个档案里的 /d/obj/cloth/linen/d/city/obj/linen/d/obj/cloth/choupao/d/city/obj/choupao 全部改正,jieding.lpc 的 sengyi/sengxie 两行手工改正。LPC 格式化器对 57 个档案确认"已经 是规范格式,无需改动",biaoshi.lpc 一个档案被格式化器自己的安全 网拦下(token 不匹配,跟这次的改动无关,是这个档案本身早就存在的 问题,格式化器选择不写入而不是冒险破坏它——我的单行路径修复本身不 受影响)。重启驱动后重新走一遍之前会报错的房间,debug.log 确认 linen/choupao/sengyi/sengxie 这几个类别的错误全部消失。

同一次排查也确认了三个真的无法修复、只能如实记录的缺失内容(同 样在 AGENTS.md §7.66 里归档):hua_girl.lpc(花店老板娘 NPC)的 /d/moon/obj/luoyi(月宫罗衣,整个 d/moon/ 区域这份快照根本没 带)、jieding.lpc 自己的 /d/obj/books-nonskill/book-qujing (取经书)、dashi.lpc(疥癞和尚)的 /d/obj/weapon/staff/gangzhang(钢杖)——这三样物件在整个档案里 搜不到任何同名同类的替代文件,不是路径写错,是内容本来就没有被打 包进这份存档,没有去凭空捏造替代内容。另外 d/city/zhuque-e2.lpc/ zhuque-e3.lpc 因为引用的 /d/nanhai/npc/bonze(南海观音座下的和 尚 NPC)同样缺失,报错形态不太一样(room.lpcmake_inventory() 没有对 load_object() 返回 0 做防御,表现为 Bad argument 1 to EFUN call_other()),但根因和上面完全一样。

关于"东门(east)连不通"的提示:"你要去的区域还没有连通,请post通 知巫师。"——这不是 bug,是这个 lib 自己设计好的、对未搭建区域的优 雅降级提示(cmds/std/go.lpc 显式处理了 load_object() 失败的情 况),南城客栈的东门(指向 /d/obj/heidian/city_heidian,同样属 于上面提到的整个缺失掉的 /d/obj/ 子树)就是这种情况——是内容缺 口,不是程序错误,未做处理。

未覆盖范围(诚实记录,不假装测试过):本轮多次尝试对城内 NPC 发起战斗(先后试过 jieding、nukid/"小女孩"),但长安鼓楼一带有个 "土地"公告 NPC,几乎每次进入房间或稍作停留都会自动弹出一段很长的 历史开发者留言分页阅读,反复吞掉后续测试指令的时机,导致战斗指令 始终没能在一次干净的会话里送达并观察到结果;nukid 本身也有 random_move 漫游行为,不一定总在原地。技能学习(learn)、门派加 入、经济系统(购物、加工厂)同样因为时间原因没有实测,仅通过代码 review 确认了对应的 cmds//d/city/* 逻辑存在,不代表已验证可 用。

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

西游记2003/光辉岁月(master.c 约等于 xyj2000f)。

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

§7.100 sweep (2026-08-19)

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

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

Round-four gap-closing pass (2026-08-23): combat / learn / 拜师 / buy

Closed the four items this lib's earlier round explicitly left "未覆盖" (code-reviewed only, never live-tested): the 土地 news-daemon pagination obstacle (adm/daemons/newsd.lpc::auto_notice(), a call_out-driven periodic broadcast to ANY connected player with unread news, not room-specific as the earlier round assumed) was worked around by auto-detecting its 未完继续...(ENTER 继续下一页,q 离开,b 前一页) prompt and sending q to dismiss it before the next scripted command.

Real bug found and fixed — §7.66 addendum, 49+12 files, 165 sites: while routing around the 土地 NPC, hit the exact §7.66 "missing /d/obj/ shared-item tree" crash class on several DIFFERENT NPCs than the original pass covered (youxia.lpc's xijian sword, bing.lpc's bingfu uniform, sengren.lpc's sengyi, etc.) — but this time also found that dashi.lpc's /d/obj/weapon/staff/gangzhang, previously declared "genuinely gone" in AGENTS.md §7.66, is NOT actually gone: a byte-for-byte set_name()-verified match (钢杖) sits at /obj/weapon/staff/gangzhang.lpc — the top-level, non-/d/, generic weapon-class library this lineage also ships. Auditing every /d/obj/ reference in the whole archive (161 unique path strings) against BOTH recovery locations (/obj/... with the leading /d stripped, and the already-known /d/city/obj/... relocation) found 49 more recoverable under /obj/... and 12 more recoverable under /d/city/obj/... beyond what the earlier pass had already fixed — all verified by matching set_name() between the broken reference and the candidate file before touching anything, never by filename alone. Swept via a binary-safe literal-string substitution (preserves each file's own line endings), 89 files / 165 replacements total. lpcc --batch before/after: 2322/277 (89.3%) → 2436/163 (93.7%) with zero new failures among the touched files — every remaining FAIL in a touched file traces to a separate, still-genuinely-missing item in the same create() (e.g. yulinjun.lpc now compiles past its fixed jingua hammer but still fails on an unrelated, still-missing /d/obj/armor/tongjia). Full addendum with the detection-rule correction filed under AGENTS.md §7.66.

Combat (item 1) — CONFIRMED WORKING, live. fight <target>/real combat both exercised. The fight "safe spar" path itself was blocked for the two nearest peaceful NPCs (jieding/nukid in d/city/zhuque-e1) by a related, ALREADY-documented, genuinely-unfixable content gap (see below) that also turned out to have a nastier consequence than the earlier round realized: std/room/room.lpc's make_inventory() loop over a room's objects mapping has no per-entry error isolation, so ONE NPC's create() throwing (e.g. jieding's book-qujing load) silently voids EVERY subsequent NPC in that same room's spawn list for the rest of the driver's uptime — jieding AND nukid both, even though nukid itself has no missing-content problem at all. Also confirmed this crash reproduces on every occasion the room falls out of the driver's live-object cache and gets recreated (not a true one-time-only cold-start cost as an earlier working theory this session assumed) — routing around it long-term isn't reliable; picking a target NPC with a verified-clean spawn chain is. Pivoted to bing (city guard, d/city/npc/bing.lpc, verified clean after the §7.66 addendum fix above) at the west gate (d/city/ximen), reached via goto on the admin account: a full, real (non-spar — bing's can_speak apparently false, so fight took the direct-kill_ob() branch) combat exchange ran to completion — multiple attack/defend rounds with real damage numbers on both sides, ending in the admin character's death. Confirms the combat resolution mechanism itself works correctly, no crash in the fight/damage code path.

NEW, severe content gap found via that same death — /d/death/gate (DEATH_ROOM) does not exist anywhere in this archive; EVERY player death crashes. feature/damage.lpc::die()'s this_object()->move(DEATH_ROOM); DEATH_ROOM->start_death(this_object()); (same unguarded shape already catalogued as AGENTS.md §7.96) throws *call_other() couldn't find object '/d/death/gate' on the second line — confirmed live via debug.log. This is the SAME root cause as the already-documented /d/ zone gaps (only d/city/ and d/wiz/ survive in this snapshot at all — d/death/ never shipped, and grep -rl start_death finds zero implementations anywhere in the archive), so per this lib's own established policy this is recorded as a genuine, unfixable-without-fabricating-content gap, NOT patched — there is no real death/resurrection room anywhere in the archive to route to instead. Worth flagging as more severe than it first looks: this is not the "benign shared-lineage no-op" shape noted elsewhere in this project's cross-lib findings (where DEATH_ROOM exists as an object but merely lacks a start_death() method) — here DEATH_ROOM doesn't resolve to an object AT ALL, so death is a hard crash on this specific snapshot, for every character, every time. Side effect discovered the hard way: the crash happens AFTER the standing 30-real-minute new-account retention window is what actually protects a save file across a disconnect (cmds/usr/quit.lpc's force_quit(), gated on me->query("mud_age") < 1800, no wizardp() exemption despite an earlier session's note to the contrary) — combining a fresh sub-30- minute test session with this crash's abrupt link-loss cost the project's own persistent admin test account (fluffos) its save file twice this session. Re-registered a third time and left a background connection holding the line open past the 30-minute mark before quit-ing cleanly to re-establish it long-term (same credentials as before: id fluffos, password Mud@2026, wizard password Wiz@2026, granted via the pre-existing adm/etc/wizlist entry which was never touched). Lesson for future sessions on this lib: don't fight/ force death on a short-lived test connection here — any real death crashes before move()/start_death() complete, and if that connection then also drops abnormally before 30 real minutes of total mud_age, the account is gone, wizard or not.

Skill learning (learn, item 2) — mechanism confirmed non-crashing, full success not reachable. learn <teacher> <skill> against multiple live NPCs (jieding) consistently returned the correct, graceful notify_fail messages ("你要向谁求教?" when the teacher wasn't present/found, matching real room state each time) — no crash, no undefined-function trace, in any of several live attempts. Reading cmds/std/learn.lpc in full: the teacher-gate logic (is_apprentice_of/family-match/recognize_apprentice) is intact and consistent with the rest of this lineage. Completing an actual lesson requires an accepted apprenticeship first (see below), which this snapshot's surviving content cannot produce — same root cause, not a separate bug.

Sect/clan joining (拜师, item 3) — mechanism confirmed non-crashing, but genuinely unreachable in this snapshot's surviving content. apprentice <target> was live-tested against jieding (correctly rejects: target has no family) and reaches the real accept/pending flow against a real family-holder. Traced the full accept path: when the target NPC is not itself a connected player, cmds/std/ apprentice.lpc calls ob->attempt_apprentice(me) to let the NPC decide whether to accept — and in this ENTIRE archive, attempt_apprentice() is defined in exactly ONE file, d/city/npc/shubao.lpc (秦琼, "将军府" sect master) — and that file is never placed in any room's objects list anywhere in the archive (confirmed: grep -rn '"shubao"' outside its own file returns zero hits). Every OTHER family-holding NPC that IS actually reachable (d/city/shangshu/npc/ shangshu.lpc, family "朝廷", confirmed reachable via d/city/zhuque-e2 → south → shangshu/gate → east → yuan → east → keting) has no attempt_apprentice() override, so ob->attempt_apprentice(me) call-others into nothing (silently returns 0, no crash — confirmed the non-crashing part live), and the player's request just sits in pending/apprentice forever with no NPC-side code path that will ever accept it. This is content-authoring, not a shared-mixin bug: checked sibling xyj2000f (closest relative per this lib's own lineage note above) — its feature/apprentice.lpc has NO default attempt_apprentice() either, and instead DOZENS of individual sect- master NPCs across d/nanhai, d/xueshan, d/moon, d/qujing/*, d/lingtai, d/death, d/jjf, d/sky each hand-author their own attempt_apprentice() with custom dialogue — confirming every reachable sect master is SUPPOSED to have one, hand-written per-NPC, and this snapshot's surviving city-zone content (only d/city/+d/wiz/, per the well-established missing-zone finding above) simply never included one for shangshu (or d/city/misc/npc/bing.lpc's "将军府" family, or d/city/quest/biao/npc/robber.lpc's, both also missing the override). Not fixed — adding a plausible attempt_apprentice() body to shangshu.lpc would mean inventing game-design content (what the NPC says, whether/how it decides to accept), which is exactly the kind of judgment call this lib's own standing scope policy prohibits.

Shop purchase (buy, item 4) — mechanism verified sound by code read; live purchase blocked by pre-existing content gaps on the specific vendors reached, not by any new bug. Read cmds/std/buy.lpc and feature/vendor.lpc in full: price calculation, can_afford() gating, and the owner->complete_trade(me, item) item-transfer call are all internally consistent and call real, defined methods. Noted one harmless pre-existing dead call: buy.lpc line 98 calls owner->compelete_trade(me, item) (note the typo — extra e) AFTER the real complete_trade() already ran; this method is never defined anywhere in the archive, so it's a silent no-op call_other (confirmed: call_other on an undefined method returns 0 without crashing, same as the attempt_apprentice() case above) — not fixed, since it has zero observable effect on the transaction either way and "fixing" it would require guessing what a second, currently-nonexistent post-trade hook was originally meant to do. Live-tested buy N from <vendor> against three different vendor NPCs (wangsao/miantiao noodles, cuihua, and attempted laowei/bag): the first two both have a genuinely-missing /d/obj/cloth/skirt clothing item in their OWN create() (same §7.66 class, confirmed in this session's miss-list, not fixable), which crashes their spawn before the vendor NPC ever exists to sell anything — correctly reproduces the "target not found" notify_fail on buy, no crash. Also confirmed a fresh, newly-registered character starts with zero money (score shows "钱庄存款:目前是个穷鬼" — "pauper"), so even a fully-reachable vendor would additionally need a funded test character to reach the actual can_afford()/payment code path — noted as unverified-live per this lib's own standing policy (§10.7 checklist item 6) rather than silently presented as fully tested. No item- processing/加工厂 mechanic exists anywhere in this archive at all (grep -rl 加工厂 across all .lpc/.h files returns zero hits; the phrase only appears in doc/help/master_job and two doc/help/story/* narrative files, describing a mechanic near "泾水桥南" that has no corresponding room/NPC file anywhere in the snapshot) — genuine missing content, not a bug, consistent with this lib's other confirmed missing-zone findings.

AGENTS.md §7.19 sweep (2026-09-01): false positive, not fixed

Flagged by the corpus-wide scoping scan as sharing the mhxy/wuhanzhan enable_player()-reentrancy-from-init() shape (feature/command.lpc does define an enable_player() wrapper around the raw enable_commands() efun, and feature/damage.lpc's revive() / cmds/std/sleep.lpc's wakeup() do legitimately re-invoke it while living()), but the actual reachability leg never exists here: a body-aware static scan of every init() function in this lib (354 init() bodies total, two independent regex passes) found zero cases of init() calling setup()/reset_me(), directly or indirectly. The naive whole-file grep that the original scoping scan likely used (file contains both "void init()" and "setup()") over-matches here: several d/city/*.lpc room files (e.g. proom.lpc) call setup() from create() and separately define an init() that only does unrelated add_action() calls -- same function names, same file, but no call edge between them. Same false-positive pattern already ruled out for shzs/xzyx earlier in this sweep (see the sweep's top-level memory doc). No fix applied.