Rhapsody Realm

✅ 可玩

狂想空间

kxkj

🔑 fluffos / Mud@2026 更新 b8fb664 2026-09-01 源码 下载 ZIP

▶ 开始游玩 · Play Now

游戏配置和进游戏后的横幅上写的都是《狂想空间》,压缩包文件名里的"新狂想空间II"其实是收藏站点自己加的标题,并非游戏本名。这是一套 1995 年起源自"ES II"(Eastern Stories II)架构的老牌 Mud 引擎,2002 年由台湾巫师 wade 发布,与本项目里的 es1_win、esI、xkx2001、辽宁热线站等多个库同属 ES II 大家族。背景设定是一个"古代中国"的江湖世界,主打门派、帮派制度和轻功、内功养成;新手报到的"狂想空间新手入门房间"里直接给出新手介绍、指令快速上手、帮派说明、小秘技等导览,非常照顾第一次玩这类 Mud 的玩家。创角时要在体格、定性、悟性、胆识、根骨、灵性、容貌七项属性间分配天赋点数,另有一项"根骨/福气"按剩余点数自动计算。

English

Both the game configuration and the in-game banner call it "Rhapsody Realm" — the archive filename's "New Rhapsody Realm II" is a title the collecting site added, not the game's real name. It's set in an "ancient China" jianghu built around sects, gangs, and the cultivation of qinggong (lightness skill) and neigong (internal power); the newcomer's start room lays out a full orientation menu (introduction, quick command reference, gang/sect overview, tips) aimed squarely at first-time MUD players. Character creation distributes points across seven stats — physique, willpower, comprehension, courage, bone structure, spirituality, and appearance — with an eighth, "bone structure/fortune," auto-computed from whatever points remain. It's a venerable mudlib engine dating to 1995 ("ES II" / Eastern Stories II architecture), released in 2002 by Taiwanese wizard wade, and belongs to the same large ES II family as this project's es1_win, esI, xkx2001, and Liaoning Hotline Station archives, among others.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/kxkj/

管理员账号 / Admin account

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

本地运行

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

游戏端口:40047

NOTES · 移植与修复记录

kxkj — 狂想空间 (Fantasy Space) II

