Journey to the West: Glory Days

✅ 可玩

西游记[光辉岁月]

xiyouji2003

🔑 fluffos / Mud@2026 更新 66a3bd8 2026-10-28 源码 下载 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), static→nosave (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 static→nosave 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 gates — adm/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 loopback — adm/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 (gb → 1 进入 → 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.lpc 的 make_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.

深度功能测试(§10.7,2026-10-04)— negative range ends

scripts/lpc_fix_negative_ranges.py: 5 line(s) in 2 file(s) count a range from the end with < (x[a..-1] -> x[a..<1], x[-2..] -> x[<2..]; KB 06 §7.209). In this driver a negative constant end gave "" / ({ }) and a negative start the whole value, so each of these returned the wrong slice; the compile warning A negative constant as the second element of arr[x..y] is gone from the files that compile. Range lvalues (s[0..-1] = text, the prepend idiom) are left alone. A paired compile of the changed files at HEAD and in the working tree showed no new error.

深度功能测试(§10.7,2026-10-05)— tail() simul_efun

scripts/lpc_add_tail.py --apply: cmds/wiz/tail.lpc calls tail(file), an efun of the MudOS this archive ran on that FluffOS does not have (Undefined function tail: the command did not compile). adm/simul_efun/file.lpc now defines void tail(string file): the last 10 lines of the file, written to the player. Load check (HEAD against the working tree): 1 of the 1 tail command file(s) failed to load at HEAD, all load now, and the simul_efun file loads. KB 04 §6.2.

深度功能测试(§10.7,2026-10-05)— 任务表里的 .c 路径与任务守护进程的防护

scripts/lpc_dynamic_quest_guard.py:1 处 spread_quest() 调用(init_dynamic_quest() 的循环里)包进 catch(),缺失的任务物品或编译不过的房间只跳过那一个任务,守护进程照常加载、其余任务照常生成。 验证:改动过的 adm/daemons/taskd.lpc 在新进程里加载,HEAD 通过 0 个、工作树通过 0 个(共 1 个),无回退。

深度功能测试(§10.7,2026-10-05)— 房间列出的物件缺档时房间照常加载

房间基类 std/room/room.lpc:make_inventory() 用 catch() 包住 new(file),做不出来就返回 0;reset() 的 case 1: 拿到 0 就跳过这一项(if (!objectp(ob[list[i]])) break;);reset() 的 default: 拿到 0 就跳过这一项(if (!objectp(ob[list[i]][j])) continue;)。档案里没有的 NPC 或物品(或 create() 出错的物件)原先会让列出它的房间整个加载失败(Bad argument 1 to environment(),或对 0 的 call_other()),连带这个房间后面的区域;现在房间照常加载,只是少了那个物件,下次 reset() 再试(KB 05 §7.25;scripts/lpc_resilient_room.py、scripts/lpc_room_reset_guard.py)。 验证:探针(scripts/lpc_room_reset_probe.py:房间基类列出一个不存在的物件,再调用 reset())HEAD 单件 崩溃、多件 崩溃,工作树 单件 OK、多件 OK。

深度功能测试(§10.7,2026-10-07)— 兵器的 actions 函数指针

set("actions", (: WEAPON_D, "query_action" :));:旧 MudOS 把 (: 字符串, "函数" :) 当作 call_other 函数指针,本驱动把它当作表达式函数指针,求值只得到字符串 "query_action"。没有对应兵器技能时 feature/attack.lpc 直接用兵器的 actions,COMBAT_D 拿到的不是 mapping,reset_action() 后仍不是,记一条 bad action 就不出招——拿着这些兵器打不出攻击。改成别的兵器早已用的 (: call_other, WEAPON_D, "query_action" :)(KB 04 §6.10):std/weapon/lance.lpc、std/weapon/katana.lpc、std/weapon/stabber.lpc、std/weapon/club.lpc。改动的文件在 HEAD 与工作树分别加载(新进程),无回退。

深度功能测试(§10.7,2026-10-07)— 编译警告整理

scripts/lpc_warnings.py xiyouji2003 --fix 加 scripts/lpc_fix_no_effect.py --apply:共改动 411 个 .lpc 和若干 .h。HEAD 与工作树分别加载(新进程):389 -> 389 通过,无回退。 手工:adm/daemons/natured.lpc 删去空语句 lt[LT_MON];;job/merchant/chaomai/npc/obj/check.lpc 进货 jinhuo all 时 if (... used_money < 0) used_money == 0; 是比较不是赋值,钱数溢出成负数时没有归零、下一行"帑钱不足"的检查也就拦不住;改成 =。std/weapon/club.lpc 等四个兵器基类的 actions 见同日的兵器函数指针一节。

启动时警告 94 -> 0;新进程登录正常。

- adm/daemons/diablod.lpc、toptend.lpc 各自多声明了一个从未使用的 nosave mapping dbase,与 F_DBASE 的重名;删去。 - std/char/kzznpc.lpc 的 remove() 改 void,与 feature/move.lpc 一致。 - yaoguai_type 重复声明、alias.lpc 的 last_time 照同源各库修。 - 改动的 12 个对象 HEAD 与工作树各载入一次,无回退。

内力/精力当作伤害种类(2026-10-15,KB 06 §7.241 第二类)

本库 feature/damage.lpc 的 receive_damage() 不接受 jingli/neili/force 等种类,me->receive_damage("jingli", 30) 之类在练功、绝招、吸取时报 F_DAMAGE: 伤害种类错误。8 处改为 add(type, -N)(scripts/lpc_fix_damage_calls.py 第二步)。

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

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

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

30 个文件(技能、绝招、兵器、书籍、区域 NPC 等)来自侠客行体系(qi/jing/neili),而本库人物是 gin/kee/sen/force:receive_damage("qi", ...) 报 伤害种类错误,query("neili") 永远为 0。按 qi→kee、jing→sen(本库学习/读书指令消耗的就是它)、neili→force、max_neili→max_force、jiali→force_factor 改名(scripts/lpc_port_es_props.py --any-file)。

find_player() 只认在世界里的身体(2026-10-15,KB 07 §8.26)

simul_efun 的 filter_player() 加上 environment(ob):注册中途断线留下的、不在任何房间的身体不再被当成玩家,两个以上时 find_player() 不会再报 The player(<id>) has existed in the world.(wllz3 实测过的问题)。本库若注册流程不会留下带玩家 uid 的身体,这一改动不改变行为。

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

query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):huntianqigong→huntian-qigong(baguazhou、buddhism、dao、dengxian-dafa、dragonforce、duoqing-force 等 34 个文件)。

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

技能自己的文件(或它的绝招目录)里,query_skill() 读的是差一个字母的自己的名字,永远得 0:“基本功夫要高过本技能”之类的限制从不生效,按等级取招、计算伤害的那一项一直是 0。改为本技能的名字(scripts/lpc_fix_skill_typo.py 的 self 判据):huoxue-force→huaxue-force(baguazhou、buddhism、dao、dengxian-dafa、dragonforce、duoqing-force、fofa、gouhunshu、huntian-qigong、huomoforce、jiuyin-xinjing、lengquan-force、lotusforce、moonforce、moonshentong、ningxie-force、pansi-dafa、pingtian-dafa、seashentong、taiyi、taoism、tonsillit、wuneng-force、wuxiangforce、xuanhu-shentong、xuanhu-xinfa、yaofa、zhenyuan-force、zhiqing-spells、zixia-shengong)。

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

61 个文件、82 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()),多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/obj/cloth/pink_cloth、/d/gao/obj/changpao、/d/obj/armor/jinjia)。本库 NPC 基类 std/char/npc.lpc 的 carry_object() 第一句是 if (file->query_unique()),对不存在的文件直接 call_other,驱动在这里报 call_other() couldn't find object,new() 来不及返回 0,所以只要 NPC 带着一件本库没有的物品就载入不了(调用处加判断对它无效)。在它前面补 if (!objectp(load_object(file))) return 0;(--base),缺的物品就跳过,NPC 照常载入;上面那些调用处同样改成带判断的块(基类以后改回去也不怕)。载入检查:改动的文件改前能载入 1 个,改后 56 个(55 个原来载入不了),没有退步;其余 6 个另有原因(多为 set_skill() 了本库没有的武功)。

