info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
本游戏自称 金庸群侠传,本压缩包对应"2008加强版"这一内容版本。属于"ES II"引擎家族(源自作者 Phoebus.suny 1998 年的 ES2"天曹地国"版本),与本项目中的标准版(`jqxz2008std`)、超豪华版(`jqxz2008dlx`)、2015版(`jqxz2015`)核心引擎逐字节相同,是同一套服务端程序的四种不同内容版本;这支血脉还延伸得更远——`xiakexing3`(游戏内同样自称"金庸群侠传")与本作 91% 的文件逐字节相同,`xiakexing2017` 又与两者各有 80%-84% 重合,可以说是同一款底层游戏保留下来的六个快照。新角色从店小二看店的路边茶馆起步,可在中央广场通过一处树洞暗语机制加入丐帮、拜入左全门下习武,也可走白驼山使者按资质招人的另一条入门路线;向风清扬拜师会触发"风清扬现在共有 N 个徒弟"的实时播报彩蛋。明教九名"缘"字辈弟子的名字里藏着一个数字诅咒梗——一伤、二败、三毁……直到八衰。深度体验里角色真的死于黑无常之手,完整走过了尸体、化鬼、转生的死亡仪式。
English
In-game titled 金庸群侠传 ("Jin Yong Heroes Gathering"), running on the "ES II" engine (credited in its own Readme.txt to author Phoebus.suny's 1998 Tiancao-Diguo release) — the same core confirmed byte-for-byte, via direct md5sum comparison of the raw archives, as this project's jqxz2008std (a leaner, earlier '2008 Standard' content cut with fewer quests and shorter room text), jqxz2008dlx ('2008 Deluxe,' its own sect-NPC/combat-tuning/map additions), and jqxz2015 (seven more years of added zones) -- one server program, four separate content builds, not independent games. A later file comparison found the lineage extends beyond that four-way family too: this collection's xiakexing3 (in-game self-titled the same "Jin Yong Heroes Gathering" despite its archive name) shares 91% of its files byte-for-byte with this build, and xiakexing2017 shares 80-84% with both xiakexing3 and this build -- two more content snapshots of the same base game (not separately re-checked against jqxz2008dlx/std/2015). New characters land at a roadside tea house run by 店小二 (Xiao-er) and can join the Beggars' Sect through an organic passphrase-gated tree-hole hideout at the Central Square, training under 左全/Zuo Quan, or the White Camel Mount sect via a separate stat-gated recruiter NPC; real combat death against a 黑无常 (Black Gargoyle) NPC leads into the full corpse/ghost/reincarnation ritual at the death-gate inn. The Mingjiao zone's nine "yuan" NPCs carry a running numeric-curse naming gag (一伤/二败/三毁.../八衰 -- "one-hurt," "two-defeat"..."eight-decline").
README
内容亮点
- 游戏系统偏传统门派养成路线(属性、武功、任务、帮派声望),"加强版" 相较标准版补充了更多的任务内容与场景描写,是三个版本中内容较为 丰富的一支。
在线试玩
https://mudlibs.fluffos.info/jqxz2008/
管理员账号 / Admin account
- 账号 id:
fluffos密码:Mud@2026中文名:浮浮 - 权限:
(admin)(本 mudlib 的最高巫师等级),通过adm/etc/wizlist数据文件授予。
警告:Mud@2026 是本地游玩用的公开默认密码。若要正式对外开放主机,请先修改此密码(或从 wizlist 中删除该账号)。
本地运行
cd libs/jqxz2008
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40082。
NOTES · 移植与修复记录
jqxz2008 — 金庸群侠传 (Jin Yong Heroes Gathering)
Archive: archives/金庸群侠传2008加强版.rar ("Jin Yong Heroes Gathering 2008,
enhanced edition"). Extracted mudlib root: raw/jy/ (a single top-level
jy/ directory inside the archive). Port: 40082. Status: done
(boots clean, full registration flow verified with a real Chinese name,
post-login look/score verified working).
What this is / lineage
config.cfg's name field (GBK-decoded) is literally 金庸群侠传,
matching the archive title exactly — this is NOT a mislabeled archive.
adm/obj/{master,simul_efun} layout, "ES II" lineage (its own
Readme.txt says "ES2 lib (Tian Cao Di Guo) Version... Wrote By
Phoebus.suny 1998").
Confirmed near-identical sibling of xiakexing3 (archive #44,
libs/xiakexing3/): md5sum diff shows adm/obj/master.c,
adm/daemons/logind.c, adm/daemons/chinesed.c, adm/obj/simul_efun.c,
adm/simul_efun/chinese.c, include/globals.h, and
adm/simul_efun/message.c/feature/message.c are all byte-identical
between the two archives. Only adm/daemons/securityd.c and
config.cfg differ. This fully explains xiakexing3's own NOTES.md
oddity ("config MUD_NAME is 侠客行三 but the live banner reads 金庸群侠传")
— xiakexing3 is a rebranded fork/snapshot of (or a common ancestor with)
this exact "金庸群侠传" codebase; whoever forked it changed the config's
name/MUD_NAME field but never touched the deeper, shared engine
files, so the in-game banner text baked into those shared files still
says "金庸群侠传". Cross-check note for later archives: per the task
brief, archives #92 (金庸群侠传2008版.rar), #93
(金庸群侠传2008超豪华版.rar), and #94 (金庸群侠传2015版.rar) are
still queued and share this title family — worth an md5sum
cross-check against this lib's core files (adm/obj/master.lpc,
adm/daemons/logind.lpc, adm/daemons/chinesed.lpc,
adm/simul_efun/chinese.lpc) once processed, the same way this pass
cross-checked against xiakexing3(#44). Given xiakexing3, #92, #93, #94
all share "金庸群侠传"/"侠客行" branding, there is a real chance several
of them turn out to be near-identical snapshots of the same underlying
engine with only site branding + a handful of content files differing
(same pattern as the shujian2008/sjtx2 and
zhongjidiyu-family precedents already in AGENTS.md) — check this
lib's fixes (§15h chinese.lpc shape, §15ae command.lpc shape, combatd.lpc's
quest.h include, the "tang"-family new-as-variable-name typo, etc.)
against them directly before re-diagnosing from scratch.
Small-to-medium lib: 3,773 raw files, 3,135 .lpc/.c files sweepable
by lpcc_check.sh.
Fixes applied (with why)
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 — this lib has no separate named.lpc): byte-count
bound strlen(name) < 2 || > 12 → character-count bound < 1 ||
> 6 (halved, matching what the user-facing message already
promises: "1 到 6 个中文字"); dropped the i%2==0 && even-byte-offset
gate in the per-character is_chinese(name[i..<0]) sliding-window
loop (every index is already one full character now).
- Verified working: real Chinese name "秦风" now passes on the
first attempt (confirmed via the registration transcript — see
below); before this fix every valid Chinese name would have been
silently rejected.
2. AGENTS.md §15ae (private nomask command-hook breaks every
post-login command silently): feature/command.lpc's
command_hook(string arg) was declared private nomask and is
dispatched via add_action("command_hook", "", 1) from
F_COMMAND, inherited transitively into the player body
(clone/user/user.lpc → inherit/char/char.lpc → inherit
F_COMMAND). Dropped private (kept nomask). Verified: look
and score both work correctly post-login (see transcript below) —
without this fix every typed command, including look, would
silently do nothing.
3. AGENTS.md §15t (variant 1): adm/daemons/combatd.lpc had
#include </quest/quest.h> — an ABSOLUTE path inside angle
brackets, which this driver's inc_open() never resolves (angle
brackets only search the configured include path, never an
already-absolute name). /quest/quest.h genuinely exists on disk.
Fixed to #include "/quest/quest.h" (quoted absolute form, which
this driver's resolver does handle). Without this, combatd.lpc
(needed by the score command and all combat) never compiled at
all — confirmed via a live score attempt throwing *No program in
object '/adm/daemons/combatd'! before the fix.
4. AGENTS.md §15t (variant 3, NEW manifestation): fixing #3 above
then surfaced a SECOND bug in the same file: once /quest/quest.h
actually got included, its file-scope global
mapping quest_name = ([...]); landed textually BEFORE
combatd.lpc's own inherit F_DBASE; line (the #include was
listed after some other #includes but before the inherit),
which is a fatal "Illegal to inherit after defining global
variables" on this driver. Fixed by moving inherit F_DBASE; above
ALL of combatd.lpc's #include lines. quest.h's own
quest_finished()/quest_failed()/lock_quest()/islocked()
function bodies are real (not just prototypes) — the earlier
"Undefined function quest_finished" sweep error was purely a
downstream symptom of the broken #include, not a missing
function; it resolved automatically once the include worked.
5. A capitalize()-on-0 robustness bug in feature/name.lpc
(same family as AGENTS.md §15e's "guard every unchecked chained
call" principle, new concrete instance): short(int raw)'s
fallback (str = name(raw) + "(" + capitalize(query("id")) + ")",
used whenever query("short") isn't set) assumes this_object()
always has an "id" property — true for characters, but this same
function is inherited by ALL items via ITEM -> F_NAME (inherit/
item/item.lpc), including bulletin boards
(inherit/misc/bboard.lpc). Any board/item without an explicit
"short" property crashes every look in its room with *Bad
argument 1 to capitalize() Expected: string Got: 0. Found via the
very first look in the tea-house starting room
(/clone/board/kedian_b has no "short" set). Fixed with a
stringp(id) guard: only append the "(id)" suffix when there
really is a string id; otherwise return the bare name. Verified:
re-ran the full test after the fix — look in the same room no
longer throws, board is now displayed cleanly as
/clone/board/kedian_b [ 没有任何留言 ].
6. d/city/npc/guidao.lpc's is_killing(who) direct-call type
mismatch (same shape as AGENTS.md §15b's nitan_ceshi finding):
is_killing(string id) is declared in feature/attack.lpc
(inherited transitively into every NPC/character); every other
20+ call site in this lib correctly calls
is_killing(ob->query("id")), but guidao.lpc's ask_me(object
who) called is_killing(who) directly with the raw object. Since
this is a same-object DIRECT call (not ->), the static type
checker enforced the declared string parameter strictly and
refused to compile the whole file. Fixed to
is_killing(who->query("id")), matching every sibling call site.
7. Duplicate inherit F_UNIQUE; (copy-paste typo, pre-existing, not
a conversion artifact): d/mingjiao/npc/{yinwushou,yinwulu,
yinwufu}.lpc each had inherit F_UNIQUE; listed TWICE. Harmless
under whatever driver these were authored against, but this driver
fatally rejects re-declaring the same nomask function
(violate_unique) from a duplicate inherit ("Illegal to redefine
'nomask' function"). Removed the duplicate line in all 3 files.
8. The "tang"-family NPC template's new used as a bare variable
name (pre-existing authoring bug, not a conversion artifact): 7
files — d/quanzhou/npc/{tang,tang1,tang2,tang3}.lpc,
d/taishan/{tang2,tang3,xingtang}.lpc — all share one copy-pasted
do_clone() function declaring object *inv, new; and later doing
new = new("/clone/npc/..."). new is a reserved efun/keyword on
this driver, so declaring/assigning a variable literally named
new is a hard syntax error (unexpected L_NEW, unexpected
L_ASSIGN, Illegal LHS) — the whole file failed to compile.
Renamed the local variable to newob throughout (declaration +
both usages) in all 7 files; the actual new(...) efun calls are
untouched (they're function-call syntax, not the bare identifier).
9. AGENTS.md §15ac (bare SAVE_EXTENSION instead of
__SAVE_EXTENSION__): found in include/net/ftpdsupp.h,
adm/daemons/network/netmail.lpc, cmds/arch/purge.lpc, and 2 of
the "tang"-family files above (d/quanzhou/npc/tang.lpc,
d/quanzhou/npc/tang3.lpc, d/taishan/tang3.lpc — 3 files
actually). Replaced the bare SAVE_EXTENSION with
__SAVE_EXTENSION__ in all of them. Left alone:
clone/npc/meng-zhu.lpc and u/jackyboy/TOKEN.lpc, which both
#define SAVE_EXTENSION ".o" locally themselves — self-contained,
not part of the bug.
10. kungfu/class/mingjiao/mingjiao.h's string-literal-adjacent-to-
macro-expansion syntax error: ob->query("party")["party_name"]
== HIG "明教" NOR (a colored-string equality check) failed to
parse (unexpected L_STRING) because after macro expansion
(HIG → ESC+"[1;32m", NOR → ESC+"[2;37;0m") two string-
literal tokens end up textually adjacent with no operator between
them, which this driver's grammar doesn't auto-concatenate.
Rewrote as HIG + "明教" + NOR with explicit +. Also added
#include <ansi.h> to this same header, since two of its
includers (d/mingjiao/npc/{litianyuan,yinyewang}.lpc, reached
transitively via tangzhu.h) never include <ansi.h> themselves,
so HIG/NOR were genuinely undefined identifiers in that
compilation context 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" —
that path doesn't exist (an early-draft location; the real
tanzhu.h lives at d/mingjiao/npc/tanzhu.h, right next to this
file, exactly where its sibling NPC files
chengchaofeng.lpc/gaoshanwang.lpc correctly reference it via a
bare "tanzhu.h"). Fixed to #include "tanzhu.h".
12. d/mingjiao/npc/{fenggongying,baiguishou}.lpc's
#include __DIR__"tanzhu.h" (no +, no quotes around the whole
directive) failed with "Cannot read #include file" (a different,
more specific driver error than the "Cannot #include" bad-path
case — the path resolves/opens but reading its content fails,
consistent with __DIR__'s macro-expansion inside #include's
unquoted form doing something unexpected here). Rather than
forensically chase the exact interaction (low value for 2 files),
rewrote both to the already-proven-working plain quoted form
#include "tanzhu.h", matching every other sibling NPC file in
the same directory that already resolves this exact same file
successfully.
13. d/baituo/ouyangfeng.lpc, two independent pre-existing typos:
(a) a missing semicolon after write("...") inside a switch
case 0: block, causing the parser to read the next line's
break; as part of an unterminated expression
(unexpected L_BREAK); (b) object me=this-player(); — this-
player() (a subtraction of this and player()) 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) in
me->receive_damage("jing", ging_cost). Fixed to jing_cost.
15. d/mingjiao/jmqshenmu.lpc was genuinely truncated in the raw
archive itself (confirmed by diffing against the pre-conversion
.c — both are exactly 26 lines, ending mid-void init() { with
no closing braces at all — not something our pipeline introduced).
Closed the truncated function with an empty body (a single })
rather than fabricating any actual init() content, purely so the
file is syntactically valid instead of a permanent parse-error
landmine; the room's real, complete create() content is
untouched.
Confirmed NOT needed (checked by reading the actual source)
- §4 (master's lazy security-daemon load recursing to a stack overflow):
master.lpc'svalid_writeonly doesfind_object(SECURITY_D)(noload_object()at all — returns 0 if not found, no recursion risk);valid_readis a barereturn 1;. Exactly the same shape already confirmed clean onxiakexing3(this file is byte-identical between the two archives). - §7 (missing
get_root_uid()/get_bb_uid()): both already implemented inmaster.lpc(lines 177/182). - §8c (
valid_read/valid_writeoverriding caller withthis_player()):securityd.lpchas novalid_readat all (onlyvalid_write, which never touchesthis_player()to overrideuser). - §15n/§15o (custom securityd ACL blocking driver's own compile-time reads / missing
get_include_path()): this lib'ssecurityd.lpcimplementsvalid_writeonly (no customvalid_readACL), andmaster.lpc'svalid_readis an unconditionalreturn 1;— the driver's own lazy compiles are never gated by anything custom. Noget_include_path()needed either: no<local.h>-next-to-its-user#includecollisions found anywhere in the tree (checked programmatically). - §15p (DNS/intermud daemon in preload causing hangs):
adm/etc/preloadnever listednetwork/dns_masterin the first place (11-entry preload list, all registration-relevant daemons, confirmed by direct read before the first boot attempt) — nothing to exclude. - §15l (master.lpc destructing
SIMUL_EFUN_OBincreate()):master.lpc'screate()is a singlewrite(...)line, nodestruct()call at all. - §15u (dormant "phone home" destructive license check): grepped
master.lpc/securityd.lpcforshutdown/rm(/rmdir(/unlink(— only one hit, a harmless comment. - §15r (
check_config.lpc-style driver-version self-check): no such file anywhere in this lib. - §15ag (
ed_start/ed_cmd/query_ed_mode): zero occurrences. - §15al (
crypt(str, 0)random-hash-every-call footgun):logind.lpc'snew_password()usescrypt(pass, 0)only to generate a NEW password hash at registration time (stored once); every subsequent login compares viacrypt(attempt, stored_hash)(a real string salt, not0) — this is ordinary, correct password hashing, not the client-challenge-determinism footgun the catalog entry describes (which specifically involves BOTH sides needing to independently compute the samecrypt(KEY, 0)value — no such challenge/response protocol exists in this lib). - §8e (
tail()not a real efun): 2 harmless hits, both incmds/wiz/tail.lpc(an unused admin command, never reachable from registration/core gameplay) — left as documented pre-existing noise, matching the catalog's own precedent for this exact non-fatal case. - §15/§15b (simul_efun-based generic
set/query/deletedbase architecture bug, "NT/nitan/Lonely" lineage):adm/obj/simul_efun.lpcand its#included fragments define no globalset/query/deleteat all — this lib uses the architecturally-correct per-objectinherit F_DBASEpattern throughout (same asrzrmud/xiakexing3), confirmed by grep before assuming the fix was needed. - §15w/§15af (
log_error()spamming players with warnings / crashing on a lazy uid-check during compile-time warnings):master.lpc'slog_error()-equivalent apply wasn't found doing anything unusual; not investigated further since no symptom of this shape appeared in any boot or gameplay test. - §15x (hardcoded
MUD_PORTmismatch):master.lpchas no customconnect(int port)apply at all — nothing to mismatch. - §15ao (
switchwith onlydefault:): none found inmaster.lpc. - Uppercase
.Cfiles: 4 found, all underu/jackyboy/(a wizard's personal item files —TOKEN.C,WIZ_CLOA.C,CLOTH1.C,GLASS.C), already UTF-8 (not GBK), simply renamed to.lpc(convert_lib.sh's glob only matches lowercase.c). #define nosave static/#define protected staticcompat shim (§15z): none found in any.hfile.- Fixed-width
[0..<N]slice bugs (§2's<3→<5widening variant): several[0..<2]/[0..<3]occurrences found via grep, but all inspected and confirmed to be either ARRAY-removal idioms (cmdsarray element deletion incommandd.lpc), or ordinary trailing-character trims (dropping a*wildcard suffix inchanneld.lpc, dropping a trailing newline inftpd.lpc) — unrelated to the.c→.lpcfilename-extension-width bug. The one genuine filename-extension[0..<3]pattern found (explode(__FILE__,"/")[<1][0..<3]in 8kungfu/skill/*/*.lpcfiles) computes a value (pfname) that is used ONLY inside commented-out permission-check blocks in every one of the 8 files — confirmed dead code, not fixed (no live effect either way).
Interactive test result — full registration + post-login flow
Registration flow (all in one continuous mudclient.py session,
verified 3 times across the fix-iteration cycle, final clean run
shown): 英文名字(id) → y (confirm new character) → real Chinese
name "秦风" (accepted on the first try, no rejection loop) →
password → confirm password → accept random gift stats (y) → email →
gender (m) → enters the game world at 客店 (the tea-house starting
room).
您的英文名字:使用 qinfengwu 这个名字将会创造一个新的人物,您确定吗(y/n)?
请输入您的高姓大名,...
您的中文名字:/clone/user/login#0 <- (author's own printf("%O\n",ob) debug leftover; harmless)
请设定您的密码:
请再输入一次您的密码,以确认您没记错:
...膂力[20], 悟性[18], 根骨[20], 身法[22]
您接受这一组天赋吗?
您的电子邮件地址:
您要扮演男性(m)的角色或女性(f)的角色?
目前权限:(player)
...
[1;36m客店[2;37;0m -
这是一家价钱低廉的客栈,生意非常兴隆。...
这里明显的出口是 west 和 up。
/clone/board/kedian_b [ 没有任何留言 ]
店小二(Xiao er)
你上次连线是从0 on Wed Dec 31 16:00:00 1969Post-login commands, in the SAME connection:
look: works, re-renders the room + NPC + (now-fixed) board display cleanly, zero runtime errors.score: works, shows full real character sheet (title, age/gender/ birthdate, 膂力/悟性/根骨/身法 stats, 精/气 bars, 食物/饮水 bars, kill count, 潜能/实战经验) — this is the fix from combatd.lpc'squest.hinclude bug; before that fixscorecrashed with*No program in object '/adm/daemons/combatd'!.quit: clean save + disconnect message.
Zero fatal/runtime errors anywhere in log/debug.log across this final
run (only expected compile *warnings* — "Unused local variable",
"Illegal to declare nosave function" — and the one known, non-fatal,
pre-existing corrupted-save-data error described below).
Known non-fatal pre-existing issue (not fixed)
adm/daemons/chinesed.lpc's create() calls restore()
unconditionally on its English→Chinese translation dictionary save file
(data/e2c_dict.o). That file's raw bytes (confirmed identical,
md5sum, between raw/ and work/ — genuinely never touched by our
encoding pass, since file classifies it as data and .o is
deliberately excluded from the forced-text extension list) do not
decode as a valid LPC mapping (restore_object(): Illegal file format)
— this looks like pre-existing corrupted seed data in the original
archive itself (same class of bug as zhonghua2's stale versiond.o,
AGENTS.md §15m), NOT a driver-compat or encoding-conversion bug. It's
fully caught by master.lpc's own preload() catch wrapper (visible
in debug.log as "错误讯息被拦截"/"error message intercepted"), entirely
non-fatal, and chinesed.lpc's dict mapping simply stays at its
default empty value — English/Chinese name translation lookups just
return the input unchanged instead of a real translation. Does not
affect registration, look, or score.
Content gaps confirmed as real archive gaps, not bugs (AGENTS.md §13)
d/mingjiao/yuan/*(9 named "yuan" NPCs, e.g. 郑七灭/郑十灭 family) reference two genuinely-missing kungfu skill files (xuanyuan-arrow,liangyi-jian) — not present anywhere underkungfu/skill/.d/mingjiao/npc/monkey.lpcreferences an undefined race (走兽, "beast") not present in the race table.d/mingjiao/npc/{gaoshanwang,fenggongying,baiguishou,changjinpeng, chengchaofeng}.lpcall also reference a whole missing zone (/d/yanziwu/, referenced viacarry_object()/room exits from several 明教-zone NPCs) that doesn't exist anywhere in this archive.d/wudang/taoyuan/{tyroad4,5,6,7}.lpcare confirmed ORPHANED early drafts of the finished, fully-workingd/wudang/tyroad{4,5,6,7}.lpcfiles one directory up (diffed byte-for-byte content — the top-level versions are the complete/correct ones, with proper NPC spawns and the correctfeng.hinclude path; thetaoyuan/copies are shorter, broken-include, no-NPC earlier drafts). Nothing in the entire lib references thetaoyuan/directory at all (grepped) — a dead/unreachable duplicate, not fixed.u/liujun/quest/leftkiller.lpcreferences a missing skill (six-chaos-sword);u/editer/tie-luohan.lpcis a broken/unfinished wizard scratch-directory draft (garbled variable names look like a botched find-replace, e.g.skill_statussname) — both under personal wizard directories, not core content, not fixed.adm/daemons/network/*(dns_master, mudlist_a, netmail, inetd, mail_serv, and friends): confirmed via grep that NOTHING outside this subdirectory references any of these files, and none are inadm/etc/preload— genuinely dead/inert intermud subsystem code, never compiled during a real boot or any registration/gameplay path.home/simul_efun.lpc: a stray backup/scratch copy of the real simul_efun sitting in a/homesandbox directory (not the configuredsimulated efun file, which is/adm/obj/simul_efun) — the lpcc sweep's "Invalid simulated efunction override" failure on it is a pure sweep artifact (AGENTS.md §6b:main_file_name()reports differently when this fragment is compiled standalone vs. as the real thing), never loaded as the actual simul_efun in real gameplay.clone/board/*corrupted save data (11 board.ofiles underdata/board/failrestore_object()with either "Illegal file format" or "Invalid utf8 string while restoring dbase") — same class of pre-existing corrupted seed data aschinesed'se2c_dict.oabove; each board'ssetup()already wraps the failure gracefully (boards just start with no persisted notes), confirmed non-fatal via the livelooktest atkedian_b. Correction (2026-07-24 WASM long-sit boot-watch):kedian_b.oIS actually one of the 11 corrupted files (data/board/kedian_b.ois the same class of raw binary garbage, confirmed viaxxd) — the registration flow's ownenter_world()→kedian.lpc create()→kedian_b.lpc create()→bboard.lpc setup()→restore()chain throws the same "Illegal file format" error every time a fresh character registers there, caught bylogind.lpc's ownCATCH()aroundenter_world(). Still fully non-fatal (board ends up empty, exactly as documented above) — the earlier claim thatkedian_brestores cleanly was simply wrong (probably from a test transcript that only showed player-facingwrite()output, not the console-only intercepted-error banner).d/npc/m_weapon/weapon/{m_club,m_blade,m_whip,m_sword,m_staff}.lpc:create()callsthis_player()unconditionally — always null in an isolatedlpcccompile (no player context), causing acall_other()-on-0 sweep failure. This is the standard AGENTS.md §6b "compiled divorced from runtime context" false positive — these files are only evernew()'d as a direct response to a real player action, wherethis_player()is always valid.
lpcc sweep
Ran 3 times across the fix-iteration cycle (baseline → after the
combatd/command_hook/name.lpc/guidao/tang-family/mingjiao fixes → final
confirmation), each a fresh lpcc --batch sweep via
scripts/lpcc_check.sh:
- Baseline (before any post-boot fixes beyond §15h/§15ae): 3040/3135 pass (96.97%).
- After combatd.lpc / tang-family
new/ SAVE_EXTENSION / mingjiao.h / yinwushou-family duplicate-inherit / guidao is_killing / ouyangfeng.lpc / shuitan.lpc fixes: 3054/3135 (97.4%). - Final (after the mingjiao.h
ansi.hinclude + tanzhu.h path fixes + jmqshenmu.lpc truncation guard): 3064/3135 pass (97.74%).
Remaining 71 failures were triaged by category (AGENTS.md §6b) and are
all one of: corrupted pre-existing save data (11, boards), genuinely
missing zone/skill/race content (§13, ~20 files across
mingjiao/yuan, yanziwu-zone NPCs, wizard scratch quests), the dead
DNS/intermud subsystem (~16, never preloaded/never referenced), a
handful of wizard personal-sandbox scratch files
(u/liujun/, u/editer/, home/simul_efun.lpc), 2 harmless
tail()-missing-efun hits in an unused admin command (§8e, matching
established precedent), and the this_player()-is-null-during-
isolated-compile sweep artifact on 5 weapon template files (§6b). None
of these affect registration, look, or score — all confirmed via
direct source reading, not guessed.
Memory stayed healthy throughout all 3 sweeps (well under 1 minute
each, free -h never dropped meaningfully — this is a small/medium lib,
nowhere near mega-lib territory).
Process notes
- Driver launched via
nohup ... & disownfrom withinlibs/jqxz2008/(required forlog/debug.logto resolve correctly, per AGENTS.md §6). Rebooted 4 times total across the fix-iteration cycle; each old PID was killed by its own exact PID (never a broadpkillpattern) and verified gone viaps/ssbefore relaunching. Final driver process was killed (PID 632097) after all testing completed, confirmed viass -tlnpno longer showing port 40082. - Each successive registration test used a fresh English id (
qinfeng,qinfengtwo→ rejected, digit not allowed;qinfengsan,qinfengsi,qinfengwu) since a successfully-registered id's save file persists across driver restarts and re-prompts for a password instead of fresh registration — not a bug, just a test-script gotcha worth noting for whoever re-tests this lib later.
2026-07-23 re-verification pass (driver rebuild + formatter + WASM)
- LPC reformat: ran
format-corpus.mjsover all ofwork/(3,135.lpcfiles) — 3,102 written, 13 already-idempotent, 20 refused (self-checked round-trip failures on messy legacy syntax, expected). - Native retest against the freshly-rebuilt driver: booted clean on port 40082, zero fatal preload errors. Full registration flow with a fresh real Chinese name ("秦风六"/
qinfengliu, distinct from all prior test ids on this lib) through the complete wizard (id → confirm → Chinese name → password → confirm → gift accept → email → gender) into the actual game world (客店);look,score(full character sheet, correct stats/HP/potential), andquit(correct item-drop flavor text + clean save) all verified with real output, zero runtime errors in the whole session. No regressions from the reformat or the new driver binary. - WASM test (
scripts/wasm_client.jsagainstbuild-wasm/src): boots cleanly, same preload warnings as native. Full registration + login + look + quit all work end-to-end under WASM — this lib does not gate its login onquery_ip_number()'s format, so the documented WASM IP-check limitation doesn't apply here. Only cosmetic artifact observed: the WASM session's synthetic clock reports "上次连线...Wed Dec 31 16:00:00 1969" (Unix-epoch-adjacent) for the last-login timestamp on a fresh registration — harmless, purely a WASM-clock display quirk, not a functional issue.
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):
adm/daemons/band.lpc:39is_banned(): short-circuitreturn 0for loopback (127.0.0.1/127.*), empty, or non-string site values before the regexp ban-list scan.logind.lpc:67'sBAN_D->is_banned(query_ip_name(ob))gate inlogon()is thereby loopback-proof (and robust against the WASMquery_ip_number()/query_ip_name()garbage-return bug).- No
uptime()startup-grace gate and no per-IP anti-flood/registration throttle exist in this lineage (checkedlogind.lpc's full input_to chain; the only other connection gate is the in-memorymad_lockadmin lockdown flag, default off — left alone).
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)
- Native (port 40082): fresh registration
ceshier/秦风 end-to-end into 客店; re-login (restore path) + look/score/quit all correct;fluffos/Mud@2026login shows(admin)andupdate /cmds/usr/scoreprints 成功. debug.log: only the two documented pre-existing corrupted-save restore errors (chinesed e2c dict + kedian board), both caught by the lib's own handler — zero new errors. (One mid-test driver death was an external SIGTERM — the §10.5 stray-kill hazard, not a lib fault; restarted and completed cleanly.) Driver killed by exact PID. - WASM (build-wasm with query_ip_number/resolve fixes): fresh registration
wasmceshi/秦风 end-to-end + look/score/quit all correct; second invocation:fluffosadmin login(admin)+update成功 — admin flow now verified under WASM too. Verdict: native OK + wasm OK. No fixes needed this pass. Test character saves removed; fluffos.o timestamp churn reverted.
2026-07-24 深度功能测试 / Deep functional test (AGENTS.md §10.7)
One continuous native mudclient.py session (plus a second fluffos
admin session used specifically to reach death/combat, since the main
character never accumulated the money or combat skill to risk real
kill safely in the time budget). §8.3a's private nomask
command_hook fix was already applied in this lib (feature/
command.lpc:29 reads nomask int command_hook(string arg), no
private — confirmed by direct read before touching anything else) —
not re-diagnosed.
Test character: id shenshaofeng, password xia2026wu, Chinese
name 沈少峰 (male). Left in a clean, alive, fully-saved state at
/d/city/zuixianlou (醉仙楼), title 叫化子/丐帮第二十代弟子 (member of
the 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/
shenshaofeng.o, work/data/login/s/shenshaofeng.o) as evidence.
Admin account fluffos/Mud@2026 (pre-existing, from the WASM-
enablement pass) reused for the death/reincarnation leg; left alive,
saved, at /d/city/wumiao (its own now-legitimate chosen home, set via
an explicit quit there — see bug #2 below for why that's legitimate
and the room's own automatic version isn't).
What was verified working, live
- Full registration wizard with a real Chinese name (沈少峰), landing in the actual starting room (客店, the tea house) —
look/score/iall correct at every step. - Starting-zone navigation read from room source (
d/city/{kedian,beidajie1,guangchang}.lpc) rather than guessed. - Safe sparring:
help newbie/help intro(work/doc/help/{newbie, intro}) both explicitly documentfight(较量/切磋) as the non-lethal form (stops automatically at ~50% health, message: "这场 比试算我输了,下回看我怎么收拾你!") vskill(real, lethal). Usedfightagainst 流氓头 (Liumang tou) at 中央广场 — auto-stopped correctly, zero real damage risk, exactly as documented. - Sect join via the organic path: entered the 丐帮 (Beggars' Sect) hideout via the tree-hole passphrase at 中央广场 (
d/city/guangchang.lpc'sshudong→d/gaibang/inhole.lpc's passphrase gate →down→ 左全/Zuo Quan), thenbai zuo— worked first try, correct flavor text,scoreafterward shows the new title/master. Also traced (not exercised, to keep the character safe/simple) the White Camel Mount (白驼山) sect's alternate teacher-recruit gate atkungfu/class/btshan/ouyangke.lpc'sattempt_apprentice()— correctly gates oncombat_expand confirms a second, independent join mechanism exists. - Organic skill learning from a teacher NPC:
xue zuo beggingafterbai-ing him — consumed 精 and 潜能 correctly, printed the expected "你的「begging」进步了!" message, confirmed viascore. cha zuo(查功夫 — list a teacher's skills), once the crash below was fixed: full skill list rendered correctly.- Clean
quit(multiple times) followed immediately by adebug.logline-count check each time — zero newerror:/fatal lines any time (confirmed at 349 lines before and after every quit across the whole session, on the FIXED driver). - State persistence across a real driver restart + fresh restore-login (not just an in-memory reconnect): location, sect membership, master, skill level, 潜能 spend, and inventory (mailbox + a fresh cloth — clothes are deliberately non-
autoloadand get re-issued byenter_world()every login, see "confirmed not a bug" below) all came back correctly. - Unclean (net-dead, not
quit) disconnect + reconnect: exercised repeatedly (everymudclient.pyinvocation that didn't end inquitcloses the socket without running thequitcommand, whichclone/user/user.lpc'snet_dead()handles by stopping the heartbeat, clearing enemies, and schedulinguser_dumpafterNET_DEAD_TIMEOUT— 600s/10min,include/user.h:10— the player object is never moved or void-parked, matching AGENTS.md §7.20's "good" shape, not its bug shape). Reconnecting always showed "重新连 线完毕" and landed exactly where the character had been standing. Also did one deliberate net-dead disconnect followed by a real ~150-second wall-clock wait (backgroundedsleep, polled, not a short synthetic gap) before reconnecting — same clean result. Did NOT attempt the full 600sNET_DEAD_TIMEOUTwait — explicitly skipped for time budget, not silently. Readuser_dump()(clone/user/user.lpc:66) instead: on timeout it just runscommand("quit"), i.e. the same already-verified-clean quit path, so the full-duration wait is very unlikely to surface anything the shorter wait + code read didn't already cover — lower priority here than actually chasing the two live crashes below. - Real combat + death + corpse + ghost + reincarnation, end to end, live (not code-reviewed): the
fluffosadmin character fought a 黑无常 (Black gargoyle) NPC at/d/death/gateway(reached viagoto) and actually died for real. Confirmed the full chain once the crash below was fixed:你死了→ ghost title (【鬼魂】) → corpse object left in the room →scoreshows reduced stats/0 jing-qi → the documented "ask yourself about 回家" ritual at/d/death/inn1.lpc(d/death/gate→gateway→road1→inn1,ask fluffos about 回家) →reincarnate()fires and moves the (now living again) character to/d/city/wumiao. This is a genuinely deep, non-trivial path (matches checklist item 10's "get as far as reasonably possible toward combat/death") and it now works cleanly end-to-end. - Shop (
buy/list, F_DEALER pattern,feature/dealer.lpc):listat 醉仙楼 (a food stall, reached by mistake while navigating — turned out to be a useful second F_DEALER instance to check) rendered correctly with no crash.buywas exercised twice and both times produced the CORRECT rejection message with no crash: once as a 丐帮 member ("你是个穷叫化,买什麽东西!" — beggars are deliberately forbidden from buying,feature/dealer.lpc:116-117, a real, internally-consistent design choice, not a bug) and once for insufficient funds.
Not verified live (honest gaps)
- A completed, successful purchase. New characters start with zero money (
init_new_player()inlogind.lpchas the starting- balance line explicitly commented out) and the test character then joined 丐帮, which the shop code deliberately forbids from buying at all — reaching a real purchase would need either a different (non-beggar) character built from scratch or a source of starting gold, beyond this pass's time budget.buy's code path (money check,move(),call_out("enough_rest", 1)) was read and looks sound, but that's a code-review conclusion, not a live one. - The full 600-second
NET_DEAD_TIMEOUTwait. See above — done a ~150s real wait instead, plus a source read of the timeout handler, and prioritized chasing the two live crashes found instead.
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: "restore_object() ... ZEROES ... a
crash surfaces far away")
This lib's own NOTES.md already documented chinesed's
data/e2c_dict.o as pre-existing corrupted seed data whose restore()
fails at boot, and called it "fully non-fatal" because the failure is
caught by master.lpc's preload() wrapper. That part is true for
*boot* — but nothing downstream was actually safe: create() was
void create() {
seteuid(getuid());
restore();
}restore() → restore_object() throws a real runtime error on the
malformed file; since that error is never caught INSIDE chinesed.lpc
itself, it aborts create() immediately at the restore() line — the
global mapping dict = ([]); initializer had already run at object
creation, but restore_object()'s failure zeroes it back out per
§7.7's exact mechanism, and because create() never reaches any
statement after the throwing call, no local recovery code could run
either. The very first real player use of any command that calls
chinese() (confirmed live via cha zuo, but to_chinese() used
throughout combat/skill messages could hit the same path) crashed with
*Value being indexed is zero. at chinesed.lpc:94.
Fix — wrap the throwing call in catch() (so create() can actually
continue past it) AND add the standard §7.7 post-restore guard:
// BEFORE:
void create() {
seteuid(getuid());
restore();
}
// AFTER:
void create() {
seteuid(getuid());
catch(restore());
if (!mapp(dict)) dict = ([]);
}A mapp() guard alone (tried first) was NOT sufficient — confirmed
live that it still crashed identically, because the guard line is
never reached without the catch() (the runtime error unwinds straight
past it to the nearest existing catch, in master.lpc's preload()).
Verified: rebooted, repeated the exact bai zuo → cha zuo
sequence — full skill list printed, zero runtime error, zero new
debug.log lines.
2. (RETRACTED — content/design judgment call, not a programming bug;
reverted on user review) d/death/gate.lpc and d/city/wumiao.lpc's
init() unconditionally wrote the player's permanent startroom on
mere room entry. Originally flagged as an AGENTS.md §7.24 match and
fixed by deleting both init() bodies — since reverted. The key fact
that distinguishes this from the earlier zzfy §7.24 case: both
rooms already flag themselves valid_startroom via their own
create() — unlike zzfy's revive-limbo rooms, which did NOT carry
that flag and so directly contradicted the lib's own gating convention.
Here, writing startroom on entry doesn't violate this lib's own
architecture at all; it's plausibly a deliberate "these are checkpoint
locations, entering one silently updates your home" design (the death
gate and a city temple both read as thematically plausible checkpoint
spots), even though it bypasses the normal player-initiated
save/quit-while-standing-here convention and gives no on-screen
confirmation. That's surprising UX, not a proven programming defect.
Left as originally shipped (both init()s restored), documented here
rather than silently re-fixed. d/city/cangku.lpc/d/shaolin/cangku.lpc
and the d/shaolin/ jail-cluster rooms (also touch startroom on
capture/release) were separately confirmed to have genuine, matched
restore paths and were correctly never touched.
3. Missing /log/nosave/ runtime directory crashes combatd.lpc's
killer_reward() on EVERY death, kill.lpc's PK-attempt logger on
every player-vs-player kill, and bai.lpc/apprentice.lpc's
Feng-Qingyang student counter (AGENTS.md §7.11, exact match — same
example directory the catalog entry already names)
work/log/nosave/ does not exist in this archive. Four call sites
write into it with a bare write_file()/no assure_file()/no
catch():
adm/daemons/combatd.lpc:721(killer_reward(),KILLRECORD— runs on literally every death, any killer/victim)cmds/std/kill.lpc:37(ATTEMP_KILL— runs on everykillwhere the target is a player)cmds/skill/bai.lpc:57/65and the identical duplicatecmds/skill/apprentice.lpc:57/65(FENG— a rarely-reached special- case counter, gated behind a separate bug, see #4)
This is not a one-time cosmetic error — it's a live, reproduced,
severe softlock. Confirmed by actually killing the fluffos admin
character in real combat (a 黑无常 NPC): killer_reward()'s
write_file() threw *Wrong permissions for opening file
/log/nosave/KILLRECORD for append. "No such file or directory",
UNCAUGHT, which aborts die() at exactly that call — every statement
AFTER it (corpse creation, remove_all_killer(), moving the player to
DEATH_ROOM, setting the ghost flag) never ran. The character was
left standing in the combat room with 0 qi/jing, still "alive" by the
engine's bookkeeping, and the SAME crash then re-fired on every single
subsequent heart_beat() tick — an infinite crash loop, confirmed live
by watching it repeat identically 3+ times across one score check
before the fix.
Fix — the mudlib already has its own idiom for exactly this
(assure_file(), adm/simul_efun/file.lpc:11, already used by
feature/save.lpc) — used it at all four write sites instead of
introducing an out-of-band mkdir:
// combatd.lpc, kill.lpc, bai.lpc, apprentice.lpc — pattern applied at
// each write_file("/log/nosave/...", ...) call:
assure_file("/log/nosave/KILLRECORD"); // (or ATTEMP_KILL / FENG)
write_file("/log/nosave/KILLRECORD", ...);Also added a stringp() guard on the FENG counter's read_file()
result in both bai.lpc/apprentice.lpc (same file, same class of
risk as AGENTS.md §7.9 — read_file() on a not-yet-created file
returns 0, which would otherwise flow into atoi(0)).
Verified live: rebooted, repeated the exact same admin-vs-gargoyle
fight to death — this time die() ran to completion cleanly (single
你死了, ghost title, corpse present, no repeated crash spam), and
work/log/debug.log stayed at its pre-fight line count throughout.
4. 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
// BEFORE (both files, same line):
if (((string)me->query("family/master_id" == "feng qingyang")) || ((string)me->query("family/master_name" == "风清扬"))) {The == comparison is INSIDE the query(...) call's argument list,
so query() is actually invoked with a boolean/int (0, always false
for any real property name) instead of the intended string key —
query("family/master_id" == "feng qingyang") evaluates the
comparison first and passes its result, not the string, meaning the
branch could never fire regardless of the player's actual master.
Fixed to compare the RETURN VALUE of query() against the string, as
every other call site in the file already does correctly:
// AFTER:
if (((string)me->query("family/master_id") == "feng qingyang") || ((string)me->query("family/master_name") == "风清扬")) {Low real-world impact (only reachable for a player defecting FROM the specific named master 风清扬/Feng Qingyang, a rare special case) but a genuine, unambiguous programming bug — not a design question — since the code's own structure makes its intent (compare a queried value to a literal) unambiguous. 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.
Bug-class mapping (all four matched EXISTING AGENTS.md entries — no new class)
- Bug 1 → §7.7 ("Unguarded
restore()/ corrupted save data", specifically itsrestore_object()zeroes-globals-on-failure bullet). - Bugs 2 → §7.24 ("Death/reincarnation code silently overwrites the permanent login-location field") — this pass found a SECOND, more severely-reachable instance of the exact same pattern in the same lib (an ordinary city room, not just the death limbo itself), and did the extra due diligence of checking every OTHER
startroomwrite site in the lib against the same detection heuristic before concluding which were real bugs vs. legitimate matched capture/ release mechanics (§7.24's own suggested check). - Bug 3 → §7.11 ("Missing runtime directories and the silent
write_fileabort") — this pass adds a concrete, live-reproduced, data point for the catalog's own example directory (/log/nosave/) and demonstrates the worst-case severity class explicitly: an UNCAUGHT crash mid-way through a multi-step cleanup function (die()) doesn't just fail once, it can leave the object in a broken state that re-triggers the same crash everyheart_beat()forever. - Bug 4: a plain misplaced-parenthesis wrong-argument bug, the same general shape as several "obviously-wrong call-site" fixes already cataloged elsewhere in this lib's own NOTES.md (§10.7 checklist item 7's "obviously-wrong variable references" bucket) — not a numbered AGENTS.md class of its own.
Process notes
Driver run from libs/jqxz2008/ via nohup ... &
disown, restarted 4 times across the fix-iteration cycle (chinesed
fix alone → +bai.lpc paren fix → +gate.lpc/wumiao.lpc/§7.11 fixes →
final confirmation), each old PID killed only after confirming
readlink -f /proc/<pid>/cwd matched this lib's work/ directory
(other agents' driver processes for xiakexing2017, xlqy_new2007,
xo, shujian2008, xkxz2, zhonghua2 were running
concurrently throughout and were never touched). RSS stayed at
30-44MB across the whole ~10-minute final session, no unbounded growth
(§10.8 checked, nothing concerning). Final driver (PID 1718534) killed
by exact PID after all testing completed, confirmed via ps/ss no
longer showing port 40082.
补充发现(来自对同引擎兄弟档案 jqxz2008std 的深度测试):这轮 §10.7 测试当时漏掉的第 5 个 bug,§8.9 食物/饮水初始化检查错了对象
adm/daemons/logind.lpc 的 enter_world() 里:
if (!user->query("food") && !user->query("water") && ob->query("age") == 14) {age 是角色本身(user)的属性,不是登录物件(ob)的属性——
ob->query("age") 永远是 0,这个分支从来没有真正执行过,每个新
角色的食物/饮水从创建起就永远是空的,静默、无报错。这轮上面记录的
深度测试用的是 shenshaofeng/沈少峰这个角色,一路走到拜入丐帮、学
begging——没有专门核对过食物/饮水初始状态是否为满,所以当时漏掉
了这个 bug。已改成 user->query("age") == 14,用真实驱动重新注册
验证过(沈叁),score 正确显示食物/饮水两条状态全满。这个 bug 在
同引擎的"标准版"(jqxz2008std)先发现,确认三个版本档案里
logind.lpc 这一行逐字节相同,一并同步修复。
WASM 修复摘要(迁移自 meta.json 的 group_note)
金庸群侠传引擎基础版(2008 加强版)。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 18 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
深度功能测试(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)已经在更早一轮
(引用注释里的"AGENTS.md #15w")正确修复过,管理员账号
(fluffos/Mud@2026,adm/etc/wizlist 已有 fluffos (admin))也
已经用真实注册流程创建并提交过,本轮只发现并修复了 log_file()
一处。
发现并修复的 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);(含前向声明)。
更正(2026-08-13):e2c_dict.o 已删除
上面记录的"未修复"已过时。用户指示清理这类噪音后,重新确认
chinesed.lpc 的 create():catch(restore()); if (!mapp(dict))
dict = ([]);——即使 restore() 失败,dict 也会正确回退成空映
射,删除这个永远无法被正确解析的损坏文件不会改变任何行为(有它在
的时候和没有它的时候,chinesed 的实际运行状态完全一样,区别只是
每次开机是否会在驱动 stdout 上多打印一条被 CATCH() 吞掉的错误
噪音)。已删除 data/e2c_dict.o,重新走一遍完整注册/登录流程复
测,chinesed 干净加载、无任何报错,功能无回归。
Proactive checks(无需改动)
win_times修复确认存在且正确:d/city2/npc/refereew.lpc:176; 姊妹文件u/wind/refereew.lpc:176/refereew2.lpc:184同样已修。- 未发现
message()simul_efun 包装函数——不适用 message()-missing-varargs 这一类 bug。
实测过程
用已提交的 fluffos/Mud@2026 登录,score 确认"目前权限:
(admin)",update /adm/simul_efun/file(就是本轮改过的文件)确认
可正常重新编译。log/debug.log 时间戳全程未变化(Aug 5,早于本
次会话,且文件本身为空),确认无新增未捕获运行期错误。登录本身产
生的存档时间戳类微小 diff(data/{login,user}/f/fluffos.o 的
last_on 字段)已用 git checkout 撤销,不提交。驱动最终按精确
PID kill,ps -p 确认已退出。
§7.100 扫描修复(ROOM 基类多余 replace_program())
#define ROOM "/inherit/room/room":删除 824 处多余的、独立成行的
replace_program(ROOM);(保留 inherit ROOM;),与手足档案
jqxz2008dlx/jqxz2008std 同一血统同一形状。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/21):shop-purchase 缺口关闭 + 标准清单复核
Closed this lib's own explicitly-flagged §10.7 gap ("Not verified live
(honest gaps)": a completed, successful shop purchase). Booted
build-debug/src/driver clean on port 40082 (PID 976088, killed by
exact PID when done, verified gone via ps/ss). Used a raw Python
socket script (two concurrent connections, each response read/printed
and validated before the next command was sent).
Test character: fresh id qintestg, Chinese name 秦八 (password
redacted, not recorded in plaintext), registered end-to-end through the
normal wizard, landing at 客店 with title 平民/普通百姓 — deliberately
NOT a member of 丐帮 (confirmed via score: 【 平 民 】, not the
【 叫化子 】 beggar title), so the existing beggar-forbidden-from-buying
design gate does not apply to this character. Save files
(work/data/user/q/qintestg.o, work/data/login/q/qintestg.o) left in
place as evidence, matching this file's own established precedent for
this test class.
Funding (sanctioned admin clone+give pattern): logged in
separately as the pre-existing fluffos/(admin) account (already
committed from an earlier pass), goto'd to the same room as
qintestg (/d/city/zuixianlou2, the tea-house-district shop's upper
floor), clone /clone/money/coin (this lib's actual copper-coin money
object, /clone/money/coin.lpc, id coin/coin_money), call
coin->set_amount(500) to bump it from its clone-default of 1 up to
500, then give qintestg coin — qintestg received "五百文铜板" (500
coins) live, confirmed via i.
The purchase itself: qintestg walked to 醉仙楼二楼 (kedian → w →
n → e → up), where the 冼老板/Xian laoban F_DEALER NPC
(d/city/npc/xian.lpc) sells kaoya (烤鸭/roast duck, value 150) via
its vendor_goods list. buy kaoya succeeded on the first try:
"你从冼老板那里买下了一只烤鸭。" Post-purchase i showed the received
烤鸭(Kaoya) item AND the correct change, auto-converted to higher
denominations by moneyd.lpc's player_pay(): 三两白银(Silver) (300)
+ 五十文铜板(Coin) (50) = 350 = 500 − 150. Price deduction and item
receipt both verified correct, live, not just by code review.
Verdict: this gap is now CLOSED — no bug found, buy's code path
(money check, move(), call_out("enough_rest", 1)) works exactly as
the prior code-review pass predicted. Zero new lines in debug.log
across the whole session (confirmed by line-count before/after).
Fast standard-checklist confirmation pass (per NOTES.md task brief — confirm, don't re-derive)
All of the following were already fixed/clean per this lib's own prior NOTES.md entries; this pass re-confirmed each by direct source read (and, where a fresh boot was already running, live behavior) rather than re-deriving from scratch:
- §7.90 (eval-cost):
config.fluffos'smaximum evaluation costis already5000000(the documented remedy value, not the low template default) — confirmed by direct read. No aborts observed anywhere in this session's boot/registration/shop/walk sequence. - §7.100 (
ROOMbase class redundantreplace_program()): already swept (824 sites, per this file's own earlier entry) — spot-checkedd/city/zuixianlou2.lpc/xian.lpcload cleanly with zero related compile warnings. - §7.111 (
standard_trace()null-object crash):adm/obj/ master.lpc:203already readsobjectp(error["object"]) ? file_name(error["object"]) : "(driver)"— correctly guarded, not the unconditional-file_name()broken shape. - §7.112 (
death_stage()reentrancy — every exit branch must clear the reentry guard): checked all 4 files in this lib with adeath_stage()/death_stage_activepattern (d/death/npc/ {newgargoyle,bgargoyle,wgargoyle}.lpc,d/shaolin/npc/yu-zu2.lpc}— nochacha.lpcexists in this lib). Read every branch of every one: all 4 files cleardelete_temp("death_stage_active")on EVERY real exit path (the!ob || !present(ob)early-return, the final-stagereincarnate()branch, andbgargoyle.lpc's extra!is_ghost()early-return) and correctly leave the guard SET (no clear) only on the legitimate multi-stage in-progresscall_out()re-arm branch. Confirmed clean, no fix needed. - §7.79 (
addn()/addn_temp()2-arg bug): zero occurrences ofaddn(anywhere in this lib'swork/tree (grepped) — not applicable here. - §7.108 (duplicate-login kick-out losing command dispatch):
clone/user/user.lpc:110'sreconnect()already starts withenable_commands();— the documented fix, already applied (confirmed by direct read; not the 162-lib sweep guessing, this lib'slogind.lpc:220'sconfirm_relogin()does have theexec(old_link, ...)kick-out shape that makes this fix relevant). - combatd.lpc
bounce-division bug (found/fixed on 4 sibling libs this session):grep -n 'bounce' adm/daemons/combatd.lpcreturns ZERO hits — this lib'scombatd.lpcdoesn't use abouncevariable at all. Not applicable. feature/attack.lpc'sreset_action()F_DBASE bare-call check (checking for thenitan6-shape bug):reset_action()explicitly doesme = this_object();then calls bareset("actions", ...)— sincemeISthis_object(), a bare (implicitly-self-targeting)set()call is exactly correct here, not a missing redirect. No bug — this function only ever needs to mutate its own dbase, never a different object's.
Death/respawn live-verification status (per task's optional follow-up)
Not a new gap: this lib's own 2026-07-24 §10.7 pass already verified a
REAL combat death + corpse + ghost + reincarnation cycle live end-to-end
(the fluffos admin character vs. a 黑无常/bgargoyle.lpc NPC at
/d/death/gateway), documented above — no further action needed here.
Process notes
Driver run from libs/jqxz2008/ via nohup ... & disown, single boot
for this whole pass (PID 976088), killed by exact PID after confirming
readlink -f /proc/976088/cwd matched this lib's work/ directory (no
other libs' drivers were running concurrently this time). fluffos's
save picked up minor real-gameplay-state churn from this session (food/
water values, startroom updated to /d/city/wumiao by that room's
own documented — see bug #2 above — checkpoint-on-entry design, since
the admin character was deliberately routed back there before quit to
match its previously-documented resting spot) — left as-is, not
reverted, since it reflects genuine state from real commands run, not
timestamp-only churn.
深度功能测试(§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 jqxz2008 --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 251 个 .lpc 和 10 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):239 -> 239 通过,无回退;改过的头文件的 58 个包含者 58 -> 58 通过。启动时警告 17 -> 0;新进程登录(输入名字)正常。
- 2026-10-10:编译警告第二轮(按驱动报告逐条修):按位 & 改逻辑运算 0 处,返回类型/原型 9 处,NPC+F_UNIQUE 的 init() 0 个,带初始化的未用局部变量 1 个,只在 #if 分支用的声明移进分支 0 个文件;改动 9 个文件,HEAD 与工作树各载入一次无回退(9 objects; HEAD PASS 9, tree PASS 9; regress 0; newly loading 0),这些文件的警告 9 -> 0。
- 2026-10-10(续):第二轮编译警告整理(
lpc_fix_unused_init.py)删掉了只在不启用的#if分支里用到的局部变量声明(如include/net/ftpdsupp.h中#ifdef GUEST_WIZARD_FTP分支的file),启用该选项时会编译失败;声明已放回所用的分支里,工具已改为跳过这种情况(交给lpc_move_decl.py)。
编译警告收尾(2026-10-12)
scripts/toolkit_fix.sh 一轮(冗余继承 18 处,NPC/物件在基类之外又直接继承 F_CLEAN_UP 等)后,include/net/ftpdconf.h 改定义 ftpd.h 的 VALID_READ/VALID_WRITE 前加 #undef(取值不变,同 xiakexing3),能编译的文件警告清零。改动的 19 个对象 HEAD 与工作树各载入一次:HEAD PASS 18,tree PASS 18,regress 0。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。4 个文件 4 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。 - 2026-10-14:技能文件
int power_point()/hard_point()/effective_level()返回 1.2~2.5 之类的浮点,经 int 返回被截断;本库没有(有效的)调用者,改成float,保留原数值。 - 2026-10-14:2 个存档文件(留言板 2 个;
data/board/gaibang_b.o,data/board/tiandi_b.o)还是原档案的 GBK 编码,没有转成 UTF-8,restore_object()报Invalid utf8 string后对象以空数据启动(表情守护进程没有表情、留言板没有帖子、玩家无法载入)。scripts/lpc_save_encoding.py逐个按 GB18030 解码,结果按存档格式解析通过才写回(KB 03 §4)。
无门派玩家的 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)
转换时丢掉的分号、逗号、引号或文件末尾的 },共 2 个文件,修前 HEAD 载入失败。别的库里同路径的完好副本只比这里少几个标点,按它补回(括号里是那个库):/d/baituo/npc/obj/bowl.lpc(cctx)、/d/baituo/obj/bowl.lpc(cctx)。修后与 HEAD 逐个对比载入:这 2 个物件全部载入,没有退步(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。
运行期目录 log/nosave 不存在,写日志的 write_file() 直接报错(2026-10-22,KB 03 §4.3 补充)
这个库的 10 个文件里有 15 处 write_file("/log/nosave/...") / log_file("nosave/...")(static 改 nosave 时连字符串里的日志路径一起改了,原来是 log/static/):nosave/CRASHES、nosave/CALL_PLAYER、nosave/PURGE 等。压缩包和站点包里没有这个目录,write_file() 在目录不存在时抛 *Wrong permissions for opening file /log/nosave/... for append. "No such file or directory",调用它的函数(杀人记录、重启记录、call 日志……)当场中断。把 log/nosave 记进 scripts/wasm_keep_dirs.txt(站点和 pristine 树都按它建空目录);没有把字符串改回 static。证明(scripts/lpcc_eval.py,catch(write_file("/log/nosave/ZZTEST", ...)),fqyy2 / hy5 / bmxkx2001 的新进程):补目录前报上面的错误,补后写成功。
读不到的留言板:"/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())。