Archive: 新狂想空间II.rar ("New Fantasy Space II", #53). Port: 40047. Status: done (boots clean, full registration flow verified end-to-end including a real Chinese name entering the game world).

What this is

The archive's own top-level readme (狂相空间II.txt) identifies it as "狂想空间II" ("Fantasy Space II"), a Taiwanese source drop dated October 2002 ("这套源码来自台湾!2002年10月份的狂想空间II"), crediting the Taiwan wizard wade for the release. The archive's *actual* self-identified name in config.fs (name : 狂想空间, no "II"/"新") and the live in-game banner both say plain "狂想空间" — noted here since it differs slightly from the archive filename, per the task's convention (no separate slug rename needed; kxkj is kept as the working slug since it was pre-assigned).

A second file, 小熊泥苑.txt, is a collector-site readme (from the archiving site "小熊泥苑", http://dtxy.126.com) explaining that this particular archive is a BIG5→GB-converted copy of the original Taiwan "狂想空间" source, not a separate game — consistent with the wade/2002 Taiwan credit above. In-game credit line: 以 ESII 为架构改编,初次架于 1995 9 22,感谢 annihilator & elon & mineral — confirms this is genuinely built on the "ES II" (Eastern Stories II) mudlib base, the same lineage already seen in es1_win(#12)/esI(#13)/xkx2001 (#25)/rzrmud(#23)/bmxkx2001(#45) — master.c's header comment says exactly // for ES II mudlib / original from Lil / rewritten by Annihilator (11/07/94). adm/obj/{master,simul_efun} + adm/simul_efun/*.c fragment layout (not adm/single/), matching that family. Admin accounts per the readme/wizlist: bear/wade (manager), banyan (arch).

Raw archive: 14,440 files under raw/fsII/ (the mudlib root — no nesting, config.fs sits directly at top level), 12,089 of them renamed .c.lpc. A prebuilt Windows MudOS.exe binary and its bin/ support scripts are bundled alongside the source (ignored, per AGENTS.md — we use our own driver build).

Fixes applied

1. AGENTS.md §15h, standard shape, adm/simul_efun/chinese.lpc: is_chinese()'s GBK lead-byte range check (strlen(str)>=2 && str[0]>160 && str[0]<255) rewritten to a CJK Unicode codepoint check (strlen(str)>=1 && str[0]>=0x4e00 && str[0]<=0x9fff). 2. AGENTS.md §15h, check_legal_name() in adm/daemons/logind.lpc: byte-count bound < 2 || > 12 → character-count bound < 1 || > 6 (message text already said "必须是 1 到 6 个中文字", confirming the halved bound, not a guess); removed the i%2==0 && even-byte-offset gate in the per-character is_chinese() sliding check so every character position is checked (previously only every other character was checked under UTF-8 semantics). 3. AGENTS.md §15p: /adm/daemons/network/dns_master was present in adm/etc/preload — removed proactively before the first boot attempt, per standing policy. 4. NEW variant of AGENTS.md §8d/§15o (absolute-path angle-bracket #include), found here — this is the most impactful fix in this pass. 359 files across the lib write #include < immediately followed by an already-absolute path (leading /), e.g. std/armor/cloth.lpc's #include </open/open.h> and ~350 zone files' #include </open/open.h>/#include </open/badman/badman.h> etc. Read the driver source (compiler/internal/lexer_utils.cc's inc_open()) to confirm the root cause: angle-bracket resolution unconditionally joins each configured include directories entry with the given name (path + "/" + name), with no special-case for a name that's already an absolute path — so <...>-style absolute includes can never resolve, regardless of get_include_path() or preload timing (unlike the quoted "file" form, whose merge() helper explicitly detects and handles a leading / as "absolute from mudlib root"). Fix applied: added master::get_include_path() (the standard §8d/§15o shape, prepending the compiling file's own directory plus :DEFAULT:) AND, since get_include_path() is only consulted for VM-context (mid-connection) compiles per §15o's documented caveat — confirmed here too, lpcc alone still failed on this file even after adding the apply — blanket-converted every #include <ABSOLUTE_PATH> (grep -rlZ '#include *<\s*/[^>]*>' | xargs sed -E 's#include *<[[:space:]]*(/[^>]*)>#include "\1"#') to the quoted form, which resolves absolute paths unconditionally via merge() with no VM-context dependency. 359 files fixed in one shot, 0 remaining. This was NOT just cosmetic: /std/armor/cloth.lpc is the starting-clothes item every new character equips at the end of registration, and (more importantly) /open/newhand/newhand.lpc (STARTROOM) transitively depends on this same pattern — before this fix, every brand-new character's press_enter() silently fell through to VOID_OB (catch(load_object(STARTROOM)) failing twice, per the existing degrade-to-void fallback in logind.lpc), landing the player in /obj/void.lpc's "精灵圣域/天 外天" placeholder room instead of the real newbie room, AND triggering an unrelated-looking infinite error-spam loop (next item). 5. Discovered via the interactive test (not proactive), root-caused to item 4 above, no separate code fix needed: with the character landing in VOID_OB (whose path top-level dir is obj, not open), adm/daemons/check_w_r_y.lpc's anti-cheat "did a player wander outside /open" heart_beat check fired every second, calling CBIP_D->record(...) to log it to a hardcoded path (/u/b/bss/record/no_in_open — the original site's bss wizard's home directory, which doesn't exist in this archive: only /u/bear and /u/w exist under /u). write_file() failed (mkdir() can't create /u/b/bss/record in one shot since its parent /u/b/bss doesn't exist either), throwing a runtime error that aborted check_w_r_y.lpc's main() before it reached the line that would have set its "already recorded, don't retry" flag — so the failure retried, and re-failed, every single heartbeat tick forever, flooding debug.log with *Wrong permissions for opening file ... for append. once per second. Once item 4's fix let the character land in the real /open/newhand/newhand room instead, this check's dir!="open" condition is false and it never fires at all — confirmed by re-running the full registration test after the fix and finding zero occurrences of this error in debug.log. Left unfixed (no code change needed): this is a real environment/content gap (bss's missing home directory) that simply never gets exercised once characters correctly land inside /open; fabricating a /u/b/bss/record directory tree for a wizard account that isn't part of this project's scope isn't warranted.

Fixes applied — found via the lpcc_check.sh sweep (post-boot-test pass)

The interactive boot/registration test alone doesn't exercise most of the game-content tree (rooms/items/NPCs never touched by the registration path). Running the full sweep afterwards, per AGENTS.md's pipeline, surfaced these additional genuine bugs (triaged by grouping the 624 initial failures by error-message shape before fixing anything, per §6b):

6. NEW bug shape, not yet in the catalog: #include of a file that itself declares a global variable, positioned textually BEFORE inherit — "Illegal to inherit after defining global variables." (74 initial failures). The compiler enforces inherit must precede any global-variable declaration in the ASSEMBLED token stream, which includes anything spliced in by an earlier #include, not just literal declarations in the file itself. Two shapes found: - 67 files, /open/gblade/room/n_area{0..66}.lpc: each does #include "n_area.h" (declares string *n_area_msg = ({...});) THEN inherit ROOM; — one shared header, one shared bug, fixed in one shot by swapping the two lines' order in all 67 files (grep confirmed all 67 share byte-identical first-3-line structure first, then a scripted swap, then re-verified all 67 via lpcc/spot-check). - 5 standalone files with a bare object user; (or int mean;) declared directly before their own inherit: open/main/obj/super-cloth.lpc, open/japan/obj/super-cloth.lpc (both copies of the same "雀皇飞云裳" item), open/mogi/village/ obj/mcc.lpc, open/beggar/skill/pdarray.lpc, and u/bear/start.lpc (whose actual culprit was #include "/include/basic_skill.lpc", which declares mapping basic_skill = (...), sitting before its inherit ROOM; — same root shape as the header case above, just a different header). Fixed by reordering each file's own declaration/include to come after its inherit line. All 6 file groups (74 files total) re-verified clean via lpcc afterward. - Caught and self-corrected a real mistake while fixing this: my first attempt at the 67-file bulk fix used a sed one-liner that (due to a copy/paste test fragment sed -i '2,3{2!d}' left in a loop meant only as a dry-run check) actually deleted the inherit ROOM; line from all 67 files instead of moving it. Caught immediately by re-checking file contents before trusting the result, restored by re-inserting inherit ROOM; right after the #include line, then re-did the fix correctly (moving inherit to BEFORE the #include, since the include's spliced content — not just its own line position — is what has to follow inherit). Verified via a systematic post-check (grep -c "^inherit ROOM;$" == 1 in every one of the 67 files) before moving on. 7. NEW gap, ..-relative #include (driver disallows .. in include paths entirely, both quoted and angle-bracket forms, per docs/lpc/preprocessor/include.md and confirmed in compiler/internal/lexer_utils.cc's legal_path() check): - 27 files under /open/start/{npc,room,obj}/*.lpc used #include <../start.h> to reach /open/start/start.h (a real, existing file one directory up) — fixed by rewriting to the absolute quoted form #include "/open/start/start.h" (safe/exact, no content change, start.h itself was never missing). - 27 files under /open/cold/*.lpc used #include "../cold.h" to reach a cold.h that (unlike start.h above) does not exist anywhere in the raw archive at all — a genuine content gap, not a path-syntax issue alone. Reconstructed a minimal cold.h defining only the two macros actually referenced anywhere in this zone's own files (COLD_ROOM, COLD_NPC — grepped for \bCOLD[A-Z_]*\b across open/cold/*.lpc first to confirm COLD_OBJ is never used and no obj/ subdirectory exists there either, so it was deliberately not guessed/added), using the exact same path-prefix macro style as the sibling zones already defined in open/open.h (SNOW/SNOW_ROOM/SNOW_NPC/SNOW_OBJ etc.) — this is the same kind of "reconstruct one small, obviously- patterned missing macro header" precedent as xyzx3's WQA_ROOM fix (AGENTS.md §8g variant), not fabricating game content, since none of the actual room text/logic needed guessing. All 54 files (27+27) re-verified clean via lpcc afterward.

Net effect on the sweep: 130 additional files fixed beyond the two proactive §15h/§15p fixes (74 inherit-ordering + 54 ..-include + 2 header/config side effects), taking the pass rate from the initial sweep's 94.8% (11,465/12,089) up to the final number below.

Confirmed NOT needed (checked by reading the source, not assumed)

Known lpcc-sweep failures NOT fixed (pre-existing content bugs/gaps, not driver-compat)

Triaged the remaining failure categories after the fixes above; none of these are on the registration/boot path (confirmed via the clean interactive test) and all are pre-existing authoring issues in the original archive, not artifacts of this conversion pass:

Config notes

config.fluffos adapted from the lib's own config.fs (converted to UTF-8 before any other edit, per AGENTS.md §5). Dropped three driver-obsolete keys the boot log flagged (reserved size, binary directory, swap file — all warned "obsolete line in config file, please delete" on the first boot attempt). maximum local variables (50) and living hash table size (100, in the "not currently used" section) both trip a harmless "invalid new value, resetting to default" warning — cosmetic only, left as-is (matches the archive's original values, not something we need to correct). Created work/adm/tmp/ (referenced by the original swap file directive, long removed from the config but the dir doesn't hurt to have) and the sibling log/ directory (driver's log directory resolves relative to CWD, not mudlib root, per AGENTS.md §6).

Registration-flow transcript (verified end-to-end)

Read adm/daemons/logind.lpc's full callback chain before scripting the test (logon → get_id → confirm_id → get_name → new_password → confirm_password → get_email → get_gender → get_points ×7 → enter_world → press_enter) — no hidden BIG5/client-version pre-id gate in this lib (the very first prompt is the real English-id prompt). Point allocation is 8 attributes/160 points average 20 each, but only 7 of them (str,cps,int,cor,con,spi,per) are prompted individually — the 8th (kar) is auto-computed as the remainder the moment the per prompt is answered, going straight to enter_world() with no further prompt.

Ran via python3 scripts/mudclient.py 127.0.0.1 40047 --timeout 45 --idle 1 in one continuous connection, --send sequence: english id → y (confirm new char) → real Chinese name "秦风二" → password → password again → email → m (gender) → 20×7 (attribute points) → ` (press enter) → lookquit`.

Outcome: id accepted, confirmation accepted, "秦风二" (a genuine 2-character Chinese name) accepted by the fixed is_chinese/ check_legal_name, password/email/gender/all 7 attribute prompts all proceeded correctly, character creation completed, and the character was dropped into the real starting room ("狂想空间新手入门房间" — "Fantasy Space Newbie Room", under /open/newhand/newhand), with the room's full Chinese description, exits, a same-session news bulletin, and birthday-reminder message all rendering correctly. look re-displayed the same room correctly. quit produced a clean goodbye ASCII-art banner and disconnect message ("你离开狂想空间了。" — "You have left Fantasy Space."). Zero crashes, zero permission-denied errors, zero debug.log runtime errors for the whole session (only harmless "Unused local variable"/"Illegal to declare nosave function" compiler warnings, which this mudlib's error_handler happens to echo to the connected client live — pre-existing/cosmetic behavior, not something introduced by this pass, not fixed).

