The Smiling, Proud Wanderer (Mini Edition)

✅ 可玩

笑傲江湖(迷你版)

xo

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

▶ 开始游玩 · Play Now

笑傲江湖(迷你版)是以金庸小说《笑傲江湖》为背景的武侠 MUD,属于 TMI-2/ES2(Falcon 系)引擎家族,是"笑傲江湖"系列里体积最小、内容最精简的一个版本(作者称之为"mini xo"):地图刻意做得极简,只有扬州城和一座码头小镇两座城市,可加入的门派也只有无量派一家,很适合用来体验这套引擎最原始、最基础的玩法框架;同批的"笑傲江湖最终版"(xo_final)与本档几乎逐文件沿用同一张地图、同一个无量派设定(差异只在少量文件的细节修改),再在此基础上大幅扩充出 10 座城市、16 大门派的更大世界。

English

A wuxia MUD set in Jin Yong's "The Smiling, Proud Wanderer," the smallest, most minimal build in this collection's xo series — the author's own "mini xo." The map is deliberately bare-bones: two cities (Yangzhou and a wharf town) and a single playable sect, the Wuliang (Boundless) Sect, making it a good look at this ES2/"Falcon"-family engine's basic framework stripped of content. Its sibling archive, the Final Edition (xo_final), reuses this same map and sect almost line-for-line (only minor per-file edits differ) while expanding it into a much larger world with 10 cities and 16 sects.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/xo/

管理员账号 / Admin account

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

本地运行

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

游戏端口:40023。

NOTES · 移植与修复记录

xo — 笑傲江湖迷你版 (The Smiling, Proud Wanderer — Mini Edition)

Archive: xo.zip. Port: 40023. Status: done (boots clean, connects, plays through login into "create new character?" confirmation with zero crashes).

What this is

A different, smaller lineage from everything else processed so far in this batch: secure/daemon/master.c header says "Original from TMI-2 and ES2, Rewritten by Falcon 96-10-02" — TMI-2 ("The Mud International 2") is another classic MudOS-era mudlib base, distinct from the "ES II"/nitan families seen in archives #21-#26. Uses the secure/daemon/{master, sefun} path convention (not adm/obj/ or adm/single/). Small lib — 1395 .c files, "mini edition" as the name says. No set/query/ delete global simul_efun defined (like rzrmud/xkx2001) — every object provides its own via inheritance, the architecturally correct pattern (see AGENTS.md §15).

Fixes applied

1. AGENTS.md §4 (lazy security-daemon load): secure/daemon/ master.lpc's valid_write/valid_read called load_object(SECURITY_D) unconditionally on every single call (not gated behind find_object() first), wrapped in catch() but with no re-entrancy guard. Added the full guard (nosave flag + find_object() check before attempting load_object()) per the established §4 fix shape, applied proactively before the first boot attempt given the exact matching pattern. 2. New bug (case-sensitivity, Windows-origin archive on a case-sensitive Linux filesystem): 3 files (system/feature/char/ command.lpc, cmds/arch/ll.lpc, cmds/arch/localcmds.lpc) #include <Action.h> (capital A), but the actual file on disk is include/action.h (lowercase) — silently resolves on Windows' case-insensitive filesystem, hard-fails here. This was responsible for the overwhelming majority of the initial lpcc sweep's failures (762 "Undefined class 'Action'" + 155 "Cannot #include Action.h" out of 1395 files, cascading from just those 3 root includes via inheritance) — fixing the 3 #include lines dropped the failure count from 209 to 72 in one shot (85.0% → 94.8% pass). Checked the whole tree for any other same-shape case-only filename mismatches (find -iname + case-insensitive dedup) — none found, this was the only instance.

Interactive test result

Boots clean, connects; welcome message + "last modified 1999.11" credit line render correctly, empty input at the username prompt correctly triggers a polite disconnect ("欢迎下次再来" — not a bug), new + a valid English name reaches the "create new character?" (y/n) confirmation with zero crashes. Did not create a full character or play further (out of scope).

lpcc sweep

1395 files, 1323 pass / 72 fail (94.8%, after the Action.h fix). Remaining 72 failures are the usual long tail (a handful of syntax typos in individual files, a few missing daemons like TASK_D/SendToUser/ SendToMud referenced but not present in this "mini" trimmed-down archive) — not triaged individually, consistent with AGENTS.md §6b/§13.

Post-hoc fix: UTF8-native is_chinese/registration (AGENTS.md §15h)

