Legend of Jin Yong's Heroes (2015 Edition)

✅ 可玩

金庸群侠传(2015版)

jqxz2015

🔑 fluffos / Mud@2026 更新 6e86eb7 2026-10-27 源码 下载 ZIP

▶ 开始游玩 · Play Now

2015 年整理发布的版本,属于 ES II 引擎系列——master、security、 login 等底层代码与本项目中的"金庸群侠传2008加强版"几乎逐字节相同(匹配度甚至比 2008 版自己与"侠客行3"的引擎匹配度还高),但游戏内容是独立打磨了七年的真实内容分叉:净增 187 个文件、减少 67 个,新增红花会等区域,丐帮相关场景也大幅扩充。少林、武当、峨嵋、明教、丐帮等门派悉数登场,玩家从一间普通客栈起步练武、闯荡江湖。街头就能遇到乔峰这样武功盖世的成名人物,稍有不慎就会命丧刀下,经历停尸、鬼门关问话、转世重生的完整流程;追随风清扬习武途中也可能触发叛出师门的剧情分支。

English

The 2015 release of this project's 金庸群侠传/"Jin Yong Heroes Gathering" (ES II engine) family -- confirmed via md5sum an even closer engine match to sibling jqxz2008 than jqxz2008's own match to xiakexing3 (master, security, login, and Chinese-text daemon files are byte-identical or differ by a single blank line), but a genuine seven-years-later content fork: net +187/-67 files, including a new zone (honghua) and an expanded Beggars'-Sect obj set (city2's imperial-palace district itself is not actually new here -- file-count-verified present in all four 2008/2015 editions alike) and drift in both directions (a file corrupted in jqxz2008 is complete here; a different file is truncated here but fine in the 2008 builds).

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/jqxz2015/

管理员账号 / Admin account

警告:正式对外开放前请务必修改此密码(这是本地游玩用的公开默认
密码)。

本地运行

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

游戏端口:40086。

NOTES · 移植与修复记录

jqxz2015 — 金庸群侠传 (Jin Yong Heroes Gathering, 2015 edition)