(An earlier attempt reusing the already-registered id qinfeng from a first, pre-fix test run correctly hit the "already registered, enter password" branch instead of character creation — expected, not a bug; and a follow-up attempt with a fresh id within the same 10-second window correctly hit logind.lpc's per-IP anti-flood throttle ("请勿连续尝试,请您稍后再连线。", same shape as AGENTS.md §15j) — also expected, resolved by waiting past the in-memory 10s window, not a restart.)

Re-verified again after ALL fixes above (including the sweep-driven inherit-order/..-include fixes) with a fresh id/name (qinfengc / "秦风三"), on a freshly-rebooted driver: identical clean outcome, same real starting room, same clean quit, and this time zero debug.log output at all beyond the harmless compiler-warning echoes (confirming the check_w_r_y.lpc/cbipd.lpc error-spam from item 5 above is genuinely gone now that the character lands in /open/... instead of VOID_OB).

lpcc sweep

scripts/lpcc_check.sh libs/kxkj/config.fluffos libs/kxkj/work, run twice:

1. First pass (before the sweep-discovered fixes above): total=12089 pass=11465 fail=624 (94.8%). 2. Final pass (after fixing the 74-file inherit-ordering bug and the 54-file ..-include bug, both described above): total=12089 pass=11595 fail=494 (95.9%). The remaining 494 failures were re-triaged by error-message shape and confirmed to be exactly the "known, not fixed" categories documented above (decorative-item +-concatenation typos, 1-2 deliberately-disabled NPCs, and orphaned/personal-wizard/dead-network-utility content) — zero new or unexplained failure categories introduced by any fix.

Ran concurrently with other agents' sweeps on this host both times; memory stayed healthy throughout (available — the metric that matters, not the free column, which dipped much lower due to normal page-cache usage — stayed in the 13-19GB range out of 23GB total for essentially the whole run, spot-checked repeatedly via free -h).

Re-verification pass: driver rebuild + formatter + WASM (2026-07-23)

WASM-enablement pass (2026-07, loopback/uptime/throttle + admin seed)

Standard WASM-first pass per AGENTS.md §1.3(b)/(e) and §1.5. Loopback = 127.0.0.1, any 127. prefix, or an empty/non-string/malformed IP (covers older WASM query_ip_number() garbage). Gates patched:

Admin seed: registered fluffos / display 浮浮 / password Mud@2026 through the real flow (id → y → Chinese name → password x2 → email → gender m → seven "20" talent allocations → empty send → world). Granted (manager) — the TOP rank of this lib's wiz_levels (above (admin)) — by appending fluffos (manager) to /adm/etc/wizlist. Verified: relogin as fluffos → update /adm/daemons/banmultid → "重新编译 ...成功!".

Lineage quirk worth knowing: the relogin path checks the LOGIN object's save (/data/login/<c>/<id>.o), but registration/quit never writes it (ob->save() is commented out in enter_world, and cmds/std/quit.lpc destructs the body without saving the link object). The login save is only written by the NET-DEAD path (obj/user.lpc::net_dead()link_ob->save()). So to make a fresh account relogin-able, end its first session by dropping the connection (client timeout), NOT by quit — that is how data/login/f/fluffos.o was produced here. (Pre-existing behavior, documented rather than changed.)

Retest: fresh normal registration (qfkxkj / 秦风) re-verified end-to-end into the newbie welcome area with look/score/quit correct; test saves removed. No new errors in log/debug.log.

Save files for the orchestrator to add (both paths tracked, not gitignored; data/user/f/ and data/login/f/ are NEW directories):

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

狂想空间基础版(更晚的快照)。

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

深度功能测试 / Deep functional test (AGENTS.md §10.7)

先读 doc/help/newbies/newbie/newbie2:本档案是台湾巫师 wade 于 2002 年发布 的 ES II 架构老牌 MUD,与本项目的 es1_win/esI/xkx2001/kxkj1(同游戏 不同快照,见 AGENTS.md §11)同属一个大家族。新手流程:设 8 项天赋(膂力/ 定性/悟性/胆识/根骨/灵性/容貌/福缘,共 160 点自由分配)→ 进入 狂想空间新手入门房间enter 正式进入 → give cloth to girl 帮助新 手房里的小女孩 → down 两次进入中央驿站/大马路一带。

修复的程序性 bug

无。全程 debug.log 干净(仅编译期无害警告),未发现 §7.11/§8.9/§7.34 等已 知类别的确认实例。

发现但判定为既有设计、未改动的重要现象(务必记录,供后续同宗 ES II 库测试参考)

fight 指令对"不会说话"的生物(动物)走的是真实杀伤分支,可能导致角色 真的死亡——现场以 (manager) 账号验证:在 d/snow/eroad2 对野狗 (d/snow/npc/dog.lpc)使用 fight dog,系统提示"看起来野狗想杀死你!", 数回合后角色"气血"归负、陷入半昏迷、最终"你死了",被送往 阴曹入口(真 实死亡,非"昏迷后自动恢复")。追查 cmds/std/fight.lpc 源码:

if (obj->query("can_speak")) {
  me->fight_ob(obj);
  obj->fight_ob(me);          // 双方都是"点到为止",安全
} else {
  me->fight_ob(obj);
  obj->kill_ob(me);           // 对方是真杀,玩家一方仍是 fight_ob 但对方不是
}

can_speak 在全部 d/ 目录下没有任何 NPC 文件显式 set() 过(只在 adm/daemons/race/human.lpc 等种族档案里设为 1),换言之只有走过种族初始 化流程的人形角色(玩家、以及归属人类种族的 NPC)才会落入安全的 fight_ob()/fight_ob() 双向分支;纯野生动物(dog.lpc/crazy_dog.lpc 等,未经种族初始化)会让对方调用 kill_ob(),对玩家是真实杀伤。现场用 fight trainee淳风武馆 的"武馆弟子",人形 NPC,无自定义 accept_fight(),走默认 NPC::accept_fight())复测:对话式的挑衅讯息 ("你对着武馆弟子说道:...领教...的高招!",与动物分支的"你大喝一声,开 始对XX发动攻击!"用词不同),全程零受伤,武馆弟子力竭后"跌在地上一动也 不动了"(昏迷,随后自行"清醒了过来"),确认这才是"点到为止"分支。

