Journey to the West 450

✅ 可玩

西游记450

xiyouji450

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

▶ 开始游玩 · Play Now

这是一份专门用于建立"镜像站"的发行包,内附教学说明,供其他站长在其基础上另开一个分身站点。与本项目中的 `xyj2000f`、`mhxy`、 `mhxyqd` 同属一支血缘极近的"西游记/xiyouji.org"引擎家族—— 核心引擎文件(`master.c`、`chinese.c` 等)与 `mhxy` 几乎逐字一致,只有站点自定义的登录/安全层与世界内容各自不同。

English

A distribution package built specifically for setting up "mirror sites," complete with a tutorial for other site operators to launch their own branch on top of it. It belongs to the same closely related "Journey to the West / xiyouji.org" engine family as this project's xyj2000f, mhxy, and mhxyqd — its core engine files (master.c, chinese.c, and others) are nearly word-for-word identical to mhxy, differing only in the site's own login/security layer and world content. Set in the Journey to the West universe, players start at the South City Inn in Chang'an and explore the three realms while practicing martial arts and joining sects; as a mirror-site package its world closely resembles the xyj2000f/mhxy line but runs its own independent registration and security system.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/xiyouji450/

管理员账号 / Admin account

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

本地运行

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

游戏端口:40078。

NOTES · 移植与修复记录

西游记450.rar → xiyouji450

