Legend of Jin Yong's Heroes (2008 Standard Edition)

✅ 可玩

金庸群侠传(2008 标准版)

jqxz2008std

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

▶ 开始游玩 · Play Now

本压缩包对应"金庸群侠传"(ES II 引擎)家族最初的"2008版"(标准版)——与本项目中的"2008加强版"(`jqxz2008`)逐字比对,3772 个文件里只有 22 个不同,且都不涉及引擎(房间描写更短、任务条目更少),核心引擎(master、登录、安全模块等关键文件)逐字节一致;同一血脉还包含"2008超豪华版"(`jqxz2008dlx`),并进一步延伸到 `xiakexing3`/`xiakexing2017` 两个更远的快照。与兄弟版本一样,新角色从店小二看店的路边茶馆起步,经中央广场树洞暗语拜入丐帮左全门下,或走白驼山使者按资质招人的另一条路线。城内"武庙"与阴曹地府的死亡转生房间会各自记录角色的固定复活点,是这套引擎里少见的、明确区分"临时安置"与"正式归宿"的复活设计。

English

The base "2008" release of this project's 金庸群侠传/"Jin Yong Heroes Gathering" (ES II engine) family -- a full raw-archive diff against sibling jqxz2008 ("Enhanced") turned up only 22 differing files out of 3,772, none engine-relevant (shorter room text and fewer quest-list entries, consistent with the Enhanced edition's own branding). Same tea-house start, Beggars'-Sect passphrase-hideout join under 左全/Zuo Quan, and White Camel Mount recruiter path as its siblings.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/jqxz2008std/

管理员账号 / Admin account

警告:Mud@2026 是本地游玩用的公开默认密码。若要正式对外开放主机,
请先修改此密码(或从 wizlist 中删除该账号)。

本地运行

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

游戏端口:40084。

NOTES · 移植与修复记录

jqxz2008std — 金庸群侠传 (Jin Yong Heroes Gathering, standard edition)

