Book and Sword World

✅ 可玩

书剑天下 (ShuJian MUD)

bxsj

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

▶ 开始游玩 · Play Now

重庆电信 ShuJian MUD Wizard Group 自 1997 年起开发维护的经典武侠 MUD,与 bxsj1《书剑·经典》同源,共享大部分核心引擎代码。新人从"武馆前院"起步,沿东练武场、设有木人陪练的东武场、兵器库一路探索,是一套完整成型的新手引导区;木人会原样复制对手自身的属性技能来对练,是真正意义上的安全陪练。拜师系统有两条路:既可以循序渐进地拜入武当宋远桥等正统门派掌门门下,也可以直接找游戏内建的"礼物使者"报出门派名,瞬间加入十五个门派中的任意一个并领取满级绝技全套装备——这是官方明示的新手鼓励捷径。`ask <npc> about name/rumors/here` 等江湖打探指令几乎对所有 NPC 都有效,`rumor` 频道匿名传话、`chat` 频道显示真实姓名,社交玩法比较成熟。

English

A classic wuxia MUD developed and maintained since 1997 by the ShuJian MUD Wizard Group at Chongqing Telecom. New players train in a full beginner region at the martial hall, complete with a training dummy that mirrors an attacker's own stats for safe sparring, and can join any of fifteen sects either through formal apprenticeship or a built-in "gift envoy" shortcut for newcomers.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/bxsj/

管理员账号 / Admin account

警告:Mud@2026 是本地游玩用的公开默认密码。若要正式对外开放主机,
请先修改此密码。

本地运行

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

游戏端口:40004。

NOTES · 移植与修复记录

bxsj.rar → bxsj

Status: DONE — boots clean, playable over telnet

Reached name entry, length validation, existing-ID detection, and the password prompt — full login flow confirmed working. This lib surfaced three genuinely new (to this project) driver-interaction bugs, all generalizable patterns now cataloged in AGENTS.md.

What was fixed

1. Encoding: 10865 converted, 737 already UTF-8/ASCII, 75 skipped binary, 36 lossy. Found the convert_lib.sh binary-detection bug here (AGENTS.md, "Encoding — file's text/binary guess is not reliable enough to gate on"): file misclassified several genuine GBK source files as data (unusual CRCRLF line endings apparently confuse its heuristic) and the script silently skipped converting them, leaving raw GBK bytes in work/. Fixed the shared script (forces known text extensions regardless of file's guess) AND manually converted the 9 affected files found here (feature/save.lpc, clone/user/ olduser{,2}.lpc, adm/daemons/bbsd.lpc, cmds/std/testsjmud{1,2}.lpc, cmds/arch/ucp.lpc, cmds/wiz/movedata{2,cq}.lpc) plus re-scanned libs #1-3 for the same issue (clean, 0 hits — this bug's impact was specific to whatever tool/era produced this particular archive). 2. .c → .lpc: 9936 files. 834 literal .c" refs auto-fixed, 0 left over. 3. static → nosave: 199 files. 4. No §4 (load_object in valid_read) issue in this master — checked, valid_write/valid_read call SECURITY_D via find_object only (no lazy load_object), so no recursion risk there. 5. New bug (AGENTS.md §8, extended): master.lpc's log_error() — the driver's own error-reporting callback — calls CHANNEL_D->do_channel(...) unconditionally. If CHANNEL_D isn't actually loaded at the moment a PRELOADED file fails to compile (which is exactly when log_error() runs), that call needs a fresh compile, which is forbidden mid-compile → Object cannot be loaded during compilation → which re-enters log_error() → cascades. This fired repeatedly during boot for multiple different failing preload entries. Fixed with the same reentrancy-guard + catch() shape as AGENTS.md §4. 6. New bug (AGENTS.md §14 cousin): master.lpc's valid_override() was 2-arg (missing the main_file parameter needed for efun:: calls inside #included simul_efun fragments) AND defaulted to deny (return 0) rather than allow for anything not explicitly listed — stricter than lib #1's version. Added the 3rd parameter, plus an explicit allow-list for the simul_efun message-formatting wrappers (shout/write/tell_object/tell_room/say) that cmds/adm/ad.lpc needed via efun::shout(...). 7. New bug, the real blocker for this lib (AGENTS.md §8c) — took the most effort to find: securityd.lpc's valid_read() had if (this_player()) user = this_player(); unconditionally, which wrongly attributed a SYSTEM-initiated lazy load_object() (master loading BAN_D/band and UPTIME_CMD/cmds/usr/uptime, neither preloaded, both first touched from inside a brand-new connection's own login sequence) to that connecting player's own unprivileged permissions instead of the real (root-euid) caller — an exclude_read rule protecting /adm from ordinary players then denied it, which permanently stranded every single new connection at the login banner (no compile errors anywhere — looked like a total, silent hang). Diagnosed via a temporary instrumented master.lpc valid_read() (logged file/user/func/result to a scratch file) rather than guessing from the generic *Read access denied. message — found the exact two denied load_object calls in minutes. Fixed: only override with this_player() when the passed-in user doesn't already have a resolvable euid/uid.

Known remaining issues (documented, not fixed)

How to run

cd libs/bxsj
~/src/fluffos/build-debug/src/driver config.fluffos
python3 ../../scripts/mudclient.py 127.0.0.1 40004 --timeout 10 --send "n" --send "testuser" --send "quit"

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