判定为既有设计,不是 bugfight.lpc 自己的 help 文本("这种形式的 战斗纯粹是点到为止...不会真的受伤")和 newbie 文档的"就算你昏了也没关 系"都是针对"打人"(人形对手)语境说的,武侠类 MUD 里"野兽不懂收力、人可 以点到为止"是常见且合理的题材设定,并非程序逻辑缺陷——fight/kill_ob 的分流本身工作正确,只是文档的措辞没有明确排除动物这一类目标,容易让新 测试者(或新手玩家)误以为对任何生物用 fight 都绝对安全。未做任何代码 改动。给未来同宗 ES II 库测试的提醒:挑选"安全陪练"测试对象时,务必 先确认目标是人形 NPC(或亲自读一遍其 accept_fight()),不要直接假设名 字看起来温和的生物("野狗"这类)就安全;已检查 kxkj1(本库唯一已完成 §10.7 的同游戏姊妹库)现有 NOTES.md,未记录同一现象,值得下次碰到时留意。

测试覆盖

进程卫生附注

清理了两处与本次修复无关的测试残留:(a) 因现场验证死亡机制而写入 work/open/death/death_record(一个跨玩家共享的历史死亡记录文件)的一 行"fluffos浮浮被dog野狗所杀"条目,已用 git checkout HEAD -- 还原; (b) 一次 dump 指令尝试产生的诊断文件 work/OBJ_DUMP,已删除,未纳入 本次提交。