Applied in a later batch pass across the whole project: is_chinese/is_chinese2 in the shared chinese.lpc simul_efun fragment used GBK byte-range checks that silently never match real Chinese text once strings are UTF-8 (this driver's str[i] returns a Unicode codepoint, not a GBK byte). This broke character registration specifically -- any real Chinese name was rejected. Fixed the range check to test the CJK Unicode block instead, and halved the GBK-byte-calibrated length bounds in check_legal_name to match. See AGENTS.md §15h for the full writeup; confirmed via a real interactive registration test (Chinese surname + given name reaching the next prompt).

2026-07-23: driver rebuild retest + LPC formatter + WASM check

2026-07-23 (integrity review): WASM world-entry hang RESOLVED — was the harness, not the mudlib

Re-ran the full WASM registration flow (new → id → confirm → Chinese name → password ×2 → email → gender) against the same build-wasm binary but with the FIXED scripts/wasm_client.js (the copyDir() log-directory recursion fix landed in the final commit of the previous pass, AFTER this lib's WASM test had been run). Result: enter_world() now completes normally under WASM — MOTD banner, 小秦淮客寓 start room, look, and the quit save-gate prompt all appear exactly as in the native run. No mudlib race exists in xo's enter_world path; the earlier "reproducible world-entry hang" was the old harness failing to recreate the lib's nested log/ subdirectory shape in the WASM in-memory FS (this lib's log/ has 6 nested subdirs — acct/catch/log/room_log/runtime/static), so an early uncaught write_file()/log_file() during enter_world threw ENOENT and silently aborted the flow — precisely the harness gap class described in that commit's message. Verdict: fully playable under WASM; the previous NOTES entry's "possible WASM-specific gap isolated to this lib" flag is closed.

Also noted (pre-existing, original-author code, left as-is): system/ daemon/logind.lpc's enter_world() contains two leftover debug printf("beforee exec...") / printf("before setup...after exec..") lines that print to every player at world entry — present since the raw archive (verified in the initial conversion commit), cosmetic only.

WASM-enablement pass (loopback-allow / gate bypass / admin seed)

Standard WASM-first pass per AGENTS.md §1.3b/e and §1.5. Gates patched:

Admin account seeded: id fluffos, pw Mud@2026, name 浮浮, granted (admin) via fluffos (admin) appended to secure/etc/wizlist. Verified: real registration (ENTER/new/fluffos/y/浮浮/pw×2/email/m → landed in 小秦淮客寓), then relogin as fluffos → update /system/daemon/logind → 成功, score correct. Retest: fresh normal registration (秦风, f) end-to-end with look/score correct (quit shows the original 30-min new-account retention prompt — content timer, kept per policy); test char save removed; zero new debug.log errors. Save file: work/data/user/f/fluffos.o (NOT gitignored — normal add, no force-add needed).

Fail-closed retrofit for the loopback-allow gate (2026-07-24)

The loopback helper above was originally written matching the project-wide convention at patch time (AGENTS.md §1.3b), which also treated an empty/non-string/malformed query_ip_number() result as loopback, defensively, because the WASM driver used to return garbage there. That underlying driver bug is now fixed (fluffos commits e33bb5da "fix: query_ip_number() returned uninitialized garbage under WASM" and 007bb863 "feat: synthetic resolve() on WASM instead of raising an LPC error", both 2026-07-23; the locally-built build-debug/build-wasm binaries already postdate both commits), so treating unparseable IPs as trusted is a fail-open gap with no remaining justification. Retrofitted to fail-closed: loopback is now strictly ip == "127.0.0.1" || ip == "::1" || ip[0..3] == "127." (with a stringp() guard before the slice) — a malformed/empty IP now falls through to the NORMAL gate instead of being treated as local. Retested after tightening: fresh driver boot clean, fluffos loopback login and its wizard update command both still work; zero new debug.log errors.

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

First real *playthrough* pass on this lib (all prior passes verified only registration + look/score/quit + admin login). This "mini" archive has no help newbie/help intro file at all (help/user/ contains only a single sect help page and no topics index — a genuine content gap in this trimmed archive, not a conversion bug; help with no argument literally prints 0 to the player, see Bug 8 below), so the test path was derived entirely from reading room/NPC source: register → 小秦淮客寓 (start room, Yangzhou) → walk to 崇雅书院/至止堂 and learn literate from the teacher NPC 增城湛 via qingjiao → (admin-assisted, see below) reach 无量剑派东宗's hall and join via apprentice/kneel → a real fight → a real death → the death-realm/reincarnation flow → a shop purchase → quit/reconnect testing. Found and fixed eight distinct programming bugs (compile errors, a driver-API-misuse crash, and a silent-stranding move() bug), one of which is a well-established project bug class (§7.25) and most of the rest are one-off typos of a kind AGENTS.md already tracks (§6.6/§8.5-adjacent); also captured a driver-level SEGFAULT, corroborating the §10.8 "ambient background processing can crash the whole process, invisible to debug.log" class with a genuinely different C++-level mechanism (a dangling shadowed object pointer walked during the periodic reset() sweep) than either of the two prior occurrences.

Note on this pass's scope: partway through this session the orchestrating session clarified that only genuine *programming* bugs are in scope for live fixes (compile errors, driver-API misuse, missing null/objectp checks around calls that can crash, obviously-wrong references) — game-balance/content/design questions are explicitly OUT of scope, even when they look surprising. One thing found live (a "safe" sect mirror-spar that turned out to deal lethal, real damage — see "Observed, not fixed" below) falls squarely in that excluded bucket and was deliberately left untouched, documented only.

Wuliang sect is not reachable by ordinary walking in this archive: d/menpai/wuliang's only outbound connection to the rest of the map (shanjiao.lpc's northwest exit) points at /d/map/xinan/jiangbian, and /d/map/ doesn't exist anywhere in this "mini" archive (confirmed: d/map is entirely absent). No city room links into d/menpai/wuliang either. This means the sect content described in the lib's own README ("目前只有扬州城和无量派两大门派") is real and functional but currently has no in-game path to it at all — every sect-related test below (apprentice/kneel, the mirror-spar, qingjiao) was reached via admin goto/summon, not by walking. This looks like a genuine map-connectivity gap from the archive trim (not something we fixed — no defensible "correct" reconnection point exists, and fabricating one would be a content decision) but is worth flagging prominently since it's a bigger blocker to real play than any single bug below.

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