Retroactive fix (found via archive #90, jinyongwenzi): this lib was completely command-dead after registration (AGENTS.md §15ae + a new commandd.lpc variant)

Archive #90 (金庸文字版.exe) turned out to be the literal same codebase as this lib (byte-identical master.c/securityd.c/chinese.c after CRLF normalization -- confirmed via diff, not assumed) processed much later in the project, after §15ae (the private nomask command-hook bug) had already been discovered and fixed elsewhere. That later pass found this exact lib had TWO independent bugs compounding into the same symptom -- every single post-login command (even look) silently doing nothing at all, with zero error anywhere:

1. feature/command.lpc's command_hook() was private nomask -- unreachable via add_action's external dispatch on this driver (§15ae). 2. adm/daemons/commandd.lpc's rehash() filtered get_dir() output via sscanf(cmds[i]+"$", "%s.c$", cmds[i]) -- a live sscanf pattern invisible to both the .c→.lpc rename's quoted-string fixer and the bare-data-file fixer, since it's neither. After the rename this matched zero files forever, so commandd's command-search table was never populated and find_command() always returned 0 -- a second, independent cause of the exact same symptom. Fixing only one of the two would still have left every command dead.

Both fixed here (dropped private; changed the sscanf pattern to "%s.lpc$"), then re-verified with a full fresh registration (real name, one of "秦风"/"秦岭"/"秦河" tested across a few runs) followed by look, score, and quit all producing correct real output, and debug.log confirmed clean (0 error: lines). This lib's ORIGINAL testing pass (see above) never verified a post-login command -- exactly the blind spot §15ae warns about -- so this had been silently broken since this lib was first marked "done," undetected until the sibling codebase surfaced it. See AGENTS.md §15ae and the new commandd.lpc sscanf-pattern addition for the general writeup.

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

WASM-enablement pass (2026-07 standard: loopback-allow, throttle exempt, admin seed)

Gates patched (all short-circuit loopback / malformed-empty IP first, original logic intact below):

Notes:

Admin account: id fluffos / Mud@2026 / 浮浮, granted (admin) via adm/etc/wizlist. Verified update + goto work. Save files for the orchestrator to force-add: libs/bxsj/work/data/user/f/fluffos.o and libs/bxsj/work/data/login/f/fluffos.o (untracked new dirs; not gitignored).

Retest: fresh registration (id fluffos, name 浮浮) reached 武馆前院, look/score correct. Pre-existing content errors unrelated to this pass remain in debug.log (message() bad-arg in user.lpc user_dump(); call_other() bad-arg from NPC 桃花 carry_object).

Retrofit (2026-07-24): fail-closed loopback check (security correction)

The loopback-allow gate patched above originally also treated a non-string/empty/malformed query_ip_number() result as loopback (a defensive stand-in for the WASM driver bug). That driver bug is now fixed upstream, so this was tightened to fail-closed: only an exact "127.0.0.1" / "127."-prefix / "::1" match bypasses the gate; a malformed or non-string address now falls through to the original gate logic (treated as untrusted/remote) instead of being auto-allowed. Re-verified fluffos login still works after tightening.

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

First real *playthrough* pass on this lib (all prior passes verified only registration + look/score/quit + admin login, or watched boot output). Played as an ordinary new player through most systems, native driver (build-debug), with a WASM re-verification of the one fix made. Test character: id qintest, Chinese name 秦风, password Abc12345 — kept (not cleaned up) as a representative playthrough character; it joined 武当派 (Wudang) via the newbie gift-envoy shortcut and has a reasonably rich state (skills, family, rank-table entry) that's useful evidence of a working system. Save files: work/data/user/q/qintest.o, work/data/login/q/qintest.o.

Bug found and fixed

cmds/usr/top.lpc — unbounded per-hour rank-decay loop crashed on literally every quit (and on top), file:line cmds/usr/top.lpc:395-401 (the live add_rank()) plus four sibling copies of the identical pattern in add_rank_beauty() (~179), add_rank_pk() (~224), add_rank_rich() (~271), add_rank_worker() (~338).

What was tested and confirmed working

Full continuous playthrough as qintest (ordinary new player), plus one wizard-flavored digression through the game's own "newbie gift" NPC (see below) to reach the menpai/skills systems without a long trek:

Methodology notes (for the broader pass this seeds)

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

书剑 MUD 家族的基础版本。状态已从过时的 limited 修正——这份档案自己的 NOTES.md 记录了一次完整的原生驱动深度功能测试(2026-07-24),以普通新玩家身份走完了注册、移动、人物信息、战斗(木人安全对练机制)、门派/技能系统,并在过程中发现并修复了一个真实 bug(cmds/usr/top.lpc 的 add_rank() 系列函数:一个不设上限的按小时衰减排名循环,一旦这份 2008 年代的存档数据配上现代系统时钟运行,每一次 quit/top 指令都会撑爆求值成本上限——已限制在 240 次迭代以内)。只有商店/经济系统和死亡/复活没有被实测覆盖到(是测试覆盖缺口,不是已知损坏)。这份档案自己的 README 没有记录任何缺陷,本轮也没有发现新的。本轮在当前 WASM 驱动构建下重新验证了预先播种的管理员账号登录(fluffos/Mud@2026,look/score/quit 全部正常,"您目前的权限是:(admin)")——由于 NOTES.md 里 2026-07-24 的深度测试已经完整覆盖过注册流程,本轮没有重跑注册;同一代码家族的手足档案 bxsj1 本轮则重新完整验证过一次注册流程,结果一致。

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

Deep functional test round two (2026-08-14)

Independently re-verified every §10.7 checklist item against current code rather than trusting the 2026-07-24 writeup above. One proactive hardening fix applied; the one real bug found in round one (top.lpc's rank-decay crash) was re-confirmed still fixed via a genuine live regression test on the same character that originally triggered it, not just by grepping the diff.

Fix: adm/simul_efun/file.lpc's cat() had no null-guard on read_file()

Standard proactive hardening for this session's checklist (§7.11-adjacent): cat() called write(read_file(file)) unguarded — read_file() returns 0 for a missing/unreadable file, and write(0) is a latent throw risk.

// BEFORE:
void cat(string file) {
  write(read_file(file));
}
// AFTER:
void cat(string file) {
  write(read_file(file) || "");
}

log_file() in the same file was already correctly hardened — it delegates to LOG_D->log_file() (adm/daemons/logd.lpc), which unconditionally calls assure_file(file) before every write regardless of the optional db flag (the if (db) only guards the preceding dbquery() call — misleading indentation, correct semantics, verified by reading the actual statement boundaries rather than trusting the visual layout). No callers in this lib pass a truthy db flag, so the MySQL dbquery() branch is dead code, not exercised, not a live risk.

Checked and ruled out as not applicable to this lib: no log_error() severity-gate case-mismatch (none found in adm/single/master.lpc), no dbase.lpc password-write-guard shape (this lib's admin seeding doesn't use that pattern), no %-operator-on-corrupted-float shape beyond the already-corpus-fixed quest_times/win_times class (the only unguarded % hits found were unrelated weight-formatting arithmetic in d/city/weapon/weapon*.lpc, not attribute counters), no §8.9 food/water wrong-object read, no printf debug leaks in logind.lpc.

Re-verified live: top.lpc rank-decay crash (round one's fix) still holds

Logged in as qintest (the same character whose quit originally reproduced the crash on 2026-07-24) rather than trusting the diff was still applied. top correctly rendered the real ranking entry (综合评价 3470), and quit — the exact original crash trigger, since cmds/usr/quit.lpc unconditionally calls TOP_CMD->add_rank(me) — completed with zero new work/log/debug.log lines (checked the file's line count before and after: unchanged). The original crash was invisible in the player-facing output even when it was happening, so a clean-looking quit message alone isn't sufficient evidence; the debug.log line-count check is what actually confirms it.

Verification method

Booted native build-debug driver, admin login (fluffos/Mud@2026) with update /adm/daemons/logind as the real privileged-action check (succeeded — "重新编译 ... 成功!"; adm/simul_efun/file.lpc itself is #include'd into adm/single/simul_efun.lpc, a container object, so it can't be update'd directly — same limitation documented elsewhere this campaign). Two rapid consecutive admin reconnects, both clean, both landing a full fresh login (上次连线 timestamp updated each time). One login attempt as qintest timed out mid-password-entry on the login procedure's overall timer (clone/user/login.lpc's time_check()/time_out()) when sent via too many separate tool calls; resolved by sending the whole id+password sequence as a single batched tmux_mud.sh multi call with a slightly longer per-line wait — not a mudlib bug, a test-harness pacing issue (this lib's overall login-procedure timeout is comparatively tight). Noted a stale FATAL ERROR: SIGTERM: Process terminated line at the tail of work/log/debug.log predating this session's own boot — the driver (confirmed via readlink /proc/<pid>/cwd) stayed alive and responsive throughout testing, so this is leftover content from an earlier, unrelated process instance sharing the same log path, not a live crash; not investigated further since it didn't correlate with any actual failure during this pass. Driver killed by exact PID after testing; incidental fluffos.o/qintest.o save-timestamp churn reverted before commit.

Files modified this pass

Round three deep functional test (2026-08-18)

Went deeper than rounds one/two per the standing checklist: real combat death/reincarnation (not just code review), board post/read, shop dispatch, and a persistent-guild command smoke test — plus a project-wide sweep for three newly-catalogued bug patterns (AGENTS.md §7.111/§7.112, and the §8 logind.lpc enter_world() missing-save shape).

Bug found and fixed: §7.112 unguarded reincarnation-chain init() (2 live instances + 1 hardening)

Grepped every init() containing a call_out( corpus-wide (2,278 files have init(), 543 also schedule a call_out; narrowed to 81 candidates lacking any existing remove_call_out/query_temp guard, then to the ones whose scheduled function recursively re-schedules itself — a genuine multi-stage chain, not a one-shot timer). Two real hits, both in the death/reincarnation zone (/d/death/), exactly matching the reference shape from haiyang2/jym's wgargoyle.lpc:

1. work/d/death/npc/death.h (included by mengpo.lpc/孟婆, yanluo.lpc/阎罗, pusa.lpc/菩萨 — the three reincarnation-guide NPCs a dead player's ghost meets) — init() unconditionally scheduled a 5-stage death_stage() message/teleport chain (first stage 20-40s out, then 5s between stages) with no guard. Since the driver re-broadcasts init() to every NPC in a room whenever a living object's commands are enabled (including on a player reconnect), a player who disconnects and reconnects while standing in front of one of these NPCs — very plausible given the 20-60s window — would stack a second parallel chain: doubled dialogue, and a second reincarnate()/move at the end. 2. work/d/shaolin/npc/yu-zu2.lpc (狱卒, a Shaolin-jail guard) — same unguarded shape, its own private 5-stage death_stage() chain (60s between stages) ending in ob->move("/d/shaolin/woshi1"). Grep found no room anywhere in the corpus that currently places this NPC (a sibling yu-zu.lpc, no "2", is the one actually used in d/shaolin/jianyu1.lpc, and it already has its own correctly-guarded remove_call_out+call_out pattern) — yu-zu2.lpc looks like dead/orphaned code today, but fixed it anyway since the shape is real and harmless to guard.

Fix (matching the documented reference shape): a death_stage_active per-player set_temp()/query_temp() guard around the call_out() scheduling call in init(), cleared at every exit point of death_stage() (the ghost-already-resurrected early return, the target-left-the-room early return, and the final completion branch).

Proactive hardening in the same zone: work/d/death/gate.lpc (地门/鬼门关 — DEATH_ROOM, the very first room a dying player lands in) had the identical unguarded shape one level up — its own init() unconditionally scheduled a call_out("run", 1, me) that randomly routes the ghost into gateway/mpting (yanluo/mengpo). Narrower window (1s) and more benign outcome (a duplicated flavor-text line, and a possible unwanted second random reroute) than the death.h chain, but same class of bug and directly in the path being live-tested here, so hardened it with the same gate_run_active guard shape for consistency.

Live-verified end-to-end, twice:

Not a match — checked and ruled out: work/adm/single/master.lpc's standard_trace() (§7.111) formats error["object"] with %O (safe, never throws on a non-object), not an unguarded file_name(error["object"]) — this lib's error handler doesn't have the §7.111 shape at all. adm/daemons/logind.lpc's enter_world() (the §8 logind save-guard check) already unconditionally calls both user->save() and ob->save() right after setup() completes — not commented out, not missing.

Live-verified: economy/shop (closes another round-one gap, partially)

Round one flagged shop transactions as reviewed-but-never-actually-run. This pass: goto'd directly to /d/xiangyang/zahuopu (牛老板's general store) as admin, ran list (correct item/price/stock table), then buy deng long — item-id resolution worked correctly (vendor.lpc's is_vendor_good()/do_buy() dispatch fired without error), and since admin had no money, MONEY_D->player_pay()'s real insufficient-funds path executed and returned the correct in-character rejection ("穷光蛋,一边呆 着去!"). Confirms the shop's full command-dispatch and payment-check code path executes cleanly end-to-end; a successful purchase (money actually changing hands) is still not verified — no in-game money-granting wizard command exists in this lib (checked cmds/wiz/*, cmds/adm/*; no clone/cash/give tool), and legitimately earning coin would require a genuine trek/job. Left as a to-do, same as round one.

Live-verified: board post/read (new coverage)

As admin on the 巫师留言簿 (/d/wizard/wizard_room's wizard board): post <title> (title must be given inline, then drops into the real ed-style multiline editor via me->edit(...)) → wrote a body line → . to end — got "留言完毕" (posting complete), board count went from 124 to 125, and read 125 played back the exact posted title/body/author/timestamp correctly. Confirms bboard.lpc's post/read path (including the §7.86-fixed inherit/replace_program shape from the earlier corpus sweep) is still fully live-functional, not just "loads without crashing."

Smoke-tested: persistent-guild commands (cmds/group/*, new coverage)

Distinct from the menpai/sect system (bai/apprentice, tested in round one) — this lib also has a separate persistent-faction system (gcreate/glist/gmove/grant/gforce/abdicate/destory, dispatched by filename via commandd.lpc, not add_action). glist rendered a correctly-formatted (empty) faction leaderboard without error. gcreate with a syntactically-valid Chinese faction name (测试帮, ends in a legal suffix character) correctly rejected with "抱歉,当前只允许贵宾玩家才能创 建帮派" (VIP-only gate) — read gcreate.lpc: this is a genuine, deliberate VIP paywall, not a bug, so left alone per scope discipline. Confirms the command-dispatch and argument-validation paths for this whole subsystem are alive; full faction creation remains unverified (would need a VIP account).

Methodology notes

Flag for cross-lib awareness (not fixed elsewhere, per instructions)

The §7.112 pattern (unguarded reincarnation/staged call_out() chain in a room or NPC's init()) was found here in the death-zone specifically. Any other lib in this corpus with its own /d/death/-style reincarnation-guide NPCs (or any other multi-stage init()-scheduled process) is worth the same grep sweep (call_out( inside init(), minus files that already remove_call_out or check a temp flag first) — this is the third or so independent lineage this exact shape has now been found in per today's (2026-08-18) session notes.

Files modified this pass

§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). 24 live

replace_program(ROOM); occurrences across 24 files (d/wanshou/*.lpc, data/group/groom/*.lpc, d/cangzhou/dangpu.lpc) — same fatal redundant-replace-after-inherit shape as the rest of the §7.100 family. This lib's room-building tools (clone/misc/roommaker.lpc, cmds/debug/roommaker.lpc) never had the bug baked into their templates, so no factory-side fix needed. One pre-existing already-//-commented instance (d/xueshan/jlshan.lpc) left untouched. Fixed by deleting the redundant lines (binary-mode script, 24/24 matched git diff --stat). Verified via a clean native driver boot (zero new debug.log errors, port listening, killed by exact PID after ~8s).

``§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 d/death/npc/death2.h 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.

Note: the touched header here (death2.h) turned out to be dead/unreferenced code in this lib -- the live NPCs (yanluo.lpc/mengpo.lpc/pusa.lpc) actually #include a sibling death.h, which was already guarded from an earlier fix. This edit is a harmless no-op; no live vulnerability existed in this lib for this file.

§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): 4 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.

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: 5 NPC files call setup()/reset_me() from init() (e.g. data/group/npc/hufa.lpc, d/gb/npc/xixia-wushi.lpc, d/gb/npc/xiaox-wushi.lpc), 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() and cmds/std/sleep.lpc's wakeup() both call enable_player() again while the object is still living(). 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 every legitimate re-enable (revive/wakeup) unaffected. enable_player() had a single fall-through exit (no early returns), so one guard-at-top + one clear-at-bottom pair was sufficient. 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: 5 line(s) in 2 file(s) count a range from the end with < (x[a..-1] -> x[a..<1], x[-2..] -> x[<2..]; KB 06 §7.209). In this driver a negative constant end gave "" / ({ }) and a negative start the whole value, so each of these returned the wrong slice; the compile warning A negative constant as the second element of arr[x..y] is gone from the files that compile. Range lvalues (s[0..-1] = text, the prepend idiom) are left alone. A paired compile of the changed files at HEAD and in the working tree showed no new error.

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

房间基类 inherit/room/room.lpc:make_inventory() 用 catch() 包住 unew(file)(缺档时它在 simul_efun 里对 0 调用 query("unique")),else 分支只在确有克隆时才取 [<1]。档案里没有的 NPC 或物品(或 create() 出错的物件)原先会让列出它的房间整个加载失败(Bad argument 1 to environment(),或对 0 的 call_other()),连带这个房间后面的区域;现在房间照常加载,只是少了那个物件,下次 reset() 再试(KB 05 §7.25;scripts/lpc_resilient_room.py、scripts/lpc_room_reset_guard.py)。 验证:探针(scripts/lpc_room_reset_probe.py:房间基类列出一个不存在的物件,再调用 reset())HEAD 单件 崩溃、多件 崩溃,工作树 单件 OK、多件 OK。

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

scripts/lpc_warnings.py bxsj --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 515 个 .lpc 和 11 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):505 -> 505 通过,无回退;改过的头文件的 12 个包含者 11 -> 11 通过。启动时警告 17 -> 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 改了 10 处比较、0 处去后缀(如 d/xueshan/zanpugc.lpc);改动的文件 HEAD 与工作树分别加载(新进程),无回退。

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

深度功能测试(§10.7,2026-10-11):全库严格 UTF-8 检查

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

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

全库编译警告 89 -> 0(scripts/lpc_warnings.py bxsj),逐条按 KB 04 §6.10 核对后修:

改动的 37 个文件及包含改动头文件的对象共 40 个,HEAD 与工作树各载入一次(新进程):40 个对象载入无回退(regress 0);lpc_audit_removed_locals.py 0 处待查。

负数伤害当作消耗(2026-10-15,KB 06 §7.241)

receive_damage("neili", -52)、receive_damage("qi", -12) 之类把 add() 的负数写进了 receive_damage():本库 feature/damage.lpc 对负数伤害报错(或归零),练功/运功时报 F_DAMAGE: 伤害值为负值 或消耗失效。已改为正数伤害,内力/精力类改为 add(type, -N)(scripts/lpc_fix_damage_calls.py):/kungfu/skill/fanchang、/kungfu/skill/hujia-daofa、/kungfu/skill/huntian-qigong/huntian、/kungfu/skill/lanhua-shou、/kungfu/skill/longxiang-boruo/fanchang、/kungfu/skill/yiyang-zhi。

无门派玩家的 family 映射(2026-10-15,KB 05 §7.30 第三种)

mapping fam = ob->query("family"); 之后直接 fam["family_name"]:没有门派(新人或叛师后)的玩家在学习、练功、拜师、进入门派房间时报 Value being indexed is zero,而不是作者写好的提示。11 处在声明行加上 if (!mapp(fam)) fam = ([ ]);(scripts/lpc_fix_family_guard.py)。

family 映射(补)(2026-10-15)

mapping fam = (mapping)me->query("family");(带类型转换的写法)同样直接取下标,2 处补上 if (!mapp(fam)) fam = ([ ]);。

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() 之后的除外)。

技能按另一门武功的等级取招(2026-10-15,KB 06 §7.243)

这些技能文件是从别的武功复制改名来的,query_action() 仍用原武功的等级查招式表:只会本技能的人等级为 0,取不到招,每次出招都报错。改为读本技能自己的等级(scripts/lpc_fix_action_level.py):kuihua-shengong。

把经验、属性、内力当技能读(2026-10-15,KB 06 §7.247)

当前内力、精这类名字有时指的是内功技能,逐处读过后手改:lengquan-shengong 的 ob_hit() 用 query_skill("neili") 比较双方内力,恒为 0 对 0,只会走“化去一部分”的分支,改为 query("neili")。

技能名拼错(2026-10-15,KB 06 §7.249)

query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):jinshe-zhang→jinshe-zhangfa(jinshe-jianfa/kuangwu、kuangwu);qimen_baguazhen→qimen-baguazhen(suibo-zhuliu/wuzhuan);xixing-dafa→xixin-dafa(tianlong-shengong)。

技能名拼错:技能文件里拼错自己的名字(2026-10-15,KB 06 §7.249)

技能自己的文件(或它的绝招目录)里,query_skill() 读的是差一个字母的自己的名字,永远得 0:“基本功夫要高过本技能”之类的限制从不生效,按等级取招、计算伤害的那一项一直是 0。改为本技能的名字(scripts/lpc_fix_skill_typo.py 的 self 判据):xuantia-wuji→xuantian-wuji(xuantian-wuji/wuji)。

地图数据库 mapdb 读到只有文件头的 map.o(2026-10-15,KB 06 §7.250)

本库的 data/npc/map.o 只有一行文件头:genmap 第一次载入时 if (!restore()) save();,地图还没建就存了档,值为 0 的变量不写进去。mapdb 的 restore() 读它成功,map 却成了 0,query_room_info()、query_room_exits()、query_map() 都对 0 取下标或把 0 交出去:任务系统 job_area.h 的 get_arearoom() 在 keys(room) 报错。mapdb(/clone/obj/mapdb.lpc)加一个 restore():读档后 map 不是映射就重置为 ([]);地图为空时 query_random_room_path() 返回 0。地图本身仍需巫师用 genmap 生成。

npc_setup 的浮点截断警告(2026-10-15,KB 04 §6.10)

/clone/obj/npc_setup.lpc:level = pow(10 * exp, 0.333334); 把浮点数隐式截成整数,编译时警告;改为 to_int(...),结果不变。

燃木刀法「焚天」的效果一秒就消失(2026-10-15,KB 06 §7.246)

每秒检查的 check_fight() 声明了 object weapon; 却从不赋值,条件里的 !weapon 永远为真,效果在第一次检查时就被撤掉。把这一项改为 !objectp(weapon = me->query_temp("weapon")),先存兵器再判断刀的种类。

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

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

NPC 带着不存在的物品:simul_efun unew() 报错(2026-10-18,KB 06 §7.73)

adm/simul_efun/object.lpc 的 unew(str)(带 unique 判断的 new())先 ob = new(str),紧接着就读 ob->query("unique")。物品文件不存在时 new() 返回 0,读 0 的属性报 Bad argument 1 to EFUN call_other(),从 carry_object() 一路带出去:create() 里带着缺失物品的 NPC 载入不了(jh2006 的苗疆何铁手一类,该有 NPC 的房间少了人)。读属性之前补 if (!ob) return ob;(int i = ob->query("unique"), j, k; 拆成声明和赋值两句),缺的物品跳过,NPC 照常载入(scripts/lpc_guard_carry_object.py --unew)。载入检查:改动的文件改前能载入 1 个,改后 3 个,没有退步。

clone/spec/putao 的 long 里引号错位(2026-10-18,KB 03 §4.6)

set("long","这是一串由达摩传入中土HIG"神奇葡萄"NOR",……"):开引号落在 HIG 后面而不是前面,字符串在 HIG 处就闭合了,后面的 神奇葡萄 成了代码(Illegal character 0xe7),整个物件载入不了。把引号挪到 HIG 前面(中土"HIG"神奇葡萄"NOR",,与同系其他库的同名文件一致)。载入:通过。

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 载入不了,刷出它的房间也少一个人。本库有 2 个对象设了本库没有文件的武功(22 个名字,最多的是 kick、liangyi-dao、pishi-poyu)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:2 个里 1 个通过,其余 1 个停在别的错误上(clone/obj/npc_setup:*No program in object '/clone/obj/npc_setup'!)。(scripts/lpc_guard_set_skill.py)

通向不存在房间的出口,按对面房间的回头出口改正(2026-10-18,KB 06 §7.257)

房间出口 "west": __DIR__"xxx" 指向的文件本库没有,玩家走过去就被卡住(笔误、改过名的房间,或者没带进档案的编号房间)。对面的房间存在、并且它相反方向的出口正好指回这个房间,说明出口原本就是通向它:/d/fuzhou/nanmen 的 south:birdge → bridge;/d/shaolin/nanyang 的 south:yidao2 → hanshui2。改动后每个出口都能解析到本库的文件(scripts/lpc_fix_dead_exits.py;目标在运行时本来就能载入的库——虚拟房间——不动)。

武功名拼错的 NPC(2026-10-19,KB 06 §7.258)

set_skill("kunlun-shnfa", …)、map_skill("dodge", "shaolin-shnfa") 这样的调用把武功名拼错了:本库没有这个武功文件,有的是拼对的那个。以前这种 NPC 在 create() 里报 No such skill 载入不了;武功缺失时跳过之后它们能载入,可是少了那门武功(set_skill 被跳过,map_skill 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 35 处、35 个文件:shaolin-shnfa→shaolin-shenfa(14 处)、kunlun-shnfa→kunlun-shenfa(11 处)、yijinjing→yijin-jing(2 处)、guiyuan-tuanfa→guiyuan-tunafa(2 处)、banrou-zhang→banruo-zhang(2 处)、qinmang-jian→qingmang-jian(1 处)、huashen-shenfa→huashan-shenfa(1 处)、weiituo-gun→weituo-gun(1 处)等共 9 个名字。(scripts/lpc_fix_skill_names.py)

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

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

区域物件的零碎修正(2026-10-19,KB 06 §7.267–§7.271)

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

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 04 §6.15、§6.18)

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

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

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") && ...):/clone/food/yuyou.lpc(4 处)。

x != "a" || x != "b" 和 x == "a" && x == "b":同一个值的比较链永远成立/永远不成立(2026-10-20,KB 06 §7.282)

if (arg != "wan" || arg != "丸") return notify_fail(...) 对任何 arg 都为真,dir == "south" && dir == "north" 永远为假:条件下面的守卫要么永远拦、要么永远不拦。作者要的是“既不是 A 也不是 B”(&&)或“是 A 或 B”(||)。!= 链的 || 改成 &&(仅当条件下面是 return 的拒绝/守卫),== 链的 && 改成 ||(scripts/lpc_fix_cmp_chain.py)。改动:/kungfu/skill/anran-zhang/jishi.lpc(1 处)。

if (x = 常量):条件里写成赋值的比较(2026-10-20,KB 06 §7.290)

if (arg = "button" || arg = "按钮")、if (!arg || arg = "") return 0;、else if (check = 0 && tempB != ""):把 == 写成了 =,条件的值就是那个常量,分支对所有非零数和任何字符串(连 "" 都算)永远成立、对 0 永远不成立,变量还被覆盖;驱动对此没有任何警告。每一处 = 改成 ==(scripts/lpc_fix_cond_assign.py,共 16 处)。改动:/clone/npc/mj.lpc(3 处)、/cmds/group/glist_new.lpc(2 处)、/d/city/npc/budai.lpc(1 处)、/d/city/obj/budai.lpc(1 处)、/d/sld/npc/stt.lpc(1 处)、/d/sld/sanpo7.lpc(1 处)、/kungfu/skill/qiankun-danuoyi.lpc(7 处)。

socket / dumpsocket 指令:dump_socket_status() 驱动里没有(2026-10-20,KB 06 §7.291)

这些巫师指令 write(dump_socket_status());,MudOS 的 efun 在 FluffOS 里不存在,指令编译不过。在模拟 efun 文件里补了 dump_socket_status():用 socket_status() 排出同样的表(没有 sockets 包的构建,比如 WASM,只有表头;scripts/lpc_add_sefun.py dump_socket_status)。

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

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

峨嵋 d/emei/npc/jing-xin.lpc 找不到 nigu.h,静心师太加载不了(2026-10-20,KB 04 §6.24)

这个 NPC 是 kungfu/class/emei/jing-xin.lpc 的副本(文件头注释还写着原来的路径),#include "nigu.h" 却没有跟着改:nigu.h 只在 kungfu/class/emei/ 里,d/emei/npc/ 下没有,所以静心师太从来加载不了(登录调查的游戏阶段控制台 5 次 /d/emei/npc/jing-xin.lpc:5: error: Cannot #include nigu.h)。改成绝对路径 #include "/kungfu/class/emei/nigu.h",不复制头文件(两份会各自漂移)。加载检查:HEAD 0 通过 / 1 失败,工作树 1 通过,无退化。同一家族的六个库(bxsj、bxsj1、shujian2008、shujian3、sjecl、sjtx2)是同一处。

exchange 指令:数据库结果还没检查大小就取下标(2026-10-20,KB 06 §7.324)

cmds/usr/exchange.lpc 的 Update_Credit() 在声明里就读 ret[0][0],下一条语句才检查 sizeof(ret) == 1:博物馆没有 MySQL,dbquery() 返回空数组,每次敲 exchange 都是 *Array index out of bounds.(指令扫描里的裸指令)。改成 int credit = (sizeof(ret) == 1 && sizeof(ret[0])) ? ret[0][0] : 0;。实测:exchange 显示 您目前可用书剑通宝为:零个……,exchange gold 显示用法,控制台无报错。同一份文件在 bxsj、bxsj1、shujian2008、shujian3、sjecl、sjtx2 六个库里逐字节相同。

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

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

bwscore:比武大会的广场房间档案里没有(2026-10-21,KB 06 §7.299)

玩家指令 bwscore(cmds/usr/bwscore.lpc)不带参数时打印 /d/bwdh/sjsz/square.lpc 的描述,本库没有 d/bwdh(只有 xkm、xkx2000zxb、shenzhou 带着),new() 答 0,0->long_desc() 就是玩家看到的系统错误。改成 (square ? square->long_desc() : ""),用法提示照常。

苗疆山谷 shangu1:第二条蛇写成了 jinshe = new(..yinshe)(2026-10-21,KB 06 §7.346)

烧灵芝兰(do_shao)引出两条蛇:jinshe = new(__DIR__ "npc/jinshe"); jinshe = new(__DIR__ "npc/yinshe");,第二句把第一条覆盖了,yinshe 一直是 0,后面的 yinshe->move(...) 抛错。第二句改成 yinshe = new(...)(sje、jym 的同一份文件是对的)。

张三丰 ask_me_6:who->query("id")(2026-10-21,KB 06 §7.348)

非武当弟子问“太极道袍”时先 command("shake " + (string)who->query("id")),who 声明了没赋值,应是 this_player();抛错在拒绝的 say 之前,什么也说不出来。改成 this_player(),去掉 who。改动:/kungfu/skill/zhang.lpc。

保护任务的商人 clone/obj/job/job_protect_npc.lpc:两处笔误让整个文件从来没编译过(2026-10-21,KB 04 §6.29)

clone/obj/job/protect.h(保护商人的任务)克隆这个 NPC,但文件有两处笔误:int baohu(string age) 的函数体用的是 arg(Undefined variable 'arg'),而 init() 登记的是 do_baohu,函数名也对不上;query->("id") 多了一个 ->。改成 int do_baohu(string arg) 和 query("id")(if (!player = find_player(...)) 报的 Illegal LHS 只是前面语法错误引出来的连锁错误,这种写法本身是对的,没有动);文件第一次能编译后带出 7 个 Unused local variable 警告(sacrifice、exp_rate、skill_rate、lmt、menpai、killer、kill_num),按 KB 04 §6.10 删掉。baohu / protect 两个动词现在有处理函数,任务的商人也第一次能出现。改动:/clone/obj/job/job_protect_npc.lpc。

注册到不存在函数的 add_action(2026-10-21,KB 06 §7.349)

add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 19 条注册(14 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/adm/daemons/robot-judge.lpc(do_test)、/d/city/pinqiting.lpc(do_pass、do_resign)、/d/dali/wuliang/muwu1.lpc(do_tiao)、/d/dali/wuliang/shanlin-6.lpc(do_tiao)、/d/huanghe/npc/mafu.lpc(do_rideyz)、/d/huashan/jinshe/obj/haigu.lpc(do_move)、/d/huashan/npc/yaba.lpc(do_follow、do_kill)、/d/mr/obj/chahua3.lpc(do_chu)、/d/plummanor/hall.lpc(do_answer)、/d/songshan/fengchantai.lpc(do_decide、do_jieti) 等共 14 个文件。最多的函数名:do_tiao 2 处、do_learn 2 处、do_test 1 处、do_pass 1 处、do_resign 1 处。没有动(读过):/clone/obj/job/job_protect_npc.lpc 的 do_baohu(对象本身编译不过,登记留着,等对象能加载再处理)、/cmds/leitai/dzd/dzd_flag.lpc 的 do_check(对象本身编译不过,登记留着,等对象能加载再处理)。

对象路径里的 ..:new(__DIR__ "../obj/x") 报 Read access denied(2026-10-22,KB 06 §7.351)

__DIR__ 是当前文件所在目录(带结尾斜杠),__DIR__ "../obj/x" 就是 /d/zhiye/npc/../obj/x;驱动的 legal_path() 不接受带 .. 的路径,new() / load_object() 直接抛 *Read access denied.(实测,find_object() 对它答 0)。走库自己的 carry_object() / 房间 objects 表的路径,库的基类会先规范化,能用;直接把路径交给 new() / clone_object() / load_object() / move() 的不行,调用到那一步才报错。改成规范化后的绝对路径(scripts/lpc_fix_dotdot_dir.py --direct):/d/dali/yideng/npc/yuren.lpc(__DIR__ "../obj/boat" → "/d/dali/yideng/obj/boat"), /d/dali/yideng/npc/yuren.lpc(__DIR__ "../obj/jiang" → "/d/dali/yideng/obj/jiang"), /d/lanzhou/npc/chen.lpc(__DIR__ "../obj/jiuyin-book2" → "/d/lanzhou/obj/jiuyin-book2"), /d/wudang/npc/xiaocui.lpc(__DIR__ "../obj/nuerxiang" → "/d/wudang/obj/nuerxiang")。

缺失物品的 new() / carry_object() 调用链加了保护(2026-10-22,KB 06 §7.73 补充)

new(路径)->move(...); / carry_object(宏("x"))->wear(); 这类语句,路径指向的文件在这个压缩包里没有:new() 返回 0,->move() 在 0 上报 Bad argument 1 to EFUN call_other()。在 create() 里这个对象永远加载不了,在 reset() 里第一个缺失物品之后的刷新每次 reset 都被跳过。1 个文件里 1 处(new)改成 { object new_ob = new(路径); if (new_ob) new_ob->move(...); }(scripts/lpc_guard_carry_object.py --new:路径按这个文件自己看到的宏解析,宏名冲突、虚拟对象路径一律跳过)。 对这 1 个改动文件做加载检查(HEAD 与工作区对比):HEAD 能加载 1 个,现在 1 个(新增可加载 0 个,回归 0 个)。

调用了、但这个库自己的守护进程里没有的函数(2026-10-22,KB 06 §7.354)

MACRO->函数() 调用的函数在这个库的守护进程里不存在:call_other() 对不存在的函数不报错、只返回 0,所以这些功能(时间显示、昼夜事件、名字检查、价格文字……)一直没起作用。同一血统另一个库的同名守护进程里有这些函数,原文照抄到本库守护进程末尾(scripts/lpc_port_function.py;调用处传的参数个数、函数体用到的函数 / 全局变量 / 宏 / 字符串键都在本库里有,已核对;守护进程在 HEAD 和工作区都能加载,每个函数用固定参数各调用一次:HEAD 返回 0,现在返回正常的值):/adm/daemons/chinesed.lpc ← fengyun434:check_control()(1 处调用)、check_length()(4 处调用)、check_return()(1 处调用)、check_space()(1 处调用)。

(: 函数 :) 指针指向对象自己没有定义的函数(2026-10-22,KB 06 §7.355)

item_desc / inquiry / chat_msg_combat 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:编译不报错,用到时驱动报 *Undefined lfun pointer called: 函数名(look 某物、ask NPC about 话题 直接出错,战斗 / 闲聊里的指针在 NPC 的 chat() 里出错,这一拍心跳的后半段被跳过)。scripts/lpc_scan_callout_targets.py --kinds=lfun。 还剩 1 个函数没有补(inquiry 1):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/d/mr/npc/fu.lpc:ask_dan1。

(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)

inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 1 个文件里 1 行只含这个指针的行("玉露清新散": (: ask_dan1 :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。

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

原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字);各派掌门的辈分判断(name[0..1] == "澄")永远不成立;改名官 zhuce 的长度检查按字节算,合法的名字一个也过不了;NPC 的称呼和提示里取到两个字而不是一个字。

本库 91 处(32 个文件):拼名字的赋值 19 处、与汉字常量比较 52 处、改名官的长度检查 14 处、对切片赋值 3 处、两个名字切片互比 2 处、query("name")[..] 1 处。例:name[2..3] → name[1..1](/d/shaolin/npc/fangsheng.lpc:150);old_name[2..3] → old_name[1..1](/d/shaolin/npc/rong.lpc:66);old_name[0..3] → old_name[0..1](/d/thd/npc/yaoshi.lpc:405)。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,不要重跑。

长度检查按字节写,旁边的提示按汉字写:24 处(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 你好吗)。本库 24 处(7 个文件):if (strlen(cmd) < 4 || strlen(cmd) > 8)(/cmds/group/gforce.lpc:180);if (strlen(rname) < 4 || strlen(rname) > 16) {(/cmds/usr/irc.lpc:135)。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 区间 9 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。

从末尾按字节取汉字、书剑开帮长度(2026-10-23,KB 06 §7.364)

§7.360 只折了数字下标的 [a..b],从末尾取的 [<2..<1](GBK 下一个汉字)和 [<4..<1](两个字)还留着:开帮 gcreate 的帮助写「二至六个汉字 / 3到8个英文字母」,检查却是 (strlen%2)==1 || <4 || >12,末字对 ({ "帮","会",… }) 用 gname2[<2..<1] 永远对不上;少林 / 峨嵋法名 "明" + name[<2..<1]、死讯 killer[<2..<1] != "。"、兵器改名的 can_name 同样坏掉。scripts/lpc_fix_tail_slice.py 只改汉字语境(对照非 ASCII 字面量、can_name / valid_group_name、拼名字),ASCII 补零 / ctime / 复数词尾 / .lpc 不动;[<2..<1]→[<1..<1],[<4..<1]→[<2..<1],开帮门槛按帮助改写。本库 开帮 gcreate 1 个文件;从末尾切片 9 个文件。证明(scripts/lpcc_eval.py bxsj):HEAD 上 斧头帮[<2..<1] 对不上「帮」;改后 [<1..<1] 对上;长度放行 斧头帮/fut;"明"+"张三"[<1..<1] 得「明三」。

Beyond 出口仍指向 /u/beyond(2026-10-23,KB 06 §7.368)

宁波 / 苏州 / 慕容等区域已进 /d/ningbo、/d/suzhou、/d/mr、/d/hz,出口与任务物路径仍写 /u/beyond/…。scripts/lpc_fix_beyond_exits.py 在目标 .lpc 存在时改写(murong→mr)。本库 5 处。证明:sjplii xikou HEAD 西向 /u/beyond/ningbo/aywsi,改后 /d/ningbo/aywsi 可加载;bxsj hubian1 三向均可加载。

无参数时命令对 0 的调用(玩家/--all-dirs 残留,2026-10-27,KB 06 §7.336 Also)

无参数命令把 0 交给 present / sscanf / file_exists 等会运行时报错。lpc_fix_bare_arg.py --all-dirs 在首次使用参数前加守卫。多行 if (arg && !sscanf)(吐纳默认消耗)与已有 can_learn/get_object 守卫的误报已回退;本库本批改动见该提交。

丐帮长老 gb_nz 无法加载(2026-10-27,KB 04 §6.25 Also)

d/gb/npc/gb_nz.lpc:mixed wherei=({ 少了内层 ({(手足 xkxz2 / xkxc98sj 的 d/gaibang/npc/gb_nz 有),query_temp("promotion_target) 缺收尾引号,accept_object 在注释掉的备用奖励块前多了一个 }。补内层数组、补引号、去掉多余 },并删掉未用的 fam。/u/hunthu/ 路径未动(内容)。证明:HEAD 加载失败;改后加载成功。

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