WASM 未验证说明

按本 session 约定:emsdk 固定从 storage.googleapis.com 拉取,被本 session 出站代理策略拒绝(curl -sS $HTTPS_PROXY/__agentproxy/status 返回 403),WASM 编译/运行验证本轮继续跳过,仅做原生驱动(linux-debug 预设,ASAN/UBSAN)下的完整 §10.7 测试。

深度功能测试第二轮 / Deep functional test round two (2026-08-14)

第一轮(上一节)确认零程序性 bug;本轮独立复核第一轮结论,并新增修复。

新发现并修复的 bug

``lpc // 修复前 if ((obj = present(file, environment(me))) && interactive(obj)) // 修复后 if (environment(me) && (obj = present(file, environment(me))) && interactive(obj)) ` 已跨库扫描确认同一路径变体还命中 kxkj1kxkjii2njhhdxdes2hxxbtxiii(含本库共 5 库),随本库一并提交、已推送 (独立 commit 413ad703415`)。AGENTS.md §7.106 已补充"第 4 种路径 变体"小节记录这批库。

复核第一轮已有结论(独立验证,非盲信原记录)

现场验证

驱动干净启动(PID 1630062,linux-debug/ASAN 预设),以管理员账号 fluffos/Mud@2026 登录,确认 目前权限 -> 【 天 帝 】(manager) (角色所在地为第一轮死亡测试遗留的真实存档位置"阴曹入口",非 bug)。 用 update /adm/daemons/logind 验证真实写入权限(重编译成功,确认 file.lpc/update.lpc 两处修复编译干净);update /cmds/apr/update 自我更新命令本身无可见输出(命令对象在执行中自毁重载的正常边界情 况,非 bug,debug.log 确认编译无错误)。debug.log 由 570 行增至 673 行,逐行核对零已知错误特征,仅编译期无害警告。两次快速重连(各 自独立 fluffos/Mud@2026 登录)均正确显示 目前权限 -> 【 天 帝 】(manager)quit 产生正常告别横幅与断线 提示。