Archive: archives/金庸群侠传2015版.rar ("Jin Yong Heroes Gathering, 2015 edition"). Extracted mudlib root: raw/jy/ (a single top-level jy/ directory inside the archive, identical layout convention to archive #91). Port: 40086. Status: done (boots clean, full registration flow verified twice with real Chinese names, post-login look/score/quit verified working both times).

Lineage confirmation (evidence, not assumption)

config.cfg's name field (GBK-decoded) is literally 金庸群侠传, and Readme.txt carries the same "ES2 lib (Tian Cao Di Guo)... Wrote By Phoebus.suny 1998" credit already seen on archive #91 (jqxz2008). Same jy/ top-level directory name, same adm/obj/+adm/single/ dual master-file layout.

Confirmed via md5sum, not assumed, against #91's raw archive — this is an even closer match than #91 vs xiakexing3(#44) was:

| file | match vs #91 raw | |---|---| | adm/obj/master.c | byte-identical | | adm/single/master.c | byte-identical | | adm/daemons/securityd.c | byte-identical | | adm/daemons/chinesed.c | byte-identical | | adm/obj/simul_efun.c | byte-identical | | adm/simul_efun/chinese.c | byte-identical | | include/globals.h | byte-identical | | feature/command.c | byte-identical (incl. the private bug) | | home/command.c | byte-identical | | feature/name.c | byte-identical (incl. capitalize()-on-0 bug) | | adm/daemons/commandd.c | byte-identical | | d/city/npc/guidao.c | byte-identical (incl. is_killing() typo) | | d/baituo/ouyangfeng.c | byte-identical (incl. both typos) | | d/shaolin/obj/shuitan.c | byte-identical (incl. ging_cost typo) | | adm/daemons/logind.c | differs by exactly one blank line (whitespace only, confirmed via diff of decoded content) |

So this 2015 release is the SAME underlying "ES II / 金庸群侠传" engine core as #91's 2008 "加强版" (enhanced edition), 7 years later, with the engine/daemon layer essentially frozen and only game content evolved: 3,893 raw files here vs 3,773 in #91 (net +187 new / -67 removed, mostly new zones — city2, honghua, huanggon, gaibang obj expansions, etc. — plus some cleanup of old scratch/orphan files). A few individual content files (mostly under d/mingjiao/) were independently edited between the two releases (see below) — direction is NOT "2015 always newer/better", it's a genuine content fork with drift in both directions (e.g. d/mingjiao/jmqshenmu.c, truncated in #91, is COMPLETE here; but d/city2/npc/zhengmen.c is genuinely truncated here, matching, in shape, #91's jmqshenmu.c finding).

Cross-check note for #92/#93 (concurrent, not yet landed): per the task brief these could not be diffed against since they were being processed concurrently in sibling sessions — a later pass should md5sum-cross-check this lib's core files against whatever #92 (金庸群侠传2008版.rar) and #93 (金庸群侠传2008超豪华版.rar) turn out to be, the same way this pass cross-checked against #91.

Fixes applied (ported directly from #91 where the source was

byte-identical; confirmed present via direct read before applying, not assumed)

1. AGENTS.md §15h (is_chinese/check_legal_name GBK byte-range checks): adm/simul_efun/chinese.lpc's is_chinese() — GBK lead-byte range (str[0] > 160 && str[0] < 255, strlen>=2) → CJK codepoint range (str[0] >= 0x4e00 && str[0] <= 0x9fff, strlen>=1). adm/daemons/logind.lpc's check_legal_name() — byte bound <2||>12 → character bound <1||>6 (message already promised "1 到 6 个中文 字"); dropped the i%2==0 even-byte-offset gate. Byte-identical source to #91's pre-fix files, so the fix is verbatim. 2. AGENTS.md §15ae (private nomask command-hook breaks every post-login command): feature/command.lpc's command_hook(string arg) was private nomask; home/command.lpc's copy was already correct (no private) — exact same split as #91. Dropped private. Verified working (see registration transcripts below — look/ score both function correctly post-login in both test runs). 3. AGENTS.md §15ar — checked, confirmed NOT independently needed: adm/daemons/commandd.lpc's rehash() uses sscanf(cmds[i], "%s.c", cmds[i]) (no $ anchor) to strip the .c extension. Because this is a *plain quoted* "%s.c" literal (not the concatenated cmds[i]+"$", "%s.c$" shape from the archives where §15ar was first cataloged), convert_lib.sh's standard \.c"-reference fixer (§2) already caught and rewrote it to "%s.lpc" automatically during the mechanical conversion pass — confirmed by grepping the post-convert_lib.sh work/ copy before making any manual edit. This clarifies why #91's own NOTES.md never called out a separate §15ar fix: same shape, same automatic resolution, on the identical source file. 4. feature/name.lpc's capitalize()-on-0 robustness bug (same as #91 finding #5): short(int raw)'s fallback assumed query("id") is always a string; guarded with stringp(id) so non-character items (bulletin boards) don't crash look in their room. Byte-identical source to #91. 5. d/city/npc/guidao.lpc's is_killing(who) direct-call type mismatch (same as #91 finding #6): fixed to is_killing(who->query("id")). Byte-identical source to #91. 6. d/baituo/ouyangfeng.lpc, two typos (same as #91 finding #13): missing semicolon after a write(...) in a switch case 0: block; object me=this-player(); → this_player(). Byte-identical source to #91. 7. d/shaolin/obj/shuitan.lpc: ging_cost typo for jing_cost (same as #91 finding #14). Byte-identical source to #91. 8. AGENTS.md §15ac (bare SAVE_EXTENSION): 6 files this pass — include/net/ftpdsupp.h, d/quanzhou/npc/{tang,tang3}.lpc, d/taishan/tang3.lpc, cmds/arch/purge.lpc, adm/daemons/network/netmail.lpc — replaced with __SAVE_EXTENSION__. Left alone: clone/npc/meng-zhu.lpc (#define SAVE_EXTENSION ".o" locally, self-contained — verified, same as #91). 9. The "tang"-family new-as-bare-variable-name typo (same 7 files as #91, same lines: d/quanzhou/npc/{tang,tang1,tang2,tang3}.lpc, d/taishan/{tang2,tang3,xingtang}.lpc): object *inv, new; + new = new("/clone/npc/...") + new->move(...). Renamed the local variable to newob throughout (declaration + both usages) in all 7 files; the new(...) efun calls themselves untouched. 10. kungfu/class/mingjiao/mingjiao.h's string-literal-adjacent-to- macro-expansion syntax error (same shape as #91 finding #10, but the surrounding logic differs — this 2015 copy independently added an else branch with reversed rank-comparison logic and its own second, correctly-+-joined string, plus already carries #include <ansi.h> unlike #91's original): ob->query("party")["party_name"] == HIG "明教" NOR → HIG + "明教" + NOR (explicit +). 11. adm/daemons/combatd.lpc's #include </quest/quest.h> — AGENTS.md §15t variant 1 (absolute path inside angle brackets, never resolved by this driver's inc_open()) — this file's content diverges substantially from #91's (2015's combatd.lpc has different damage-formula code entirely, but kept the same broken quest.h include and the same variant-3 ordering bug independently). Fixed to #include "/quest/quest.h" (quoted absolute form) AND moved inherit F_DBASE; above all #include lines (AGENTS.md §15t variant 3 — quest.h's file-scope mapping quest_name = ([...]) global would otherwise land textually before the inherit, a fatal "Illegal to inherit after defining global variables" here). Confirmed via a live score test that score now works correctly (this is the same code path #91 needed this exact pair of fixes for, but arrived at via a fresh discovery here since combatd.lpc's content differs — the quest.h include itself, previously assumed only relevant to #91, turned out to be genuinely present and broken here too). 12. quest/quest.h's own string-literal-adjacent-to-macro syntax errors (NEW finding this pass, not present in #91's combatd.lpc since #91's copy never actually included quest.h at all — its version of combatd.lpc had no quest.h reference to trip over this): once fix #11 above made quest.h actually get included and parsed, two more of the exact same "adjacent string literal + macro, no +" bug (AGENTS.md §9/§10 family) surfaced inside it — "你可以领取"HIG + values(quest_name)[i] + NOR"任务了!\n"NOR and "你得先完成"HIY + quest_name[lock_quest] + NOR"任务才能继续这个任务。\n" — both rewritten with explicit + throughout. quest.h is only ever #included by combatd.lpc, which already has #include <ansi.h> earlier in the same file, so HIG/HIY/NOR resolve fine once the syntax itself compiles. 13. d/baituo/{obj,npc/obj}/bowl.lpc's set_name() wrong argument type (NEW finding, genuinely pre-existing, not present in #91 — #91's archive doesn't ship this file at all): set_name("海口大碗", "bowl") passed a bare string where set_name(string name, string *id) (per feature/name.lpc) requires an array — every other set_name() call site in the whole lib correctly uses ({ "id" }). Fixed both copies (d/baituo/obj/bowl.lpc and d/baituo/npc/obj/bowl.lpc, identical content) to set_name("海口大碗", ({ "bowl" }));. 14. d/city2/npc/zhengmen.lpc genuinely truncated in the raw archive itself (NEW finding, same class as #91's jmqshenmu.c — confirmed by diffing work/ against the pre-conversion raw .c, both exactly 77 lines, ending mid-valid_leave() with 2 unclosed braces, not something our pipeline introduced): closed the truncated function with the 2 missing closing braces (no fabricated logic added), purely so the file is syntactically valid — the room's real, complete create()/look_gaoshi() content above is untouched.

Confirmed NOT needed (checked by reading the actual source, not

assumed from #91's precedent alone — master.lpc/securityd.lpc are byte-identical to #91's so these transfer directly, but each was re-verified against this archive's actual work/ content)

Content gaps confirmed as real archive gaps, not bugs (AGENTS.md §13)

— same categories as #91, independently re-verified for this archive (not assumed transferred)

Interactive test result — full registration + post-login flow

(verified TWICE, across a driver restart, with the complete final fix set applied both times)

Run 1 (before the bowl.lpc/zhengmen.lpc fixes, driver PID 1099537): id qinfengwu → y (confirm new character) → real Chinese name 秦风 (accepted first try) → password test1234 (x2) → accept gift stats (y) → email → gender m → enters 客店 (the tea-house starting room). look re-renders the room correctly; score shows a full real character sheet (title/age/gender/birthdate, 膂力/悟性/根骨/ 身法 stats, 精/气 bars, 食物/饮水 bars, kill count, 潜能/实战经验) — this is exactly the code path fix #11 (combatd.lpc's quest.h include) makes work; quit saves and disconnects cleanly with the expected "你丢下一件 布衣" message.

Run 2 (after ALL fixes above, fresh driver restart, PID 1133574): id qinfengliu → y → real Chinese name 秦岭 → password test5678 (x2) → y → email → gender f → enters 客店, with the correct gender-specific NPC greeting ("这位小姑娘" instead of run 1's "这位小 兄弟"). look/score/quit all work identically to run 1, score showing correct female-specific stat roll. Zero error: lines and zero Segmentation/fatal lines in debug.log across the full run (confirmed via python3 substring count, not just grep, to sidestep any shell/locale quoting issues with the Chinese search strings).

您的英文名字:使用 qinfengliu 这个名字将会创造一个新的人物,您确定吗(y/n)?
您的中文名字:
请设定您的密码:
请再输入一次您的密码,以确认您没记错:
膂力[ 19]  悟性[ 21]  根骨[ 17]  身法[ 23]
您接受这一组天赋吗?
您的电子邮件地址:
您要扮演男性(m)的角色或女性(f)的角色?

目前权限:(player)
...
客店 -
    这是一家价钱低廉的客栈,生意非常兴隆。...
  /clone/board/kedian_b [ 没有任何留言 ]
  店小二(Xiao er)
店小二笑咪咪地说道:这位小姑娘,进来喝杯茶,歇歇腿吧。
【 平  民 】普通百姓 秦岭(Qinfengliu)
 你是一位十四岁的女性人类,...
 膂力:[ 19]  悟性:[ 21]  根骨:[ 17]  身法:[ 23]
 ...
你丢下一件布衣。
当你下次连线进来时,会从这里开始。
欢迎下次再来!

Testing-methodology note (matches #91's own pitfall exactly): the first two names attempted in initial ad-hoc testing (qinfeng2015 — contains digits) got rejected at the English-id prompt ("对不起,你的英文名字只能用英文字母"), which desynced the whole scripted --send sequence (each subsequent line got consumed as a fresh id-retry attempt instead of advancing through the intended flow) — not a bug, just confirms the id-validation is working correctly (English letters only) and a reminder to always use a letters-only id in mudclient.py scripts for this lineage.

Both known non-fatal pre-existing corrupted-save-data hits (chinesed's e2c_dict.o, one board's .o) appeared during both runs exactly as expected, both caught gracefully with zero effect on registration, look, or score.

lpcc sweep

Ran twice (before/after the bowl.lpc/zhengmen.lpc fixes found via the first sweep's own output), each a fresh lpcc --batch sweep via scripts/lpcc_check.sh against all 3,256 .lpc files:

Remaining 60 failures triaged by category (AGENTS.md §6b), every single one directly re-verified against this archive's own source (not assumed from #91's precedent) — see "Confirmed NOT needed" and "Content gaps" sections above for the full breakdown: 11 corrupted pre-existing board save files, 2 chinesed/e2c-dict-adjacent restore failures, ~19 genuinely missing zone/skill content (mingjiao/yuan x9, wudang/taoyuan x4, u/liujun/u/editer wizard scratch x3, taohua_b's missing room x1, m_weapon x6 this_player()-null sweep artifacts), 10 dead network/intermud daemon files, 1 home/simul_efun.lpc sweep artifact, 2 doc/-tree illustrative example files, 1 orphaned ASCII-art map file, 2 cmds/{usr,wiz}/to.lpc admin-utility functional-closure errors (present and left unfixed in #91 too, for consistency). None of these affect registration, look, or score — all confirmed via direct source reading and, for the network/preload-related ones, direct grep of adm/etc/preload, not guessed from precedent alone.

Memory stayed healthy throughout both sweeps (~35s wall-clock each, free -h showed only ~4.1GB used / 11GB free at peak — a small/medium lib, nowhere near mega-lib territory; no throttling needed despite concurrent sibling agents potentially sweeping #92/#93 at the same time).

Process notes

Re-verification pass: driver rebuild + LPC formatter + WASM build

WASM-enablement pass (loopback-allow / uptime / throttle / admin seed)

Standard WASM/local-play pass per AGENTS.md §1.3(b)/(e) and §1.5.

Gates patched (loopback always allowed):

Uptime startup gate: none. No uptime() < N connection-rejection gate exists (only cosmetic UPTIME_CMD display + in-game rumor/content timers).

Anti-flood throttle: none. logind.lpc has a reconnect/replace flow (有人从别处…) which is normal game behavior, not a per-IP rate limit.

Admin account: id fluffos, pw Mud@2026, name 浮浮, granted (admin) via adm/etc/wizlist. Registered through the real flow; login verified as 目前权限:(admin), update /adm/daemons/band recompiled successfully. Save files (must be committed — NOT gitignored, orchestrator: force-add if git status hides them): work/data/user/f/fluffos.o, work/data/login/f/fluffos.o.

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

同一引擎核心,2015 年内容版本。

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

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

按 AGENTS.md §10.7 流程做的第一轮真正意义上的深度游玩测试——此前几 轮都停留在注册/登录/编译层面。这份档案自己的 README 及既有 NOTES 已 确认它和"2008 加强版"(jqxz2008)、"2008 标准版"(jqxz2008std)、 "2008 豪华版"(jqxz2008dlx)、xiakexing3 共享同一套 ES II 引擎核 心,其中 jqxz2008/jqxz2008dlx/jqxz2008std/xiakexing3 都已经 各自做过一轮 §10.7 深挖并记录了具体 bug——开机前先把这几份记录读了 一遍当路线图,逐条对照本档案的实际源码,5 个全部确认存在(细节见下 "开机前主动移植"),比预期的还要更省事:不用重新从零排查,直接照着 清单核对再动手修。

WASM 本轮仍然跳过验证:emsdk 安装器把工具链下载硬编码到 storage.googleapis.com,本 session 的出站代理策略拒绝该域名 (curl -sS $HTTPS_PROXY/__agentproxy/status 确认为 403),未重试。 但这份档案自己 NOTES.md 里"WASM build test"一节记录过更早一轮会话 已经用 scripts/wasm_client.js 完整验证过注册/登录流程在 WASM 下能 跑通——本轮只是没有重新跑一遍确认这轮修的 bug 在 WASM 下同样生效, 不是"从未验证过"。

开机前主动移植(对照 jqxz2008 系家族已确认修好的 bug):

1. AGENTS.md §7.7(已确认第 N 例)——adm/daemons/chinesed.lpc 的 create() 原本是裸 restore(),本档案的 data/e2c_dict.o 是 损坏的二进制垃圾(不是合法 mapping 字面量),restore_object() 抛出的运行时错误没有被捕获,会让 create() 在那一行直接中止, 全局 mapping dict 被清零——第一次真正用到 chinese()/ to_chinese() 的地方(战斗/技能提示大量使用)就会崩溃。改成 catch(restore()); if (!mapp(dict)) dict = ([]);,和 jqxz2008 系已验证过的修复完全一致。开机日志里能直接看到这处 catch() 正确拦下了损坏存档的运行时错误(而不是让 create() 中止), 验证修复生效。 2. AGENTS.md §7.11(已确认第 N 例,且是本档案里最严重的一处)—— work/log/nosave/ 目录在这份 work/ 树里从未被创建(gitignore 忽略的运行期目录,与仓库无关),但至少 4 个 write_file()/ log_file() 调用点无保护地往里面写:adm/daemons/combatd.lpc 的 killer_reward()(每一次玩家死亡都会跑到,且这一处不像 kill.lpc 那样有 userp() 判断限定 PK 场景,任何死亡都会触发)、 cmds/std/kill.lpc(仅 userp() 场景)、cmds/skill/{bai, apprentice}.lpc 的风清扬弟子计数器。killer_reward() 这一处一旦 目录缺失就会在 die() 中途未捕获地抛异常,让角色的死亡清理(尸 体、鬼魂状态、移到死亡场景)全部不会执行,角色变成"活着但气血 精气归零"的破损状态,而且会在之后每一次 heart_beat() 都 重新触发——jqxz2008/jqxz2008dlx 都通过实际杀死角色确认过这 个死循环崩溃。本轮用该家族已验证的 assure_file() 模式在 4 处 write_file() 调用前各加一行 assure_file(...)(combatd.lpc/ kill.lpc/bai.lpc/apprentice.lpc),并给 bai.lpc/ apprentice.lpc 里 read_file() 的结果加了 stringp() 判断 (避免 atoi(0))。本轮额外发现的第 6 个同类隐患:这份档案 自己的 adm/simul_efun/file.lpc 里 log_file()(全档案通用的日 志辅助函数,被 cmds/arch/call.lpc 等至少 5 个不同的 nosave/* 日志点调用)本身也只是裸 write_file(LOG_DIR + file, text),没有调用同一个文件里现成的 assure_file() 辅助函数—— 直接在 log_file() 内部加一行 assure_file(LOG_DIR + file),一 次性覆盖它所有的调用点,比逐个日志点手工加保护更彻底(assure_file 定义原本在 log_file 后面,这个驱动的编译器要求同文件内被调用 的函数需要先声明/定义,所以顺手把两个函数的顺序对调了)。 3. cmds/skill/bai.lpc/cmds/skill/apprentice.lpc(一处括号位置 错误)——if (((string)me->query("family/master_id" == "feng qingyang")) || ...),== 比较被夹在 query(...) 的参数 括号内,传给 query() 的实际是恒为假的布尔值,不是想要的属性名 字符串,导致"从风清扬门下叛出"检测分支永远不会触发。已改成先呼叫 query("family/master_id") 拿返回值再比较,两个完全相同的文件都 改了。 4. AGENTS.md §8.9(已确认第 N 例)——adm/daemons/logind.lpc enter_world() 里 if (!user->query("food") && !user->query("water") && ob->query("age") == 14),age 取自登录阶段用完即弃的连线桩物 件 ob,不是真正的角色本体 user——桩物件永远没有 age 属性, 条件恒为假,导致每个新角色的食物/饮水从创建起就永远是空的。改成 user->query("age") == 14。Live 验证:注册后 score 食物/饮水两 栏均满格(■■■...),管理员账号 fluffos 重新登录后同样两栏被 补上(此前从未有 food/water 字段,见下方存档 diff)。 5. get_name() 遗留调试 printf("%O\n", ob)(新发现,jqxz2008 系没有,xiakexing3 有)——紧跟在玩家输入中文姓名之后,会把连 线桩物件的原始引用(/clone/user/login#0 这类内部标识)直接回显 给正在注册的新玩家,属于内部调试信息泄漏。已删除该行,Live 验证: 注册流程中输入中文名字后直接进入密码设置提示,没有任何对象引用 泄漏。

测试路径:读 doc/help/newbie 确认「较量」(fight) 是安全对练的 标准做法(气/精跌到 50% 或以下自动停止)后,在原生驱动下一次连续会 话里:注册(真实中文名 叶秋白,id jqxzdeep)→ 落地客店,look/ score/i 确认干净、食物/饮水满格(§8.9 修复验证)→ west→south 到中央广场,fight liu mang 对普通"流氓"安全对练,几回合后自动 "承让了"结束,score 确认气条降到约 50%、无死亡/无损失 → enter shudong 进入丐帮地下通道,bai zuo 组织性拜入 丐帮 第二十代弟子 (师父左全),xue zuo begging 组织性学习技能,即时进步提示正常 → 另开管理员会话(fluffos/Mud@2026,账号已在 adm/etc/wizlist 中) 用 call jqxzdeep->set_skill(sword,50)(该引擎家族没有专门的 setskill/setparty 类快捷指令,cmds/arch/call.lpc 是通用的管理 员任意函数调用指令,充当本档案的技能赋予捷径路径)验证捷径路径—— nosave/CALL_PLAYER 操作日志正确写入(验证了上面第 2 点里 log_file() 的修复),角色 skills 指令确认组织性的 begging 与捷径赋予的 sword 同时存在 → quit(触发 save())→ debug.log 全程检查 (grep -v 排除编译期 warning 后)零 error:/crash/fatal 行 → 真实间隔重新连线(nc 断开重连,非同一 TCP 会话),score/skills 确认门派归属、师父、组织性/捷径两条技能、已花费潜能全部正确持久化。

一次会话内的持久化教训(记录,非 bug):本轮为了应用 log_file() 修复重启过一次驱动,但重启前忘了让测试角色先 quit (只是杀掉了旧驱动进程)——重连后发现角色回到全新状态(无门派、无 技能、潜能满格),排查后确认这不是 mudlib 的持久化 bug:cmds/usr/ quit.lpc 的 main() 明确调用 me->save(),本档案没有自动周期性 存档,纯粹是测试流程上少做了一次 quit 才导致进度丢失。之后重新走 了一遍拜师/学艺流程并正确 quit,最终的持久化验证(见上)就完全 正常了。

测试角色:id jqxzdeep,中文名 叶秋白,密码 Jqxz2026Test, 丐帮第二十代弟子(师父左全),技能 begging(组织性)+ sword(捷径路 径,50 级),位于客店。存档:work/data/{login,user}/j/jqxzdeep.o。 管理员账号 fluffos 存档因本轮测试产生了正常的 food/water/mud_age 增量(§8.9 修复的直接体现,非损坏),按这份档案自己既有 NOTES 里 "必须提交,不受 gitignore"的约定一并提交。

验证通过:真实中文名注册;look/score/i 多次状态检查;食 物/饮水初始化(§8.9);安全对练 fight(自动停止机制);丐帮组织 性拜师+学艺;管理员捷径路径(call)技能赋予;chinesed.lpc 损坏存档的 catch() 防护;log_file()/assure_file() 缺失目录防 护(含新发现的 log_file() 自身漏洞);quit(save())后 debug.log 检查;真实断线重连后的完整状态持久化。

明确未验证(记录而非静默跳过):完整战斗到死亡的循环(fight 安全对练已覆盖核心战斗机制,真正杀死角色需要更多时间预算构造合适 对手,本轮未做);风清扬弟子叛出这个罕见分支(bai.lpc/ apprentice.lpc 的括号修复已通过代码比对验证,未构造这个特定场景 实机触发);商店购买(起始区域客店本身没有商店,真实商业区未探索); WASM 下的重新验证(详见上方说明,proxy 阻断,且此前已有会话独立确 认过 WASM 可用)。

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

Re-tested against the freshly-rebuilt build-debug/src/driver(post 全库 quest_times/win_times %-operator 修复 + Warning/warning 驱动文本回退)。本次复核确认这份档案已经是本轮 jqxz2008 家族里 最干净的一个:log_error()(已有 #15w 引用注释)、log_file() (早前会话自己就发现并修复了同一个 assure_file() 缺失 bug,见 commit 4603617af84)、管理员账号(fluffos/Mud@2026, wizlist 已有授权且存档已提交)全部依然正确生效,本轮没有发现 新 bug,未做任何改动。

win_times 修复确认存在且正确:d/city2/npc/refereew.lpc:176; 未发现 message() simul_efun 包装函数。用已提交的 fluffos/ Mud@2026 登录,update /adm/simul_efun/file 确认可正常重新编 译,零诊断刷屏(这次连线也没有再触发姊妹档案 jqxz2008std 那次遇 到的随机昏迷事件)。log/debug.log 时间戳全程未变化(Jul 23, 早于本次会话),确认无新增未捕获运行期错误。登录本身产生的存档时 间戳类微小 diff 已用 git checkout 撤销,不提交。驱动最终按精确 PID kill,ps -p 确认已退出。

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

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

深度功能测试(2026-08-20,round three,补齐三处"明确未验证")

本轮专门针对上面 2026-08-07 记录的三处"明确未验证"项,预算充足, 逐一构造场景实机验证。开机前先做了一遍标准清单快速核对(全部确认 已经是干净状态,未发现新问题,细节见下)。

标准清单核对:§7.90(config.fluffos 第 40 行 maximum evaluation cost : 5000000,确认——注意 work/config.cfg 里另有一份 maximum evaluation cost : 300000,但那不是驱动实际加载的配置文件, config.fluffos 的 master file/mudlib directory 等字段才是真正 生效的一份,已核实);§7.100(全档案 replace_program(ROOM); 42 处 命中全部是注释行,零处存活,确认已是上一轮修复后的干净状态); §7.111(adm/obj/master.lpc——config.fluffos 的 master file 字段指向的真正生效文件——第 200 行已经是 objectp(error["object"]) ? file_name(error["object"]) : "(driver)" 的空指针防护写法;adm/single/master.lpc 那份没有防护但从未被驱动 加载,非生效代码,确认无需处理);§7.79(全档案零 addn()/ addn_temp() 2 参数裸调用,本档案这套血统本来就不含这个 bug 形 状,确认不适用);§7.108(clone/user/user.lpc 第 110-116 行 reconnect() 明确调用了 enable_commands(),确认干净);§7.112 (death_stage() 家族 4 个实例——bgargoyle/wgargoyle/ newgargoyle/yu-zu2——逐一检查每一个真正终止流程的 exit 分支, 全部正确清除了 death_stage_active 重入锁,确认无残缺分支)。

1. 完整战斗到死亡的循环——已验证,真实死亡+复活全流程跑通: 新建测试角色 jqxzfour(中文名 秦四,密码 Jqxz2026Four,男性), 读 cmds/std/kill.lpc 确认(与本 session 其它库的规律一致)kill 指令对 NPC 目标确实没有任何安全阀(obj->kill_ob(me) 对方直接反 击)。找到 d/npc/xiaofeng.lpc(萧峰,北大街)——max_qi 4500/ max_jing 1500 级别的高战力 NPC,远超新手角色——在原生驱动下对他 下 kill xiao feng:几回合后被"雷霆一击"重创,"眼前一黑",真实死 亡(非 fight 安全对练)。死亡后走的是完全真实、未修改的代码路 径:feature/damage.lpc 的 die() → CHAR_D->make_corpse() 掉落 尸体 → 移动到 DEATH_ROOM(/d/death/gate.lpc,鬼门关)→ 房间内 white gargoyle(白无常)NPC 的 death_stage() 对话流程逐条播放 ("你叫什么名字?"→…→"阳寿未尽?")→ 最终 reincarnate() 并移动到 复活点 /d/city/wumiao(武庙)。score 确认惩罚符合设计(潜能从 99 减半到 50,气/精降为约 75%,随心跳自然恢复中)、身上物品全部 随尸体掉落(i 显示"目前你身上没有任何东西",与登录时"你丢下一件 布衣"的既有设计一致)。整段过程 debug.log 零 error:/crash/ Segmentation。断线重连验证状态正确持久化。(注:feature/ damage.lpc 里 DEATH_ROOM->start_death(this_object()) 这一行调用的函数在全档案任何地方都未定义——call_other 到不存在 的函数在这个驱动下是静默返回 0,不是运行时错误,debug.log 全程 确认没有相关报错;真正的死亡流程是靠玩家被移动进鬼门关房间后,房 间里 wgargoyle 的 init() 自己探测 previous_object() 触发的, 不依赖这个从未实现的 start_death(),纯粹是历史遗留的死代码,不 是本轮范围内的 bug)。

2. 风清扬弟子叛出——已验证,真实触发,且额外发现并修复一个真 实运行时崩溃 bug(详见下):读 cmds/skill/apprentice.lpc/ kungfu/class/huashan/feng.lpc 确认触发条件——先要成为风清扬的弟 子(family/master_id == "feng qingyang"),再拜一个不同门派的师 父,且对方已经对你 pending/recruit(即对方已经用 recruit 指 令点名收你为徒、只等你回拜)。feng.lpc 收徒门槛很高(剑法/紫霞 神功/身法/文学等技能全部 ≥130~160、shen ≥ 100000、int ≥ 41、 随机 kar 检定),管理员 call 赋予测试角色对应技能/属性后(符 合任务允许的"用管理员捷径进入场景,但让真正的触发指令走真实代码" 原则),先在 /d/huashan/buwei2 拜入门槛较低的入门弟子路线(岳不 群,仅需 shen≥1000+query_int()≥25),确认拿到 family_name == "华山派" 后,再到 /d/huashan/houshan 拜风清扬,成功——family/ master_id 确认变为 "feng qingyang"。随后用管理员 possess 指令附身到丐帮 左全(/d/gaibang/inhole)身上手动对 jqxzfour 下一次 recruit 指令(不经由 attempt_apprentice() 的自动即时收 徒分支,只是先挂起 pending/recruit),退出附身后,jqxzfour 玩 家自己下 bai zuo——这次真正命中 apprentice.lpc(原括号 bug 修复处)里 ob->query_temp("pending/recruit")==me 那个分支,输出 "你决定背叛师门,改投入左全门下!!",family_name 变为"丐帮", family/master_id 变为左全,score 也确认师承信息正确更新;/ kungfu/class/huashan/FENG 计数文件从"0"变为"2"(两次拜风清扬产生 的正常计数递增,一次是最初的入门测试,一次是为复现这个分支而重 新拜的),确认原括号错误修复后这条分支的运行时行为完全符合预期。

(补充观察,非 bug,未修改):feng.lpc/kungfu/class/gaibang/ hong.lpc 的 attempt_apprentice() 用 (string)ob->query("family/family_name") != "" 判断"对方是否已 经有师父"——对一个从未加入任何门派的全新角色,query(...) 返回 的是裸 int 0,(string)0 在这个驱动下转成字符串 "0" 而非空 字符串 "",导致该判断对"完全没有门派"和"已有其它门派"两种情况 一视同仁地拒绝,实机复现确认(bai feng 对一个从未拜过师的全新 角色会被拒绝,提示"既然已有名师指点,何必又来拜老朽呢")。但这条 分支在正常玩法里几乎不可能被触发——华山派/丐帮的高阶传人一定是先 经过入门弟子(岳不群/宁中则/令狐冲,或丐帮的左全等)才可能有资格 再进阶拜访风清扬/洪七公,不会有全新角色跳过所有入门步骤直接来见 这两位——按本 session 的 scope 纪律(无编译错误/运行时崩溃/ debug.log 报错/驱动级拒绝,大概率是设计而非 bug)未作修改,只记 录在此供后续参考。

3. 商店购买——已验证,价格扣除+实物到手全部正确:d/city/ zahuopu.lpc(杂货铺,东大街南)用 list/buy 两个指令,NPC 杨永福(d/city/npc/yang.lpc)用 F_DEALER 混入。给测试角色 jqxzfour 用管理员 clone /clone/money/coin + call coin->set_amount(500) + give coin to jqxzfour(任务允许的 "clone+give 该档案真实货币物件"标准手法,需要角色在线才能接收, 先重连再 give);list 确认价目表,buy budai(麻布袋,标价一 两白银=100)购买成功,i 确认物品到手且找零正确(四两白银, 500-100=400=4两,数学验证一致)。有一处值得记录但非 bug 的现象: buy 命令刚执行完的一瞬间,i 仍然显示"五百文铜板"(旧的、本该 被清空的硬币堆)——根源是 inherit/item/combined.lpc 的 set_amount(0) 并不会立即把 amount 归零,只是 call_out ("destruct_me", 1) 延迟 1 秒后再摧毁物件,这 1 秒窗口内 query_amount()/short() 显示的是即将消失前的旧值;2 秒后再查 i,旧硬币堆已经正确消失,只剩找零后的银两,最终状态完全正确。 无编译错误/运行时崩溃/debug.log 报错,纯粹是这个 ES2 引擎家族的 货币物件通用的、无害的、会自我纠正的显示延迟,不是本档案独有, 不作修改。

新发现并修复的真实 bug(AGENTS.md §7.30 第二例):在"1.完整战 斗到死亡"测试过程中,debug.log 捕获到一条真实未拦截的运行时错 误——执行时段错误:*Bad argument 1 to keys() Expected: mapping Got: 0,来自 /d/npc/xiaofeng.lpc 第 99 行 check_skills()。根 源:feature/skill.lpc 的 mapping query_skills() { return skills; },skills 这个成员变量对一个从未学过任何技能的全新角色 从来没有被赋值过,LPC 对未初始化 mapping 变量的默认值是裸 int 0 而非 ([]),xiaofeng.lpc 的 init() 在任何玩家进入他 所在房间(北大街)2 秒后排程的 check_skills() 无防护地对这个 可能是 0 的返回值调用 keys(),直接崩溃——任何全新、从未学过 任何技能的角色路过北大街都会触发,属于高可及性的真实 bug,不是 本档案独有内容/平衡设计。全档案排查所有 keys(...->query_skills ())/sizeof(...) 调用点(多数早已用 mapp()/真值判断正确防 护),额外发现 4 处同款无防护实例并一并修复(均改为在调用 keys() 前加 if (!mapp(skl)) return; 或等价的提前返回防护):

全部 5 处用 update 指令在活驱动上重新编译确认"成功",并用一个全 新注册、skills 指令确认"你目前并没有学会任何技能"的角色(秦五 /jqxztest)真实走进萧峰房间验证不再崩溃、debug.log 全程零 error:。已提交。

测试角色:jqxzfour(秦四,Jqxz2026Four,男性,历经商店购 买→真实死亡复活→华山派→风清扬弟子→丐帮叛徒的完整测试链)、 jqxztest(秦五,Jqxz2026Test5,男性,仅用于验证 §7.30 fix, 零技能状态)。存档:work/data/{login,user}/j/{jqxzfour, jqxztest}.o。管理员账号 fluffos 存档因本轮测试(clone/ give/possess 等操作)产生正常增量,按既有约定一并提交。

测试过程中的插曲(记录,非 bug):possess(附身 NPC)后, 账号与"当前受控身体"的绑定关系(find_body(id))会保持指向被 附身的 NPC,若不先用某种方式正确退出附身就断线,下次用密码重新 连线会走 confirm_relogin 的"是否踢掉旧连线"流程而不是直接 reconnect,且如果同一批命令里把 y/n 确认和后续指令一起发送, 非 y/n 的后续指令会被当成 y/n 回答误吃掉(yn[0]!='y' 直接判定 为"否",destruct(ob) 断开这次新连线)——这不是 mudlib bug,是 测试脚本没有先手动处理这个确认提示;后续通过重启驱动清空所有连 线状态解决,之后的 update 验证等操作均在干净状态下完成。验证通过:真实商店购买(价格扣除+实物到手+找零正确);真实 kill战斗死亡到复活的完整流程(掉落尸体/鬼门关对话/复活点正 确/惩罚符合设计/断线重连持久化正确);风清扬弟子叛出的完整真实 触发(含用管理员 possess 精确构造出 apprentice.lpc 里那条此 前只做过代码比对、从未实机触发过的 pending/recruit==me 分支); 新发现并修复一处 §7.30 家族真实运行时崩溃 bug(5 处实例);标准 检查清单六项全部确认干净。

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

深度功能测试(§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 jqxz2015 --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 266 个 .lpc 和 10 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):253 -> 253 通过,无回退;改过的头文件的 67 个包含者 67 -> 67 通过。启动时警告 17 -> 0;新进程登录(输入名字)正常。

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

scripts/toolkit_fix.sh 一轮去掉 16 处多余继承;include/net/ftpdconf.h 把 VALID_READ、VALID_WRITE 定为 1,覆盖 ftpd.h 的 0,先 #undef(同 xiakexing2017),行为不变。能编译的文件警告清零;改动的对象在 HEAD 与工作树各载入一次(16 objects; HEAD PASS 15, tree PASS 15; regress 0; newly loading 0)。

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

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

3 个文件、3 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/city/obj/tudao、/clone/cloth/female-cloth、/clone/cloth/cloth)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 0 个,改后 2 个(2 个原来载入不了),没有退步;其余 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 个对象设了本库没有文件的武功(2 个名字,最多的是 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 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 6 处、5 个文件:xinxiu-duzhang→xingxiu-duzhang(2 处)、tiaji-jian→taiji-jian(2 处)、jinyin-baiguzhao→jiuyin-baiguzhao(1 处)、qishang--quan→qishang-quan(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)

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

本库在新驱动下重扫,机械修复:9 处浮点数赋给 int 的地方改成 to_int()。修后与 HEAD 逐个物件对比载入,没有退步。另有 13 处浮点数赋值要手工改(lpc_fix_float_hooks.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 文件。

技能的 power_point() 返回类型(2026-10-19,KB 04 §6.10)

kungfu/skill/ 下 11 个技能的 int power_point(object me) { return 1.5; } 返回的是浮点数,新驱动按 int 声明截成 1。声明改成 float,值回到设计的 1.1、1.5…。这个库里读 power_point() 的只有遗留的 combatd.b(二进制,不加载),combatd.lpc 不调用,所以对运行没有别的影响。

玩家打造兵器模板没有打造者时(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; }:没有打造者就留一个没有名字的空模板,玩家打造的流程不变。

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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 15 条注册(15 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/mingjiao/npc/changjinpeng.lpc(do_join)、/d/mingjiao/npc/lengqian.lpc(do_join)、/d/mingjiao/npc/pengyingyu.lpc(do_join)、/d/mingjiao/npc/shuobude.lpc(do_join)、/d/mingjiao/npc/tangyang.lpc(do_join)、/d/mingjiao/npc/wensong.lpc(do_join)、/d/mingjiao/npc/yanyuan.lpc(do_join)、/d/mingjiao/npc/yinwufu.lpc(do_join)、/d/mingjiao/npc/yinwulu.lpc(do_join)、/d/mingjiao/npc/yinwushou.lpc(do_join) 等共 15 个文件。最多的函数名:do_join 13 处、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。

(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(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] == "澄" 等 21 处(2026-10-22,KB 06 §7.360)

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

本库 21 处(13 个文件):拼名字的赋值 13 处、与汉字常量比较 8 处。例:name[0..1] → name[0..0](/kungfu/class/emei/kong.lpc:134);name[2..3] → name[1..1](/kungfu/class/shaolin/cheng.h:157);name[0..1] == "慧" → name[0..0] == "慧"(/kungfu/class/shaolin/cheng.h:140)。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 加载失败;改后加载成功。