Verified working

Not verified live (explicitly)

Bugs found and fixed

1. Unterminated string literal (×3, same shape, different files) — AGENTS.md §6.6-adjacent (pre-existing authors' typo, not a new bug class): a Chinese-text string literal missing its closing " before the trailing comma, causing the driver to keep consuming subsequent lines as string content until it hits the next " (usually inside a //-commented line, since the comment marker means nothing once already inside an unterminated string) and then throwing a cascade of "Illegal character" / UTF-8-mid-codepoint errors once the accidental early-close lands mid-multibyte-character. - d/menpai/wuliang/npc/zuo.lpc:69 — this one is the single most impactful fix in this pass: 左子穆, the sect's own leader/master, failed to compile entirely, so d/menpai/wuliang/dating.lpc's room population silently dropped him (caught by std/room.lpc's already- catch()-guarded make_inventory() — this lib is NOT vulnerable to the unguarded §7.25 crash shape, just the graceful-degradation side of it) — meaning the ENTIRE sect-join mechanism was inert (no master NPC to apprentice) until this fix. ``lpc // BEFORE: "姓名" : "嗯,你既知我姓左,怎会不知在下便是无量东宗掌门人左子穆?, // AFTER: "姓名" : "嗯,你既知我姓左,怎会不知在下便是无量东宗掌门人左子穆?", ` - system/skill/misc/yuanyang-dao.lpc:33 (the "鸳鸯刀法" weapon skill's 4th combat move) — same shape, fixed identically. - system/skill/misc/taizu-quan.lpc:181` (the "太祖长拳" unarmed skill's "英雄独立" move) — same shape, fixed identically.