set_skill() 遇到本库没有文件的武功不再抛错(2026-10-18,KB 06 §7.256)

feature/skill.lpc 里的 set_skill()、map_skill()、prepare_skill() 碰到没有武功文件的名字就 error("F_SKILL: No such skill (…)"),调用它们的 NPC create() 整个中断:NPC 载入不了,刷出它的房间也少一个人。本库有 1 个对象设了本库没有文件的武功(1 个名字,最多的是 freezing-force)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:1 个里 0 个通过,其余 1 个停在别的错误上(obj/insect/bingcan:*No program in object '/obj/insect/bingcan'!)。(scripts/lpc_guard_set_skill.py)

武功名拼错的 NPC(2026-10-19,KB 06 §7.258)

set_skill("kunlun-shnfa", …)、map_skill("dodge", "shaolin-shnfa") 这样的调用把武功名拼错了:本库没有这个武功文件,有的是拼对的那个。以前这种 NPC 在 create() 里报 No such skill 载入不了;武功缺失时跳过之后它们能载入,可是少了那门武功(set_skill 被跳过,map_skill 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 2 处、2 个文件:hellfire_whip→hellfire-whip(1 处)、lahua-shou→lanhua-shou(1 处)。(scripts/lpc_fix_skill_names.py)

RANK_D 对 0 的称呼(2026-10-19,KB 06 §7.273)

adm/daemons/rankd.lpc 的 query_respect()、query_rude()、query_self()、query_self_rude() 里 4 个函数开头补上 if (!objectp(ob)) ob = this_object();。NPC 在 create() 里写 "…" + RANK_D->query_respect(this_player()) + "…"(inquiry 对话、chat_msg),驱动自己跑 reset() 时没有人在场,this_player() 是 0。旧驱动里 0->query("age") 返回 0,称呼走默认分支;新驱动直接报 Bad argument 1 to EFUN call_other(),房间的 make_inventory() 吞掉错误,被打死的 NPC 再也刷新不回来。用 scripts/lpc_reach_census.py --walk 找到(xkx100 的武当弟子、一灯、汴梁书生…);修在守护程序里,不动 1,800 个 NPC 文件。

member_array('/', str) 改为 strsrch(str, "/"):字符字面量的 member_array 按字节搜索(2026-10-20,KB 06 §7.283)

member_array('/', str) 里的 '/' 是整数,驱动按 UTF-8 字节搜索,返回字节偏移(带第三个参数时起点也是字节);最新的 FluffOS(923cdbc0)在字符串里有非 ASCII 文字时,对每个调用点警告一次(Warning: member_array(int, string) searches BYTES)。而 str[0..idx-1] 按字符切,中文字符串会在错误的位置截断。改为 strsrch(str, "/")(找不到同样返回 -1,按字符计数;scripts/lpc_fix_member_array_char.py,JSON 编码器等少数几处手改)。改动:/std/room/vrm_server.lpc(1 处)。

call_out_info() 的每一项只有 3 个元素(2026-10-20,KB 06 §7.287)

驱动返回的是 ({ 物件, 函数, 延迟 }),不带 call_out 的参数,旧代码读第 4 项 info[i][3](callouts 指令、calloutd、cod 工具)一读就报 Array index out of bounds,指令从第一行起什么也打印不出来。改为 (sizeof(info[i]) > 3 ? info[i][3] : 0)(scripts/lpc_fix_callout_info.py)。改动:/cmds/wiz/callouts.lpc(1 处)。

if (x = 常量):条件里写成赋值的比较(2026-10-20,KB 06 §7.290)

if (arg = "button" || arg = "按钮")、if (!arg || arg = "") return 0;、else if (check = 0 && tempB != ""):把 == 写成了 =,条件的值就是那个常量,分支对所有非零数和任何字符串(连 "" 都算)永远成立、对 0 永远不成立,变量还被覆盖;驱动对此没有任何警告。每一处 = 改成 ==(scripts/lpc_fix_cond_assign.py,共 17 处)。改动:/adm/daemons/race/human.lpc(1 处)、/cmds/std/look.lpc(1 处)、/cmds/usr/inventory.lpc(1 处)、/cmds/wiz/dns.lpc(1 处)、/d/city/mjlady3.lpc(3 处)、/d/city/npc/mjlady3.lpc(3 处)、/daemon/skill/qiankun-danuoyi.lpc(7 处)。

callouts:sizeof(info[i]) == 4 把每一行都挡掉了(2026-10-20,KB 06 §7.287)

驱动的 call_out_info() 每项只有 3 个元素,输出行前的守卫 if (sizeof(info[i]) == 4) 永远不成立,指令只打印表头。改为 >= 3。改动:/cmds/wiz/callouts.lpc(1 处)。

socket / dumpsocket 指令:dump_socket_status() 驱动里没有(2026-10-20,KB 06 §7.291)

这些巫师指令 write(dump_socket_status());,MudOS 的 efun 在 FluffOS 里不存在,指令编译不过。在模拟 efun 文件里补了 dump_socket_status():用 socket_status() 排出同样的表(没有 sockets 包的构建,比如 WASM,只有表头;scripts/lpc_add_sefun.py dump_socket_status)。

登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)

驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/adm/obj/simul_efun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。

目录读出后的后缀截取宽度(2026-10-20,KB 06 §7.296)

.c 改名成 .lpc 之后,get_dir("*.lpc") 读出的文件名还按两个字符截取($1[0..<3]):baguadao.lpc 变成 baguadao.l,用它拼出的路径加载不到对象(故事、战场、事件、帮助、自造物品数据、召唤列表等)。改成 [0..<5](scripts/lpc_fix_getdir_strip.py)。改动:/adm/daemons/storyd.lpc(1 处)。

get_config(__MUD_PORT__) 经旧版 runtime_config.h 得到字符串(2026-10-20,KB 06 §7.302)

本库的 include/runtime_config.h 是老 MudOS 的编号(整数设置从 15 起,本驱动从 256 起),get_config(__MUD_PORT__) 读到的是另一项设置(字符串),(int) 又只是编译期标注,于是 socket_bind(fd, LOCAL_PORT()) 报 Bad argument 2 to socket_bind(),版本守护进程 port = get_config(__MUD_PORT__) + VERSION_PORT 是字符串相加。头文件自己的注释写明 get_config(__MUD_PORT__) == __PORT__,所以代码里的这个调用(连同外面的 (int) / to_int())改成了驱动的预定义 __PORT__(scripts/lpc_fix_mud_port.py)。改动:/include/getconfig.h(1 处)。