Lineage: confirmed via diff (not assumed from title) — genuine sibling of the whole "西游记/mhxy" ES-II-derived family, closest specifically to mhxy(#19)/mhxyqd(#56), NOT a duplicate of any of them

Per the task's explicit instruction, all 5 already-processed Journey-to-the-West-themed siblings' NOTES.md were read first (xyj2000f/#15, mhxy/#19, mhxyqd/#56, shenmo/#73, xiyouji2003/#81), then this archive's adm/obj/master.c, adm/simul_efun/chinese.c, adm/daemons/logind.c, adm/daemons/securityd.c, adm/daemons/chinesed.c, adm/daemons/convertd.c were diffed (raw pre-conversion bytes, CRLF-normalized) against the corresponding raw files of xyj2000f and mhxy before any conversion work began.

Findings, by file:

Conclusion: genuine member of the same 西游记/xiyouji.org ES-II-lineage codebase family as xyj2000f(#15)/mhxy(#19)/mhxyqd (#56), sharing byte-for-byte-or-near-identical core engine files (master.c, chinese.c, convertd.c), but with its OWN distinct site-customization layer (logind.c/securityd.c/chinesed.c) and its own game-world content tree (d/, daemon/class/) — not a duplicate of any of them. Distinct again from shenmo(#73, "Neolith" mega-lib fork) and xiyouji2003(#81, itself confirmed closer to xyj2000f specifically) — those two were read but not diffed byte-for-byte here since master.c/chinese.c already clearly separate this archive from them by shared-lineage-but-different-generation reasoning documented in their own NOTES.md.

FLAGGED FOR LATER CROSS-CHECK (per task instructions): archive #82 (西游记2006之最终幻想.rar → libs/xiyouji2006/) was being processed by a concurrent sibling agent while this archive was worked and could not be diffed against here. A follow-up pass should md5sum/diff this archive's adm/obj/master.c/adm/simul_efun/chinese.c against libs/xiyouji2006/raw/.../adm/obj/master.c once #82 has landed, to see whether #82 is ALSO part of this same xiyouji.org lineage (plausible given the shared "西游记" title and 2006 vs. this mirror-kit's apparent late-90s/2002-ish vintage) or a separate fork.

Status: DONE — boots clean, full registration + post-login-command flow verified end-to-end into the game world, multiple independent runs

Fixes applied (proactive, before first boot, plus reactive fixes found via boot/registration/lpcc testing)

1. Standard conversion (scripts/convert_lib.sh): GB18030→UTF-8 on 6,399 files (already_utf8=812, converted=5514, lossy=52, skipped_binary=20, 1 failed-to-auto-convert — see below), .c→.lpc rename (4,988 .lpc files), 459 literal ".c" references fixed, 62 local angle-bracket #include <x.lpc> → "x.lpc" conversions, 39 files' static→nosave. Zero uppercase .C files found (checked proactively per §"Watch for uppercase .C"). - One file the auto-converter flagged as failed: cmds/imm/cast1.c — genuinely truncated in the RAW archive itself (1062 bytes, cuts off mid-string inside notify_fail(...), no closing quote/parens/semicolon/brace for the rest of the function or file — confirmed by inspecting the raw pre-conversion bytes, not an artifact of our pipeline). Converted what bytes exist with iconv -c so it's valid UTF-8 (won't corrupt anything downstream); left the resulting syntax error undiagnosed/unfixed since completing it correctly would mean fabricating the missing command logic (an admin-only imm command, not on any critical path) — documented here per §13's "don't fabricate missing content" policy, not fixed. 2. §15p (standing policy): proactively excluded dns_master from adm/etc/preload before the first boot attempt (found listed there). 3. §15ai: the exact predicted "dns_master exclusion surfaces a second gate" bug, found and fixed. adm/daemons/logind.lpc's encoding() callback (right after GB/BIG5 selection) has a one-time ("new_start==0... only check for once") mirror-site IP-verification gate: ``lpc if(!find_object(DNS_MASTER) || !"/adm/daemons/band"->check_ip( DNS_MASTER->get_host_name(mud_nname()))) { log_file("shutdown", ...); shutdown(1); } ` Since dns_master is now never loaded, find_object(DNS_MASTER) is always 0, and the || short-circuits straight to shutdown(1) — this would kill the ENTIRE DRIVER PROCESS on the very first connection's encoding choice, every time. Fixed by changing || to && so the whole IP-verification gate is skipped (not "failed") when the daemon is simply absent — matches §15ai's documented fix pattern exactly. Verified: shutdown(1) was NOT called across ~10 subsequent test connections after this fix. 4. §15ac: include/net/ftpdsupp.h used the bare SAVE_EXTENSION identifier (never defined anywhere in this lib) instead of this driver's real autogenerated __SAVE_EXTENSION__ constant. ftpd is on the preload list, so this would have broken preload entirely (a Cannot #include-adjacent-shaped chain: ftpd.lpc → net/ftpd.h → net/ftpdconf.h → net/ftpdsupp.h). Fixed the one occurrence. 5. §15h (project-wide, most important recurring bug): applied to adm/simul_efun/chinese.lpc's is_chinese() (GBK byte-range check str[0] > 160 && str[0] < 255 → CJK Unicode codepoint range check str[0] >= 0x4e00 && str[0] <= 0x9fff; length bound strlen>=2 → strlen>=1) and adm/daemons/logind.lpc's check_legal_name() (bound strlen(name) < 2 || > 12 → < 1 || > 6, matching the message text's own "一到六个中文字" promise; dropped the i%2==0 && byte-stride guard in the per-character Chinese-check loop since every UTF-8 index is already one character). Verified via 3 independent real registrations with real Chinese given names (秦风/秦淮/张三/秦风 again post-fix) all correctly accepted through to the password prompt. 6. §14: upgraded master.lpc's valid_override(file, name) to the 3-arg valid_override(file, name, main_file) form, also allowing when main_file == SIMUL_EFUN_OB || main_file == MASTER_OB (defensive/free fix per the catalog; never actually observed to matter on a real boot here, matching the catalog's own note that this rarely bites in practice). 7. §8h (recurring convertd.lpc Greek-table typo): adm/daemons/convertd.lpc had 45 lines shaped like "α\", (a stray trailing backslash escaping the closing quote, corrupting the rest of the file into one runaway unterminated string — this crashed the WHOLE BOOT the very first attempt: error: Illegal character 0xce at line 249 and cascading). Confirmed CRLF line endings, used the CRLF-aware fix sed -E 's/\\"(,)?\r?$/"\1\r/'; verified 0 remaining matches afterward and a clean reboot. 8. §15w (log_error() broadcasting compile WARNINGS, not just real errors, to every non-wizard player) — the single highest-impact bug found in this pass, and a genuinely NEW manifestation worth noting: master.lpc's log_error() unconditionally showed the scary default error message (你发现事情不大对了,但是又说不上来。) to this_player(1) for EVERY compile diagnostic reaching that apply, warnings included. This was invisible during boot (preload only compiles a handful of daemons, no warnings hit a connected player there) but fired 26-27 times in a row the FIRST time any character's make_body() ran — because /obj/user.lpc's entire ~16-feature inheritance tree (std/char.lpc + feature/*.lpc) is never preloaded, so its ~26 pre-existing soft warnings ("Unknown #pragma, ignored", "Unused local variable", etc.) all got lazily compiled and individually broadcast to the one connected (non-wizard) player mid-registration, right between the email prompt and the gender prompt. This one took real diagnostic effort to pin down because it looked exactly like a flaky/intermittent RUNTIME bug (present on some driver boots' first character, absent on others depending on incidental timing/whether /obj/user's tree had already been touched) rather than a deterministic compile-warning-count issue — confirmed by cross-referencing the exact error COUNT (26) against the exact number of warning: lines in debug.log appearing right after "Accepting telnet connections", which matched precisely. Fixed exactly per the catalog: gate the player-facing broadcast on strsrch(message, "warning:") == -1 (still logs everything to home+"log" regardless). Verified: 0 spurious messages across 3 subsequent full registrations post-fix. - Also hardened, as insurance, while investigating this (per §15w's own related note that error_handler()'s "falls through to debug.log automatically" comment doesn't hold on this driver): added catch(efun::write_file("/log/RUNTIME_ERRORS", trace)); at the end of error_handler(). Discovered in the process that securityd.lpc's valid_write() asserted objectp(user) (via a bare error()) BEFORE its own LOG_DIR-write allow-check, which would have made even this insurance write silently fail for any caller whose user argument isn't a live object (e.g. deep in the driver's own error-unwind machinery) — reordered so the /log/-write-file allow-check runs first. (No real errors have occurred since the §15w fix, so /log/RUNTIME_ERRORS has stayed empty in every subsequent run — expected, it's forward-looking insurance, not evidence of a live bug.) 9. §11-shaped copy-paste bug, d/city/npc/duguoyin.lpc: this NPC (a gambling-den boss) is a near-verbatim clone of d/city/npc/bookseller.lpc (identical buy_object()/greeting() logic, confirmed via diff) but its inherit line was left as plain NPC instead of F_VENDOR_SALE — the base class that actually defines the buy_object() its own override calls via ::buy_object(...). Fixed by changing the inherit to match what the file's own content clearly needs (matching the established "match inherit to actual content" precedent, §11). 10. §11-shaped stale content, 2 files: d/city/obj/dami.lpc and d/obj/food/dami.lpc (two near-identical rice-bag food items, both still headed // chicken_leg.c — another copy-paste-and-never-renamed artifact) each called init_unarmed(5) in create() — a function that doesn't exist ANYWHERE in this lib (confirmed via grep), and d/city/obj/dami.lpc additionally inherited a bare UNARMED macro that's also never #defined anywhere (a hard syntax error, not just an undefined-function warning). Since no real implementation exists anywhere in the archive to restore, and it's peripheral non-critical content (a rice-bag item, not on any critical path), fixed by dropping the erroneous inherit/call rather than fabricating a function — inherit UNARMED;→inherit ITEM; (matching its own sibling's already-correct inherit) and removed both init_unarmed(5); calls. 11. §15i (comment-eats-next-statement), daemon/class/moon/moonshentong/shiyue.lpc: line 1 was // 蚀月咒inherit SSERVER; — the Chinese skill-name comment and the inherit SSERVER; declaration sat on the same physical line with no separating space, so // swallowed the whole line including the inherit statement, silently deleting it. This broke offensive_target() (defined in std/sserver.lpc, the file SSERVER points at) with "Undefined function offensive_target". Fixed by splitting the comment onto its own line and restoring the inherit SSERVER; as real code. Checked broadly for recurrence: grepped the whole lib for the shape ^//.*inherit [A-Z_]*; (18 hits) and manually inspected every one — the other 17 are all genuine, deliberately-commented-out alternate/historical inherit lines with normal spacing (each such file has its own real, active inherit line elsewhere) — only shiyue.lpc had the actual merged-line bug. 12. §8e (tail() is not a real efun on this driver): cmds/wiz/tail.lpc (an admin command, not on any critical path) called bare tail(file). Reimplemented in plain LPC (do_tail_lpc, last-10-lines) per the catalog's standard pattern; needed a forward declaration (§8b — the new helper is called from main() before its own textual definition in the same file, same shape as help()'s existing forward decl one line above it). 13. Missing shared header restored verbatim from an identical sibling: obj/toy/buwawa.lpc and obj/toy/mallet.lpc both #include "message.h" (quoted — searches their own directory first) but no message.h exists in obj/toy/. Found two byte-identical existing copies elsewhere in the lib (d/obj/misc/message.h, d/obj/flower/message.h — a generic message_vision() helper duplicated per-zone rather than centralized in /include, apparently this lib's own long-standing convention). Copied the same file to obj/toy/message.h — not fabricated content, a verbatim restoration of an already-duplicated generic helper, matching the established "restore from an identical sibling" precedent (e.g. xajhzcjh's cloth.lpc). 14. Genuine pre-existing truncation, d/nanhai/obj/jingu2.lpc (Honghaier's "golden hoop" item): raw archive file is exactly 1024 bytes, cutting off right after the LAST statement of free_jingu() with no closing } for the function (all the actual logic/dialogue text is intact and complete — only the trailing brace is missing, unlike cast1.c's mid-string truncation). Added the single missing } — a safe, minimal syntax fix that adds no new logic, not a §13 "don't fabricate" violation. 15. §13-shaped missing-header content gap, restored where safely possible: d/kaifeng/npc/old/{shan,bei,zhi}.lpc (3 quest-giving NPCs) #include <quest_ak.h> / <quest_fd.h> / <quest_gv.h> / <quest_kl.h> / <reporting.h> using ANGLE BRACKETS — but these headers actually live one directory UP, in d/kaifeng/npc/ (their sibling NPCs in that same parent directory use QUOTED includes and compile fine). Angle-bracket resolution only ever searches the configured /include path, never a relative/parent directory, so master::get_include_path() (§8d) can't fix a PARENT-directory header reference either. Since the target headers are genuinely real, matching content (verified quest_ak.h's quests_ask mapping is exactly what shan.lpc expects, keyed by the same quest text/NPC names it references), copied quest_ak.h/quest_fd.h/quest_gv.h/ quest_kl.h/reporting.h/quest.h (a further transitive header-guarded dependency of all four) into old/, and converted the 3 npc files' <...> includes to quoted "..."` form to match. This is restoring real, correct, already-existing content to where it's needed — not fabricating anything.

Confirmed NOT needed (checked proactively, verified via source read)

Re-verification pass (QA sweep, later session)

Re-tested the full flow end-to-end this pass and found two real bugs:

1. Stray pre-existing debug leftover printf("%O\n", ob); in adm/daemons/logind.lpc's Chinese-name-confirmation step -- dumps a raw internal object reference (e.g. /obj/login#0) straight to the connecting player right after their Chinese name is accepted, on every single registration. Confirmed live during a fresh registration test. The exact same leftover was found (and removed) in all three sibling libs processed in this same session (xiyouji, xiyouji2003 -- 2 occurrences, xiyouji2006), confirming shared lineage at the source level. Removed here too; re-verified with a fresh registration (qfmei/秦美): no stray object-reference text anywhere in the transcript, look/score/quit all still correct (correct female-specific "你目前待字闺中" text). 2. §15s, tell_room()'s raw-int 0-as-4th-arg bug, ported defensively. This file's own earlier pass concluded (via source reading) that the driver tolerates a raw 0 in message()'s exclude slot -- but a live crash of the exact same call shape was subsequently confirmed on siblings xkx2001 and xiyouji2006 in this same later session. Ported the standard fix (exclude || ({})) here too, even though this archive's own boot/registration path never actually triggered the crash, since the underlying call shape is identical. Re-verified with a clean boot: zero regressions.

Registration-flow test — exact transcript/outcome (real Chinese names, full continuous connections)

Shape (read from logind.lpc's actual input_to chain, not inferred from prompt text, per the standing "always read the actual callback" lesson): gb/big5 encoding choice → banner + age-gate question (yes/no, any non-"no" ends session) → English id (any unused id skips straight to confirm, no separate "new" keyword needed) → y/n confirm new character → Chinese name → password → confirm password → email → gender (m/f) → stat-roll accept (n/y/q) → enters the actual game world.

Run 1 (qinfeng / real Chinese name 秦风, male):

您是否是中小学学生或年龄更小?(yes/no)
> no
您的英文名字:(新玩家请选择一个您喜欢的名字)使用 qinfeng 这个名字将会创造一个新的人物,您确定吗(y/n)?
> y
您的中文名字:
> 秦风
请设定您的密码:
> test12345
请再输入一次您的密码,以确认您没记错:
> test12345
您的电子邮件地址:您要扮演男性(m)的角色或女性(f)的角色?
> m
[stat roll displayed]
您同意这组天赋数值吗?[n|y|q]
> y
目前权限:(player)
[...]
南城客栈 - [full room description, exits west/east/up, board, 2 NPCs]
> look
[same room re-displayed correctly]
> score
【 平  民 】普通百姓 秦风(Qinfeng)
你是一位十四岁的男性人类,...
[full correct stat sheet: 膂力/胆识/悟性/灵性/定力/容貌/根骨/福缘, 气/神 bars,
 战斗攻击力/防御力, 食物/饮水 bars, kill count, 潜能: 99]
> quit
欢迎下次再来!

Confirmed 0 spurious error messages, 0 debug.log errors, real Chinese name accepted, look/score/quit all correct.

Run 2 (real Chinese name 林枫, female, different driver boot/session): identical flow, correct female-specific text ("你目前待字 闺中" vs. run 1's male "你目前是光棍儿一条"), 0 spurious errors, 0 debug.log errors — confirms the §15h fix and §15w fix both hold across independent sessions/genders.

Run 3 (post-§15w-fix final verification, real Chinese name 秦风 again as a fresh id qinfengd since the plain qinfeng id was already registered from run 1's persisted save data — confirms save/load and duplicate-id handling both work correctly too): same clean flow, 0 spurious messages, entered 南城客栈 again, look/score/quit all correct, 0 debug.log errors.

lpcc sweep results

Ran twice: once before the post-boot/registration-driven content fixes (items #9-15 above), once after, to get an accurate final number.

Remaining 54 failures, triaged by category (per §6b, not chased individually — none are on the boot/registration/critical path):

How to run

cd libs/xiyouji450
~/src/fluffos/build-debug/src/driver config.fluffos
python3 ../../scripts/mudclient.py 127.0.0.1 40078 --timeout 40 --idle 1 \
  --send "gb" --send "no" --send "<english-id>" --send "y" \
  --send "<real-chinese-name>" --send "<password>" --send "<password>" \
  --send "<email>" --send "m" --send "y" --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), same shape as sibling xiyouji (band.lpc is byte-identical between the two apart from this patch):

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 are empty, so this is a defensive standard patch, not a live-blocker fix. 2. Uptime startup gate: none in this lib. 3. Anti-flood throttles: none per-IP (global player-count caps and match_wiz_site() — which allows wizards with no wiz_sites entry — left intact as game/security design). 4. Admin account seeded — id fluffos, password Mud@2026, 浮浮 (male), via the real flow (gb → no → id → y → name → password ×2 → email; NOTE unlike sibling xiyouji there is no gender/talent prompt — after email the character enters the world directly). Granted (admin) via adm/etc/wizlist. Verified after restart: 目前权限:(admin), update /adm/daemons/band OK (only pre-existing unused-variable warnings), goto OK. Save files: work/data/user/f/fluffos.o + work/data/login/f/fluffos.o (untracked, NOT gitignored — orchestrator must git add).

Flaky-first-registration note (environment, not a code bug): the very first registration attempt after a cold boot aborted at make_body() with Too long evaluation (config caps eval at 400000 usec) while lazily compiling the whole /std/char inherit chain, with several other drivers loading the host concurrently. The immediate retry (chain now cached) completed normally. No mudlib change made for this.

Retest: fresh registration reached 南城客栈, look correct; fluffos re-login (admin) + wizard commands OK; debug.log has no runtime errors beyond the documented first-attempt eval-cost abort (which did not recur).

深度功能测试 / Deep functional test (2026-08-06)

第一次完整游玩测试(原生驱动 build,ASAN/UBSAN debug 构建)。测试角 色 id xyfourf,中文名 白龙马。本轮 WASM 未重新验证:emsdk 工具链下 载硬编码指向 storage.googleapis.com,本次会话的出口代理策略性拒绝 该域名(403,已用 curl $HTTPS_PROXY/__agentproxy/status 确认是策略 拒绝而非临时故障),本地无法构建 WASM 驱动。

更正此前的误诊:上面记录的"首次注册偶发 eval-cost 中止"其实是 100% 可复现的真实配置 bug,不是环境偶发(AGENTS.md §7.90 严重实例)

上面"Flaky-first-registration note"一节把这次中止归因为"冷启动时并 发编译负载",判定为环境偶发、未做任何修复。刚测试完同一血缘家族的 xyj2000f(config.fluffos 的 maximum evaluation cost 同样被设成 400000,比这个项目常见的 700000 模板默认值还低)才发现:这不是并 发负载导致的偶发,而是每一次冷启动后的第一次注册都 100% 会撞上 ——make_body() 第一次编译整条 /std/char 继承链的开销本来就稳定超 过 400000 usec 预算,与是否有其它驱动同时在跑毫无关系。此前那次记录 之所以看起来像是"重试一次就好了",只是因为失败的那次编译已经把继 承链的编译结果缓存进了驱动进程,同一进程内后续的注册自然不会再触发 冷编译——如果当时重启驱动再试一次全新注册,会看到同样的中止 100% 复现。这次深度测试在动手注册前就先主动对齐了 xyj2000f 那次找 到的修复:把 maximum evaluation cost 从 400000 提到 5000000 (AGENTS.md §7.90 记录的、本项目 30+ 份档案已验证安全的数值),此后 两次独立的全新注册(重启驱动之间跨越两个不同的驱动进程)均一次通 过,无需任何重试或运气。已在 AGENTS.md §7.90 的对应实例段落里追加了 这份档案作为第二例确认。

顺带更正 README 的一处失实描述

原 README 写"与姊妹版 xiyouji 不同,本版注册流程没有性别/天赋选择 环节,更简洁"——本轮实测注册流程完整包含性别选择(m/f)与八项天赋 (膂力/胆识/悟性/灵性/定力/容貌/根骨/福缘)确认环节,与 xyj2000f 一致,并没有省略。已在 README 里更正这一句。

测试内容与结果

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

西游记450 手足档案(master.c 约等于 mhxy)。

§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). 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, same lineage as sibling xiyouji). 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 40078.

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

深度功能测试第二轮(2026-08-21):门派/拜师、经济/商店、真实战斗/死亡/复活 — 全部覆盖,未发现新 bug

补上前一轮明确标注"未覆盖"的两个系统,以及从未真正测试过的"真实致死 战斗 + 死亡/转生"流程。测试角色 id qzhan(中文名 秦战,密码已略去不 记录),全程使用真实驱动(~/src/fluffos/build-debug/src/driver)、 config.fluffos(maximum evaluation cost 已确认仍是 5000000,本轮 高强度测试全程未再触发 eval-cost 中止,§7.90 修复保持有效)。

1. 门派/拜师(原文档"未覆盖")——真实测试通过

找到方寸山三星洞第三代弟子云清(d/lingtai/npc/yunqing.lpc,房间 /d/lingtai/inside6)——attempt_apprentice()没有任何前置条件,直接 recruit,是全库里唯一无门槛收徒的师父,适合测试完整拜师链路。用 apprentice yun(注意:其 id 数组是 "yun qing"/"yun",不含 "yunqing",apprentice yunqing会因 present() 找不到目标而报错 "你想拜谁为师?"——不是 bug,只是 NPC 自身的 id 词组里确实没有这个 连写形式,其他别名同样能命中)。真实执行链路:apprentice.lpc里玩 家先 set_temp("pending/apprentice", ob)再调用 ob->attempt_apprentice(me),NPC 内部 command("recruit "+id), recruit.lpc里检查ob->query_temp("pending/apprentice")==me成立后 调用me->recruit_apprentice(ob)——feature/apprentice.lpc里的 create_family/assign_apprentice/recruit_apprentice全部有 mapp()前置检查,没有踩到任务里提到的"ldtx 那种未加 mapp 保护直接 下标 family["family_name"]"的第二种形状(cmds/std/apprentice.lpc 第41行的me->query("family/family_name")用的是安全的路径查询语法, 不是裸下标)。实测一次执行即完整完成拜师:称号从"普通百姓"变为 "方寸山三星洞第四代弟子",score正确显示"你的师父是云清", class被设为taoist,debug.log全程零新增错误。

2. 经济/商店(原文档"未覆盖")——真实测试通过

d/city/npc/xiaowang.lpc(小酒馆/d/city/jiuguan,inherit F_VENDOR) 出售jiuping(粗磁酒瓶,1两银子=100文)。用管理员账号 clone /obj/money/silver + call silver_money->set_amount(5) + give silver to qzhan(项目规定的合法管理员注资手法)给测试角色5两 银子,然后玩家真实执行buy jiuping from xiaoer:正确扣除1两银子 (5两→4两),正确收到"粗磁酒瓶"实物,feature/vendor.lpc里 ob->query("value")(ob是字符串路径,LPC的 "path"->func()语法糖,不是对字符串调用方法的 bug)工作正常, debug.log全程零新增错误(除既有的惰性编译警告)。

3. 真实战斗→死亡→转生(原文档从未测试,仅测过 wimpy 安全脱战)

依据任务里"kill 指令通常对 NPC 无战力门槛检查"的经验(本会话 10+个库已验证),核实cmds/std/valid_kill.h确认属实——valid_kill() 只检查no_pk_time(防止连续杀人的玩家间冷却),对 NPC 目标完全放 行。进一步核实feature/damage.lpc的实际死亡判定逻辑: receive_damage()扣减kee/sen/gin(临时值,归零后触发unconcious() "昏迷",会自动revive(),不是真死);只有receive_wound()扣减 eff_kee/eff_sen/eff_gin(永久性"内伤")归负才会真正触发die()—— 而adm/daemons/combatd.lpc第466-472行只在me->is_killing(victim) (即用kill而非fight发起)且伤害超过护甲时才调用receive_wound, 证实"safe spar vs. 真实kill有战力门槛差异"这条项目级经验在这份档案 上同样成立、且能精确定位到代码层面的原因。

实测:将秦战(刚拜师的初始角色,无技能)传送到朱雀大街疥顶小僧 (d/city/npc/jieding.lpc,闪避/招架技能79,此前只做过安全"fight"陪 练测试)身边,wimpy 0(禁用自动脱战)后kill seng——数回合内被 连续命中要害,从"气喘嘘嘘"→"头重脚轻"→"半昏迷"→真实昏迷 ("你的眼前一黑")→真实死亡("你死了",全服谣言广播"秦战被疥顶 小僧杀死了")。角色正确移动到 /d/death/gate.lpc("阴阳界"),遇到 崔判官(d/death/npc/pang.lpc,即§7.112清单里已核对过 reentry guard 的四个death_stage()实现之一),逐条完整走完5条death_msg台词, death_stage_active标记按代码路径应在最后一条消息后清除并调用 ob->reincarnate(),实测该函数被真实调用——角色魂魄送回阳间,落地 在"荒郊小店",宗门称号("方寸山三星洞第四代弟子")在死亡/转生全程 正确保留,气血值重置为极低值后按heal_up()自然恢复(非满血复活, 符合死亡惩罚设计),潜能从99降为50(死亡惩罚,符合预期),随身物品 (银子、酒瓶)因reincarnate()里的command("drop all")留在了阴阳界 (無法找回,属于"死亡掉落"式的老派惩罚设计,不是 bug)。全程 debug.log零 FATAL/SIGSEGV/Undefined function/Cannot replace 等错误 信号,只有一贯的惰性编译未使用变量警告。

注:feature/damage.lpc的die()函数里有一行 DEATH_ROOM->start_death(this_object())——全库grep确认start_death 这个函数从未被任何文件定义过(包括std/room.lpc),这是一次对未定 义函数的call_other。实测证实驱动对这种调用静默返回0、不记录任何 debug.log错误、不影响后续死亡/转生流程——不是一个需要修的 bug(无 错误信号,符合项目"没有错误签名就是设计/无害遗留代码"的判定标准), 但记录在此供后续如遇到类似"看起来像功能缺失但从未报错"的情况参考。

附带核实:§7.108 重复登录踢人路径,真实触发测试通过

标准清单里§7.108要求确认"踢出重复登录角色"路径不会导致新连线角色卡 死收不到任何指令。之前的"netdead 后重连"测试路径(get_passwd()里 user->query_temp("netdead")分支)本轮已在每个测试阶段间隔间接测试 多次(每次开新 socket 连接前一个已断线),但§7.108 真正描述的是 "仍在线时被顶号踢出"的confirm_relogin()分支,代码路径不同(走 exec(old_link,user) + destruct(old_link) + reconnect())。额外 专门测试:保持连线A在线(未断线),开新连线C用同一账号登录,确认收 到"您要将另一个连线中的相同人物赶出去,取而代之吗?"提示,回复y 后连线A收到"有人从别处连线取代你所控制的人物"并被正常踢出,新连线C 立即可执行look/score并有正常输出(未卡死),确认这份档案的 logind.lpc已经是修复后的正确实现,不需要改动。

标准清单复核结果汇总

本轮结论:门派/拜师、经济/商店、真实致死战斗/死亡/转生三大此前 "未覆盖"或"从未测试"的系统均已通过真实端到端测试,零崩溃、零 debug.log新增错误,未发现需要修复的代码缺陷。

AGENTS.md §7.19 fix: enable_player() reentrancy from init()

feature/command.lpc's enable_player() (wrapper around enable_commands()) was reachable from an NPC's init() (via the shared std/char.lpc setup() chain), and enable_commands() is only safe to call from create() -- calling it again on an object already living() makes the driver re-invoke that same object's init() as a side effect, which recursed back into enable_player() on the same call stack until "Too deep recursion" aborted the boot on a room's first-ever visit. Fixed with a true reentrancy flag (in_enable_player_now, set at entry, cleared before every return), NOT a living()-gated guard -- disable_player() in the same file legitimately re-calls enable_commands() while already living() (sleep/wakeup via cmds/std/sleep.lpc, revive via feature/damage.lpc), which a living() guard would silently break. Verified via lpcc --batch single-file compile check (PASS). Part of the corpus-wide §7.19 sweep (Batch C).

深度功能测试(§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 (ported from xiyouji)

xiyouji450 is xiyouji's sibling (4946 of 4987 files the same text). The fixes of the root were adopted file by file (scripts/lpc_twin_port.py adopt xiyouji xiyouji450: 287 reformatted twins, 2 spliced, 23 raw copies), then the same sequence ran here (scripts/lpc_family_pass.py: redundant inherits, void overrides, macro redefinitions, prototype types, forward declarations, here-document terminators, clean_up() name winners after a rescan) and the second round of the root's warning fixes (scripts/lpc_fix_no_effect.py, unused locals with getter initializers, d/kaifeng/ground.h's tables defined once, see the root's NOTES). 645 diagnostics (the 2026-10-04 census) -> 0 warnings and 18 error rows in 4 files. 338 files changed.

The four defects the root's NOTES describe are present here too and were repaired the same way: d/gao/obj/pen.lpc (([ "/u/bula/gao/obj/kaoji" ]) -> : 1), d/obj/books-nonskill/hmeng014.lpc (the closing " of string name), data/armor.lpc (ob->set_alias(cmd, "); -> 0, as do_opencommand() in the same file), and obj/file.lpc (416 bytes of binary noise, referenced nowhere) renamed to obj/file.orphaned-junk.

Load check of all 4987 .lpc objects (one VM per 700, HEAD against the working tree): HEAD 4947 PASS, tree 4955 PASS, 0 that loaded before fail now, 8 more load (d/gao/obj/pen, d/obj/books-nonskill/hmeng014, data/armor among them).

What is left (4 files, 18 error rows, the same as the root). cmds/imm/cast1.lpc (cut in the middle of a string), d/moon/obj/poem.lpc (a garbled GBK line), d/obj/fabao/EXAMPLE.lpc (a template with placeholders), d/qujing/jingjiling/jingji6.lpc (truncated; the sibling's copy is a reformatted version).

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

房间基类 std/room.lpc:make_inventory() 用 catch() 包住 new(file),做不出来就返回 0;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。

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() 了本库没有的武功)。

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)

武功名拼错的 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)

武功基类缺少 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 文件。

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

sefun cat():缺文件时 write(0) 报错(2026-10-20,KB 06 §7.297)

void cat(string file) { write(read_file(file)); }:文件不存在(压缩包没带的欢迎/公告文件,或只有运行中的 mud 才会写的日志)时 read_file() 回 0,write(0) 在本驱动里报 Bad argument 1 to receive(),调用者(登录对话、cat 指令)被中断;老驱动什么也不打印。改成 write(read_file(file) || "");(scripts/lpc_fix_cat_unchecked.py)。改动:/adm/simul_efun/file.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])) ...,不检查列表是否为空。本库有 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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 7 条注册(4 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/changan/pinqiting.lpc(do_pass、do_resign)、/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_push 2 处、do_pass 1 处、do_resign 1 处、do_vendor_list 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 处函数体被作者整段注释掉(指针没跟着删),是有意关掉的,没动。

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

这个库的 7 个文件里有 11 处 write_file("/log/nosave/...") / log_file("nosave/...")(static 改 nosave 时连字符串里的日志路径一起改了,原来是 log/static/):nosave/CRASHES、nosave/PURGE、nosave/promotion 等。压缩包和站点包里没有这个目录,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 的新进程):补目录前报上面的错误,补后写成功。

(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(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 加载失败;改后加载成功。

悬空 ::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 同 xiyouji 截断;用 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… 行。