2. Undefined-macro inherit (×2 files): d/city/yangzhou/waifang.lpc and d/city/yangzhou/woshi.lpc (both real, reachable rooms — a courtesan-house's outer/inner room, linked from meixiang_yuan.lpc) did inherit DOOR_ROOM; — DOOR_ROOM is never #defined anywhere in this archive (no door_room.lpc base class exists at all), a hard compile error (expecting L_STRING or '('). Every other room in this lib that needs a door (e.g. d/menpai/wuliang/damen.lpc) just inherits plain ROOM and calls create_door(), which std/room.lpc already provides directly — DOOR_ROOM was never real content, just a stale reference. Fix: inherit ROOM; in both files.

3. Malformed function declaration swallowed into a comment: system/skill/basic/kongshou.lpc:37-38 — the base "unarmed combat" skill, needed on literally every unarmed combat round for every character. The Chinese explanatory comment and the following function signature were on the same physical line with no separating newline, so the ENTIRE function header got absorbed into the // comment, leaving a bare orphaned { on the next line (syntax error, unexpected '{'). Reproduced live: fighting as an unarmed disciple spammed a "Compiling error .../kongshou.lpc ... *No program in object" message to the player on every single combat round (both attack and parry attempts), since skill.lpc's combat dispatch tries to load this file fresh every time it's referenced and it never successfully compiles. ``lpc // BEFORE: // 这个函数用来区别这种天生的技能与其他后天学习的技能int is_native_skill() { return 1; } // AFTER: // 这个函数用来区别这种天生的技能与其他后天学习的技能 int is_native_skill() { return 1; } ` Checked the other 4 sibling files in system/skill/basic/ with the same is_native_skill() pattern (wuqi.lpc, horsedodge.lpc, dodge.lpc, yeshou.lpc`) — all correctly formatted, this was an isolated one-off typo, not a copy-pasted class.

4. Missing #include <armor.h> (×2 files) — AGENTS.md §6.1: d/city/yangzhou/npc/cloth/cloth.lpc (the base clothing template) and d/menpai/wuliang/npc/obj/choushan.lpc (a sect disciple's robe, worn by the live npc/qyz.lpc... which itself turns out to be dead/ commented-out content, see below — fixed anyway since it's a one-line, zero-risk fix and the exact §6.1 shape) both did inherit CLOTH; without #include <armor.h> (where CLOTH is #defined). Every sibling cloth file in the same directory has the include; these two were just missing it. Fix: add #include <armor.h> above the inherit. - cloth.lpc also had a second, independent bug on the same pass: set("value",); — a call with a dangling comma and no second argument (Wrong number of arguments to 'set', expected: 2, got: 1). Every sibling cloth file's equivalent line reads set("value", 800)-shaped; fixed to set("value", 0) (a sensible zero default for what is explicitly a base/template object, per its own empty set_name("", ...) treatment elsewhere in the same create()).

5. Driver-API misuse: missing objectp() guard after a move() call that can legitimately self-destruct its argument — matches AGENTS.md §7's "missing objectp()/stringp() checks" class, closest existing relative is §7.25's shape (a call that can legitimately fail/vanish, with no guard on the caller side). cmds/verb/drop.lpc:57's do_drop() calls obj->move(environment(me)), checks only the integer return value, then unconditionally calls further methods (obj->is_character(), obj->query("unit"), obj->value()) on the same obj reference. d/menpai/wuliang/npc/obj/sword.lpc's own move() override (a soulbound-weapon "剑在人在,剑亡人亡" mechanic) legitimately destruct(this_object())s itself and returns 1 (success) when dropped outside a valid ownership context — a fully intentional, working design (see sword.lpc:33-52), but do_drop() has no way to know that and dereferences the now-destructed obj immediately after. Reproduced live: drop sword (as a disciple standing in the death realm, where the sword's ownership check fails) printed the correct in-fiction "剑转眼间断成了数块,不见了。" message, immediately followed by an uncaught *Bad argument 1 to EFUN call_other() Expected: object, string, array, Got: int(0) runtime error (visible in log/runtime), aborting the rest of do_drop() mid-function. ``lpc // BEFORE: if (obj->move(environment(me))) { if (obj->is_character()) // AFTER: if (obj->move(environment(me))) { // obj->move() can legitimately destruct obj as a side effect (e.g. // a bound/soulbound weapon shattering when dropped outside its // valid owner context -- see d/menpai/wuliang/npc/obj/sword.lpc's // own move() override) -- guard before touching it further. if (!objectp(obj)) return 1; if (obj->is_character()) ` This is a general, call-site-independent fix (guards the ONE shared do_drop() used by every drop invocation, including the amount- split and drop all` paths), not specific to the sword.

6. Silent-stranding move() to nonexistent content in the death/ reincarnation flow — same shape as AGENTS.md §7.18 (hardcoded path into content missing from THIS archive), new file/lineage instance: d/place/death/npc/chacha.lpc's death_stage() — the 判官 NPC that finalizes reincarnation — picks a random(3) destination for a newly -reincarnated ghost. Only branch case 0 (/d/city/yangzhou/kuixing_ge) resolves to real content in this trimmed archive; case 1 (/d/map/zhongyuan/nanmen — /d/map/ doesn't exist anywhere in this archive) and case 2 (/d/city/lanzhou/guangchang — lanzhou doesn't exist either) both silently fail. Confirmed the failure mode live (via goto on the admin account, which has its own explicit existence check and reported "没有这个玩家、生物、或地方" without moving): move()'s own implementation (system/feature/base/move.lpc:74-80) already gracefully notify_fail()s rather than crashing for a target that doesn't exist at all (as opposed to one that exists but fails to compile, which throws) — but chacha.lpc never checks move()'s return value, and notify_fail() from a call_out-driven context with no active command dispatch never surfaces to anyone. Net effect: 2 out of 3 times, a reincarnating ghost's ghost flag gets cleared (reincarnate() already ran) but they're never actually relocated — permanently stuck in 阎罗大殿, a death-realm-only room with (per 鬼门关's own room text) no way back to the living world through ordinary movement. Also checked the "西域"/gaochang special-case branch one line above — /d/city/gaochang/center doesn't exist either, but confirmed (via logind.lpc's own start_rooms array, where the gaochang entry is commented out) that no character in this archive can ever actually be tagged hydra/desert/from=="西域" in the first place, so that branch is already-dead and left alone. ``lpc // BEFORE: switch (random(3)) { case 0: ob->move("/d/city/yangzhou/kuixing_ge"); break; case 1: ob->move("/d/map/zhongyuan/nanmen"); break; case 2: ob->move("/d/city/lanzhou/guangchang"); break; } // AFTER: (both dead branches repointed to the one confirmed-live // destination already used by case 0, matching AGENTS.md #7.18's // fix pattern -- see the file for the explanatory comments kept in // place) switch (random(3)) { case 0: ob->move("/d/city/yangzhou/kuixing_ge"); break; case 1: ob->move("/d/city/yangzhou/kuixing_ge"); break; // was the dead /d/map/ path case 2: ob->move("/d/city/yangzhou/kuixing_ge"); break; // was the dead lanzhou path } ` Verified live end-to-end after the fix: dropped all items (a prerequisite the same NPC enforces earlier in its dialogue), re- entered 阎罗大殿, sat through 判官's full multi-stage dialogue, and was moved to 小秦淮酒楼` (a real, live room) successfully.

7. Operator-precedence + typo bug, NOT observed live (dead code given current data, fixed proactively since it's a trivial and clearly unintentional pair of mistakes): cmds/skill/study.lpc:127-128 — if (bonus = book["bonus_jibie"] > 0) me->improvve_jibie(...). Two independent bugs stacked: = binds looser than > in this grammar, so bonus was being assigned the BOOLEAN result of the comparison (0 or 1) rather than the actual configured bonus amount (contrast with the parallel bonus_jilei branch a few lines up, which correctly does bonus = book["bonus_jilei"] then checks it); and improvve_jibie (double-v) is a typo for the real function improve_jibie (defined in system/feature/char/skill.lpc:351, used correctly by name everywhere else including 3 lines below in the same function's else branch) — an Undefined function error waiting to happen the moment it's ever reached via ->. Not reachable with current data: grepped every book/skill data file in this archive and none of them ever set "bonus_jibie" to a positive value, so book["bonus_jibie"] > 0 is always false and this exact branch never executes today — but the fix is one-line-obvious and harmless, so applied proactively rather than left as a landmine for the first book that ever sets that key. ``lpc // BEFORE: if (bonus = book["bonus_jibie"] > 0) me->improvve_jibie(book["name"], bonus); // AFTER: if ((bonus = book["bonus_jibie"]) > 0) me->improve_jibie(book["name"], bonus); ``

8. Cosmetic, not fixed (too minor/ambiguous to count as a real programming bug, documented only): cmds/comm/help.lpc's main() does cat(HELP_DIR + "topics") when called with no argument; help/user/topics doesn't exist anywhere in this trimmed archive (genuine content gap, not a conversion bug). secure/sefun/file.lpc's cat() is write(read_file(file)); read_file() of a missing file returns int 0, and write() coerces that to the literal string "0" — so a bare help with no topic just prints 0 to the player instead of a message like "没有说明文件". Left as-is per the "don't fabricate missing content" policy (the fix would require deciding what a proper topics index should say, a content question) — but flagged here in case a future content pass wants to add a real topics file, at which point this stops mattering entirely.

Observed, not fixed (design/content, out of scope per this pass's corrected instructions)

Driver segfault (corroborates AGENTS.md §10.8, new immediate mechanism)

~26 minutes into this session, during ordinary ambient play (not triggered by a specific command — the crashing call stack is entirely inside the driver's own periodic reset() sweep, backend.cc's look_for_objects_to_swap()), the native driver process died outright with a SIGSEGV. debug.log showed absolutely nothing — identical to both prior §10.8 occurrences (xjcq2000, shiji), reinforcing that this class of failure is genuinely undetectable by log-watching alone; only the redirected driver stdout (captured per the standing recommendation from that section) had anything.

Captured backtrace (driver build fluffos-20260721-25c87bbc):

backend_run_one_gametick -> look_for_objects_to_swap -> reset_object
  -> safe_apply(APPLY_RESET) -> apply -> apply_low -> eval_instruction
  -> f__call_other -> apply -> apply_low [SIGSEGV]
src/vm/internal/apply.cc:201, in apply_low:
  while (ob->shadowed && ob->shadowed != current_object &&
         (!(ob->shadowed->flags & O_DESTRUCTED))) {   // <- crash here

This is a different immediate C++-level mechanism than either prior occurrence (both of those were FATAL: Object ... ref count 0, but not destructed internal-consistency aborts from free_svalue(); this one is a raw segfault dereferencing ob->shadowed, apparently a dangling pointer, while a reset()-triggered call_other() walks the shadow chain) — but the same broad category (driver-level object-lifecycle memory corruption surfacing later, during unrelated background activity, as a process-ending crash). Not mudlib-fixable; not root- caused to a specific LPC source pattern (the corruption, whatever it was, had already happened before this crash — the reset cycle just happened to be the next code to touch the bad pointer). Driver was restarted (fresh PID, confirmed via readlink -f /proc/<pid>/cwd before doing anything with it) and testing continued; no second occurrence in the remaining ~30 minutes of this session. This is now the third independent occurrence of "background driver processing crashes the whole process, invisible to debug.log" across three unrelated libs/lineages — per §10.8's own escalation note, worth promoting to a driver-level (~/src/fluffos) investigation rather than continuing to treat each new occurrence as a per-lib mudlib finding, if this keeps recurring.

lpcc sweep

Baseline (start of this pass) 1323/1395 pass (94.8%). After this pass's fixes: 1331/1395 pass (95.4%). Remaining 64 failures are the already-documented long tail: genuinely corrupted/non-LPC content under d/menpai/wuliang/east/ (plain-text poem fragments, garbled binary, a "By XO team... since 1997 April." credits blob — not LPC source at all, pre-existing archive corruption, not a conversion bug, and this whole subtree turns out to be unreachable anyway per the map-connectivity gap noted above), missing daemons (TASK_D, BET_D, SendToUser, NBUG) consistent with this "mini" archive's documented trimming, and a handful of dead/superseded duplicate files (system/menpai/wuliang/zuo_zimu.lpc etc. — confirmed via grep to be shadowed by a live npc/-subdirectory version and never referenced by any room's objects mapping, left alone per the project's "leave dead code alone" convention) — not triaged further as none are live/reachable.

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

笑傲江湖 XO/TMI-2/ES2/Falcon 基础版(迷你版)。状态已从过时的 limited 修正——这份档案自己的 README 和 group_note 里从未记录过任何缺陷说明,本轮重新测试也没有发现:这份压缩包没有随附管理员存档(只有一条 wizlist 阶层授权)——已改为用一次全新的 'new' 注册(fluffos/Mud@2026)来验证,干净地进入起始区域(小秦淮客寓),并以正常的告别信息 quit。

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

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

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

1. cmds/app/update.lpc(AGENTS.md §7.106):完全没有 environment(me) 空值防护(if (obj = present(file, environment(me)))),补上 if (environment(me) && (obj = present(file, environment(me))))。 2. secure/sefun/file.lpc:log_file() 没有 assure_file() 目录预建保护,补上调用及前向声明;cat() 补上 read_file() || "" 空值防护。 3. secure/daemon/master.lpc::log_error()(AGENTS.md §7.10 大小写 坑的又一实例,与本轮在 xlqy_new2007 上发现的一样): strsrch(message, "warning: ")(小写、带尾随空格)逐字节对不上 本驱动实际吐出的大写 Warning: ,等于完全没有过滤——本档案这处 还额外没有 wizardp() 分流(对所有连线玩家一视同仁广播),改成 不含开头 w/W 的 "arning:" 子串匹配(§7.10 标准写法)后即可正常 过滤警告,未额外新增巫师分流(不在本轮范围内,且原代码本就没 有这层区分)。 4. clone/user/user.lpc、clone/user/wizard.lpc 的 reconnect() (AGENTS.md §7.108,第十六条独立确认的血统):两个档案各自独 立定义 reconnect()(wizard.lpc 覆写而非继承 user.lpc),均 缺少 enable_commands()。system/daemon/logind.lpc 有同款 exec(old_link, user); 踢掉重复登录写法。按 §7.108 记录的写法 预防性修复两处,现场用两个真实连线复现"保持第一个连线不断开→ 第二个连线登录→答 y 踢掉旧连线"验证:score 修复后立即正常显 示完整角色档案。

现场验证摘要

驱动干净启动,管理员 fluffos/Mud@2026 登录(回车进入→id+密码) 成功进入游戏世界,update /system/daemon/logind 成功验证真实写入 权限,现场未观察到编译警告泄漏(确认 log_error 修复生效)。踢掉重 复登录重连路径现场验证通过(见上)。debug.log 全程干净(216 行, 无真实错误)。

本轮修改的文件

§7.100 sub-threshold instance (2026-08-20)

Found during the §7.100 tail-sweep (below the original 166-lib survey's

=100-occurrence threshold, never checked). 73 live

replace_program(ROOM); occurrences across 73 room files — mostly a large d/menpai/wuliang/*.lpc sect-zone cluster (68 rooms including a west/ subdirectory), plus d/city/matou/mache.lpc/kechuan.lpc and a d/place/death/*.lpc cluster of 8 rooms. No room-building tool exists in this lib. 4 pre-existing already-//-commented instances left untouched. Fixed by deleting the redundant lines. Verified via a clean native driver boot (zero new debug.log errors, port listening, killed by exact PID after ~8s).

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

新角度:巡捕游荡触发的城门 reset/make_inventory 路径(log/catch 里成串的 *Bad argument 1 to EFUN call_other() ... Got: int(0))、 留言板 post/read、酒楼 list/buy、真实死亡进入地府。

Bug(AGENTS.md §7.73 新实例):迷你版把装备挪到 npc/obj/,

NPC 仍按全局 HEAD_DIR/BLADE_DIR 取值 → 每次城门 reset 都 call_other(0)

xunbu 的 random_move() 懒加载城门房间时,make_inventory 克隆 士兵/卖花姑娘,其 create() 里无防护的 carry_object(...)->wear()/ wield() 对缺失路径返回 0,立刻炸:

| NPC | 坏路径 | 本档案实际位置 | |---|---|---| | liu_yan / run_yu | HEAD_DIR "huang_m" / "yu_jin" 等花 | d/city/yangzhou/npc/obj/*.lpc | | shibing1 / shibing2 / xiaowei | BLADE_DIR + "changdao" | 同上 obj/changdao.lpc |

xo_final/xajh2 在全局 clone/wear/head/、clone/weapon/blade/ 里有这些文件,所以同名代码在兄弟档不炸——这是迷你版裁剪后的路径 错位,不是内容彻底丢失。vendor_goods 也指向同样的坏 HEAD_DIR 路径,卖花 list/buy 同样会失败。

修复:花与长刀改为 __DIR__ "obj/...",并对所有 carry_object→wear/wield 链加上 §7.73 标准守卫(先赋值再 if (ob))。现场验证:goto 通泗门/安定门/镇淮门/草河小街/ 禾嘉街,士兵与柳烟均正常出现;list liu yan 列出六种花; buy 干菜包子 from xiao er(酒楼)成交;游荡约 20s 后 log/catch 中 0 条 call_other 错误(仅剩启动时已知的 emoted 损坏存档 restore_object 警告,CATCH 住、与本轮无关)。

本轮其它现场验证(无新 bug)

本轮修改的文件

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

scripts/lpc_fix_negative_ranges.py: 4 line(s) in 1 file(s) count a range from the end with < (x[a..-1] -> x[a..<1], x[-2..] -> x[<2..]; KB 06 §7.209). In this driver a negative constant end gave "" / ({ }) and a negative start the whole value, so each of these returned the wrong slice; the compile warning A negative constant as the second element of arr[x..y] is gone from the files that compile. Range lvalues (s[0..-1] = text, the prepend idiom) are left alone. A paired compile of the changed files at HEAD and in the working tree showed no new error.

深度功能测试(§10.7,2026-10-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):d/menpai/wuliang/west/npc/obj/flower.h。改动的文件在 HEAD 与工作树分别加载(新进程),无回退。

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

scripts/lpc_warnings.py xo --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 29 个 .lpc 和 3 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):23 -> 23 通过,无回退;改过的头文件的 4 个包含者 1 -> 1 通过。启动时警告 1 -> 0;新进程登录(输入名字)正常。

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

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

编译警告收尾(2026-10-13)

手工清零(改动及相关对象在 HEAD 与工作树各载入一次,regress 0)。

family 映射(再补)(2026-10-15,KB 05 §7.30)

mapping fam; fam = (mapping)ob->query("family");(先声明后赋值)和泥潭式的 query("family", me) 同样直接取下标:无门派的新人拜师(attempt_apprentice/recognize_apprentice)、NPC 的 greeting、房间的 valid_leave、ask_me 等报 Value being indexed is zero。3 处补上 if (!mapp(fam)) fam = ([ ]);(刚 recruit_apprentice() 之后的除外)。

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

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

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

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

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

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

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

/d/city/yangzhou/npc/obj/guo.lpc:#include <ansi.h>。这些指令接在上一行 // 注释的末尾(档案里丢了换行),成了注释的一部分,驱动看不到:缺 inherit 的技能报 Undefined function offensive_target,缺 #include <weapon.h> 的兵器报语法错误。scripts/lpc_fix_swallowed_directive.py 把换行放回去。载入检查 1 个文件,1 个原来编译不过、现在载入,没有退步。

补回本库没有的基类文件,第二轮(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 对比载入,没有退步。

标准整理轮,第二轮(2026-10-19,scripts/toolkit_fix.sh)

本库在新驱动下重扫,机械修复:1 个带初始化的未使用声明。修后与 HEAD 逐个物件对比载入,没有退步。

补回丢失的标点(2026-10-19,KB 03 §4.6)

转换时丢掉的分号、逗号、引号或文件末尾的 },共 1 个文件,修前 HEAD 载入失败。一行文字在逗号前丢了结尾的引号(下一行是同一个表的下一项),补上:/d/menpai/wuliang/west/npc/xin_shuangqing.lpc。修后与 HEAD 逐个对比载入:这 1 个物件全部载入,没有退步(scripts/lpc_restore_from_twin.py、lpc_fix_eaten_quote.py --comma、lpc_fix_eof_braces.py)。

set_name 的 id 参数(2026-10-19,KB 06 §7.260)和没有修的 delete()

/clone/wizard/edit.lpc(TMI-2 的行编辑器)里 set_name("编辑器", "editor") 改成 ({ "editor" })。文件仍然载入失败:它用 TMI-2 的 simul_efun delete(数组或字符串, 位置, 个数)(8 处),本库没有这个函数,调用落到物件自己的 delete(string)(属性删除)上,类型不符。这是巫师工具,没有重写。

补回丢失的标点,另一个版本的完好副本(2026-10-19,KB 03 §4.6 第四轮)

/system/menpai/wuliang/zuo_zimu.lpc:"姓名" : "嗯,你既知我姓左,……左子穆?, 丢了收尾的引号,补回;多出来没用到的局部变量声明(master_id、bonus、test)删掉。文件仍然载入失败:refuse_msg 只在 system/std/char/questnpc.lpc 里声明,这个物件没有继承它,是另一处改名遗留,没有改。

无量剑派掌门:补回 refuse_msg、wl-shenfa 改成 wuliang-shenfa、太祖长拳丢掉的引号(2026-10-19,KB 06 §7.258)

同 xajh2 的同名一节:system/menpai/wuliang/zuo_zimu.lpc 丢了 string *refuse_msg 的声明(取自 xajh2 的 d/menpai/wuliang/npc/zuo_bak_xli.lpc,补在 #include "east_master.h" 之后);zuo_zimu.lpc、qu_lianxing.lpc、rong_ziju.lpc 授的 wl-shenfa 是不存在的技能名,改成 wuliang-shenfa;system/skill/misc/taizu-quan.lpc 719 行 ......, 丢了结尾的引号(补成 ......",,文本取自 xo_final/system/skill/unarmed/level2/taizu-quan.lpc)。system/menpai/wuliang/obj/choushan.lpc 还写着 inherit EQUIP;,EQUIP 在 globals.h 里已经注释掉(xajh2 的同一个文件早已改成 #include <armor.h> + inherit CLOTH;,这里照改,两份现在逐字节相同)。qu_lianxing.lpc、rong_ziju.lpc、shang_yuqing.lpc 授的 parry 本库没有技能文件,system/feature/char/skill.lpc 的 set_wugong() 对没有文件的技能 error(),物件在 create() 里中止;set_wugong() 开头加 if (!valid_skill(skill)) return 0;(同文件的 set_xiuwei() 早已这样处理),物件照常载入,只缺这一项技能(KB 06 §7.256)。授了缺失技能的物件(活目录里)共 7 个。修后在 system/menpai/ 和这些物件上对比 HEAD 与工作树:载入 9 → 15(zuo_zimu、qu_lianxing、rong_ziju、shang_yuqing、choushan 和 pangzi),没有回归,没有警告。

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,共 4 处)。改动:/d/city/yangzhou/shufeng_guan.lpc(1 处)、/include/poison.h(3 处)。

tail 指令:tail(file) 驱动里没有(2026-10-20,KB 06 §7.291)

cmds/*/tail.lpc 调用 MudOS 的 tail() efun,FluffOS 没有,指令编译不过。模拟 efun 文件里补了 tail(file)(Discworld 家族的兼容版:文件最后约 20 行,从完整的一行开始,存在返回 1;scripts/lpc_add_sefun.py tail)。

登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)

驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/secure/daemon/sefun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。

翻页器 start_more(read_file(...)):文件缺失时 explode(0)(2026-10-20,KB 06 §7.300)

东方故事系的翻页器 void start_more(string msg) { ... explode(msg, "\n") ... } 被这样调用:start_more(read_file(HELP_DIR + "help/topics"))、start_more(read_file(NEWS))。压缩包没带的文件使 read_file() 回 0,explode(0, ...) 在本驱动里报 Bad argument 1 to explode()(不带参数的 help 指令就是这样出错的),老驱动把 0 当空文本。在 start_more 开头加了 if (!stringp(msg)) return;(scripts/lpc_fix_start_more.py)。改动:/system/feature/user/more.lpc。

巫师命令不带参数时对 0 调用 efun(2026-10-21,KB 06 §7.336)

巫师(管理员)把命令敲成不带参数时,驱动把 0 传给字符串参数;下面的命令第一句就把这个 0 交给 efun,运行时报 Bad argument 1 to ...,这条指令从来没有用过。用 scripts/lpcc_bare.py 不登录、在一次 lpcc 求值里以 0 调用 main() 逐个确认后,由 scripts/lpc_fix_bare_arg.py 在第一处使用参数的语句前加守卫:文件自带 int help(object me) 的,守卫是 if (!arg) return help(me);(help() 定义在 main() 之后的 2 个文件补了原型 int help(object me);,否则编译报 Undefined function help);没有 help() 的是 return notify_fail("…\n");。改动(3 个文件,括号里是接住 0 的 efun):/cmds/admin/changebody.lpc(present)、/cmds/admin/replace.lpc(get_dir)、/cmds/arch/ll.lpc(present)。加载检查 HEAD 与工作树对照,没有退步。

注册到不存在函数的 add_action,第二轮:宏路径父类和静态解析不了的对象(2026-10-21,KB 06 §7.349)

第一轮的静态解析读不了经宏路径继承父类的对象(#define ROOM STD_DIR "room/room" 这类定义的正文以另一个宏开头)和它解析不了的继承,这些对象当作 UNSURE 没有扫;第二轮修了解析器,重扫并对这些对象在运行时问 function_exists()。add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 4 条注册(3 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/clone/misc/bandage.lpc(do_remove)、/d/city/yangzhou/yuelao_ci.lpc(do_divorce)、/system/std/room/kezhan.lpc(DoCheck、DoLoad)。最多的函数名:do_remove 1 处、do_divorce 1 处、DoCheck 1 处、DoLoad 1 处。

写死的 "/路径"->函数() 指向库里没有的文件:改成现存的同名文件(2026-10-22,KB 06 §7.359)

2 处语句级的 call_other 字面量路径,库里没有这个文件(区域改了名 / 大小写不对 / 差一个字母 / 文件挪了地方):"/cmds/arch/xplor"->QueryLevel()(/system/daemon/biwud.lpc)→ "/cmds/app/xplor"。call_other() 对不存在的对象抛 couldn't find object,所在函数当场中断。改成库里现存、并且定义了被调用函数的文件(scripts/lpc_fix_call_path.py;巫师目录、备份目录、名字不同的兄弟文件都不算候选)。

长度检查按字节写,旁边的提示按汉字写:4 处(2026-10-22,KB 06 §7.362)

老档的 strlen() 数字节(一个汉字 2 字节),转成 UTF-8 后它数字符:提示写「必须是 2 到 6 个中文字」的检查还是 strlen(name) < 4 || strlen(name) > 12,于是两三个字的房名、帮会名、群名、聊天室名、称号被提示自己说允许的那句话拒绝,上限却是提示的两倍;留言板的标题检查 i < 4 || i > 24 || i % 2 还会拒绝所有奇数个字的标题(post 你好吗)。本库 4 处(3 个文件):if (strlen(msg) > 20)(/clone/misc/chatroom.lpc:69);if (strlen(msg) > 30)(/clone/misc/chatroom.lpc:75)。scripts/lpc_fix_strlen_gate.py 以提示为准:条件里每个 strlen 门槛都恰是提示所说数字的两倍才折半,存长度的变量(i = strlen(arg);)的门槛和 || i % 2 奇偶项、其后循环的 i % 2 == 0 && 一并处理;提示里有英文 / 字母 / 字节的不动。证明(scripts/lpcc_eval.py,bxsj 的留言板,求值对象继承 NPC 并重写 edit() 记录调用,command("post 标题")):HEAD 拒绝「你好」和「你好吗」、放行 23 个字的标题;改后接受前两个、拒绝第三个;shenmo 的 named.lpc invalid_new_name("张三"):HEAD 回 名字只能是2-4个中文字。,改后回 0。抽样 24 + 15 个库的 HEAD / 工作树加载对比 0 退步。

按字节写的汉字循环、茶馆猜诗和同盟频道标题(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 区间 3 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。

血条 / 表情标题按字节算显示宽度(2026-10-23,KB 06 §7.365)

老档把汉字当两字节:bar_string[0..(val * N / max) * 2 - 1] 把「格数」换成字节下标,半格血条在本驱动(按字符算区间)上会整条画满;巫师表情 edemote 的 repeat_string("─", 13 - (strlen(emotion) / 2)) 也会少减汉字宽度。scripts/lpc_fix_display_pad.py:去掉 CJK 血条 / 空白条上的 * 2(jings/qis 赋值和 blank_string3[0..(23-a)] 的 23→11 一并改),表情标题去掉 / 2;ASCII 空格的 none_string / empty_string 仍保留 * 2(一格两个空格)。本库 2 处(1 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。

estate ROOM_HEAD 宏引号 / #inherit / dir / retrun(2026-10-27,KB 04 §6.25 Also)

system/daemon/estate 的 ROOM_HEAD/EXIT_STRING 宏里裸双引号打断 #define,模板写成 #inherit AREA,CreateRoom 用未定义的 dir,AddItem 写 retrun 且参数 class Location 本库无此类型。改为转义引号、inherit AREA、out_dir、return、mixed location。证明:HEAD 失败;改后加载。

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

u/xli/mooncake2 的 do_write 在 tell_object(me,"你将祝福写在 处截断,且 if 缺 )。用同目录完好的 mooncake 的 do_write 覆盖。证明:HEAD 失败;改后加载。

深度功能测试(2026-10-27,§6.25 tell_object 字符串目标)

xinfang/chatroom/pplroomd:tell_object("ob", …)→tell_object(ob, …)。

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