taskd 的 create() 读不到任务表就加载失败(2026-10-20,KB 06 §7.326)

/adm/daemons/taskd.lpc 的 create() 把 /d/obj/quest/dynamic_location、dynamic_quest 和 /adm/daemons/find.map 直接 explode();本库没有 /d/obj/quest/,job 指令一加载守护就 Bad argument 1 to explode()。三处改成 read_file(...) || "":任务表为空,job 列出空榜,其余循环(sizeof(quests) / 2)本来就按空表处理。加载检查:HEAD FAIL → tree PASS。wuhanzhan 的 qqzqqjob.lpc 是同一份代码,那里加了以后下一步是缺 /adm/daemons/cron.lpc,所以没有动。

NPC 的 chat() 在 chat_msg 为空数组时下标越界(2026-10-21,KB 06 §7.334)

NPC 基类的 chat() 是 if (arrayp(msg = query(...))) { if (random(100) < chance) { rnd = random(sizeof(msg)); if (stringp(msg[rnd])) ...,不检查列表是否为空。本库现有的 NPC 文件里没有字面写成空 chat_msg 的(扫描只认 set("chat_msg", ({ })));守卫挡的是运行时才为空的列表和以后新增的 NPC。在 random(100) < chance 前面加 sizeof(msg) &&(scripts/lpc_fix_chat_empty.py;if (random(100) > chance) return 0; 的写法是 !sizeof(msg) ||)。改动:/std/char/npc.lpc(共 1 个文件)。

巫师命令不带参数时对 0 调用 efun(2026-10-21,KB 06 §7.336)

巫师(管理员)把命令敲成不带参数时,驱动把 0 传给字符串参数;下面的命令第一句就把这个 0 交给 efun,运行时报 Bad argument 1 to ...,这条指令从来没有用过。用 scripts/lpcc_bare.py 不登录、在一次 lpcc 求值里以 0 调用 main() 逐个确认后,由 scripts/lpc_fix_bare_arg.py 在第一处使用参数的语句前加守卫:文件自带 int help(object me) 的,守卫是 if (!arg) return help(me);(help() 定义在 main() 之后的 1 个文件补了原型 int help(object me);,否则编译报 Undefined function help)。改动(1 个文件,括号里是接住 0 的 efun):/cmds/adm/dumpprog.lpc(new)。加载检查 HEAD 与工作树对照,没有退步。

xupdate:file[sizeof(file)] != '/' 永远为真(2026-10-21,KB 06 §7.343)

管理员扫描里 xupdate(xyj2000)从根目录 / 开始递归时报 Bad argument 2 to foreach Expected: array Got: 0.:UpdateDir() 用 file[sizeof(file)] != '/' 判断路径是否已经以 / 结尾,下标等于长度读到的是 0,条件永远成立,/ 变成 //,get_dir("//") 答 0。改成 file[<1] != '/'。同一个写法在 xrm、rm、rmdir1、rmhir 里有 55 处,没有动:对普通路径新旧写法的行为相同,唯一改变的是 xrm /,旧写法让它死在第一个 get_dir("//"),改了它就能递归删光整个游戏目录。

注册到不存在函数的 add_action(2026-10-21,KB 06 §7.349)

add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 10 条注册(7 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/city/biaoju-e.lpc(do_yao)、/d/city/misc/chatroom2.lpc(do_jump)、/d/city/misc/npc/guinu.lpc(do_sell)、/d/city/npc/duguoyin.lpc(do_vendor_list)、/d/city/npc/petboss.lpc(do_cost、do_list、do_upgrade)、/daemon/skill/hubo.lpc(do_hubo)、/std/char/smith.lpc(do_buy、do_list)。最多的函数名:do_list 2 处、do_yao 1 处、do_jump 1 处、do_sell 1 处、do_vendor_list 1 处。

缺失物品的 new() / carry_object() 调用链加了保护(2026-10-22,KB 06 §7.73 补充)

new(路径)->move(...); / carry_object(宏("x"))->wear(); 这类语句,路径指向的文件在这个压缩包里没有:new() 返回 0,->move() 在 0 上报 Bad argument 1 to EFUN call_other()。在 create() 里这个对象永远加载不了,在 reset() 里第一个缺失物品之后的刷新每次 reset 都被跳过。3 个文件里 6 处(new, carry_object)改成 { object new_ob = new(路径); if (new_ob) new_ob->move(...); }(scripts/lpc_guard_carry_object.py --new:路径按这个文件自己看到的宏解析,宏名冲突、虚拟对象路径一律跳过)。 对这 3 个改动文件做加载检查(HEAD 与工作区对比):HEAD 能加载 2 个,现在 3 个(新增可加载 1 个,回归 0 个)。

调用了、但这个库自己的守护进程里没有的函数(2026-10-22,KB 06 §7.354)

MACRO->函数() 调用的函数在这个库的守护进程里不存在:call_other() 对不存在的函数不报错、只返回 0,所以这些功能(时间显示、昼夜事件、名字检查、价格文字……)一直没起作用。同一血统另一个库的同名守护进程里有这些函数,原文照抄到本库守护进程末尾(scripts/lpc_port_function.py;调用处传的参数个数、函数体用到的函数 / 全局变量 / 宏 / 字符串键都在本库里有,已核对;守护进程在 HEAD 和工作区都能加载,每个函数用固定参数各调用一次:HEAD 返回 0,现在返回正常的值):/adm/daemons/chinesed.lpc ← fengyun434:check_control()(1 处调用)、check_length()(4 处调用)、check_return()(1 处调用)、check_space()(1 处调用)。

运行期目录 log/nosave 不存在,写日志的 write_file() 直接报错(2026-10-22,KB 03 §4.3 补充)

这个库的 2 个文件里有 5 处 write_file("/log/nosave/...") / log_file("nosave/...")(static 改 nosave 时连字符串里的日志路径一起改了,原来是 log/static/):/log/nosave/LASTTIME、/log/nosave/LASTCRASH 等。压缩包和站点包里没有这个目录,write_file() 在目录不存在时抛 *Wrong permissions for opening file /log/nosave/... for append. "No such file or directory",调用它的函数(杀人记录、重启记录、call 日志……)当场中断。把 log/nosave 记进 scripts/wasm_keep_dirs.txt(站点和 pristine 树都按它建空目录);没有把字符串改回 static。证明(scripts/lpcc_eval.py,catch(write_file("/log/nosave/ZZTEST", ...)),fqyy2 / hy5 / bmxkx2001 的新进程):补目录前报上面的错误,补后写成功。

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

原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:各派掌门的辈分判断(name[0..1] == "澄")永远不成立。

本库 3 处(1 个文件):与汉字常量比较 3 处。例:chk[0..1] == "你" → chk[0..0] == "你"(/adm/npc/ganjiang.lpc:526)。scripts/lpc_fix_gbk_slice.py 把下标按字符重算(社区的 UTF-8 版 xkx100utf8 里就是同样的写法,作对照);没有改的:截断(name = name[0..7],现在最多 8 个字,只是放宽)、ASCII 的区间(fname[0..3] == "room"、ctime(time())[4..19])和上下文看不出是汉字的区间。证明(scripts/lpcc_eval.py,bixiecanyang;其余各库是同一份文本):让澄字辈掌门 attempt_apprentice() 一个名叫「张三」的访客,HEAD 的法名是 慧,改后是 慧三;抽样 28 个库的 HEAD / 工作树加载对比 0 退步。工具不幂等([0..3] 折半得 [0..1],又是对齐区间),改过的文件记在 scripts/gbk_slice_converted.txt,不要重跑。

长度检查按字节写,旁边的提示按汉字写:4 处(2026-10-22,KB 06 §7.362)

老档的 strlen() 数字节(一个汉字 2 字节),转成 UTF-8 后它数字符:提示写「必须是 2 到 6 个中文字」的检查还是 strlen(name) < 4 || strlen(name) > 12,于是两三个字的房名、帮会名、群名、聊天室名、称号被提示自己说允许的那句话拒绝,上限却是提示的两倍;留言板的标题检查 i < 4 || i > 24 || i % 2 还会拒绝所有奇数个字的标题(post 你好吗)。本库 4 处(3 个文件):if (strlen(arg) > 32) {(/adm/daemons/newsd.lpc:203);if ((strlen(name) < 2) || (strlen(name) > 12)) {(/d/city/npc/zhangsunwuji.lpc:166)。scripts/lpc_fix_strlen_gate.py 以提示为准:条件里每个 strlen 门槛都恰是提示所说数字的两倍才折半,存长度的变量(i = strlen(arg);)的门槛和 || i % 2 奇偶项、其后循环的 i % 2 == 0 && 一并处理;提示里有英文 / 字母 / 字节的不动。证明(scripts/lpcc_eval.py,bxsj 的留言板,求值对象继承 NPC 并重写 edit() 记录调用,command("post 标题")):HEAD 拒绝「你好」和「你好吗」、放行 23 个字的标题;改后接受前两个、拒绝第三个;shenmo 的 named.lpc invalid_new_name("张三"):HEAD 回 名字只能是2-4个中文字。,改后回 0。抽样 24 + 15 个库的 HEAD / 工作树加载对比 0 退步。

按字节写的汉字循环、茶馆猜诗和同盟频道标题(2026-10-23,KB 06 §7.363)

老档按字节做「一个汉字」:while (i--) if (i % 2 == 0 && !is_chinese(name[i..<0])) 只检查偶数下标,奇数位上的拉丁字母(张a)会过;茶馆猜诗 mixup() 用 strlen(str)/2 再 j=j+j、str[k..k+1] 交换两字节;泥潭系 channeld 的 switch (strlen(fname)) case 4/6/8/10 和 strlen(family)==4/6 按字节长度给同盟 / 门派名加空格。本驱动的 strlen() 和区间都按字符算。scripts/lpc_fix_byte_char.py:去掉奇偶项、把区间改成 name[i..i],按字符重写 mixup() 并把 strlen(first)>=14 折成 7,频道标题的 case / 门派长度改成字符数(对照 aoxiangtianji)。本库 汉字循环 / is_chinese 区间 5 个文件;茶馆猜诗 mixup 1 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。

血条 / 表情标题按字节算显示宽度(2026-10-23,KB 06 §7.365)

老档把汉字当两字节:bar_string[0..(val * N / max) * 2 - 1] 把「格数」换成字节下标,半格血条在本驱动(按字符算区间)上会整条画满;巫师表情 edemote 的 repeat_string("─", 13 - (strlen(emotion) / 2)) 也会少减汉字宽度。scripts/lpc_fix_display_pad.py:去掉 CJK 血条 / 空白条上的 * 2(jings/qis 赋值和 blank_string3[0..(23-a)] 的 23→11 一并改),表情标题去掉 / 2;ASCII 空格的 none_string / empty_string 仍保留 * 2(一格两个空格)。本库 4 处(2 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。

深度功能测试(2026-10-27,§6.25 one-arg tell_object 残渣)

medcined:tell_object("请输入…") → tell_object(me, …)。

深度功能测试(2026-10-27,§6.25 one-arg message_vision)

message_vision("…") 补第二参(me / TP / this_player())。

深度功能测试(2026-10-27,§6.10 未知 pragma save_binary/optimize)

删除本库中 FluffOS 不认的 #pragma save_binary / #pragma optimize… 行。