本轮修改的文件

深度功能测试第三轮 / Deep functional test round three (2026-08-18)

第一、二轮已确认注册/新手引导/fight 安全分流/正规拜师/quit-净断线重连均 正常。本轮先读完第一、二轮记录,确认无重复,转向前两轮未覆盖的系统:经 济(商店买卖)、布告栏(真实发帖/读取)、帮派(cmds/clan/ 整套子系 统)、以及死亡后的完整"奈何桥→鬼门关→酆都城门→黑白无常"复活流程(前两 轮只读过 /open/death/start.lpc 源码,从未真正走完全程)。另确认本库 adm/obj/master.lpc::standard_trace()sprintf("%O", error["object"]) 格式化,并未调用 file_name(),AGENTS.md §7.111(file_name(error["object"]) 未判空导致的崩溃)不适用本库,无需修补。

修复的程序性 bug

无。全程 debug.log 完全干净(未生成该文件,即零运行时错误),只有编译 期无害警告。

新覆盖并确认工作正常的系统

排查但判定非 bug(读代码确认,未改动)

现场验证摘要

驱动 linux-debug(ASAN/UBSAN)预设,两次全新冷启动分别验证:(1) 管理 员账号 fluffos/Mud@2026((manager) 权限)用于经济/布告栏/帮派/死亡 复活流程测试;(2) 全新注册角色 kxkjrthb / 中文名"秦风卅一"(英文 id 只能纯小写字母,无数字——首次因误用带数字 id 被反复拒绝后遭防灌水机制 断线,重新用合规 id 后一次成功注册)验证新手引导(entergive cloth to girldown)流程与第一轮结论一致,down 第二次进入的 是暗室("四周到处黑沉沉的, 看也看不清。",需要光源,属正常暗房设计非 bug)。两条会话全程 log/debug.log 均未生成(零运行时错误)。

进程卫生附注

测试产生的存档churn(data/board/start_b.o 因真实发帖测试、 data/user/f/fluffos.o 因管理员多次登入/移动/复活状态变化)已用 git checkout -- 还原;测试用的全新角色存档 (data/user/k/kxkjrthb.o,未跟踪)与临时创建的 u/f/(供 eval 指 令使用的巫师工作目录,未跟踪)均已删除,未纳入本次提交。驱动进程按 PID 精确 kill(非 pkill 模式匹配)。

§7.100 跨库扫描修复(ROOM 基类同款 replace_program() 致命形状)

``§7.112`` residual-gap closure (2026-08-20)

Corpus re-scan (grep -rl 'call_out("death_stage"' ... | filter for missing guard) found unguarded init()-scheduled death_stage() call_out chain(s) in open/death/npc/bgargoyle.lpc, open/death/npc/wgargoyle.lpc that the original two-wave sweep (see AGENTS.md §7.112) missed -- same reconnect-triggered duplicate-chain bug, different filename/lineage. Added the standard query_temp("death_stage_active")/set_temp/delete_temp re-entry guard, adapted per file's own exit points. Compile-verified via lpcc --batch.

§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 four (2026-08-21) —

