Rhapsody Realm

✅ 可玩

狂想空间

kxkj

🔑 fluffos / Mud@2026 更新 66a3bd8 2026-10-28 源码 下载 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) → look → quit`.

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发动攻击!"用词不同),全程零受伤,武馆弟子力竭后"跌在地上一动也 不动了"(昏迷,随后自行"清醒了过来"),确认这才是"点到为止"分支。

判定为既有设计,不是 bug:fight.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)) ` 已跨库扫描确认同一路径变体还命中 kxkj1、kxkjii2、 njhhdxdes2hx、xbtxiii(含本库共 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 后一次成功注册)验证新手引导(enter→ give cloth to girl→down)流程与第一轮结论一致,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.lpc、cmds/imm/lstree.lpc(巫师用的目录列表工 具):同样的比较错误纯属外观问题——.lpc 档案永远被归类 进"其他"颜色(洋红)而非应有的档案类型色,不影响任何功能。一并 改成 4 字元切片,保持三处修法一致。 - 全部 4 个文件用 lpcc --batch 单独编译验证通过(含既有的无害 unused-variable 警告),修复后重开一次全新驱动,log/debug.log 完全没有生成(零运行时错误)。 2. cmds/clan/c_create.lpc:CLANV_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_create, this_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) 的普通测试角色拿来 测试时,两次独立重连都验证到这条清空逻辑确实会把 kxjtesta 的 clan/* 清空**(先用 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.

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

scripts/lpc_fix_negative_ranges.py: 21 line(s) in 10 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-04)— <../x.h> includes resolve: 5 more files load

8 files include a header named relative to themselves in angle brackets (#include <../badman.h>); the driver refuses .. in a <...> name. adm/obj/master.lpc now answers include_file() with "./" + path for such a name (KB 04 §6.1). lpcc --batch loads 6 of the 9 files that carry such an include (HEAD: 1); open/start/room/bad{2,3,4}.lpc still fail, their ../badman.h is not in the archive. The lib's warnings pass has not been done (the master itself still declares nosave functions; stray Big5 backslashes in open/ files).

深度功能测试(§10.7,2026-10-04)— stray backslash before a closing quote

A Big5 character whose second byte is 0x5C was written with one extra backslash after it (the old driver read 5C 5C as one backslash and so completed the character) and the UTF-8 conversion kept it (KB 03 §4.4). Before a closing quote it escapes the quote, the string runs into the next line and the file does not compile. scripts/lpc_fix_big5_quote_backslash.py deletes it when the character before it encodes to cp950 with 0x5C as second byte and the quote ends an expression. Here: open/scholar/room/newplan/npc/guard8.lpc (set_name("…豹\", …)) and data/autoload/scholar/secret_book.lpc (the force and dodge skill names ending in 功\). lpcc --batch: guard8 now loads; the book compiles and fails only when loaded without a this_player().

深度功能测试(§10.7,2026-10-05)— here-document terminators

scripts/lpc_fix_heredoc_terminator.py --all --apply: 5 here-document block(s) repaired (3 with the terminator glued to the last text line, 2 with it indented), so End of file in text block no longer hides the room or object behind them: open/capital/wind-rain.org/obj/bird-blade.lpc, open/capital/obj/bird-blade.lpc, open/capital/room/king/kr4-5.lpc, open/prayer/room/westarea/t.lpc, open/prayer/room/uhen/16.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-04)— paths named in another case

The archive comes from a case-insensitive host: files are stored as BAGUA0.lpc, longjing.C, d/SHAOLIN/, the code names them in lower case (__DIR__ "bagua0", "tea/longjing", "/d/shaolin/..."), so the rooms, NPCs and items behind them could not be loaded (a .C file is not an .lpc file; KB 03 §4.5). scripts/lpc_case_paths.py --convert --apply kxkj: 14 file(s) renamed to lower case (.C -> .lpc, .H -> .h) and 26 path reference(s) in 22 file(s) rewritten to the new spelling; 0 skipped, 0 ambiguous. Load check of the 60 objects that were renamed, edited or name a renamed path (HEAD against the working tree, an object is the same under any spelling of its path): 46 PASS -> 60 PASS, 0 that loaded before fail now, 14 more load.

14 files moved (13 .lpc, 1 .C); mostly under open/main/room (13), open/killer/home (1). A rescan with scripts/lpc_warnings.py kxkj is still due: the renamed files are compiled for the first time.

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

scripts/lpc_resilient_room.py --apply: make_inventory() of std/room.lpc loads each inventory object as if (catch(ob = new(file)) || !objectp(ob)) return 0;. new() of a file that is not there returns 0 (the room just lacks that NPC), but new() of a file whose create() raises (a skill file for set_skill() this archive never had, a carry_object() item that is not there, a header that never shipped) raised inside the room and the room did not load at all. The wrong-case renames of this lib (the section above) make the NPCs behind them reachable for the first time, so a room that holds one with such a content gap would have gone from "NPC missing" to "room missing"; now it loads without that NPC, as it did before. The room base compiles and loads with the change (HEAD against the working tree). KB 03 §4.5.

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

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

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

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

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

scripts/lpc_warnings.py kxkj --fix 加 scripts/lpc_fix_no_effect.py --apply:共改动 1744 个 .lpc 和若干 .h。HEAD 与工作树分别加载(新进程):1654 -> 1654 通过,无回退;改过的头文件的 68 个包含者 68 -> 68 通过。 手工:

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

深度功能测试(§10.7,2026-10-07)— 群发信件没有收信人

obj/mailbox.lpc send <group> <file> <title>:循环里 mail["to", groups[i]]; 是什么也不做的表达式(LDMud 多值 mapping 的写法),每封信都没有 to 字段;改成 mail["to"] = groups[i];。新进程编译通过。

深度功能测试(§10.7,2026-10-08)— .lpc 后缀的定宽切片

.c 改名 .lpc 时只改了字符串没改切片宽度:f[<2..] == ".lpc" 拿两个字符比四个字符,永远不成立(!= 永远成立),按后缀筛文件、去后缀的代码因此从不运行或截成 x.l(KB 06 §7.238)。scripts/lpc_lpc_suffix_slice.py --apply 改了 1 处比较、0 处去后缀(如 adm/daemons/network/httpd.lpc);改动的文件 HEAD 与工作树分别加载(新进程),无回退。

深度功能测试(§10.7,2026-10-11):“无副作用表达式”警告复查(批 4)

深度功能测试(§10.7,2026-10-12):编译警告收尾

可编译文件中的编译警告 62 → 0,不加 pragma,逐条在源头修;可编译文件 11632 → 11635。先用 lpc_twin_port.py 把 kxkj1 同日的修正搬过来(18 个文件逐 token 相同),其余按本库代码手改。

发现的真 bug:

其余:

改动的 58 个对象 HEAD 与工作树各载入一次:HEAD PASS 54,tree PASS 57,regress 0,新载入 3(上面 3 个 NPC);open/gblade/npc/wen-fun 两边都在 create() 装备一把不存在的 /u/l/longhair/... 刀时失败,open/clan/new_heaven/room/room3 是贴进来的终端记录,本身不是代码,未动。lpc_audit_removed_locals.py 0 处待看。

独孤九剑招式表越界(2026-10-15,KB 06 §7.243)

daemon/skill/lonely-sword.lpc 的 query_action() 按 15 招写的取招表(action[random(9) + 6] 等),文件里只有 8 招:100 级时有解谜标记 200 次出招 160 次报 Array index out of bounds,没有标记 99 次。加 pick_action(),越界时取最后一招;实测 0 次。

存档读不出来:转码损坏(2026-10-15,KB 03 §4.4)

/data/board/guilty.o、/data/board/skillswy_b.o 在转码时坏了:原档里孤立的 GBK 前导字节被 iconv -c 连同后面的引号一起丢掉,restore_object() 报 Illegal mapping format,读档的对象(玩家、留言板、信件、表情)从空白开始。按 raw/ 原档重新解码(孤立字节丢掉、引号保留),驱动核对全部能读回(scripts/lpc_save_encoding.py --from-raw、scripts/lpc_save_restore_check.py)。

存档读不出来:BIG5/GBK 0x5C 字后的反斜杠(2026-10-15,KB 03 §4.4)

/data/board/bss.o、/data/board/dancer.o、/data/board/hide_b1.o、/data/board/sun.o、/data/bss_record/heart.o、/u/w/wade/chinese.o:第二字节为 0x5C 的字(功、许、么、俞、賊……)MudOS 存档时写成 \\,转成 GB 后字保留,多出的一个反斜杠转义了后面的引号(成功\", 字符串收不了尾),或反过来,MudOS 当作被 0x5C 转义的引号成了字符串的结尾(出\"俞" >)。restore_object() 报 Illegal mapping format,留言板等从空白开始。在出错处去掉或补上这一个反斜杠,直到能读回(scripts/lpc_save_encoding.py --from-raw),驱动核对全部能读回。

留言板 mblade_b 读不出来(2026-10-15,KB 03 §4.4)

data/board/mblade_b.o 原档里一篇留言的标题是 "师□,,少了右引号(GBK 的“□”是真字符),restore_object() 报错,整个板子空白。补上引号,驱动核对能读回。

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

1 个文件、3 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/snow/npc/obj/thin_sword、/d/snow/npc/obj/pink_cloth、/d/snow/npc/obj/shoe)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 0 个,改后 0 个(0 个原来载入不了),没有退步;其余 1 个另有原因(多为 set_skill() 了本库没有的武功)。

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

老版 MudOS 给每个 LPC 对象预先定义了 SAVE_EXTENSION(".o");FluffOS 的 ChangeLog(COMPAT_BUSTER 一条)把它删了,改用 __SAVE_EXTENSION__。本库没有自己 #define 这个宏,用到它的文件一律 Undefined variable 'SAVE_EXTENSION',整个对象载入不了。改成 __SAVE_EXTENSION__(scripts/lpc_fix_predefined_macro.py):adm/daemons/network/netmail.lpc、obj/token.lpc。token 是帮派领袖信物;netmail 是网络邮件守护进程(本库缺 mailer.h 等头文件,改后仍载入不了)。改后用 lpcc 逐个载入这些对象:1 个通过,1 个还有别的错误(adm/daemons/network/netmail:/adm/daemons/network/netmail.lpc:17: error: Cannot #include mailer.h)。

move() 的目标房间文件不存在时不再抛错(2026-10-18,KB 06 §7.96)

feature/move.lpc 的 move() 把 err = catch(dest = load_object(dest)) 当存在检查,可目标文件不存在时 load_object() 返回 0、catch 不触发,接着 dest->receive_object() 抛出 Bad argument 1 to EFUN call_other()。留言板 std/bboard.lpc 的 setup() 把自己 move() 到 location 房间,那是巫师的私人房间时(档案里没有这个文件),留言板连同刷出它的房间一起载入不了。catch 之后补上 if (!objectp(dest)) return 0;(kxkj 系是 ob),移动失败、对象留在原处。抽样(每库 300 个对象)里原先因此失败的 31 个对象,现在 15 个能载入。

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

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

全局头文件丢了 1 个宏(2026-10-19,KB 04 §6.16)

/include/globals.h 里没有定义 MAILDIR,用到它们的物件(名单里 1 个)编译时报 Undefined variable/Undefined function,整个物件载入不了(编译器在第一个未定义的名字上停下,后面的错误和警告都看不到)。同系的库在各自的头文件里都有这几行:照其中和本库共有文件最多的那个库,原样补到 /include/globals.h 最后一个 #define 之后,宏体里出现的路径在本库里都存在。名单里的物件除了这几个宏之外还有别的错误,暂时仍载入不了。(scripts/lpc_restore_defines.py)

BIG5 的 0x5C 尾字节(扩展)(2026-10-19,KB 06 §7.266)

BIG5 里 功 等字的第二个字节正好是 0x5C(反斜杠),作者在它后面多写了一个 \,转 UTF-8 后这个多出来的 \ 跑到了收尾引号前面,把引号转义掉,字符串一直吃到文件后面。scripts/lpc_fix_big5_quote_backslash.py 原先只在引号后面跟 , ) ; } ] + 或行尾时才删;这次把“引号后面紧跟颜色宏再接字符串”(HIY"功\"HIR"』")也算进去,删掉多余的 \。

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 文件。

两个斜杠开头的物件路径(2026-10-19,KB 06 §7.276)

2 个文件、2 处物件路径写成 //…(如 carry_object("//clone/cloth/hic_cloth")):/open/gsword/room1/blood/room1/r1.lpc、/open/gsword/room2/lee.lpc。当年的驱动不介意(路径先被规范化);新驱动把原样的字符串交给 master 的 valid_read(),securityd 按 //clone 查目录,NPC 的 euid 没有权限,报 Read access denied.,NPC 载入不了。改成一个斜杠。

补回本库没有的基类文件,第二轮(2026-10-19,KB 04 §6.15、§6.18)

宏有定义、基类文件却不在档案里:继承它的对象载入时报 Inherited file '…' does not exist!,编译扫描看不到这一类(scripts/lpc_missing_inherits.py 能列出)。同系的库里有同一个文件,原样拷来(scripts/lpc_restore_bases.py);基类和继承它的对象 include 而本库没有的头文件一起带来。每个基类补回后单独载入,再载入使用它的对象,载不进来、或一个用户也载不进来的撤掉了。

逐个与 HEAD 对比载入,没有退步。

旧驱动的类型宽松:缺省参数、无类型形参、数组初始化、class 变量(2026-10-19,KB 04 §6.19、§6.20)

原来的 MudOS 把调用少传的参数补 0、把没写类型的形参当 mixed、不检查变量的声明类型与初始值,也允许变量叫 class;这个驱动在编译时全部拒绝,整个物件载入失败。修了 5 个文件:/cmds/arch/edguild.lpc、/obj/dagger.lpc、/open/capital/npc/master_chen.lpc、/open/capital/npcbak/master_chen.lpc、/u/w/wade/npc/lover.lpc(scripts/lpc_round_files.py 的修复轮:调用处补写 0、无类型形参补 mixed、string X = ({...}) 补星号、void 函数的返回值,以及 class 变量改名 pick_class)。逐个文件在 HEAD 和改后载入对比:这些文件里 5 个原来载入失败、现在载入,没有退步。

x == "a" || "b":后面的字符串常量永远为真(2026-10-19,KB 06 §7.281)

if (arg == "yuyou" || "yu you") 里的 "yu you" 是非空字符串,永远为真,整个条件对任何 arg 都成立;作者要的是每个字符串都和变量比较。在每个字符串前补上变量和运算符(scripts/lpc_fix_literal_or.py;forrest2.lpc 是 dir == "north" && "south",手工改成 (dir == "north" || dir == "south") && ...):/daemon/skill/moon_sword.lpc(1 处)。

一个巫师目录里的 NPC 多出来的局部变量(2026-10-19,KB 04 §6.10)

u/w/wade/npc/lover.lpc 的 string who, dir, master, item; 四个局部变量都没有用到(这个文件因为前面的修复才开始能载入,终局扫描才看到警告),删掉。

比较链的人工修正(2026-10-20,KB 06 §7.282)

daemon/class/blademan/obj/mkblade.lpc:注释写明 sharp 在 8 以上的武器和材质为 blacksteel / crimsonsteel 的刀剑(倚天剑、屠龙刀)不会被血气特攻溶毁,条件却写成 material != "blacksteel" || material != "crimsonsteel"(永远为真),这两把刀照样被毁;改成 &&(两处)。

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,共 2 处)。改动:/daemon/skill/dragon-blade.lpc(1 处)、/daemon/skill/lungshan.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)。

(: 函数 :) 指针指向对象自己没有定义的函数(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。 还剩 4 个函数没有补(other 4):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/daemon/class/killer/obj/ninja-force.lpc:force_damage、/daemon/skill/darkforce.lpc:force_damage、/daemon/skill/ejinforce.lpc:force_damage、/daemon/skill/god-try.lpc:sp_attack2。

log/ 下的日志子目录没有人建:写日志的 write_file() / log_file() 报错(2026-10-22,KB 06 §7.358)

这个库的代码往 log/ 的 2 个子目录写日志:log/RECORD(adm/daemons/channeld.lpc)、log/sky(daemon/class/bandit/master_du.lpc)。压缩包、git clone 和站点包里都没有这些目录(本地启动没有写过它们,gen_keep_dirs.py 没记),write_file() 对不存在的目录抛 *Wrong permissions for opening file /log/dlog/money for append. "No such file or directory",调用它的函数在写日志那一行中断,后面的奖励、标志、步骤都没有执行。本库的 log_file() 会自己建目录(实测,或读源码:nitan 系的 assure_file()),所以这里只记了直接用 write_file() / save_object() 写的目录。把 log/RECORD、log/sky 记进 scripts/wasm_keep_dirs.txt(scripts/lpc_keep_log_dirs.py;站点和 pristine 树按它建空目录)。证明(scripts/lpcc_eval.py,在 /adm/daemons 这类目录下的求值对象里写 /log/zzdirprobe/...,新进程)见 KB 06 §7.358 的实测表。

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

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

本库 1 处(1 个文件):与汉字常量比较 1 处。例:msg[0..1] != "【" → msg[0..0] != "【"(/feature/message.lpc:45)。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 区间 1 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。

Gsword 出口仍指向 /u/b/bigcat(2026-10-23,KB 06 §7.371)

剑客区已在 /open/gsword/,出口与 mob 路径仍写 /u/b/bigcat/…。scripts/lpc_fix_bigcat_gsword.py 在目标存在时改写;无对应文件的 ntree/wtree/etree 出口删除。本库 36 处。证明:kxkjii2 的 forest3 HEAD 北向 /u/b/bigcat/room/forest4,改后 /open/gsword/room/forest4 可加载;tree1 只剩南向回 tree。

巫师区出口 faceoff / rence / niulang / yihua(2026-10-23,KB 06 §7.372)

/u/f/faceoff/、/u/r/rence/、/u/onskyer/niulang/、/u/skinys/yihua/ 对应区域已在 /open/world1/faceoff/bird/、/open/prayer/、/d/niulang/、/d/yihua/。scripts/lpc_fix_wizard_area_exits.py 在目标存在时改写,仍缺失的出口行删除。本库 12 处。证明:dreamofseven wood5 四向可加载;xlqy_new2007 天河东西向 niulang;kxkj kitchen7 → prayer。

/open/ 上残留的 == !"…" / || "…"(2026-10-23,KB 06 §7.280 / §7.281 Also)

机械修复器曾把 /open/ 当成巫师家目录跳过;本库的可玩世界就在 open/。 open/badman/ride/deserter.lpc:class == !"bandit"(实为 == 0)使非山贼驯驼奖励永不触发 → != "bandit"。 open/killer/room/doorm.lpc:str == "all" || "corpse" 恒真,任意 sac 抽干气血 → str == "corpse"。 证明:lpcc_eval.py kxkj 加载 doorm;fighter != "bandit" 为 1。

小火龙学者/毒师闪避分支恒假(2026-10-23,KB 06 §7.282 Also)

open/fire-hole/npc/small-dragon.lpc:class == "scholar" && class == "poisoner" 恒假 → ||。 与 kxkjii2 同源;/open/ 不再被 cmp_chain SKIP。

/open/ 上残留的条件赋值(2026-10-23,KB 06 §7.290 Also)

15 文件:gring 的 str = "guard ring"、密道 arg = "water"/"light"、麻将 check = 0 等 → ==。 证明:gring / waterm / mj 可加载。

/open/ 上丢失引号的 shout/say(2026-10-23,KB 03 §4.6 Also)

open/poison/npc/low.lpc(庄静柔)里 command("shout …) / say 在 GBK 转换后丢了引号,机械 closer 仍留下非法中文拼接,对象无法加载。手改为 command("shout 好!" + me->query("name") + "能成功寻回月石,看来本教复兴有望了。");(say 同形)。lpc_fix_eaten_quote.py 的 SKIP 不再把 /open/ 当巫师家。证明:lpcc_eval 加载 NPC,name() 为庄静柔(HEAD FAIL)。

/open/ 上残留的 score/HP 条 *2(2026-10-23,KB 06 §7.365 Also)

lpc_fix_display_pad.py 曾把 /open/ 当巫师家跳过;本库可玩世界在 open/。 open/killer/obj/two.lpc 的 tribar_graph:bar_string / blank_string / bmin_string(○)上的 * 2 在字符区间下把半条画满。SKIP 去掉 open,并教会识别 bmin_string。证明:lpcc_eval tribar_graph(10,15,20) HEAD 二十个●,改后十个●+五个⊙+五个○。

/open/ 上残留的 force 当 wound(2026-10-23,KB 06 §7.241 Also)

open/steeple/obj/npc/npc48.lpc:receive_wound("force", 10000) 而本库 wound 只认 gin/kee/sen → add("force", -10000)。证明:lpcc_eval receive_wound("force",…) 报错,add 正常;NPC 可加载。

/open/ 上残留的 .c 路径字面量(2026-10-27,KB 06 §7.356 Also)

lpc_fix_dotc_path.py 曾把 /open/ 当巫师家跳过。open/killer/home/r0u.lpc 的货物表仍写 "/open/killer/npc/train_a.c",档案是 train_a.lpc;显式扩展名按原样解析,new() 得 0。改为 .lpc。证明:lpcc_eval HEAD new(...train_a.c) 为 0,改后 new(...train_a.lpc) 为对象。

open 下死出口由对侧房间修好(2026-10-27,KB 06 §7.257 Also)

lpc_fix_dead_exits 的 SKIP 曾把 /open/ 当巫师目录。本库本批出口改写见该提交(大小写/错名,由对侧出口回指判定)。证明见 KB。

对象路径残留 .c 后缀(2026-10-27,KB 06 §7.356 Also)

set("objects"/"exits", …) 与 __DIR__"….c" 在改名为 .lpc 后仍带 .c 时,new/load_object/房间 reset 找不到文件(血池房 inv=0)。凡旁有对应 .lpc 的,去掉 .c 后缀。证明:HEAD stone2 reset 后 inv=0;改后 inv=1(/d/youxia/obj/blood)。

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