A Journey to the West

✅ 可玩

西游记 (A Journey to the West)

xiyouji

🔑 fluffos / Mud@2026 更新 66a3bd8 2026-10-28 源码 下载 ZIP

▶ 开始游玩 · Play Now

登录画面自称"A Journey to the West",版本号 2.01,最早制作于 1996-1998 年;本项目考证认为它极可能是"ES II / 西游记"这一整个引擎家族(包括 xyj2000f、mhxy、xiyouji2003、xiyouji2006、xiyouji450、shenmo 等众多同题材姊妹游戏在内)最早的祖先版本——文件时间戳最早,且没有任何后续站点加上的"破解者"署名痕迹;游戏以《西游记》原著世界观为背景,玩家从长安城出发游历天下,探索盘丝洞、五指山、南海等小说中的经典地点,作为整个西游记 MUD 世系的源头,世界内容相对朴素、原始,适合用来对比后续各分支如何演化。

English

Self-titled "A Journey to the West," version 2.01, originally created 1996-1998. This project's research concludes it is very likely the earliest ancestor of the entire "ES II / Journey to the West" engine family (which includes xyj2000f, mhxy, xiyouji2003, xiyouji2006, xiyouji450, shenmo, and other sibling games in this collection) — its files carry the earliest timestamps and show none of the "cracker" credit lines later sites added. Set in the world of the original novel, players set out from Chang'an to explore the story's iconic locations (the Silk-Web Cave, Five-Finger Mountain, the South Sea, and more). As the wellspring of the whole Journey to the West MUD lineage its world content is comparatively simple, the most primitive form in the series and a useful baseline for comparing how the later branches evolved.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/xiyouji/

管理员账号 / Admin account

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

本地运行

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

游戏端口:40079。

NOTES · 移植与修复记录