c_create/c_join 缺口补测

第三轮把 c_create/c_join 判定为"合理的既有设计门槛...未做门槛测试"。本轮 专门用两个全新的普通玩家账号(非巫师)把这条路径真正走通一次,同时顺带发 现并修复了三个独立的真实程序性 bug(其中一个影响面很大,波及全库任何未在 原始码里显式写 .lpc 后缀的出口)。

修复的程序性 bug

1. cmds/std/go.lpc(严重,影响全库所有出口移动):第 42 行判断"目标 路径是否已经有 .lpc 后缀"时,切片长度写错——exit[arg][sizeof(...)-2.. sizeof(...)-1] 只取最后 2 个字元,却拿去和 4 字元的 ".lpc" 比较, 永远不相等。后果不是"多余地""重复追加一次"这么简单:由于 exit = env->query("exits") 传回的是房间物件内那个 mapping 的引用exit[arg] += ".lpc" 会真的原地修改房间的 exits mapping、且每次 都会追加,不管上次追加过没有。凡是源码里 exits mapping 值没有显式写 .lpc 结尾的出口(很常见的写法,例如 newhand.lpc"enter": "/open/common/room/inn"),第一次有人走这个方向时"侥幸"补上 一次 .lpc 能正常到达;但只要再有第二个人(甚至同一人重连后再走一次) 尝试同一个出口,就会变成 ...inn.lpc.lpc...inn.lpc.lpc.lpc…… file_size() 找不到这个不存在的路径,玩家会看到"这个方向云雾深锁, 无 法进入, 请通知 wiz!!"——且这个 mapping 是房间的常驻实例数据,一旦第二 次出现问题就会在这次开机剩余时间内对所有玩家永久失效,直到重开机。 现场用三个连续注册的全新角色复现:第一个角色(重连后)成功用 enter 从新手房抵达"狂想空间入口处";第二、三个角色的同一条 enter 立刻失败;用 eval((manager) 权限)直接读出该房间当时的 exits["enter"] 值确认为 "/open/common/room/inn.lpc.lpc.lpc.lpc"(4 次累加,与四次尝 试对应)。修复:把切片改成后 4 字元(sizeof(...)-4..sizeof(...)-1, 并加上 sizeof(...)<4 短字串防护),update 热更新后用 eval 手动把 受污染的活体 mapping 修回 "/open/common/room/inn.lpc",随后新角色的 enter 恢复正常且不再继续污染(lpcc --batch 单独编译通过,git status 复核无存档 churn)。 - 同一处切片错误的另外三个实例,一并修正(均为纯粹的 2 vs 4 字元 写错,非本次修复的核心,但顺手扫到,风险极低): - cmds/adm/update_dir.lpc(管理员批量重编译某目录的工具): if (files[i][0][len-2..len-1] != ".lpc") continue; 永远为真, 这个指令其实对任何目录都是彻底的空操作,从未真正编译过任何档 案。改成 4 字元切片 + 短字串防护。 - cmds/imm/ls.lpccmds/imm/lstree.lpc(巫师用的目录列表工 具):同样的比较错误纯属外观问题——.lpc 档案永远被归类 进"其他"颜色(洋红)而非应有的档案类型色,不影响任何功能。一并 改成 4 字元切片,保持三处修法一致。 - 全部 4 个文件用 lpcc --batch 单独编译验证通过(含既有的无害 unused-variable 警告),修复后重开一次全新驱动,log/debug.log 完全没有生成(零运行时错误)。 2. cmds/clan/c_create.lpcCLANV_D->create_clanv(cid)/adm/daemons/clanvd.lpc)这个函数根本不存在——只有一份从未合并 的巫师个人存档 clanvd.acky(不同的、不兼容的旧版帮派编号系统重写)里 才有同名函数。本驱动对 -> 呼叫不存在的函式是静默返回 0、不报错(现场 用 eval 验证过),所以 if (CLANV_D->create_clanv(cid)) ob->set("clan/name", cname); 这个条件永远为假,导致帮主自己的 clan/name 栏位从未真正被设过CLAN_D 自己的帮派登记表里的 name 字段不受影响,是分开设的,所以帮派本身显示正常,只有帮主个人 角色数据缺这个栏位)。这不只是"少显示一个名字":c_join.lpc 判断 "对方是否已经加入其他帮派"用的正是 ob->query("clan/name") (不是 clan/id),栏位缺失会让帮主的这项检查失真。修复:拿掉这个死 引用的判断式,ob->set("clan/name", cname) 改成无条件执行,与紧接着 的另外四个 clan/* 栏位写法一致。现场验证:修复前用 eval 直接呼叫 CLANV_D->create_clanv(...) 确认回传 0;修复后完整走一次 c_createthis_player()->query("clan") 五个栏位(id/name/rank/passwd/ title)全部正确,c_list 也正确显示新帮派名称与帮主。 3. adm/daemons/cland.lpc::clan_query():紧邻的 clan_set() (上面 8 行)对 undefinedp(clans[clan]) 有防护,clan_query() 却没 有,两者本应对称。现场真实触发过一次崩溃(测试过程中一度出现 /open/clan/<id>/ 目录先于 CLAN_D 数据登记存在的状态——细节见下方 c_create 小节——此时 c_list 会因为 clans[clan][what] 对不存在的 clan 键值做二次索引而崩溃:执行时段错误: *Value being indexed is zero. 程式: /adm/daemons/cland.lpc:165)。修复:比照 clan_set() 的写法补上 !undefinedp(clans[clan]) 防护,未命中时安全返回 0(与既 有"没有这个字段就是 0"的调用惯例一致)。lpcc --batch 编译通过, update 热更新后复测 c_list 恢复正常。

c_create/c_join 缺口的最终结论

kxjtesta/kxjtestb(两个全新注册的普通角色,密码从略)实测,过程中 读代码 + 现场复现,理清楚了第三轮没深入看的两层机制:

``lpc // 帮派wiz, 玩家总管才可以有帮派 by ACKY if (!user) return; if (wizardp(user)) if (user->query("id") != "acky" && user->query("id") != "bss" && user->query("id") != "cgy") user->delete("clan"); if (!CLAN_D->have_clan(user->query("clan/id"))) user->delete("clan"); ` 也就是说:**任何巫师账号每次登入/重连都会被强制清空自己的 clan/* 数据,只有三个写死的账号 id(acky/bss/cgy,本档案里的原始开发者/ 管理员 id,cgy 本人在既有存档里确实就是"恶魔城"帮的真实帮主)例外c_create 要求呼叫者自己先有帮派数据,正是把"能开新帮"的权限锁定在这 三个特定管理员账号上(一个刻意的、极窄的管理员专属功能,不是给普通巫师 用的),可以反复开帮但不受一般巫师权限变动影响。这与档案里 5 个真实帮 派全部是原始存档自带内容(而非任何一次现场 c_create 产生)完全吻合。 现场把一个刚用 promote(游戏内建管理指令,账号 fluffos/(manager) 执行 promote kxjtesta (admin))临时提升为 (admin) 的普通测试角色拿来 测试时,两次独立重连都验证到这条清空逻辑确实会把 kxjtestaclan/* 清空**(先用 eval 手动补上 clan/id 通过前置条件、验证 c_create 真的能跑通并生效,重连后再用 eval 复查,栏位又变回空)—— 这不是巧合触发一次,是每次重连都必然发生,与源码逻辑完全对应,确认属 于既有设计,未做任何改动(这也是为什么第 2 项 bug 修复時仍然只用 eval`——游戏内建的管理工具——去补足前置条件,而不是绕过或修改这条清 空逻辑本身:这是在满足一个真实的前置条件,不是在跳过一个安全检查)。

标准清单快速核对(本 session 新发现,确认本库现状)

进程卫生附注

AGENTS.md §7.19 sweep (2026-09-01): enable_player() reentrancy from init()

Same corpus-wide bug class as mhxy/wuhanzhan (AGENTS.md §7.19): this lib's feature/command.lpc enable_player() wrapper (around the raw enable_commands() efun) is reachable from an NPC's init() via a redundant create()-then-init()-calls-setup()/reset_me() chain -- confirmed via a body-aware static scan of every init() in this lib: 6 NPC files call setup()/reset_me() from init() (e.g. d/snow/npc/waiter.lpc and the open/gblade/npc/pker*.lpc family), after create() already made the object living(). Calling enable_commands() a second time on an already-living() object makes the driver re-invoke that object's own init() as a side effect, re-entering the same chain while the original call is still on the stack -- genuine reentrancy, crashing with "Too deep recursion" (most likely on an NPC's first-ever preload/compile).

feature/damage.lpc's revive() calls enable_player() again while the object is still living() (kept alive across the disable_player() interval by that lib's own internal re-enable_commands()). This confirms a bare if (living(this_object())) return; guard would be the WRONG fix -- used the same true reentrancy-flag fix as mhxy instead: a nosave private int in_enable_player_now; set for the duration of the wrapper's body, guarding only genuine same-call-stack reentrancy while leaving the legitimate revive re-enable unaffected. This lib's enable_player() (unlike most others in the sweep) already had one early return, if (!this_object()) return;, right at the top -- added in_enable_player_now = 0; before that return too (rewritten as a 2-line block) alongside the fall-through exit at the end, so both return paths clear the flag. Verified via a single-file lpcc --batch compile check (PASS) -- not individually live-boot-tested.