Archive: archives/金庸群侠传2008版.rar ("Jin Yong Heroes Gathering 2008, standard edition" — plain "2008版" title, as opposed to archive #91's "2008加强版"/enhanced edition). Extracted mudlib root: raw/jy/ (a single top-level jy/ directory inside the archive — same layout as #91). Port: 40084. Status: done (boots clean, full registration flow verified with a real Chinese name, post-login look/score verified working).

Lineage confirmation (evidence, not assumption)

This archive is not merely "the same family" as #91/jqxz2008 — it is the exact same codebase at an earlier/lighter content-configuration point, confirmed by direct md5sum/diff comparison of the raw archives before any conversion:

- adm/tmp/swapfile.ZY054.4000 — a runtime-only artifact present in #91's archive but not this one (leftover editor swapfile, irrelevant). - log/debug.log, log/log, log/USAGE, mud.dat — pre-existing runtime logs/state from whatever original server session produced each archive; irrelevant to source content. - d/shaolin/matou3.c — one NPC/room text file with a shorter room description in this ("std") archive vs a longer one in #91 ("加强版"/enhanced) — consistent with "enhanced edition adds more flavor text." - quest/qlist*.c (20 files) — quest-list content-data files; #91's "加强版" copies have MORE quest entries than this archive's (e.g. qlist0.c here has fewer mapping entries than #91's) — consistent with "加强版 add more quest content on top of the same engine," exactly what the "enhanced edition" branding implies. - Zero differences in any of master.c/securityd.c/logind.c/ chinesed.c/config.cfg/chinese.lpc/command.lpc/combatd.lpc/ name.lpc/any of the other files this pass needed to fix (verified individually below, not inferred).

Practical consequence: rather than rediscovering #91's bugs from scratch, this pass converted the archive with the standard pipeline, then for every fix location documented in #91's NOTES.md, diffed this lib's freshly-converted (pre-fix) file against both the pre-fix state implied by #91's diffs and #91's own fixed file. Every single one of the ~26 files fixed in #91 was confirmed byte-identical, pre-fix, to this archive's own freshly-converted copy (i.e., the exact same bug, in the exact same file, with the exact same surrounding code) — so #91's fixed versions were copied over directly rather than re-typed, with a final diff -q verification pass confirming every copied file now matches #91's fixed version exactly.

Cross-check note carried forward (per #91's own NOTES.md): archives #93 (金庸群侠传2008超豪华版.rar, concurrent sibling agent) and #94 (金庸群侠传2015版.rar, not yet started) are still queued in this same title family. Given how tight the match turned out to be between #91 and this archive (engine 100% identical, only quest-content-depth differs), there's a good chance #93/#94 are further snapshots of the same underlying codebase too — worth a follow-up md5sum cross-check against this lib's (and #91's) core files once both land, the same way this pass cross-checked against #91.

Fixes applied (all ported directly from #91 — verified byte-identical

pre-fix source, not independently rediscovered)

1. AGENTS.md §15h (is_chinese / check_legal_name — GBK byte-range checks silently wrong under UTF-8 strings): - adm/simul_efun/chinese.lpc's is_chinese(): GBK lead-byte range check (str[0] > 160 && str[0] < 255, strlen>=2) → CJK Unified Ideographs codepoint range check (str[0] >= 0x4e00 && str[0] <= 0x9fff, strlen>=1). - adm/daemons/logind.lpc's check_legal_name() (inline in logind.lpc, same as #91 — no separate named.lpc in this lib either): byte-count bound strlen(name) < 2 || > 12 → character-count bound < 1 || > 6; dropped the i%2==0 && even-byte-offset gate in the per-character sliding-window loop. - Verified working: real Chinese name "秦风" accepted on the first attempt during the registration transcript below.

2. AGENTS.md §15ae (private nomask command-hook breaks every post-login command silently): feature/command.lpc's command_hook(string arg) was private nomask — dropped private (kept nomask). home/command.lpc's copy was already correct in the raw archive (confirmed identical to #91's already-correct copy, no fix needed there — same as #91). Verified: look/score both work post-login.

3. AGENTS.md §15t (variant 1): adm/daemons/combatd.lpc had #include </quest/quest.h> (absolute path inside angle brackets, never resolved by this driver's inc_open()). Fixed to #include "/quest/quest.h".

4. AGENTS.md §15t (variant 3): fixing #3 then surfaced quest.h's file-scope global mapping quest_name landing textually before combatd.lpc's own inherit F_DBASE; — fatal "Illegal to inherit after defining global variables". Fixed by moving inherit F_DBASE; above all of combatd.lpc's #include lines.

5. capitalize()-on-0 robustness bug in feature/name.lpc: short(int raw)'s fallback assumed query("id") is always a string; any board/item without an explicit "short" property (e.g. /clone/board/kedian_b, the starting-room bulletin board) has no "id" at all, crashing every look in that room with *Bad argument 1 to capitalize() Expected: string Got: 0. Fixed with a stringp(id) guard.

6. d/city/npc/guidao.lpc's is_killing(who) direct-call type mismatch: is_killing(string id) called directly with a raw object instead of who->query("id") — every other call site in the lib does it correctly. Since it's a same-object direct call (not ->), the static type checker rejected the whole file. Fixed to is_killing(who->query("id")).

7. Duplicate inherit F_UNIQUE; (pre-existing copy-paste typo): d/mingjiao/npc/{yinwushou,yinwulu,yinwufu}.lpc each had it listed twice — fatal "Illegal to redefine 'nomask' function" on this driver. Removed the duplicate line in all 3 files.

8. The "tang"-family NPC template's new used as a bare variable name (pre-existing bug, new is a reserved efun/keyword here): 7 files — d/quanzhou/npc/{tang,tang1,tang2,tang3}.lpc, d/taishan/{tang2,tang3,xingtang}.lpc — renamed the local variable to newob throughout (declaration + both usages); actual new(...) efun calls untouched.

9. AGENTS.md §15ac (bare SAVE_EXTENSION instead of __SAVE_EXTENSION__): include/net/ftpdsupp.h, adm/daemons/network/netmail.lpc, cmds/arch/purge.lpc, and 3 of the "tang"-family files (d/quanzhou/npc/tang.lpc, d/quanzhou/npc/tang3.lpc, d/taishan/tang3.lpc). Fixed all 6.

10. kungfu/class/mingjiao/mingjiao.h's string-literal-adjacent-to- macro-expansion syntax error: HIG "明教" NOR (no + between two macro-expanded string literals) — rewrote as HIG + "明教" + NOR. Also added #include <ansi.h> to this header (some includers reach it transitively without ever including <ansi.h> themselves, leaving HIG/NOR genuinely undefined once the syntax error above stopped masking it).

11. d/mingjiao/npc/changjinpeng.lpc's wrong absolute #include path: #include "/kungfu/class/mingjiao/tanzhu.h" (doesn't exist — the real file lives next to this NPC file). Fixed to #include "tanzhu.h".

12. d/mingjiao/npc/{fenggongying,baiguishou}.lpc's #include __DIR__"tanzhu.h" (unquoted macro-concatenated form, "Cannot read #include file"). Rewrote both to the plain quoted form #include "tanzhu.h".

13. d/baituo/ouyangfeng.lpc, two independent pre-existing typos: (a) missing semicolon after a write("...") inside a switch case 0: block; (b) object me=this-player(); (subtraction instead of this_player(), missing underscore). Fixed both.

14. d/shaolin/obj/shuitan.lpc: ging_cost typo for the declared variable jing_cost (transposed letters). Fixed.

15. d/mingjiao/jmqshenmu.lpc was genuinely truncated in the raw archive (confirmed against the pre-conversion .c, same as #91 — ends mid-void init() { with no closing braces). Closed with an empty body rather than fabricating content.

All 26 fixed files were verified with a final diff -q against #91's corresponding fixed file: zero differences remain.

Confirmed NOT needed (checked directly against this lib's own source,

not assumed from #91)

Interactive test result — full registration + post-login flow

Full registration + post-login test, one continuous mudclient.py session:

金庸群侠传已经执行了三十九秒
目前共有 0 位巫师、0 位玩家在线上,以及 1 位使用者尝试连线中。
您的英文名字:              <- sent "qinfengstd"
...您确定吗(y/n)?          <- sent "y"
请输入您的高姓大名,...
您的中文名字:              <- sent real Chinese name "秦风" -- ACCEPTED first try
请设定您的密码:            <- sent "test1234"
请再输入一次您的密码...     <- sent "test1234"
...您接受这一组天赋吗?     <- sent "y"
您的电子邮件地址:          <- sent "[email protected]"
...男性(m)的角色或女性(f)... <- sent "m"

[1;36m客店[2;37;0m -
    这是一家价钱低廉的客栈,生意非常兴隆。...
    这里明显的出口是 west 和 up。
  /clone/board/kedian_b [ 没有任何留言 ]
  店小二(Xiao er)
你上次连线是从0 on Wed Dec 31 16:00:00 1969

Post-login commands, same connection:

log/debug.log across the whole run: zero error: lines, zero fatal/segfault-shaped lines. Only expected non-fatal noise:

Known non-fatal pre-existing issue (not fixed, matches #91 exactly)

adm/daemons/chinesed.lpc's create() calls restore() unconditionally on data/e2c_dict.o (English→Chinese dictionary save file). That file's raw bytes do not decode as a valid LPC mapping (restore_object(): Illegal file format) — genuinely corrupted seed data in the original archive (same file, same corruption, as #91 — consistent with both archives sharing this exact save file). Fully caught by master.lpc's own preload() catch wrapper ("错误讯息被拦截"), entirely non-fatal; chinesed.lpc's dict mapping just stays empty. Does not affect registration, look, or score.

Content gaps (same as #91, not independently re-verified per-item since

the raw trees are confirmed near-byte-identical outside the 22 files

listed under "Lineage confirmation" above — none of those 22 touch any

of the content-gap areas #91 documented)

See libs/jqxz2008/NOTES.md's "Content gaps confirmed as real archive gaps" section — d/mingjiao/yuan/* missing skills, missing /d/yanziwu/ zone, orphaned d/wudang/taoyuan/* drafts, dead adm/daemons/network/* intermud code, corrupted board save data, etc. all apply identically here (verified the specific files involved are outside this archive's 22-file diff from #91).

lpcc sweep

Single sweep via scripts/lpcc_check.sh (small/medium lib, no fix-iteration cycle needed since every fix was ported pre-verified from #91): 3064/3135 pass (97.74%) — numerically identical to #91's final sweep result.

Diffed the two libs' failure lists directly: 70 of 71 failing files are byte-for-byte the same set as #91's. The one apparent difference (/d/mingjiao/npc/bigmonkey fails here where #91's sweep instead flagged /d/mingjiao/npc/monkey) is confirmed to be a batch-sweep ordering artifact, not a real content difference — both monkey.lpc and bigmonkey.lpc exist, unchanged, and byte-identical between this lib and #91 (diff confirms zero differences in either file); which one happens to surface as the sweep's FAIL line depends on load/object-table state carried across the whole --batch run (AGENTS.md §6b), not on file content. Same underlying failure category (missing race 走兽 in this zone's NPC template) either way.

Memory stayed healthy (free -h: ~11GB free, well under 1 minute runtime) — nowhere near mega-lib territory, no backing-off needed.

Process notes

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

WASM-enablement pass (2026-07: loopback-allow + admin seeding)

Standard pass per AGENTS.md §1.3b/e + §1.5 (applied identically across the three jqxz2008 builds — engine files are byte-identical in this group):

Admin account: fluffos / Mud@2026 / 浮浮, registered through the real native flow (id → y → Chinese name → password ×2 → gift y → email → gender m). Granted (admin) via adm/etc/wizlist (file was shipped empty; now contains fluffos (admin)), which securityd.lpc reads at create(). Verified after restart: login shows 目前权限:(admin), update /cmds/usr/score prints 重新编译成功. Save files (must be committed): work/data/user/f/fluffos.o, work/data/login/f/fluffos.o.

Retest: fresh registration (秦风/ceshizhe) end-to-end + look/score/quit clean; fluffos login + wizard update clean; debug.log shows only the known pre-existing corrupted-save restore errors (chinesed e2c dict / kedian board) documented above — zero new errors; test character saves removed.

Dual-mode verification pass (2026-07-24)

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

One continuous native mudclient.py session against a freshly-built native driver (port 40084), plus a second fluffos admin session used specifically to reach real combat/death/reincarnation (same reasoning as jqxz2008's own §10.7 pass: the main test character never accumulated enough money/combat standing to safely risk real kill within the time budget). This lib is the confirmed-identical-engine lightweight-content sibling of jqxz2008 (see "Lineage confirmation" above) — this pass independently re-derived the starting zone's room graph and NPC ids from THIS lib's own source (not assumed from the sibling) and live-reproduced every bug it fixed, since none of jqxz2008's own §10.7-round fixes (chinesed restore guard, /log/nosave assure_file(), the bai/apprentice paren bug) had been ported to this archive yet — they are engine-level files outside the 22-file diff between the two archives, so they carried the exact same pre-fix bugs untouched.

Test character: id shenrongfeng, password xia2026wu, Chinese name 沈容风 (male). Left in a clean, alive, fully-saved state at /d/city/kedian (客店, the starting inn), title 叫化子/丐帮第二十代弟子 (Beggars' Sect, 20th generation, master 左全/Zuo Quan), begging skill improved once via the organic teacher path, 潜能 98/99%. Save files left in place per the task instructions (work/data/user/s/shenrongfeng.o, work/data/login/s/shenrongfeng.o) as evidence. Admin account fluffos/Mud@2026 (pre-existing) reused for the death/reincarnation leg; left alive, at /d/city/wumiao (its own now-legitimate chosen home per the reincarnation flow's own explicit ob->move("/d/city/wumiao") — see the startroom-discriminator finding below), title 天神, stats reduced from the death (this is expected/correct post-death state, not a bug).

What was verified working, live

Not verified live (honest gap)

Bugs found and fixed

1. adm/daemons/chinesed.lpc — corrupted save data leaves a global mapping as raw 0, crashing the FIRST real use of cha/chinese() (AGENTS.md §7.7, third bullet — identical bug, identical file, to jqxz2008's own §7.7 finding; this archive carried the exact same pre-fix create(), confirmed byte-identical before the fix)

// BEFORE:
void create() {
  seteuid(getuid());
  restore();
}
// AFTER:
void create() {
  seteuid(getuid());
  catch(restore());
  if (!mapp(dict)) dict = ([]);
}

restore() throws on this archive's malformed data/e2c_dict.o (same known-corrupted seed data documented since the very first pass on this lib); uncaught, it aborts create() before local recovery can run, and §7.7's exact mechanism zeroes dict back to raw 0. Live-reproduced: cha zuo and xue zuo begging both crashed with *Value being indexed is zero. at chinesed.lpc:94 before the fix. Verified: rebooted, repeated bai zuo → xue zuo begging → cha zuo — all three now work cleanly, zero new debug.log lines.

2. Missing /log/nosave/ runtime directory crashes combatd.lpc's killer_reward() on EVERY death and kill.lpc's PK-attempt logger on every player-vs-player kill (AGENTS.md §7.11, exact match — same example directory the catalog entry already names; identical bug/files to jqxz2008's own §7.11 finding, confirmed byte-identical pre-fix)

work/log/nosave/ did not exist in this archive either. Fixed the same two live write sites plus the bai/apprentice FENG counter (bug #3 below shares the same missing-directory root cause) using the lib's own assure_file() idiom (adm/simul_efun/file.lpc:11):

// combatd.lpc killer_reward(), kill.lpc do_kill():
assure_file("/log/nosave/KILLRECORD");   // or ATTEMP_KILL
write_file("/log/nosave/KILLRECORD", ...);

Live-reproduced and verified the severe shape this catalog entry warns about: before the fix, killing the fluffos admin character in real combat threw *Wrong permissions for opening file /log/nosave/KILLRECORD for append. "No such file or directory" UNCAUGHT inside die(), aborting every statement after it (corpse creation, moving the player to the death room, setting the ghost flag) — the character was left standing in the combat room, still "alive" by the engine's bookkeeping. After the fix: full death → corpse → ghost → /d/death/gate chain completes cleanly in one shot, confirmed live, log/nosave/KILLRECORD created on disk.

3. cmds/skill/bai.lpc / cmds/skill/apprentice.lpc (identical duplicate files) — misplaced parenthesis passes the WRONG ARGUMENT TYPE to query(), so the Feng-Qingyang defection-counter check never actually ran (same class as jqxz2008's own bug #4 finding, identical files, confirmed byte-identical pre-fix)

// BEFORE (both files, same line):
if (((string)me->query("family/master_id" == "feng qingyang")) || ((string)me->query("family/master_name" == "风清扬"))) {
// AFTER:
if (((string)me->query("family/master_id") == "feng qingyang") || ((string)me->query("family/master_name") == "风清扬")) {

The == comparison sat INSIDE query()'s argument list, so query() was actually invoked with a boolean (always producing 0/false for any real property name) instead of the intended string key — the branch could never fire regardless of the player's actual master. Also added the matching stringp() guard on the FENG counter's read_file() result (§7.9-shaped: read_file() on a not-yet-created file returns 0, which would otherwise flow into atoi(0)) and assure_file() before its write_file(), same as bug #2's fix. Not separately live-exercised (would need a character to actually be Feng Qingyang's disciple first, out of scope for this pass) but confirmed by direct read that it now matches every other correctly-written call site in the same file.

4. Shared tell_room() simul_efun passes a raw int 0 as message()'s 4th (exclude) argument when called with only 2 args — an UNCAUGHT crash inside the net-dead force-quit handler user_dump() silently disables the ENTIRE net-dead safety net (AGENTS.md §7.12, exact match to its "Severity escalation" paragraph — this pass independently found and live-reproduced the SAME shape in the SAME function (user_dump()'s DUMP_NET_DEAD case) that the catalog entry's own severity-escalation example describes on dtsl, in a completely unrelated lineage — strong evidence this shape is worth actively hunting for on every future §10.7 pass, not just noting when stumbled upon)

// adm/simul_efun/message.lpc — BEFORE:
varargs void tell_room(mixed ob, string str, object *exclude) {
  if (ob) message("tell_room", str, ob, exclude);
}
// AFTER:
varargs void tell_room(mixed ob, string str, object *exclude) {
  if (ob) message("tell_room", str, ob, exclude || ({}));
}

clone/user/user.lpc's user_dump(DUMP_NET_DEAD) — the function that runs when a net-dead player's NET_DEAD_TIMEOUT (600s) call_out finally fires — opens with a bare 2-arg tell_room(environment(), ...) call (no exclude list), leaving exclude at its varargs default of 0 (a plain int, not an array). Live-reproduced directly (via the fluffos admin's own call me->user_dump(1) wizard command, matching DUMP_NET_DEAD's constant value — chosen specifically so the crash could be confirmed deterministically without a real 600-second wait, see the "not verified live" section above for why this substitution is sound here): before the fix, this threw *Bad argument 4 to EFUN message() Expected: object, array, Got: int(0). uncaught inside user_dump() itself, which aborts the function at that exact line — the very next statement, command("quit") (the actual force-quit save), never ran. Net real-world consequence, exactly as §7.12 warns: any player who genuinely net-deads and never manually reconnects would stay alive in server memory forever (until a driver restart), never actually saved via this path — the safety net the whole mechanism exists for silently does nothing. This is a SHARED simul_efun wrapper (29 call sites across 16 files use the vulnerable 2-arg form lib-wide, per a grep -c sweep), so the single fix above covers all of them, not just this one call site. Verified: rebooted, re-ran call me->user_dump(1) on the admin account — the "断线超过 10 分钟,自动退出这个世界" message now prints correctly AND the admin is actually logged out via a real, clean quit (save confirmed via the "当你下次连线进来时,会从这里开始" prompt), zero new debug.log lines.

Startroom-discriminator check (per this task's explicit instructions)

d/city/wumiao.lpc's init() and d/death/gate.lpc's init() both unconditionally write the entering player's permanent startroom field on mere room entry — structurally the exact shape AGENTS.md §7.24 describes, and the exact same two files jqxz2008's own §10.7 pass flagged, briefly "fixed," and then retracted on user review. Applying that pass's own discriminator explicitly, on THIS archive's own source (not assumed from the sibling, though the files are confirmed byte-identical): both rooms carry set("valid_startroom", 1) / set("valid_startroom", "1") in their own create() — grepped and confirmed directly:

d/city/wumiao.lpc:20:  set("valid_startroom", "1");
d/city/wumiao.lpc:38:  me->set("startroom", base_name(environment(me)));
d/death/gate.lpc:18:  set("valid_startroom", 1);
d/death/gate.lpc:31:  ob->set("startroom", "/d/death/gate");

Per the discriminator ("more likely a genuine bug if the target room does NOT carry valid_startroom... more likely intentional design if the target DOES carry that flag"), this is not treated as a bug — left exactly as shipped, consistent with the sibling's own retraction. Reinforcing evidence found live this pass: d/death/inn1.lpc's reincarnation ritual (do_stuff()) EXPLICITLY does ob->move("/d/city/wumiao") right after ob->reincarnate() — i.e. the game's own death-recovery flow deliberately routes every reincarnated player through wumiao regardless of where they died, which only makes narrative/mechanical sense if standing there is SUPPOSED to (re-)anchor your home, matching the "deliberate checkpoint" reading from the sibling's retraction writeup.

As an extra due-diligence step (verifying the OTHER set("startroom" call sites in this lib don't hide a same-shaped bug the sibling's pass didn't have occasion to check, since it's a different, if byte-identical, archive), grepped every set("startroom" site lib-wide (28 hits): the large majority (kongkong.lpc, killer-{n,s,e,w}.lpc ×2 (both clone/npc/ and d/npc/ copies), mao18.lpc, shisong.lpc, ouyangke.lpc) are NPC files calling bare set(...) on their OWN object in their own create() — cosmetic NPC-flavor data, never touches a player object, not this bug shape at all. Of the remainder that DO write to a player object (ob->set/me->set/dest->set): cmds/usr/save.lpc, cmds/usr/quit.lpc, adm/daemons/logind.lpc are the lib's own legitimate save/quit/login mechanism (the thing the bug pattern is defined relative to, not an instance of it); d/city/cangku.lpc, d/shaolin/cangku.lpc, and the d/shaolin/ jail-cluster files (jianyu.lpc, jlyuan.lpc, shulin9/11/13.lpc, andao2.lpc) are a matched capture/release mechanic already separately confirmed genuine by the sibling's own pass (identical files); kungfu/condition/bonze_jail.lpc resets a released prisoner's startroom to the START_ROOM macro itself (/d/city/kedian, which — checked directly — carries valid_startroom as the archive's own default landing room, so this is the SAFEST possible target, not a bug); d/city/npc/tang.lpc's ransom-redemption flow moves a freed captive to /d/city/kedian and sets that same already-valid_startroom room. No additional instances of the bug pattern found.

Testing-methodology note (not a mudlib bug — a note for future passes)

Killing the driver process via plain kill <PID> (SIGTERM) does not autosave connected players — debug.log logs FATAL ERROR: SIGTERM: Process terminated with no save/cleanup step visible before it. A character's in-memory-only changes since their last quit/save are lost if the driver is restarted this way. This bit this pass directly: the test character's freshly-joined sect membership (from bai zuo, never followed by quit) was lost across a driver restart done to load the chinesed fix, and had to be redone. Not a mudlib defect — restarting a live driver to pick up simul_efun/daemon-file changes without asking connected players to quit/save first is inherently lossy on any lib, and worth remembering on any future fix-then-reboot iteration cycle: either quit test characters before a restart, or expect to redo whatever wasn't saved.

Bug-class mapping

Process notes

Driver run from libs/jqxz2008std/ via setsid nohup ... driver config.fluffos > driver_stdout.log 2>&1 & disown, restarted twice across the fix-iteration cycle (once after the chinesed/log/nosave/ bai-paren fixes, once — unnecessary in hindsight but done for a clean before/after comparison — after the tell_room() fix). Each old PID killed only after confirming readlink -f /proc/<pid>/cwd matched this lib's own work/ directory; several other agents' driver processes for other libs (fy2qh, hymud, xajh2, and others) were running concurrently throughout and were never touched. Final driver killed by exact PID after all testing completed, confirmed gone via ps -p/ss -tlnp no longer showing port 40084. Scratch file driver_stdout.log removed before finishing, per task instructions.

深度功能测试(§10.7,本轮):发现并修复一处 §8.9 wrong-object 食物/饮水初始化 bug

这份档案自己此前没有做过深度游玩测试。这次先核对了同引擎兄弟档案 jqxz2008("2008 加强版")已经发现的 4 个 bug——结果这四处 (chinesed.lpc 损坏存档、combatd.lpc/kill.lpc/bai.lpc/ apprentice.lpc 缺失 /log/nosave/ 目录、bai.lpc/apprentice.lpc 括号位置错误)在这份"标准版"里全部已经是正确的写法,说明这几 个 bug 是"加强版"/"超豪华版"在标准版基础上二次修改时引入的回归, 标准版本身反而更干净。

不过,用真实驱动完整走一遍注册流程时发现了一个新的、三个版本共同 携带的 bug:注册后 score 显示的〖食物〗〖饮水〗状态条都是空的 (□□□...),而不是新角色应有的满格状态。查 adm/daemons/logind.lpc 的 enter_world():

user->setup();
if (!user->query("food") && !user->query("water") && ob->query("age") == 14) {
  user->set("food", user->max_food_capacity());
  user->set("water", user->max_water_capacity());
}

这正是 AGENTS.md §8.9 那个"检查了错误对象的 age"模式:age 是角色 本身(user)的属性,不是登录物件(ob)的属性——ob->query("age") 永远是 0/undefined,永远不等于 14,所以这个 if 分支从来没有真正 执行过,每一个新角色的食物/饮水从创建那一刻起就永远是空的,静默地, 没有任何报错。改成 user->query("age") == 14 后,用真实驱动重新注册 了一个新角色(沈贰),score 正确显示食物/饮水两条状态全满。

这个 bug 同样存在于 jqxz2008 和 jqxz2008dlx(同一份 adm/daemons/logind.lpc 逐行相同)——jqxz2008 自己之前那轮相当扎 实的 §10.7 深度测试也没有发现它(大概率是因为那次测试用的角色已经 拜入丐帮,"没有钱怎么办不要问巫师"式的贫困角色对食物条空着这件事没 有特别在意)。已经在这两个档案里用完全一致的修法同步修复,并各自用 真实驱动重新注册验证过。 三个档案的这一处修复分别提交,不合并成一 个 commit。

其它验证过的内容

用测试角色(沈贰)走了:注册 → "客店" → west 到"北大街"(遇到白驼 山"欧阳克")→ south 到"中央广场"(遇到"流氓头"/"流氓"/"菩提子")→ 对"流氓头"使用非致命的 fight 指令切磋——正确在角色明显落于下风时 自动停手("你向后一纵,恨恨地说道:君子报仇,十年不晚!"),没有真 实死亡风险。全程 debug.log 保持空白。

未继续测试的部分

时间关系,没有测试拜师、购物、真实死亡(kill)流程,以及留言板。 下一轮如果继续深挖,可以直接复用 jqxz2008dlx 那一轮已经验证过的 死亡流程(同一引擎、同一 d/death/ 内容),重点关注拜师和购物这两 块还没被任何一个版本验证过的部分。

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

同一代码库,内容更精简。

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

深度功能测试(2026-08-13,round two,新驱动重测)

Re-tested against the freshly-rebuilt build-debug/src/driver(post 全库 quest_times/win_times %-operator 修复 + Warning/warning 驱动文本回退)。log_error()(adm/obj/master.lpc)、§8.9 食物/ 饮水初始化 bug(user->query("age") 而非 ob->query("age"))、管 理员账号(fluffos/Mud@2026,adm/etc/wizlist 已有 fluffos (admin))均已经在更早一轮正确修复/创建过,本轮复核确认 全部依然生效,只发现并修复了 log_file() 一处(和同引擎兄弟档案 jqxz2008/jqxz2008dlx 同一形状)。

发现并修复的 PROGRAMMING bug

1. log_file()(adm/simul_efun/file.lpc)完全没有 assure_file() 保护(AGENTS.md §7.11-class 的又一确认实例):注册/登录本身只 写 log_file("USAGE", ...)(无子目录,本来就存在),不受影响, 但 nosave/CRASHES/nosave/SUICIDE/nosave/CALL_PLAYER 等管理 指令路径会在首次使用时未捕获抛出。已补上 assure_file(LOG_DIR + file);(含前向声明)。

Proactive checks(无需改动)

观察到但判定为既有游戏内容、非 bug 的现象

首次登录 fluffos 进入起始区域后不久,屏幕上出现了 feature/damage.lpc 的"你的眼前一黑,接著什么也不知道了...."( unconcious() 函数的标准昏迷提示),score 确认 <精>/<气> 条大幅下降到接近底部。第二次重新连线(同一账号)没有再次触发,角 色状态正常持续、逐步回血,未死亡。追查 unconcious() 本身: if (wizardp(this_object()) && query("env/immortal")) return;—— 巫师账号只有在同时设置了 env/immortal 才会被豁免昏迷,本档案的 fluffos 测试账号显然没有设置这个标志,所以和普通玩家一样会被起 始区域里的某个(未深入追查具体来源,未见明显敌对 NPC)伤害来源打 到接近昏迷。这是游戏内容/平衡范畴(巫师默认不豁免战斗伤害,是否要 豁免属于设计选择),不是程序逻辑 bug,未修改。登录测试产生的这部分 存档状态变化(<精>/<气> 大幅下降)已用 git checkout 撤销,不 提交一个"半死不活"的管理员存档。

实测过程

用已提交的 fluffos/Mud@2026 登录,update /adm/simul_efun/file (就是本轮改过的文件,第二次连线时确认,第一次连线时因为上述昏迷 状态导致指令被暂时阻塞)确认可正常重新编译。log/debug.log 时间 戳全程未变化(Aug 5,早于本次会话,且文件本身为空),确认无新增 未捕获运行期错误。驱动最终按精确 PID kill,ps -p 确认已退出。

§7.100 扫描修复(ROOM 基类多余 replace_program())

#define ROOM "/inherit/room/room":删除 824 处多余的、独立成行的 replace_program(ROOM);(保留 inherit ROOM;),与手足档案 jqxz2008dlx 同一血统同一形状。clone/misc/roommaker.lpc 同样有 两套模板——"造一间空房间"的 heredoc 本来干净,"克隆我所在的房间" 命令的字符串拼接模板把同一枚多余的 replace_program(ROOM); 烤进 了每一个新克隆的房间,已同步修正。已用 build-debug 驱动干净启动 验证(0 个新增编译错误,端口正常监听);未做完整 §10.7 深度游玩 测试。

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

Round-four (2026-08-20): shop-purchase gap resolved + standard checklist pass

Targeted follow-up on this lib's own single explicitly-flagged §10.7 gap ("A completed, successful shop purchase — never verified live", see above): the earlier test character organically joined the beggar sect (feature/dealer.lpc:116-117 unconditionally rejects beggars at buy), so a fresh non-beggar character was needed, funded via the project's standard admin clone+give pattern for this lib's real money object.

Gap resolved — a real successful purchase, live, price deduction + item receipt both verified. New test character (id qinliushi, Chinese name 秦六石, password redacted) registered natively, no sect join — a plain 平民. Native build-debug driver, port 40084. Admin fluffos (pre-existing, Mud@2026) used goto qinliushi to join the test character at 醉仙楼 (d/city/zuixianlou, the same food-stall shop the earlier rejection-path testing used — NPC 店小二/d/city/npc/ xiaoer2.lpc, F_DEALER), then clone /clone/money/coin + call coin->set_amount(500) + give coin to qinliushi. buy jitui (烤鸡腿, listed at 八十文铜板/80) succeeded: i afterward shows 四两白银 + 二十 文铜板 (400 + 20 = 420 = 500 − 80, exact) plus the 烤鸡腿 itself in inventory — both the price-deduction and item-receipt halves of the "honest gap" are now directly confirmed, not just inferred from the rejection paths. log/debug.log across the whole session (two logins, the admin gift, the purchase): exactly the two long-documented pre-existing intercepted restore errors (chinesed dict, kedian_b board) and zero new error:/uncaught lines.

Incidental finding, not a bug — confirms and extends the 2026-08-13 round's own "wizard non-immunity to unconsciousness" observation: the committed fluffos save (work/data/user/f/fluffos.o) was *already* sitting at "qi":-1,"jing":0 (mortally wounded / unconscious, disable_type already set) before this round touched anything — apparently never fully healed after that earlier incident. Logging in re-triggered inherit/char/char.lpc's heart_beat() → unconcious() (qi < 0 check) immediately, which calls disable_commands() and blocks every add_action-based command (goto/clone/call/give, all "什么?") until unconcious()'s own call_out("revive", ...) fires 30–~108s later (random(100-con)+30, this account's con 22 → up to 108s) — confirmed by reading feature/damage.lpc directly, not guessed. Rather than waiting out that timer, reconnecting immediately worked instead: adm/daemons/logind.lpc's netdead-reconnect path calls clone/user/user.lpc's reconnect(), which (per the already-applied §7.108 fix on this lib) unconditionally calls enable_commands() — confirmed live, a fresh fluffos reconnect immediately after the unconscious episode had full command access again, no wait needed, and natural heal_up() regen over the session brought qi/jing from -1/0 up to 14/14 by the end (still not full, but no longer disabled). This is a strict improvement over the pre-existing committed save state, not new damage caused by this round, so the resulting fluffos.o diff (healthier stats, last known location /d/city/wumiao from a goto) is committed alongside the new test character's saves, per this lib's own established convention (see the 2026-08 round's jqxz2015-sibling precedent for the same clone+ give shop test funding an admin's save diff being committed as a "normal increment," not reverted) — not a mudlib defect, just evidence that a previous session's admin-account "half-alive" state was never cleaned up and this round happened to walk through it and improve it.

Standard checklist pass (fast confirm-only, not re-derivation):

Death/respawn cycle: already verified live, not just this round — the 2026-07-25 §10.7 pass (see above) took the fluffos admin through a real fight-to-death against a 黑无常 NPC and the full corpse → ghost → /d/death/inn1.lpc reincarnation-ritual chain, live, not code-reviewed. Re-checked that section of this file to confirm it wasn't a "mentioned but not exercised" case — it explicitly says "Real combat + death + corpse + ghost + reincarnation, end to end, live (not code-reviewed)" with a full step-by-step transcript description. Nothing further needed here.

No new programming bugs found this round — this pass was scoped to closing the one explicitly-flagged gap plus a confirm-only checklist sweep, and both came back clean/already-fixed.

深度功能测试(§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)— tail() simul_efun

scripts/lpc_add_tail.py --apply: cmds/wiz/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-05)— 房间列出的物件缺档时房间照常加载

房间基类 inherit/room/room.lpc:make_inventory() 用 catch() 包住 new(file),做不出来就返回 0;reset() 的 case 1: 拿到 0 就跳过这一项(if (!objectp(ob[list[i]])) break;)。档案里没有的 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-05)— 巫师留言板存档 data/board/*.o.gz

巫师留言板 wiz_b / towiz_b 以 .o.gz 存档(1996 年的帖子)。本机驱动 restore_object() 优先读 .o.gz,而留言板用 save_object(file) 写明文 .o,所以本机上新帖每次重启都退回旧快照;网页版驱动没有 zlib,只读 .o,而归档自带的 .o 是读不出的二进制。.gz 内容为 GB18030(转码时没被处理),转成 UTF-8 写成 .o,替换归档里那个读不出的二进制 .o;towiz_b 原文有 4 个残字节,转为替换符。删除 .o.gz(KB 01)。用 lpcc 载入留言板核对条数(jqxz2008:wiz_b 47 条、towiz_b 115 条)。

深度功能测试(§10.7,2026-10-07)— 空的 if (current_water == 0) {}

3 个房间的 valid_leave() 里有一行什么也不做的 if (current_water == 0) {}(编译警告 Expression has no side effects),删去,行为不变:d/xingxiu/nanjiang2.lpc、d/xingxiu/nanjiang3.lpc、d/xingxiu/shanjiao.lpc。

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

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

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

孪生库 jqxz2008 同日的整理(5c9cd3ac3bf:冗余继承 18 处,include/net/ftpdconf.h 的 VALID_READ/VALID_WRITE 前 #undef)用 scripts/lpc_twin_port.py adopt jqxz2008 jqxz2008std --pre 5c9cd3ac3bf~1 搬来,19 个文件与 jqxz2008 修前逐 token 相同,清单里的警告全部落在这些文件里。HEAD 与工作树各载入一次:19 个对象,HEAD PASS 18,tree PASS 18,regress 0。

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

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

其他体系的属性名(2026-10-15,KB 06 §7.241 第三类,--any-file)

另有 3 个文件(绝招、状态、物品、NPC 等,不限于 kungfu/skill)来自 gin/kee/sen/force 体系,同样按 kee→qi、gin/sen→jing、force→neili 改名(scripts/lpc_port_es_props.py --any-file)。

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。5 处补上 if (!mapp(fam)) fam = ([ ]);(刚 recruit_apprentice() 之后的除外)。

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

query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):beimin-shengong→beiming-shengong(taiji-shengong、yunv-xinfa);mizongxinfa→mizong-xinfa(longxiang/fanchang)。

family 映射内联取下标(2026-10-15,KB 05 §7.30)

ob->query("family")["master_name"]、me->query("family")["family_name"](泥潭式为 query("family", ob)[...])直接对门派映射取下标:没有门派的人(新玩家、叛师者、大多数 NPC)在 NPC 的打招呼和问话、绝招的门派判断、房间的 valid_leave 里报 Value being indexed is zero,而不是得到拒绝。7 处(6 个文件)改为 dbase 路径查询 query("family/master_name"):有门派时值相同,没有时得 0;原有的判断照常生效(scripts/lpc_fix_family_index.py)。给映射赋值的写法未动。

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

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

留言板存档损坏时 setup() 不再抛错(2026-10-18)

data/board/*.o 里有几个是外来的二进制数据(以 ?inh 开头,restore_object() 报 Illegal file format - 1)。inherit/misc/bboard.lpc 的 setup() 里 restore() 直接抛,留言板对象(连带创建它的客店房间,比如 d/city/kedian)载入失败。改成 catch(restore());,留言板以空板启动(xkx2017 的同一个文件早就是这样写的)。上面「存档损坏」那一条里说的“setup() 已经包住了”并不准确。

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 载入不了,刷出它的房间也少一个人。本库有 9 个对象设了本库没有文件的武功(3 个名字,最多的是 changquan、xuanyuan-arrow、liangyi-jian)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:9 个里 9 个通过。(scripts/lpc_guard_set_skill.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 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 5 处、4 个文件:xinxiu-duzhang→xingxiu-duzhang(2 处)、tiaji-jian→taiji-jian(2 处)、jinyin-baiguzhao→jiuyin-baiguzhao(1 处)。(scripts/lpc_fix_skill_names.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)

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

/d/npc/m_weapon/weapon/m_blade.lpc、/d/npc/m_weapon/weapon/m_club.lpc、/d/npc/m_weapon/weapon/m_staff.lpc、/d/npc/m_weapon/weapon/m_sword.lpc、/d/npc/m_weapon/weapon/m_whip.lpc(共 5 个)的 create() 开头 me = this_player(); 之后无条件读 me->query("weapon/name") 等打造数据。房间刷新时没有人在场(me 为 0),或者 NPC 在别的 NPC 的命令环境里 carry_object() 这个模板(NPC 的 weapon 属性是文件名字符串),旧驱动里读出来是 0 或字符串,switch 照样落到 default;新驱动报 Bad argument 1 to EFUN call_other() / Bad argument 1 to switch,NPC 和它所在的房间载入不了(--walk 普查里一个玩家走进房间就触发)。在 //读入数据 之后加 if (!me || !mapp(me->query("weapon"))) { setup(); return; }:没有打造者就留一个没有名字的空模板,玩家打造的流程不变。

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

转换时丢掉的分号、逗号、引号或文件末尾的 },共 3 个文件,修前 HEAD 载入失败。别的库里同路径的完好副本只比这里少几个标点,按它补回(括号里是那个库):/d/baituo/npc/obj/bowl.lpc(cctx)、/d/baituo/obj/bowl.lpc(cctx)、/d/city2/npc/zhengmen.lpc(jqxz2008)。修后与 HEAD 逐个对比载入:这 3 个物件全部载入,没有退步(scripts/lpc_restore_from_twin.py、lpc_fix_eaten_quote.py --comma、lpc_fix_eof_braces.py)。

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)。改动:/d/mingjiao/shanmen.lpc(1 处)。

行编辑器指令编译失败:(: this_object(), ({ "do_to", me, arg }) :)(2026-10-20,KB 06 §7.284)

Illegal to use local variable in functional.:call_other 形式的函数指针 (: ob, ({ "函数", 参数 }) :) 的数组里只能是常量,写了局部变量 me / arg 整个指令就编译不过,to 这类先开编辑器、再回调自己的指令因此从未能用。改成 (: call_other, this_object(), "do_to", me, arg :)(参数在建立指针时绑定,编辑器再把输入的文字接在最后,do_to(me, arg, text) 收到全文;scripts/lpc_fix_functional_locals.py)。改动:/cmds/usr/to.lpc(1 处)、/cmds/wiz/to.lpc(1 处)。

call_out_info() 的每一项只有 3 个元素(2026-10-20,KB 06 §7.287)

驱动返回的是 ({ 物件, 函数, 延迟 }),不带 call_out 的参数,旧代码读第 4 项 info[i][3](callouts 指令、calloutd、cod 工具)一读就报 Array index out of bounds,指令从第一行起什么也打印不出来。改为 (sizeof(info[i]) > 3 ? info[i][3] : 0)(scripts/lpc_fix_callout_info.py)。改动:/cmds/arch/callouts.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)。

sefun cat():缺文件时 write(0) 报错(2026-10-20,KB 06 §7.297)

void cat(string file) { write(read_file(file)); }:文件不存在(压缩包没带的欢迎/公告文件,或只有运行中的 mud 才会写的日志)时 read_file() 回 0,write(0) 在本驱动里报 Bad argument 1 to receive(),调用者(登录对话、cat 指令)被中断;老驱动什么也不打印。改成 write(read_file(file) || "");(scripts/lpc_fix_cat_unchecked.py)。改动:/adm/simul_efun/file.lpc。

翻页器 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)。改动:/feature/more.lpc。

存档里的乱码文件:restore_object() 报 Illegal file format(2026-10-20,KB 06 §7.316)

存档里有一批数据文件是乱码:开头是 ?adm、?inh、?clo、?obj(#/adm/… 的第一个字节被换成了 ?),后面是随机字节,没有任何驱动读得了。adm/daemons/chinesed 在预载时 restore() 读 data/e2c_dict.o,首次登录时起始房间的留言板 /clone/board/kedian_b 读 data/board/*_b.o;restore_object() 对不是文本的文件抛 Illegal file format,调用处的 catch()(KB 06 §7.255)保住了房间和精灵,但本库 master 的 error_handler() 把调用栈返回给驱动,每次启动和首次登录都在控制台打印一次或两次 错误讯息被拦截。现在共用的 feature/save.lpc 的 restore() 先读文件第一个字节,不是 # 就返回 0(读被 master 拒绝时 read_bytes() 返回 0,仍由 restore_object() 决定,和以前一样);留言板和字典从空开始,第一次 save() 写出合法文件,乱码文件原样留在树里。实测:启动和登录都没有报错,已保存的角色重新连线照常加载。

NPC 的 chat() 在 chat_msg 为空数组时下标越界(2026-10-21,KB 06 §7.334)

NPC 基类的 chat() 是 if (arrayp(msg = query(...))) { if (random(100) < chance) { rnd = random(sizeof(msg)); if (stringp(msg[rnd])) ...,不检查列表是否为空。本库现有的 NPC 文件里没有字面写成空 chat_msg 的(扫描只认 set("chat_msg", ({ })));守卫挡的是运行时才为空的列表和以后新增的 NPC。在 random(100) < chance 前面加 sizeof(msg) &&(scripts/lpc_fix_chat_empty.py;if (random(100) > chance) return 0; 的写法是 !sizeof(msg) ||)。改动:/inherit/char/npc.lpc(共 1 个文件)。

存档里的 .c 文件名改成 .lpc(2026-10-21,KB 03 §4.2 第 9 项)

存档里写着 "/d/city/npc/wei.c" 这样的文件名;extract 时源文件已经全部改成 .lpc,而驱动对带扩展名的路径只认字面,new() / load_object() / move() 都找不到它,调用方多半把失败当成「没有这个东西」悄悄跳过。本库 3 个存档共 6 处按 scripts/lpc_fix_save_dotc.py 改成 .lpc(只改 .lpc 存在而 .c 不存在的字符串;玩家存档、备份、留言板、巫师目录和文件索引缓存不动)。改动:/d/shaolin/npc/obj/kat.o(1 处)、/d/shaolin/obj/kat.o(1 处)、/d/shenlong/obj/lxlx.o(4 处)(共 3 个存档)。

巫师命令不带参数时对 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(present)。加载检查 HEAD 与工作树对照,没有退步。

blockade / recover:data/keep 不存在,解封之后世界仍然锁着(2026-10-21,KB 06 §7.340)

管理员扫描里 blockade 报 Error copying file from /data/user/f/fluffos.o to /data/keep/fluffos, Error: No such file or directory,随后的 recover 在第一个 cp() 上死于 */data/keep/fluffos: lstat failed,来不及执行最后的 LOGIN_D->set_madlock(0):封锁解不开。data/keep/ 是运行时目录,git 克隆里没有空目录。blockade 在第一次写之前 if (file_size("/data/keep") != -2) mkdir("/data/keep");(mkdir() 失败答 0,不抛错),recover 对没有保存档的玩家 if (file_size(src) < 0) continue;,其余照常恢复、照常解锁(scripts/lpc_fix_keep_dir.py,hy5 上实测:没有保存档时 recover 答“时空解封完毕”,blockade 建出 data/keep 并保存,recover 取回并删除)。改动(2 个文件):/cmds/adm/recover.lpc、/cmds/arch/blockade.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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 2 条注册(2 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/inherit/misc/fboard.lpc(do_list)、/kungfu/class/gumu/yangguo.lpc(do_qiecuo)。最多的函数名:do_list 1 处、do_qiecuo 1 处。

对象路径里的 ..: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/wudang/npc/xiaocui.lpc(__DIR__ "../obj/nuerxiang" → "/d/wudang/obj/nuerxiang")。

(: 函数 :) 指针指向对象自己没有定义的函数(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/huashan/npc/teller.lpc:tell_fortune。

读不到的留言板:"/clone/board/x"->foo(); 在 create() 里报错(2026-10-22,KB 06 §7.357)

xkx 系的房间在 setup() 之后写 "/clone/board/gaibang_r"->foo(); 来加载留言板(foo() 本来就没有定义,只为了触发加载)。这个库没有这个文件,call_other() 报 *call_other() couldn't find object '/clone/board/gaibang_r',对象留在内存里、load_object() 却抛错:玩家第一次走进房间就看到错误,写在 create() 中间的还会切掉后半段。scripts/lpc_fix_missing_foo.py 把 1 条单独成行的语句(/clone/board/mingjiao_b;/d/mingjiao/square.lpc)注释掉并写明原因(别的房间里同样的语句本来就是作者自己注释掉的)。证明和 45 个库的整体加载检查见 KB 06 §7.357。

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

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

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

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

本库 18 处(13 个文件):拼名字的赋值 10 处、与汉字常量比较 8 处。例:name[0..1] → name[0..0](/kungfu/class/emei/kong.lpc:137);name[2..3] → name[1..1](/kungfu/class/shaolin/cheng.h:155);name[0..1] == "慧" → name[0..0] == "慧"(/kungfu/class/shaolin/cheng.h:139)。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,不要重跑。

黑木崖脚下的老者:init() 里留着调试语句,每个走进 /d/heimuya/road1 的人都触发运行时错误(2026-10-22,KB 06 §7.361)

d/heimuya/npc/laozhe.lpc 的 init() 在 ::init() 之后有 what = new(__DIR__ "obj/yancao"); sprintf("%s aaa\n", what);:调试遗留,%s 收字符串或数字、不收对象,于是 (s)printf(): Incorrect argument to type %s. (arg: 0) 每次有人进入乱石坡都抛一次,后面的 call_out("greeting", ...) 永远不执行,老者不再搭话(还每次白克隆一个烟草)。两行都注释掉(同系的 haiyang2 / hy / hy5 / cctx / fqyy2 早就注释了)。证明:scripts/lpc_reach_census.py xiakexing2017 --load --walk(一个普通 NPC 访客走进每个加载成功的房间):HEAD 336/337,改后 337/337。

按字节写的汉字循环、茶馆猜诗和同盟频道标题(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 两字同盟名得到 【 兄 弟 】。关键对象加载通过。

血条 / 表情标题按字节算显示宽度(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(一格两个空格)。本库 4 处(2 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。

doc/build devil 丢分号/粘连 inherit(2026-10-27,KB 04 §6.25 Also)

doc/build/devil.lpc(建档示例倭寇)被格式化弄坏:inherit NPC void create() 粘在一起,语句之间的 ; 全丢,还有嵌套坏掉的 set("con", set("con", 10)。按原意重写成带 setup() 的完好 NPC(原 /obj/blade、/obj/cloth 在多数库不存在,示例里去掉携带以免 create 炸)。证明:HEAD 加载失败;改后加载成功。

深度功能测试(2026-10-27,§6.25 one-arg message_vision)

message_vision("…") 补第二参(me / TP / this_player())。