西游记.rar → xiyouji (archive #84)

Lineage: confirmed the likely ANCESTOR of the whole "ES II / XYJ" 西游记 family, not a duplicate of any already-done sibling

Per the task's explicit instruction, read libs/xyj2000f/NOTES.md (#15), libs/mhxy/NOTES.md (#19), libs/mhxyqd/NOTES.md (#56), libs/shenmo/NOTES.md (#73), libs/xiyouji2003/NOTES.md (#81) first, then diffed/md5sum'd this archive's adm/obj/master.c, adm/daemons/chinesed.c (the translator daemon; the byte-check daemon is adm/simul_efun/chinese.c here), and adm/daemons/logind.c against the raw pre-conversion files of all five before doing any conversion work.

Findings:

Conclusion: this archive is a genuine, distinct member of the "ES II / XYJ" lineage shared with xyj2000f (#15), mhxy/ mhxyqd (#19/#56), shenmo (#73), and xiyouji2003 (#81) — almost certainly their common ancestor snapshot (oldest file timestamps, least site-specific modification) — but NOT a byte-duplicate of any of them. Known-lineage fixes (§15h chinese-detection, dns_master preload exclusion, master.lpc valid_write/valid_read shape) were ported proactively; fixes specific to a LATER snapshot's added features (convertd.lpc's Greek-table typo, §8h) were independently re-discovered here since this codebase's own copy of convertd.lpc has the bug too (see below) — expected, since §8h itself is documented as recurring across the whole family.

Flag for later cross-check: archives #82 and #83 (processed concurrently by sibling agents, not yet available to read at the time of this pass) are also 西游记-themed per the task description. A future pass should diff this archive's adm/obj/master.c/adm/simul_efun/chinese.c/ adm/daemons/logind.c against whatever libs/<slug>/raw/ those two archives extract to, to determine if either is an even-closer sibling or duplicate of this one. (One such sibling, working under slug xiyouji2006, was observed running concurrently on this same host during this pass — its own separate NOTES.md should be consulted once available.)

Fixes applied (with rationale)

1. §15h — GBK byte-range Chinese-detection bug, in adm/simul_efun/chinese.lpc's is_chinese(): replaced the byte-range check (strlen(str)>=2 && str[0]>160 && str[0]<255) with a CJK Unicode codepoint range check (strlen(str)>=1 && str[0]>=0x4e00 && str[0]<=0x9fff). Without this, every real Chinese name is silently rejected at registration (§15h's core, most-impactful finding across the whole project). Also fixed check_legal_name() in adm/daemons/logind.lpc: halved the byte-calibrated length bounds (strlen<2→strlen<1, strlen>12→strlen>6, matching what the error message already says: "一到六个中文字" = 1 to 6 Chinese characters) and dropped the i%2==0 && byte-alignment gate on the per-character is_chinese() sliding check (every UTF-8 index is already one full character, not every-other-byte). 2. §15p — proactively excluded /adm/daemons/network/dns_master from adm/etc/preload before the first boot attempt. This lib's preload list had it listed (confirmed via adm/daemons/network/ dns_master.c's presence and its own hardcoded remote boot-server dependency). 3. §15ai — a dns_master-absence shutdown(1) gate in logind.lpc's encoding() callback. Found on read-through, exactly as the task description flagged as a known risk of the §15p exclusion: a "mirror-IP-verification" check (if(!find_object(DNS_MASTER) || !"/adm/daemons/band"->check_ip(...)) shutdown(1);) that unconditionally treated "daemon never loaded" (guaranteed true here, since we exclude it) as "verification failed" and called shutdown(1) on the entire driver process on the very first connection. Fixed by changing the guard from !find_object(...) to find_object(...) && ... — daemon absent now degrades to "skip the gate" instead of "gate failed", matching the established fix pattern. Applied proactively before ever booting (read the code first, per the task's explicit instruction to check every catalog section against the actual source before boot). 4. §8h — convertd.lpc's Greek/CJK-table stray-backslash typo, recurring exactly as documented for xyj2000f/mhxy: 45 separate lines (not just the usual handful) had a stray literal backslash immediately before the closing quote of a two-character string entry ("α\", → "α",, "功\", → "功",, etc — one base character + a trailing Private-Use-Area companion codepoint that is itself completely legitimate content in this BIG5/GB conversion table, confirmed by cross-checking ~1000 other *unaffected* lines with the identical "char + PUA companion" shape compiling fine). Root-caused via lpcc's exact error line + a Python pass over the decoded file finding every line with an odd number of trailing backslashes before the closing ",/" — not a blind sed (this file's CRLF-preserving §8h counterexample was specifically checked for and confirmed absent: the fix script here operated on already-LF-normalized decoded text and verified zero remaining hits afterward). 5. §3's blanket static→nosave sed collided with "static/CRASHES"- and "static/PURGE"-style log-path string literals (the exact counterexample already documented for moniHuafu, archive #57): adm/obj/master.lpc (4 occurrences, "static/CRASHES"), adm/daemons/securityd.lpc + adm/daemons/ss.lpc (1 each, "static/promotion"), cmds/usr/suicide.lpc ("static/SUICIDE"), cmds/arch/purge.lpc/purgehouse.lpc ("static/PURGE"), cmds/wiz/call.lpc ("static/CALL_PLAYER") — all reverted from the sed's "nosave/..." back to the original "static/...", confirmed against the raw pre-conversion files first (cross-checked with iconv-decoded originals to be certain these strings were genuinely "static in the archive, not something the encoding pass introduced). 6. §15w — log_error()/APPLY_LOG_ERROR broadcasting every compile WARNING (not just real errors) to the connected non-wizard player. Found live during the FIRST full interactive registration test: the default error message (你发现事情不大对了,但是又说不上来。) spammed ~26 times during ordinary post-registration play (the first-ever lazy compile of every never-preloaded room/feature file reached by make_body()/enter_world() — std/char.lpc's #pragma, feature/*.lpc's unused-local-variable warnings, etc — all harmless, but all routed through master.lpc's log_error(), which didn't distinguish a warning from a fatal error). Fixed by gating the player-facing broadcast on strsrch(message, "warning:")==-1 (still logs everything to home + "log" regardless). Re-verified with a fresh registration after the fix and restart: zero spam. 7. Absolute angle-bracket #include, §15t pattern #1: d/obj/books-nonskill/book-qujing.lpc had #include </d/qujing/obstacle.h> (an absolute path inside angle brackets, which this driver's <...> resolver never special-cases) — converted to quoted form #include "/d/qujing/obstacle.h". Confirmed book-qujing is real, referenced content (d/westway/npc/laoren.lpc, d/city/npc/jieding.lpc both new()/carry_object() it), not dead content — worth fixing, not just noting. 8. ..-relative #include, §15t pattern #2: d/ourhome/honglou/npc/niu.lpc had #include "../honglou.h" (this driver disallows .. in #include paths outright) — repointed to the real absolute quoted path "/d/ourhome/honglou/honglou.h". 9. §8d-style local header one directory removed from its user: three files under d/kaifeng/npc/old/ (shan.lpc, zhi.lpc, bei.lpc) #include <quest_ak.h>/<reporting.h>, which exist one directory UP at d/kaifeng/npc/ (this driver's get_include_path() fix only prepends the COMPILING file's own directory, not its parent, so this doesn't resolve automatically) — every OTHER file referencing these headers already lives directly alongside them and compiles fine. Fixed narrowly (not a generalized parent-directory search, to avoid unintended side effects elsewhere) by copying both headers into d/kaifeng/npc/old/ too. 10. Pre-existing typo, §10-shaped missing closing quote: d/obj/books-nonskill/hmeng014.lpc had string name = "《红楼梦》 第十四回; (missing closing " before the ;), confirmed against 12+ sibling hmeng0NN.lpc files in the same directory which all use the correct "..."; form — fixed to match. 11. Pre-existing typo, corrupted/dropped-byte argument: data/ armor.lpc's do_closecommand() had ob->set_alias(cmd, "); (an unterminated string literal swallowing the next several lines, cascading into a wall of "Illegal character" errors reported much further down the file) — confirmed via the raw pre-conversion bytes that this was originally set_alias(cmd, "<one genuinely-undecodable byte>");, dropped by the lossy iconv -c recovery pass, and confirmed via the sibling do_opencommand() function IN THE SAME FILE (ob->set_alias(cmd,0);) what the correct call shape actually is — fixed to ob->set_alias(cmd,0); to match. 12. Mapping-literal syntax typo: d/gao/obj/pen.lpc's is_container() had set("objects", (["/u/bula/gao/obj/kaoji"]) ); — a mapping literal with a bare key and no :value, which this driver rejects as a syntax error (compared against d/changan/playerhomes/h_croc.lpc's own, valid, set("objects", ([ path:1, path:1 ])); usage of the same "objects" property) — added the missing :1. The referenced target (/u/bula/gao/obj/kaoji) still doesn't exist anywhere in the archive (genuine content gap, not fabricated), so this only takes the file from a hard compile error to a graceful missing-content no-op. 13. Orphaned junk data mistakenly caught by the .c→.lpc rename, §12 pattern: obj/file.c (416 bytes of literal random binary noise, file reports "data", confirmed unreferenced anywhere via grep -rn "/obj/file\b") — renamed to obj/file.orphaned-junk so it no longer pollutes the lpcc sweep's pass/fail signal. 14. Corrupted pre-existing NPC-vendor save data, §15m-adjacent: four data/npc/boss/*.o files (city_kongfang.o, city_weiluqi.o, laosun.o, yangzhongshun.o) are genuinely random/binary garbage (confirmed byte-identical to the raw un-touched archive via md5sum — not something our conversion pipeline broke), which would throw *restore_object(): Illegal file format at runtime the first time a player actually visits the NPC that owns one (found via the lpcc sweep compiling /d/city/bookstore in isolation, which clones its bookseller NPC). Moved out of the way into data/npc/boss/ corrupted-original-backup/ (not deleted) so restore() finds nothing and proceeds cleanly, matching the established zhonghua2 precedent — this was NOT triggered during the real boot/registration test (the bookstore isn't on the path from the start room), so it wasn't blocking anything we tested, but is a real latent crash for normal play. 15. §14 — valid_override() given the documented 3rd main_file parameter. master.lpc's copy was the old 2-arg form, and adm/simul_efun/object.lpc (an #included fragment of simul_efun.lpc, not that file itself) defines its own destruct() override calling efun::destruct() — the 2-arg check's file == SIMUL_EFUN_OB test is false for this fragment (file is the physical object.lpc, not simul_efun.lpc), which per the docs is exactly the scenario the 3rd parameter exists to fix. Applied proactively; per the catalog's own note this may never surface as a real boot symptom, but the fix is free and correct.

What was checked and confirmed NOT needed

Fullwidth punctuation / mojibake escape sequences (harmless, not fixed)

daemon/class/fighter/oldstuff/champion.lpc (5 hits) and obj/npc/ garrison.lpc (2 hits) both have a stray literal backslash immediately before a bare Chinese character inside a string ("水烟阁传功\使", "许\多武林人物") — confirmed pre-existing in the raw archive, and confirmed non-fatal (warning: Unknown escape sequence, not an error) both via the lpcc sweep and by cross-referencing that these two files are never reached by the registration/look/score test path. Left as-is per the project's policy of fixing only what a real error/lpcc-FAIL demonstrates matters, not every cosmetic warning.

Genuine archive-content gaps (not fixed, not fabricated)

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 one real, shared-lineage bug while cross-checking against sibling xiyouji450's own re-verification pass: adm/daemons/logind.lpc's get_name() had a stray, pre-existing debug leftover printf("%O\n", ob); right after a new player's Chinese name is accepted -- dumps a raw internal object reference (e.g. /obj/login#0) straight to the connecting player, on every single registration. Purely cosmetic (never affected registration itself -- confirmed the Chinese name still gets set and stored correctly either way) but visibly unprofessional. The exact same leftover was found in all three other siblings in this session's batch (xiyouji2003 -- 2 occurrences, xiyouji2006, xiyouji450), confirming shared lineage at the source level; removed in all four. Re-verified with a fresh registration (qfrong/秦荣) after restarting the driver: no stray object-reference text anywhere in the transcript, look/score/ quit all still correct.

Boot + registration + post-login command test (the actual verification)

Booted ~/src/fluffos/build-debug/src/driver config.fluffos from libs/xiyouji/ (via the tool's run_in_background, after one earlier attempt via nohup setsid ... & disown died to the documented unexplained-external-SIGTERM issue — matches AGENTS.md's own note about this exact symptom on this host). debug.log shows only compiler WARNINGS (unused locals, #pragma notices, "Illegal to declare nosave function" — all cosmetic/non-fatal) and ends cleanly with Accepting telnet connections on 0.0.0.0:40079. / Initializations complete. — zero fatal errors, zero segfaults, zero "Too deep recursion".

Full registration + post-login command transcript (via scripts/ mudclient.py 127.0.0.1 40079 --timeout 25 --idle 0.6, one continuous connection, real Chinese name), final confirmation run with a never-before- used id (qfengyu) after the §15w fix landed and the driver was restarted:

--send "gb"                        -> GB/BIG5 charset prompt accepted
--send "no"                        -> student age-gate declined
--send "qfengyu"                   -> English id (new, unregistered)
--send "y"                         -> confirmed new-character creation
--send "秦风"                       -> REAL Chinese name, ACCEPTED on the
                                       first try (confirms §15h fix)
--send "test1234"                  -> password set
--send "test1234"                  -> password confirmed
--send "[email protected]"       -> email
--send "m"                         -> gender: male
--send "y"                         -> accepted the rolled stat block
--send "look"                      -> full room description of 南城客栈
                                       (South-City Inn), NPCs/board listed
--send "score"                     -> full character sheet: 【平民】普通
                                       百姓 秦风(Qfengyu), age/birth date,
                                       all 8 stats, HP/MP bars, food/water
                                       bars, kill counts, 潜能:99
--send "quit"                      -> clean disconnect, "欢迎下次再来!"

Zero occurrences of the log_error() default-error-message spam this time (confirms fix #6 above); zero silent/no-op commands (confirms add_action dispatch works, ruling out §15ae); zero missing-environment symptom (confirms the character actually landed inside /d/city/kezhan, ruling out §15aj). An earlier interactive test (before the §15w fix, id qfeng/qinfeng) reached the identical successful outcome but with ~26 spurious default-error-message lines interleaved — that repro is what found bug #6 in the first place.

debug.log after this session: only compile warnings from the lazy first-time compile of std/char.lpc, its inherited feature/*.lpc files, std/room.lpc, d/city/kezhan.lpc, feature/equip.lpc, std/bboard.lpc, obj/mailbox.lpc — all cosmetic, zero errors.

lpcc_check.sh sweep results

First pass (before the fixes in items 4/7-14 above): 4921/4988 pass (98.7%), 67 failures. Second pass (after fixes): 4931/4987 pass (98.9%), 56 failures (file.lpc's rename-away accounts for the total dropping by 1). Memory stayed healthy throughout both runs (free -h never dropped below ~4.4GB free on this 23GB host) — well below the OOM-risk threshold noted in AGENTS.md §6b, so no need to back off.

Remaining 56 failures triaged by category, all either genuine archive-content gaps (documented above) or expected lpcc-isolation artifacts per §6b (a room/NPC create() calling into another object or the skill system before anything else is loaded in a bare single-file compile — e.g. /d/qujing/pingding/shilang1's "Eval interrupted... cost limit reached" during an isolated NPC-attack-loop compile, or /adm/ simul_efun/object.lpc failing standalone since it's an #include-only fragment of simul_efun.lpc) — none affect the real boot or the registration/look/score test, which is the actual completeness gate per AGENTS.md's "Definition of done".

Directories/files created that weren't in the raw archive

Standing scratch-file hygiene

No boot_stdout.log/trace_lpcc.json/similar left in libs/xiyouji/'s top level or work/ (checked and removed a stray work/trace_lpcc.json before finishing). lpcc_batch_raw.log and lpcc_fail.log at the top level are the standard lpcc_check.sh output artifacts, kept per the same convention every other already-done lib in this project follows.

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 all three entry points the login flow calls: is_banned(), create_char_banned(), is_strict_banned() (each now return 0 immediately for local IPs). These gates were not actively blocking (shipped ban lists are empty) but are patched per standing policy so runtime-added bans can never lock out local/ WASM play. 2. Uptime startup gate: none in this lib (checked logind.lpc). 3. Anti-flood throttles: none per-IP in this lib. The "玩家已经太多" gates are global player-count caps, not per-IP throttles — left intact. SECURITY_D->match_wiz_site() returns 1 when a wizard has no wiz_sites entry, so it does not block fluffos — left intact. 4. Admin account seeded — id fluffos, password Mud@2026, display name 浮浮 (male), registered through the real flow (gb → no student gate → id → y → name → password ×2 → email → gender → accept talents). Granted (admin) via adm/etc/wizlist (fluffos (admin)), read by securityd.lpc::create(). Verified after restart: re-login shows 目前权限:(admin), update /adm/daemons/band recompiled OK, goto worked. 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 南城客栈 as (player), look correct; fluffos re-login as (admin) with working wizard commands; log/debug.log clean (0 errors).

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

Second lineage in the project's round-two deep-playthrough pass (after bxsj, a completely different codebase/lineage — see libs/bxsj/NOTES.md's "深度功能测试" for the worked-example this pass follows). Played as an ordinary new player through registration, exploration, safe combat, organic skill-learning and sect-joining, a real quit, and a real-wall-clock-gap reconnect, native driver (build-debug). Read doc/help/newbie in full first (the single highest-value planning resource, exactly as the methodology predicts — it named the fight-vs-kill safety distinction, apprentice/learn syntax, and the general command set up front). doc/help/ also has ~90 other topic files (combat, menpai, individual sect writeups, etc.) but no separate general-help directory beyond it.

Test characters (both kept, not cleaned up, as representative playthrough evidence):

Bug found and fixed: unbounded init()/reset() recursion crashes the FIRST visit to any of the game's 9 sect-entrance rooms

This is a genuinely new bug class for this project's catalog — not the §7.16 rank-decay class (bxsj's bug), and not any previously-cataloged shape. Filed as file:line d/jjf/npc/zhangmen.lpc:36/623 (the crash site) and std/room.lpc:25 (reset(), the structural root).

- maximum call depth : 30 in config.fluffos is a dead setting on this driver build — raising it to 150 (the driver's own hardcoded default) had zero effect on the crash. Checked the actual driver source (~/src/fluffos/src/vm/internal/base/ interpret.cc): the enforced limit is the compile-time constant CFG_MAX_CALL_DEPTH (150); the config key is registered in rc.cc but never read by the interpreter. The archive's own "(unused currently)" comment on this key is, unusually, accurate for this driver build (most of this file's "(unused currently)" comments are stale — see logind.lpc's call-depth-adjacent settings elsewhere in this project's other libs — so don't generalize this either way without checking the specific key). Left the config value unchanged (30, the archive default) with a comment recording this so a future agent doesn't re-spend the time. If a lib's crash trace shows "Too deep recursion" at a depth that looks legitimately-deep-but-finite (not cyclic), the fix has to be an actual code change, not a config tweak, on this driver. - Disabling *only* create_identity()'s call from init() still crashed (blamed reset_me() / feature/dbase.lpc's query_temp() instead). Disabling reset_me()/restore()/fully_recover() too (leaving init() almost empty) *still* crashed, now blamed on me->setup()/std/char.lpc's setup(). This is what established that the recursion isn't really "inside" any one of zhangmen.lpc's own functions — it's that init() itself was being re-entered on the same object, so whatever code happened to be running when the call-depth limit hit varied by exact timing. - Applying *only* the std/room.lpc reentrancy guard (without the zhangmen.lpc init() guard) still crashed. Applying *only* the zhangmen.lpc init() guard (confirmed via git stash isolation, without the room.lpc guard) was the point at which live retesting stopped reproducing the crash across several repeated fresh-boot attempts — but std/room.lpc's setup() calling reset() synchronously (see root cause #1) is real, structural, and shared by every room in the game, so the guard was kept anyway as defense in depth even though the zhangmen.lpc-side guard alone was sufficient in this specific reproduction.

What was tested and confirmed working

Methodology notes (for the broader pass this seeds)

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

西游记/xiyouji.org 家族的祖先快照(1996-98)。

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

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

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

1. config.fluffos:maximum evaluation cost 从 400000(已知 风险区间)提升到 5000000。 2. adm/simul_efun/file.lpc:log_file() 没有 assure_file() 目录预建保护,补上调用及前向声明;cat() 补上 read_file() || "" 空值防护。 3. obj/user.lpc::reconnect()(AGENTS.md §7.108,第十三条独立确 认的血统——本档案是 xiyouji.org 大家族本身的祖先快照,§7.17/ §7.19 等多条既有条目的血统源头,这次在它自己身上也确认了同一类 bug):adm/daemons/logind.lpc 有同款 exec(old_link, user); 踢掉重复登录写法,reconnect() 缺少 enable_commands()。按 §7.108 记录的写法预防性修复,现场用两个真实连线复现"保持第一个 连线不断开→第二个连线登录→答 y 踢掉旧连线"验证:score 修复后 立即正常显示完整角色档案。

cmds/wiz/update.lpc(§7.106)与 master.lpc::log_error()(§7.10 的 "arning:" 大小写无关写法)均已是正确写法,均无需改动;本档案无 adm/daemons/closed.lpc,不受 §7.107 影响。

现场验证摘要

驱动干净启动,管理员 fluffos/Mud@2026 登录(GB/BIG5 选择→未成 年人关卡"no"→id+密码)确认 目前权限:(admin),update /adm/daemons/logind 成功验证真实写入权限。踢掉重复登录重连路径现 场验证通过(见上)。debug.log 全程干净(387 行,无真实错误)。

本轮修改的文件

§7.100 sweep (2026-08-19)

Fixed the corpus-wide inherit ROOM; ... replace_program(ROOM); redundant-replace bug (AGENTS.md §7.100). 172 live occurrences deleted: 171 via scripted sweep (fix_710_room.py), plus 1 hand-fixed roommaker-tool template (obj/roommaker.lpc, simple string-builder variant). 2 already-commented-out instances left untouched. 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 40079.

§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): 3 accessor(s) in this file returned a raw never-initialized mapping instance variable (defaults to int 0, not ([]), until first assigned), crashing any unguarded keys()/sizeof()/indexing caller for a fresh/untrained character. Fixed at the accessor level (mapp(x) ? x : ([])) per the documented remedy. Verified via lpcc --batch static compile check only (not a live boot) as part of a large mechanical sweep; not individually functionally re-tested live on this lib.

深度功能测试第三轮 / Deep functional test round three-plus-four (2026-08-21) — CLEAN PASS, no new bugs

Round-two (2026-08-15, above) had explicitly flagged two gaps: no successful shop purchase completed live, and death/respawn not live-tested at all. This pass closes both, plus adds a first-ever live test of the mail system, and re-confirms the §7.108 reconnect fix and the corpus-wide §7.112 death_stage() reentrancy guard (already present in d/death/npc/pang.lpc via the earlier 7ff1b78860d sweep commit — not previously exercised live on THIS lib). Native build-debug driver, existing character shenqy/沈青云 (still apprenticed to 秦富, 将军府第四代弟子) plus admin fluffos.

Economy/shop purchase (round-two's gap #1): the player-side economy is not a wallet integer — money is a physical carried item (/obj/money/{coin,silver,gold,thousand-cash}, feature/finance.lpc's can_afford()/pay_money() scan present("coin_money", me) etc.). Granted shenqy 5000 coin via clone /obj/money/coin + call coin->set_amount(5000) + give coin to shenqy (admin). buy huasheng from xiao er then succeeded (你向店小二买下一碟花生豆), confirming the full economy code path (money-item detection, price deduction, item creation) works correctly. Note for future testers: std/char/npc.lpc's add_money(string type, int amount) only exists on the NPC mixin, not the player one — call shenqy->add_money(...) returns 0 (no-op) on a real player object; the clone+give path above is the correct way to grant a player money live.

Death/respawn (round-two's gap #2, now fully closed): call shenqy->die() (admin) triggered the complete cycle cleanly — corpse left behind, die()'s DEATH_ROOM->start_death() call is a benign undefined-function no-op (same shared-lineage artifact noted elsewhere in this project, confirmed harmless again here), player moved to /d/death/gate (阴阳界) as a ghost. d/death/npc/pang.lpc's death_stage() ran its full 5-message, 25-second judge dialogue (崔判官...) via the already-guarded death_stage_active temp-flag pattern (§7.112 fix, present since the earlier corpus sweep) with zero reentrancy issues, then called reincarnate() and moved the player to REVIVE_ROOM (荒郊小店). debug.log clean throughout (grepped for non-warning lines across the whole session: zero hits). Potential (潜能) dropped from 99 to 50 as an intended death penalty (COMBAT_D->victim_penalty()); HP shown at 35/100 post-revive is correspondingly partial, not a crash artifact.

Mail system (new — not covered by any prior pass on this lib): obj/mailbox.lpc itself is dead/orphaned content (not placed in any room's objects mapping, same class as round-two's noted muren.lpc gap) — the *live* entry point is d/ourhome/npc/bigeye.lpc (邮差 千里眼/Bigeye, the NPC standing in the start room), whose inquiry mapping wires "mail"/"信"/"收信" etc. to receive_mail(), which clones a personal MAILBOX_OB into the player only when the player is physically standing in their own startroom. ask bigeye about mail → received a personal mailbox → from correctly reported empty → mail fluffos (compose flow: title → body → . to end → save-copy y/n) sent successfully, and admin fluffos got a real live notification (千里眼跟您说:有您的信!快来一下!). Full round-trip confirmed working, zero errors.

Reconnect (spot-check re-verification of round-two's §7.108 fix, unmodified since b24f99518c4): disconnected shenqy's telnet session uncleanly (killed the tmux pane) and reconnected fresh with the same id/password — 重新连线完毕, score immediately showed full correct state (title, 潜能 50 matching the post-death value from this session).

Verdict: no new bugs found this round — a clean confirmatory pass that closes both gaps round-two left open. Test character shenqy's save state (money, death/revive location, mailbox) was left as-is per this project's standing convention of keeping test characters as representative playthrough evidence, not reset.

AGENTS.md §7.19: enable_player() reentrancy guard (2026-09-01)

Same corpus-wide bug class as mhxy/wuhanzhan: feature/command.lpc's enable_player() wraps enable_commands() and is unconditionally reachable from an NPC's init() via setup()/reset_me() (confirmed on this lib's own d/*/npc/zhangmen*.lpc-family NPCs, matching mhxy's originally-documented d/xueshan/npc/zhangmen.lpc pattern). Calling enable_commands() on an object that's already living() makes the driver re-invoke that object's init() as a side effect; since init() calls back into enable_player(), that is genuine same-call-stack reentrancy that repeats until "Too deep recursion" aborts a room's first-ever visit.

Fixed with a true reentrancy flag (nosave private int in_enable_player_now;), NOT a bare if (living(this_object())) return; guard — this lib's feature/damage.lpc revive() and cmds/std/sleep.lpc wakeup()/wakeup2() all legitimately re-invoke enable_player() while the object is still living() (that's how a fainted/asleep character gets commands back), so a living()-gated guard would silently break every one of those real re-enables. enable_player()'s single body has no early return statements, so the flag is set at entry and cleared once, before the function's fall-through end. Verified with a single-file lpcc compile check (exit 0, no errors) against feature/command.lpc.

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

scripts/lpc_fix_negative_ranges.py: 4 line(s) in 1 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)— here-document terminators

scripts/lpc_fix_heredoc_terminator.py --all --apply: 1 here-document block(s) repaired (1 with the terminator glued to the last text line, 0 with it indented), so End of file in text block no longer hides the room or object behind them: d/moon/bedroom.lpc. The terminator must start its line (the lexer's own test, KB 04 §6.10); a glued one lost its newline in the GBK -> UTF-8 conversion together with a half character, which stays out.

深度功能测试(§10.7,2026-10-05)— compile-warning pass, truncated files completed

scripts/lpc_warnings.py xiyouji (4987 files; the oldest ancestor of the Journey-to-the-West / ES II family, see the English description): 659 distinct diagnostics at the start (492 unused locals in 273 files, 39 unknown escapes, 25 prototypes with another argument count, 14 nosave functions ...), 78 at the end, 4937 files compile and 50 do not before the repairs below. 329 files changed. Tools: scripts/lpc_warnings.py --fix, scripts/lpc_family_pass.py's sequence (redundant inherits, void overrides, macro redefinitions, prototype return types, clean_up() diamonds named after a rescan, forward declarations); this lib has no wrong-case paths.

Truncated files completed from a sibling (four files, each checked byte for byte). The archive's copies of d/qujing/jindou/obj/zhuofake.lpc (1137 bytes), d/qujing/jingjiling/jingji1.lpc (1089) and d/qujing/wudidong/npc/fangzhang-jail.lpc (1150) stop in the middle of a function; each is exactly the first bytes of the same file in xiyouji2006 (4546 / 2293 / 10490 bytes), whose remainder was appended (End of file in all three before). d/nanhai/obj/jingu2.lpc lacks only the closing } of free_jingu() that the sibling's copy has. d/qujing/jingjiling/jingji6.lpc is cut the same way but the sibling's copy is a reformatted version, so it was left.

What is left (19 files with a compile error). Dead or damaged: d/kaifeng/npc/old/* (headers quest_*.h, reporting.h never shipped), cmds/imm/cast1.lpc, d/obj/fabao/EXAMPLE.lpc, d/moon/obj/poem.lpc, /obj/toy/* (message.h), d/city/obj/dami.lpc and d/obj/food/dami.lpc (a weapon class UNARMED this lib does not have), d/city/npc/duguoyin.lpc (::buy_object() in an NPC with no dealer base), cmds/wiz/tail.lpc (no tail() simul_efun, as in 92 other libs).

深度功能测试(§10.7,2026-10-05)— room base survives a raising NPC

scripts/lpc_resilient_room.py --apply: make_inventory() of std/room.lpc loads each inventory object as if (catch(ob = new(file)) || !objectp(ob)) return 0;. new() of a file that is not there returns 0 (the room just lacks that NPC), but new() of a file whose create() raises (a skill file for set_skill() this archive never had, a carry_object() item that is not there, a header that never shipped) raised inside the room and the room did not load at all. This lib has no wrong-case paths; the change is the one made in the libs whose renames made such NPCs reachable (KB 03 §4.5), and keeps a room loadable when an NPC in its inventory has a content gap: it loads without that NPC. The room base compiles and loads with the change (HEAD against the working tree).

深度功能测试(§10.7,2026-10-05)— compile-warning pass, second round (29 warnings -> 0)

The first round (above) left 29 warnings that the tools did not take; each is fixed at its source now, so the lib prints no compiler warning when it boots. 17 files changed.

深度功能测试(§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)— 房间列出的物件缺档时房间照常加载

房间基类 std/room.lpc:reset() 的 case 1: 拿到 0 就跳过这一项(if (!objectp(ob[list[i]])) break;)。档案里没有的 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、多件 OK。

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

15 个文件、18 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()),多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/city/npc/obj/choupao、/d/city/npc/obj/bingfu、/d/city/npc/obj/cloth)。本库 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 个,改后 15 个(14 个原来载入不了),没有退步;其余 1 个另有原因(多为 set_skill() 了本库没有的武功)。

去掉的预定义宏 SAVE_EXTENSION(2026-10-18,KB 04 §6.11)

老版 MudOS 给每个 LPC 对象预先定义了 SAVE_EXTENSION(".o");FluffOS 的 ChangeLog(COMPAT_BUSTER 一条)把它删了,改用 __SAVE_EXTENSION__。本库没有自己 #define 这个宏,用到它的文件一律 Undefined variable 'SAVE_EXTENSION',整个对象载入不了。改成 __SAVE_EXTENSION__(scripts/lpc_fix_predefined_macro.py):include/net/ftpdsupp.h。

d/moon/obj/poem 两句诗丢了收尾的引号(2026-10-18,KB 03 §4.6)

string *poem 数组里 "不放玉花飞堕地,留在广寒宫阙。, 和 "把酒送春春不语,黄昏却下潇潇雨。, 的收尾引号没了(转码时跟半个汉字一起丢了),下一行的开引号把字符串合上,后面的诗句成了代码:Illegal character 0xe6,整个物件载入不了。补回 ",。载入:通过。

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 载入不了,刷出它的房间也少一个人。本库有 12 个对象设了本库没有文件的武功(19 个名字,最多的是 perception、celestial、celestrike)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:12 个里 12 个通过。(scripts/lpc_guard_set_skill.py)

补回本库没有的基类文件(2026-10-18,KB 04 §6.15)

下面这些基类本库没有,继承它们的对象(房间、NPC、物品)整批载入不了(inherit XXX; 的宏没定义,或者 inherit "/std/…"; 的文件不在)。同系的库里有同名文件,两库有大量逐字节相同的文件(亲缘关系),所以从那边原样拷来,缺的宏补进全局头文件 include/globals.h:/std/weapon/unarmed(宏 UNARMED)←sjsh,1 个对象继承它,抽样 1/1 载入。每个基类补回后单独载入、再抽样载入使用它的对象,基类载不进来或者一个用户也载不进来的就撤掉(本库撤掉了它们,不再补)。(scripts/lpc_restore_bases.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 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 3 处、3 个文件:yange_blade→yange-blade(1 处)、dragon-step→dragonstep(1 处)、huntian-qiong→huntian-qigong(1 处)。(scripts/lpc_fix_skill_names.py)

调用了父类没有的函数的对象(2026-10-19,KB 06 §7.259)

::create();、::init();、return ::buy_object(who, item); 这类调用写的是父类里有这个函数的版本;本库的父类没有,旧驱动把它当空操作(返回 0),现在的驱动在编译时就报 Unable to find the inherited function,整个对象载入不了。单独成句的调用删掉,return ::f(...) 改成 return 0;(void 函数里是 return;),与旧驱动的行为一致。共 1 处、1 个文件:::buy_object()(1 处)。只改了出错原因全是这一类的文件;还带着别的错误(多半是父类本身没了)的文件没动,等父类补回后再看。(scripts/lpc_fix_dangling_super.py)

武功基类缺少 NewRandom()(2026-10-19,KB 06 §7.261)

基本武功(boxing、houquan、westsword、taiji-quan 等)用 action[NewRandom(i, 20, level/5)] 挑招式;NewRandom() 和它调用的 sum() 在别的库的武功基类里有,本库的 /std/skill.lpc 是更精简的版本,没有,于是这些武功全都报 Undefined function NewRandom 编译不过,有这门武功的人物只能空手。把这一对函数(115 个库共用的同一份文字)补进武功基类,受益武功 2 个:/daemon/skill/jueqingbian.lpc、/daemon/skill_wiz/whip/jueqingbian.lpc。(scripts/lpc_restore_skill_helpers.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 文件。

被注释吞掉的 inherit / include(2026-10-19,KB 06 §7.274)

/daemon/class/moon/moonshentong/shiyue.lpc:inherit SSERVER;。这些指令接在上一行 // 注释的末尾(档案里丢了换行),成了注释的一部分,驱动看不到:缺 inherit 的技能报 Undefined function offensive_target,缺 #include <weapon.h> 的兵器报语法错误。scripts/lpc_fix_swallowed_directive.py 把换行放回去。载入检查 1 个文件,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/arch/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,共 5 处)。改动:/cmds/wiz/dns.lpc(1 处)、/d/city/puke-room.lpc(4 处)。

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

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

登录密码超过 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)。

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])) ...,不检查列表是否为空。本库有 1 个 NPC 文件写成 set("chat_msg", ({ }))(或 chat_msg_combat)并带正的 chat_chance(例如 /daemon/class/oldstuff/ronin/master.lpc),每次心跳有 chat_chance% 的概率 random(0) 为 0、msg[0] 报 Array index out of bounds(驱动捕获,心跳继续,但每次一条运行时错误),这个 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/wiz/test.lpc(present)。加载检查 HEAD 与工作树对照,没有退步。

cast1:宏 SYNTAX 带着分号(2026-10-21,KB 04 §6.28)

#define SYNTAX "指令格式:cast <法术> [<参数>| on <目标>]\n"; 的 ; 属于宏展开,notify_fail(SYNTAX); 变成 notify_fail("..." ;);,整个指令编译不了(unexpected ';')。去掉宏里的分号,并删掉没用到的局部变量 dest(编译警告)。这份文件在 return notify_fail("对方正在断线中,不能对其施法 这句字符串中间就断了(档案丢了后半截),后半截(。\n"); 之后的 main() 余下部分、destruct_flag()、help())按 xyj42 的同一份文字补全。

拼错的 call_out() / input_to() 目标(KB 06 §7.349,2026-10-21)

计时器或输入提示给驱动的函数名和文件里定义的差一个字母;驱动对不存在的函数静默不理,所以对应的计时从来没有触发过。调用和配套的 remove_call_out() 改成已定义的拼法:find_seal:disapprear → disappear(法术产生的封印十分钟后从不消失)。改动:/d/jjf/obj/find_seal.lpc。

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

add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 8 条注册(5 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/changan/pinqiting.lpc(do_pass、do_resign)、/d/city/npc/duguoyin.lpc(do_vendor_list)、/d/lingtai/npc/kancai.lpc(do_vendor_list)、/d/qujing/jisaiguo/obj/luche.lpc(do_get、do_put)、/d/qujing/wudidong/cave2.lpc(do_push)。最多的函数名:do_vendor_list 2 处、do_push 2 处、do_pass 1 处、do_resign 1 处、do_get 1 处。

(: 函数 :) 指针指向对象自己没有定义的函数(2026-10-22,KB 06 §7.355)

item_desc / inquiry / chat_msg_combat 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:编译不报错,用到时驱动报 *Undefined lfun pointer called: 函数名(look 某物、ask NPC about 话题 直接出错,战斗 / 闲聊里的指针在 NPC 的 chat() 里出错,这一拍心跳的后半段被跳过)。scripts/lpc_scan_callout_targets.py --kinds=lfun。 另有 1 处函数体被作者整段注释掉(指针没跟着删),是有意关掉的,没动。

(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)

inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 1 个文件里 2 行只含这个指针的行("剃度": (: ask_for_join :), "出家": (: ask_for_join :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。

汉字按字节切片的老写法没有换成按字符:改法名 "澄" + 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] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字)。

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

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

老档按字节做「一个汉字」:while (i--) if (i % 2 == 0 && !is_chinese(name[i..<0])) 只检查偶数下标,奇数位上的拉丁字母(张a)会过;茶馆猜诗 mixup() 用 strlen(str)/2 再 j=j+j、str[k..k+1] 交换两字节;泥潭系 channeld 的 switch (strlen(fname)) case 4/6/8/10 和 strlen(family)==4/6 按字节长度给同盟 / 门派名加空格。本驱动的 strlen() 和区间都按字符算。scripts/lpc_fix_byte_char.py:去掉奇偶项、把区间改成 name[i..i],按字符重写 mixup() 并把 strlen(first)>=14 折成 7,频道标题的 case / 门派长度改成字符数(对照 aoxiangtianji)。本库 汉字循环 / is_chinese 区间 4 个文件;茶馆猜诗 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 是两字符牌码,未改。

洗衣盆 is_container 映射缺值编不过(2026-10-27,KB 04 §6.28 Also)

d/gao/obj/pen.lpc(或 d/qujing/gao/obj/pen)的 is_container() 写成 set("objects", ([ "/u/bula/gao/obj/kaoji" ]));:映射只有键没有值(unexpected ']'),对象无程序。同族 hy5 / xyj2000 已把该函数注释掉。改成标准的 return 1;。证明:HEAD 加载失败;改后加载成功。

toy 旁缺 message.h(2026-10-27,KB 04 §6.23 Also)

obj/toy/*.lpc(及少数 obj/obj/toy、嵌套副本)写 #include "message.h",引号 include 只解析同目录;本 lib 的 d/obj/misc/message.h 有同内容副本,toy 目录没有。玩具对象无法编译(Cannot #include message.h)。从本 lib 的 d/obj/misc/message.h 复制到缺失的 toy 目录旁。证明:HEAD buwawa 加载失败;改后加载成功。

悬空 ::init / unique::init(2026-10-27,KB 06 §7.259 Also)

kugu-1/2/3、kunxian-suo 写 ::init();,tianyi 写 unique::init();,而 F_UNIQUE / ITEM / feature/unique 没有 init;本驱动编译报 Unable to find the inherited function 'init'(tianyi 带 in file 'unique'),物件无法加载。老驱动把空父调用当返回 0。lpc_fix_dangling_super.py 删掉这些语句;cloth::init() 保留。证明:HEAD 加载失败;改后加载成功。

dami 误调 init_unarmed(2026-10-27,KB 04 §6.25 Also)

d/obj/food/dami.lpc 继承 ITEM+F_FOOD 却调用 init_unarmed(5)(并 #include <weapon.h>),未继承空手兵器基类,驱动报 Undefined function init_unarmed。手足 hymud 已注释该行。删掉该调用与无用的 weapon 头。证明:HEAD 加载失败;改后加载成功。

fabao EXAMPLE 缺 ) 与 !(2026-10-27,KB 04 §6.25 Also)

d/obj/fabao/EXAMPLE.lpc 写 if (objectp(victim = present(target, environment())),丢了 ! 和右括号,驱动报 unexpected L_RETURN。按手足 kunxian-suo 改为 if (!objectp(victim = present(target, environment(me))))。证明:HEAD 加载失败;改后加载成功。

深度功能测试(2026-10-27,§6.25 jingji6 message_vision 截断)

d/qujing/jingjiling/jingji6 在 message_vision("$N想 处截断。用 xlqy_new2007 完好本覆盖。证明:HEAD 失败;改后加载。

深度功能测试(2026-10-27,§6.25 tell_object 参数顺序)

tell_object("…", me/who) 参数颠倒(消息当对象);改为 tell_object(me/who, "…")(guest.lpc)。证明:残渣 0;抽样 guest/rukou/forest_c 加载。

深度功能测试(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… 行。