Journey to the West 450

✅ 可玩

西游记450

xiyouji450

🔑 fluffos / Mud@2026 更新 85d6a35 2026-09-02 源码 下载 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之最终幻想.rarlibs/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' staticnosave. 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.lpcnet/ftpd.hnet/ftpdconf.hnet/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>=2strlen>=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 choicebanner + 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 characterChinese namepasswordconfirm passwordemailgender (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 gatesadm/daemons/band.lpc: added is_local_ip(string ip) helper (127.*, empty/non-string, or non-dotted-quad => local) and short-circuited is_banned(), create_char_banned(), is_strict_banned() to return 0 for local IPs. Shipped ban lists 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 (gbno → 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"一节把这次中止归因为"冷启动时并 发编译负载",判定为环境偶发、未做任何修复。刚测试完同一血缘家族的 xyj2000fconfig.fluffosmaximum evaluation cost 同样被设成 400000,比这个项目常见的 700000 模板默认值还低)才发现:这不是并 发负载导致的偶发,而是每一次冷启动后的第一次注册都 100% 会撞上 ——make_body() 第一次编译整条 /std/char 继承链的开销本来就稳定超 过 400000 usec 预算,与是否有其它驱动同时在跑毫无关系。此前那次记录 之所以看起来像是"重试一次就好了",只是因为失败的那次编译已经把继 承链的编译结果缓存进了驱动进程,同一进程内后续的注册自然不会再触发 冷编译——如果当时重启驱动再试一次全新注册,会看到同样的中止 100% 复现。这次深度测试在动手注册前就先主动对齐xyj2000f 那次找 到的修复:把 maximum evaluation cost400000 提到 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.fluffosmaximum 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被设为taoistdebug.log全程零新增错误。

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

d/city/npc/xiaowang.lpc(小酒馆/d/city/jiuguaninherit 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.lpcob->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.lpcdie()函数里有一行 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).