info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
取材自金庸小说《侠客行》,压缩包标注为"2017完整版";与本项目中同名"侠客行"游戏 xiakexing100 代码库彼此独立(内容匹配度仅约 2%-4%),但经过逐档比对,它其实与本项目另一款"侠客行三"xiakexing3、以及以"金庸群侠传"为名的 jqxz2008 系列是关系密切的近亲——共有路径下约 80%-84% 的文件逐字节相同,是同一套 adm/single/master.lpc 服务端核心与同一个基础游戏世界(茶馆/客栈起步、丐帮枯树洞收徒、明教弟子按"缘"字辈份编号取名等)在不同站点换皮后产生的多个内容快照,而非各自独立开发的游戏。
English
Based on Jin Yong's novel Ode to Gallantry (Xia Ke Xing) in its own login banner, this archive is labeled the "2017 Complete Edition." Despite the shared name, its codebase is genuinely independent of this collection's xiakexing100 (only ~2-4% of files match by content). It is, however, a much closer relative of xiakexing3 and of this collection's jqxz2008/jqxz2008dlx/jqxz2008std/jqxz2015 "Legend of Jin Yong's Heroes" (Jin Yong Qunxiazhuan) family than either side's own description previously acknowledged: a direct file comparison found roughly 80-84% of the files it shares a path with are byte-identical to both xiakexing3 and to jqxz2008 — one server program and one base game world (adm/single/master.lpc core, tea-house/inn start, Beggars'-Sect-via-hollow-tree recruitment, Mingjiao's numeric-curse-named NPCs) produced across several separately-branded content builds, not independent games. New characters start at an inn, can apprentice into named sect teachers (e.g. the Beggars' Sect's 余洪兴/Yu Hongxing), and the game clock displays "Xia Ke Xing time," real Beijing time, and the lunar calendar side by side.
README
内容亮点
- 以《侠客行》的江湖门派与武学体系为背景,新人物从"客店"起步,可以 习武、结交江湖人物,还能拜入丐帮余洪兴一类具名门派师傅门下。
- 游戏世界会同时显示"侠客行时间"、现实北京时间以及农历日期,带有 一点"活历法"的氛围感。
在线试玩
https://mudlibs.fluffos.info/xiakexing2017/
管理员账号 / Admin account
- ID:
fluffos - 密码 / Password:
Mud@2026 - 中文名 / Display name: 浮浮
- 权限 / Level: 天神
(admin)—— 本 lib 的最高等级,通过adm/etc/wizlist授予。
警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。
本地运行
cd libs/xiakexing2017
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40017。
NOTES · 移植与修复记录
MUD侠客行2017完整版.zip → xiakexing2017
- Archive:
archives/MUD侠客行2017完整版.zip(4.7MB — "侠客行"/"Ode to Gallantry" [Jin Yong novel], 2017 complete edition, MudOS v21). Mudlib root atxkx/(archive top level), config at top-levelconfig.cfg. Note:xkx/directory name here is coincidental — unrelated to the "xkx" zone-name reuse seen inside other libs'/d/trees. - Port: 40017.
Status: DONE — boots clean, playable over telnet
Zero fixes needed — clean boot and full login flow on the first attempt.
What was fixed
Nothing needed for boot. Routine conversion: encoding (3330 converted,
521 already UTF-8/ASCII, 59 skipped binary, 15 lossy), .c→.lpc (3266
files, 146 refs auto-fixed), 8 local angle-bracket includes converted to
quotes automatically, static→nosave (42 files).
Known remaining issues
- 79 lpcc-sweep failures (of 3266, 97.6% pass) — not triaged given the small count; boot/login path unaffected.
How to run
cd libs/xiakexing2017
~/src/fluffos/build-debug/src/driver config.fluffos
python3 ../../scripts/mudclient.py 127.0.0.1 40017 --timeout 10 --send "" --send "look" --send "quit"Post-hoc fix: UTF8-native is_chinese/registration (AGENTS.md §15h)
Applied in a later batch pass across the whole project: is_chinese/is_chinese2
in the shared chinese.lpc simul_efun fragment used GBK byte-range checks that
silently never match real Chinese text once strings are UTF-8 (this driver's
str[i] returns a Unicode codepoint, not a GBK byte). This broke character
registration specifically -- any real Chinese name was rejected. Fixed the
range check to test the CJK Unicode block instead, and halved the
GBK-byte-calibrated length bounds in check_legal_name to match. See
AGENTS.md §15h for the full writeup; confirmed via a real interactive
registration test (Chinese surname + given name reaching the next prompt).
Re-verification pass (driver rebuild + LPC formatter + WASM build)
- Reformatted all 3266
.lpcfiles underwork/withtools/lpc-syntax/format-corpus.mjs: 3238 written, 0 already idempotent-clean, 28 refused by the tool's own token/byte-identity guard (expected on messy legacy code, not chased). Verified the earlier §15ae fix (feature/command.lpc'scommand_hookstayingnomaskwithprivatestill commented out, in bothfeature/andhome/copies) survived unchanged. - Native retest against the freshly-rebuilt driver (
~/src/fluffos/build-debug/src/driver, rebuilt from latest upstream master): clean boot, zero fatal errors inlog/debug.log. Full registration flow re-verified with a fresh real Chinese name (秦岳/idxkxbnem), reaching an actual starting room,look/score/quitall producing correct output — no regressions from either the driver rebuild or the reformat. - WASM build test (
scripts/wasm_client.jsagainst~/src/fluffos/build-wasm/src): boots cleanly (only benign compile- warning spam, no fatal errors). Full registration completed end-to-end under WASM too — idxkxwasme→ real Chinese name秦徽→ password/ stat-roll-accept/email/gender → landed in the game world,look/score/quitall produced correct output. This lib has no IP-format-dependent login gate, so it isn't affected by the knownquery_ip_number()WASM limitation — fully playable under WASM.
WASM-enablement pass (loopback-allow / admin seeding)
This lib's adm/daemons/band.lpc/logind.lpc are byte-identical (per
§2.1 lineage check) to the xiakexing3/jqxz2008 engine
files, whose WASM pass was already done and verified with the correct
fail-closed convention — ported that fix here and re-verified
independently rather than assuming it.
adm/daemons/band.lpcis_banned(): added a loopback short-circuitif (stringp(site) && (site == "127.0.0.1" || site == "::1" || (strlen(site) >= 4 && site[0..3] == "127."))) return 0;before the regexp ban-list scan. Written fail-closed from the start: only a real loopback-shaped string counts as local; a malformed/empty/non-string site is NOT treated as local and still goes through the regexp ban check.logind.lpc:66'sBAN_D->is_banned(query_ip_name(ob))gate inlogon()is thereby loopback-proof.- No
uptime()startup-grace gate and no per-IP anti-flood/registration throttle exist in this lineage (only the in-memorymad_lockadmin lockdown flag, default off — game/admin design, left alone).
Admin account: fluffos / Mud@2026 / 浮浮, registered through the
real flow (id → y → 浮浮 → password ×2 → talent accept y → email →
m). Granted (admin) by appending fluffos (admin) to
adm/etc/wizlist (shipped with rwz (admin) already present, no
trailing newline — added one; securityd.lpc reads this file at
create(), so a driver restart was needed to pick up the new line).
Verified after restart: 目前权限:(admin), title 【天神】, update
/adm/daemons/band → 重新编译 /adm/daemons/band.lpc:成功!.
Save files for the orchestrator to force-add (untracked, not gitignored):
libs/xiakexing2017/work/data/user/f/fluffos.olibs/xiakexing2017/work/data/login/f/fluffos.o
Two pre-existing caught (non-fatal) restore_object(): Illegal file
format errors were observed in debug.log during this pass —
/adm/daemons/chinesed's save data at boot preload, and
/clone/board/kedian_b's board save data at enter_world() (matches
the raw-path /clone/board/kedian_b display already visible in the
game, instead of a proper board name — its corrupted save data is why).
Both are wrapped in catch() by master.lpc's preload()/
logind.lpc's own catch respectively, non-fatal, and confirmed via
git log/git status to be genuinely corrupted data already committed
as part of the original archive conversion (archive #20, "booted clean
with zero fixes") — not introduced by this session. Left as-is per the
project's established emoted.o/similar precedent; not a new
regression.
Retest: fresh boot, fresh registration (id qretest, real Chinese name
秦风十一) through look/score/quit — landed in 客店, correct
output, clean quit. fluffos/Mud@2026 admin login verified via
score (confirms (admin)/天神) and update. debug.log had exactly
the two pre-existing corrupted-save errors above and zero NEW runtime
errors. Test character qretest removed afterward; fluffos kept.
深度功能测试 / Deep functional test (2026-07-24, round two)
First real *playthrough* pass on this lib (all prior passes verified only
registration + look/score/quit, or watched boot output). Read
doc/help/newbie and doc/help/intro in full first — this is the
"东方故事2" (ES II) -derived 金庸-themed game: newbie names the
fight (safe, to-half-health sparring) vs kill (real) distinction up
front, the bai/xue teacher-apprentice loop, the 扬州 (Yangzhou)
starting-city layout with turn-by-turn routes to every sect, and dozens
of shortcuts. Played one continuous native-driver (build-debug)
session as an ordinary new player: registration with a real Chinese
name, exploring Yangzhou (客店→北大街→中央广场→东大街→东门, then the
deterministic ml1–ml7 "青竹林" maze into d/city/pomiao and the
丐帮 hideout gbandao), the organic bai/apprentice sect-join path
against four different 丐帮 teacher NPCs (all correctly rejected per
their own, met-or-not stat/alignment gates — not a bug, see below), the
fight safe-sparring command against a 流氓 (real multi-round combat
output, no crash), a list/buy shop-purchase attempt (correctly
rejected for insufficient funds), a real clean quit→relogin round
trip (state preserved), a real prompt net-dead reconnect (uncleanly
closing the socket, reconnecting ~25s later — resumed in the exact same
room, confirming this lib's net_dead() never void-parks the player at
all, see "§7.20 checked" below), and — critically — a real full-
duration (600s = NET_DEAD_TIMEOUT) unclean-disconnect wait, which
crashed the native driver process outright. Found and fixed four
separate bugs: one already-cataloged class (§7.12, promoted to
CRITICAL severity by this pass — the driver crash above), one recurrence
of an existing class applied at a more general choke point (§7.25-
shaped), and one genuinely new class (drafted below) that hit in three
independent files.
Primary test character (kept, state left as evidence): id
qinfengxia, Chinese name 沈秋白 (male), password Xia@2026. Rolled
stats 膂力19/悟性26/根骨21/身法14 (all four teacher-rejection messages
below are a direct, verifiable consequence of this exact roll, not a
bug). State: explored Yangzhou + the 丐帮 hideout, sparred a 流氓 via
fight (both survived, health text on both sides — matches the "点到
为止" safe-sparring description in help combat/help intro), no
sect/skill acquired live (see honesty note below), survived a clean
quit→relogin, a prompt net-dead reconnect, and — after the fix below
— a real full-duration net-dead force-quit + fresh-login verification.
Currently sitting at 客店 (start room) after a final clean quit.
Saves: work/data/user/q/qinfengxia.o, work/data/login/q/qinfengxia.o.
Secondary throwaway character: id goumaice, Chinese name 顾买测
(female), password Gm@2026. Used to exercise the shop list/buy
flow (correctly rejected, no money) and — deliberately left net-dead at
the same real moment as qinfengxia — as the second of the two
simultaneously-net-dead characters needed to reproduce the §7.12 driver
crash (see Bug 1). Saves: work/data/user/g/goumaice.o,
work/data/login/g/goumaice.o.
§7.20 (void-parking) explicitly checked and does NOT apply to this
lib: read clone/user/user.lpc's net_dead()/reconnect() in full —
this lineage never moves a disconnected player to a VOID_OB/holding
room at all; the player object simply stays in its current room with a
netdead temp flag while call_out("user_dump", NET_DEAD_TIMEOUT,
DUMP_NET_DEAD) waits, and reconnect() (confirmed actually invoked,
adm/daemons/logind.lpc:481) just clears the flag in place. No location
to lose, no restore path needed. Confirmed live with both a prompt
(~25s) and a full 600s-plus net-dead reconnect: character stayed in the
same room both times.
Bug 1 (CRITICAL — already-cataloged class, AGENTS.md §7.12, severity re-confirmed): the shared tell_room() simul_efun wrapper forwards an unset exclude varargs as a raw int 0, crashing message() — reached from the NET_DEAD_TIMEOUT force-quit path, and reproduced as an actual native driver process crash
File:line: adm/simul_efun/message.lpc:36-38.
This is the identical bug AGENTS.md §7.12 already documents (first found
on yueyingqiyuan, severity-escalated on dtsl's own §10.7 pass) —
citing the existing class, not redrafting it. Filed here because this
lib turns out to carry a byte-identical copy of the vulnerable
adm/simul_efun/message.lpc (not currently listed as a sibling of
dtsl/yueyingqiyuan in AGENTS.md §11's lineage map — worth a lineage
note) and because this pass independently reproduced §7.12's full
"severity escalation" scenario live, including the driver crash, on a
*third* lib:
- How this was found: exactly per AGENTS.md §10.7 checklist item 8 — net-deading a real character (closing the socket without
quit) and waiting the genuine, fullNET_DEAD_TIMEOUT(600s =include/ user.h:10) forobj/user.lpc-equivalent's (clone/user/user.lpc)call_out("user_dump", NET_DEAD_TIMEOUT, DUMP_NET_DEAD)to fire, then reconnecting — rather than stopping at a prompt-reconnect test (also done this pass, and which never touches this code path). - Root cause:
clone/user/user.lpc'suser_dump()DUMP_NET_DEADcase callstell_room(environment(), <message>)— a completely ordinary 2-argument call, one of many such call sites across the lib.adm/simul_efun/message.lpc'stell_room(mixed ob, string str, object *exclude)isvarargs; when the caller omits the 3rd argument,excludeis a real, typedint 0, not "absent" — and the wrapper unconditionally forwards it asmessage()'s 4th positional argument (message("tell_room", str, ob, exclude)), which only acceptsvoid|object|object*. This throws*Bad argument 4 to EFUN message()from inside acall_out-driven function with no enclosingcatch(), aborting the rest ofuser_dump()at that exact statement —command("quit")right after it never runs, so the entire net-dead force-quit safety net was silently disabled for any player who net-deads and never manually reconnects. - Then it got worse, exactly like
dtsl's report: reproducing this live with two characters (qinfengxiaandgoumaice) both net- dead past the timeout at nearly the same real moment, followed by a third connection attempt, crashed the entire native driver process — captured in the driver's own redirected stdout:
``
md: debugmalloc: attempted to free non-malloc'd pointer ...
#3 MDfree ... #4 debugfree ... #5 dealloc_object ... #6 int_free_svalue
#7 assign_svalue ... #8 assign_value_to_lvalue ... #9 eval_instruction (F_ASSIGN)
... #12 f__call_other ... #17 call_function_interactive (processing the
reconnect command) ... Aborted (Signal sent by tkill())
`
Confirmed via ps/ss that the driver was genuinely gone (not just
the connection dropping) and via the stdout backtrace, not inferred.
The exact C-level mechanism (a destructed-object reference whose
refcount reached 0 without a proper free_object`) wasn't rigorously
proven with ASan/valgrind (out of scope), matching AGENTS.md §7.12's
own caveat, but the correlation is strong: the crash did not recur
after the fix, reproducing an equivalent two-character scenario.
- Fix (identical to AGENTS.md §7.12's prescribed fix):
``lpc
// BEFORE:
varargs void tell_room(mixed ob, string str, object *exclude) {
if (ob) message("tell_room", str, ob, exclude);
}
// AFTER:
varargs void tell_room(mixed ob, string str, object *exclude) {
if (ob) message("tell_room", str, ob, exclude || ({}));
}
``
- Verified: to avoid a second genuine 10+ minute wait, re-verified with the exact methodology AGENTS.md §7.12/
dtslused —include/user.h'sNET_DEAD_TIMEOUTtemporarily changed from600to15for testing only (LPC logic exercised is byte-for-byte identical, only real wall-clock time differs), confirmed restored to600immediately after (git diffon that file is empty). With the fix applied: net-deadedqinfengxiaandgoumaicesimultaneously again, waited a real 25+ seconds past the (shortened) timeout, checkeddebug.log(zeroBad argument/any error), then reconnected asqinfengxia— landed in a genuinely fresh login (confirmed by the "上次连线" timestamp updating, i.e.enter_world()actually ran, not a stale netdead resume) at 客店,look/quitboth correct, and the driver process stayed alive and stable throughout (psRSS tracked normally, no crash). Final full sanity pass afterward (fresh boot, fullNET_DEAD_TIMEOUTrestored to 600, register→explore→ quit) also clean.
Lineage note for AGENTS.md §11: xiakexing2017's
adm/simul_efun/message.lpc carries this exact vulnerable tell_room()
shape, matching dtsl's. Currently listed as "Standalone/distinct" in
§11 — worth flagging as sharing at least this one shared-infrastructure
file with the broader ES II family (§11's "ES II / 东方故事 mega-family"
already carries the §7.12 warning), even though this project's own
newbie/intro docs describe it as "东方故事2" (ES II) -derived
already, so this is consistent, not a surprise.
Bug 2 (new bug class, draft below): mapping-typed properties/accessors default to a raw int 0 when never set, and several call sites index into them without checking — "value being indexed is zero" / "bad argument 1 to keys()"
Two independent instances, one in shared engine code (reachable from nearly every room in the game) and one in specific zone content — same underlying shape, draft text below covers both.
Instance A — feature/skill.lpc's query_skills()/query_learned()/
query_skill_map()/query_skill_prepare() (lines 13-14, 103-109):
each is declared mapping query_X() { return X; } where the module
variable X (skills/learned/skill_map/skill_prepare) is declared
mapping X; but never initialized — LPC's default value for an unset
mapping global is int 0, not ([]). Every setter in the same file
(set_skill(), map_skill(), etc.) correctly guards with if
(!mapp(skills)) skills = (...), but the four query accessors return the
raw variable unconditionally. Reached live: d/npc/xiaofeng.lpc's
check_skills() (fired via a 2-second call_out from init(), i.e. on
ordinary room entry) does skl = ob->query_skills(); sname =
sort_array(keys(skl), ...) — for our fresh test character (0 skills
learned, completely ordinary, expected state for a brand-new player),
query_skills() returned raw 0, and keys(0) crashed:
执行时段错误:*Bad argument 1 to keys()
Expected: mapping Got: 0.
程式:/d/npc/xiaofeng.lpc 第 99 行84 call sites across 42 files call ->query_skills()/->query_learned()
— most already defensively re-guard with their own if (!mapp(...))
before use (e.g. cmds/std/check.lpc, cmds/skill/skills.lpc, which is
exactly why this didn't surface everywhere), but at least this one did
not.
Instance B — a mapping-typed per-object query() property indexed
directly, same shape, different mechanism: query("party") (an
object's faction-membership record, unset for anyone not in that
faction) and query("family") (sect-membership record, unset for anyone
sect-less) both legitimately return 0 for the common case of "hasn't
joined". Three files indexed one of these directly with no guard:
d/mingjiao/npc/mingjiao.h:6 and its byte-identical duplicate
kungfu/class/mingjiao/mingjiao.h:6 (ob->query("party")["party_name"]
in a shared greeting() used by ~20+ 明教 NPC files, fired via a 1-
second post-init() call_out — i.e. crashes for any non-明教 player
standing near ANY of them, confirmed live for d/mingjiao/npc/
changyuchun.lpc's 常遇春, who sits in the starting city's central
square that every fresh login passes through), and d/xiaoyao/obj/
bojuan.lpc:43 (me->query("family")["family_name"], a readable skill
scroll — crashes for any non-逍遥派 player who tries read/study/du
on it, which is the obvious, intended first thing to try). All three
crash with the same signature:
执行时段错误:*Value being indexed is zero.This shape is NOT universal in this lib — most other query("family")[/
query("party")[ call sites (checked: kungfu/class/mingjiao/
zhangwuji.lpc, d/xiaoyao/xiaodao1.lpc, shishi4.lpc, npc/qingyun.lpc,
npc/xuemuh.lpc, fangjian2.lpc) already correctly guard with `!ob-
query("family") ||/ob->query("party") &&` — confirming this is aneasy-to-miss oversight recurring independently, not evidence the whole convention is broken, and that the correct local idiom was already established and just not applied consistently.
Detection: grep ->query_skills(\|->query_learned(\|->query_skill_map(
\|->query_skill_prepare( (or a lib's own equivalent mapping-returning
accessor) for definitions that return <bare variable>; without a
mapp() guard, matching the setter functions' own established
discipline in the same file. Separately grep ->query("<mapping-shaped-
property>")\[ (family/party/couple/skill-map-shaped properties) for
direct indexing with no adjacent mapp()/truthiness check — cross-check
against sibling call sites in the SAME file/directory first, since (as
here) the correct guarded idiom is very often already established
nearby and the unguarded instance is the outlier, not the norm.
Fix: at the accessor/property level, not every call site — same
"shared choke point" principle as §7.25/§7.26. For an engine accessor
like Instance A: mapping query_skills() { return mapp(skills) ? skills
: ([]); } (applied to all four accessors in feature/skill.lpc). For a
one-off unguarded index like Instance B: capture the property into a
local first and guard it (party = ob->query("party"); if (mapp(party)
&& party["party_name"] == ...)), matching the idiom already used
correctly elsewhere in the same codebase.
Verified: restarted the driver after each fix; re-walked
qinfengxia past 萧峰/xiaofeng (北大街) and 常遇春/changyuchun (中央
广场) — both previously-crashing check_skills()/greeting() calls
fired cleanly with zero debug.log errors afterward, confirmed across
two independent fresh-boot sessions. bojuan.lpc's fix was verified by
code-inspection parity with the other five already-correct sibling call
sites in the same directory (not re-walked live to the 逍遥派 zone
specifically — a multi-minute maze/gate trek for a one-line, pattern-
identical fix already proven safe by the two live-verified sibling
instances; noted honestly rather than silently claimed as live-tested).
Bug 3 (recurrence of AGENTS.md §7.25, fixed at a more general choke point): cmds/std/go.lpc's destination-room force-load silently swallows the player's move command on that room's first-ever visit when the room's own tail-of-create() companion-object load throws
File:line: cmds/std/go.lpc:70-73 (plus two smaller sibling
instances at d/wizard/guest_room.lpc:24 and d/wizard/wizard_room.lpc:
21, same shape, fixed the same way).
Matches AGENTS.md §7.25's "closely related shape" almost exactly
("rooms that force-load a companion object ... via call_other('<path>',
'???') in their own create()") — citing the existing class. The
difference from a textbook §7.25 instance: the vulnerable
call_other(dest, "???") isn't inside each room's own create(), it's
in the single SHARED movement command (go.lpc) that force-compiles
*any* destination room on its first-ever visit — so this fix was applied
at that one shared choke point instead of repeating catch() across
this lib's 17 separate rooms that end their own create() with an
identical "/clone/board/xxx_b"->foo(); companion-board force-load
(confirmed all 17 have the load as the LAST statement of create(),
i.e. after exits/objects/setup() are already set — spot-checked
5 of the 17 directly, matching the one instance actually reproduced
live).
- How this was found: exploring Yangzhou's
ml1-ml7"青竹林" maze toward the 丐帮 (Beggars' Sect) hideout, perdoc/help/newbie's own documented route. The maze's final room (ml7) exits north intod/city/pomiao— a room visited for the first time this boot.pomiao.lpc'screate()ends with"/clone/board/gaibang_b"->foo();to force-load its message board; that board's SAVED DATA is corrupted (pre-existing, same class as the already-documentedkedian_bcorruption above — confirmed viagit log/git blameonwork/data/board/gaibang_b.o, unchanged since the original archive conversion commit, raw GBK/BIG5-era bytes, not something this session introduced), sorestore_object()throws*restore_object(): Invalid utf8 string while restoring dbase.— caught by the driver's own top-level handler (not an LPCcatch(), sincefeature/save.lpc'srestore()has none), which aborts the ENTIRE calling chain back up to wherever acatch()(or the top of the call) actually exists. That chain iscommand_hook() → cmds/std/go.lpc main() → call_other(dest, "???")— i.e.go.lpc's ownmain()function, mid-execution, with no enclosingcatch()anywhere in between. Net effect:main()aborts right at the force-load line, and the code immediately after it — the retryfind_object(dest)check and the actualme->move(obj)call — never runs. The room itself finishes compiling correctly (the throwing line is its very last statement), but the player's move command that triggered the compile is silently swallowed: they stay exactly where they were,lookshows the same room again, no error, no hint anything happened. Confirmed reproducible-then-fixed: before the fix, a scriptedn(or whichever direction led intopomiaofirst) from inside the maze left the character still in "青竹林" with the caught error visible only indebug.log; after the fix, the identical move landed the character in 土地庙 (pomiao) immediately, board-load error still present indebug.log(harmless, pre-existing, unrelated to the fix) but the room transition now completes. - Fix: wrap the force-load in
catch():
``lpc
// BEFORE:
if (!(obj = find_object(dest)))
call_other(dest, "???");
// AFTER:
if (!(obj = find_object(dest)))
catch(call_other(dest, "???"));
`
Safe specifically because every instance checked (17/17 by grep, 5/17
spot-checked directly) puts the risky companion-object load as the
LAST statement of the destination room's create() — so even when the
catch() swallows a throw, the room object is already fully populated
(exits/objects/setup() all ran) by the time go.lpc's retry
find_object(dest) check runs immediately after, and the move
proceeds normally into a correctly-initialized room. Also applied the
same catch() to the two structurally-identical but currently-
unexercised direct call_other(".../board", "???") force-loads in
d/wizard/guest_room.lpc and d/wizard/wizard_room.lpc (their own
create(), not reached via go.lpc` — the same §7.25 shape, proactive
fix by code-shape match, not yet live-triggered since their board data
isn't known to be corrupted).
- Verified: reproduced live pre-fix (character stuck in 青竹林, caught error in
debug.log, no transition) and post-fix (same exact scripted route now lands in 土地庙, board-load error still logged as expected/harmless, room transition succeeds) across two separate fresh-boot sessions with the deterministicml1→ml2→ml3→ml4→ml5→ml6→ml7→pomiaoroute derived by reading each maze room'sexitsmapping directly (the maze is NOT actually random despite the "你仿佛迷失了方向" flavor text — every room has fixed, asymmetric exits, several of which loop back to themselves;doc/help/newbie's own丐帮:#3 e;n;e;n;w;n;e;w;n;...route matches this exactly). Theguest_room.lpc/wizard_room.lpcsiblings were fixed but not live- reproduced (no known corrupted board data fortowiz_b/wiz_bcurrently) — noted honestly, not claimed as live-verified.
Honesty notes — not verified live, explicitly
- Sect membership /
xueskill learning were NOT completed live.bai/apprenticewas correctly exercised against four different 丐帮 teacher NPCs reachable via the documented route (鲁有脚/彭有敬atpomiao, plus the two rejects noted in the newbie-help zone) — each rejectedqinfengxiafor a real, code-verified reason specific to that NPC (int>=25, unconditional non-recruiter, etc.), all correctly matching this character's actual random stat roll (str19/int26/ con21/dex14). This is normal game-content behavior, not a bug — seeattempt_apprentice()bodies inkungfu/class/gaibang/*.lpc. The one unconditional-accept teacher found in the whole lib (kongkong.lpc, 空空儿) sits insided/city/lichunyuan(丽春院/brothel), gated behind an explicit, code-confirmed age check (d/city/nandajie2.lpc:33,age < 18) that a fresh 14-year-old character cannot pass without real in-game aging — a genuine content gate, not a bug, and not worth spending further session time defeating for its own sake. Reaching a free/no-prerequisite teacher and completing a realxuewas not achieved live within this pass's time budget. - Death/respawn was not reached live.
fight(the safe-sparring path) was exercised and confirmed non-lethal by design (help combat/help intro: stops at ~half health, matchescmds/std/fight.lpc's own help text); reaching real death would require either a deliberately lostkillfight or self-suicide, neither attempted given the time budget already spent on the §7.12 full-duration net-dead reproduction above (the highest-value finding of this pass). - A successful shop purchase was not completed live —
listand abuyattempt were both exercised (goumaiceatd/city/yaopu, correctly rejected with "穷光蛋,一边呆着去!" for insufficient funds, the expected/correct behavior for a brand-new character with no income yet) but earning money first (perdoc/help/newbie's own suggested 谷物加工厂/服装厂 wage-work path) was not attempted given the time budget.
Files modified this pass
work/adm/simul_efun/message.lpc— Bug 1 fix (§7.12).work/feature/skill.lpc— Bug 2 Instance A fix (4 accessors).work/d/mingjiao/npc/mingjiao.h,work/kungfu/class/mingjiao/ mingjiao.h— Bug 2 Instance B fix (duplicate copies).work/d/xiaoyao/obj/bojuan.lpc— Bug 2 Instance B fix.work/cmds/std/go.lpc— Bug 3 fix (primary).work/d/wizard/guest_room.lpc,work/d/wizard/wizard_room.lpc— Bug 3 fix (proactive siblings).work/include/user.h— touched only transiently for the accelerated §7.12 re-verification (NET_DEAD_TIMEOUT600→15→600); confirmed byte-identical to the pre-pass version (git diffempty) before finishing.
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 19 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
深度功能测试第三轮 / Deep functional test round three (2026-08-14)
Independently re-verified every §10.7 checklist item against current code
rather than trusting the round-two writeup above; found and fixed one new
bug (update.lpc), plus a 100%-reproducible recurrence of the
already-cataloged AGENTS.md §7.90 eval-cost class.
New fix: cmds/wiz/update.lpc — present(file, environment(me)) crashes when the caller has no environment
File:line: cmds/wiz/update.lpc:20.
main()'s first check calls present(file, environment(me)) unconditionally.
When environment(me) is 0 — reachable any time update is typed by an
object that isn't currently placed in a room — this throws *Bad argument 2
to present() Expected: object Got: 0., aborting the whole command with no
useful message.
- How this was found: reproduced twice in a row (immediately post-login, and again after an interposed
look) while investigating what first looked like a genuinely void/broken saved room for thefluffosadmin account (lookprinted "你的四周灰蒙蒙地一片,什么也没有。"). Root cause turned out to be a separate, unrelated issue (see below) that was corruptingmake_body()mid-flight and leaving the character in a genuinely environment-less state —update.lpc's missing null-check is a real bug regardless (any admin whose environment is legitimately unset for any reason would hit this), just not the *primary* cause of what was observed live. - Fix:
``lpc
// BEFORE:
if ((obj = present(file, environment(me))) && interactive(obj))
// AFTER:
if (environment(me) && (obj = present(file, environment(me))) && interactive(obj))
``
- Verified: post-fix,
update /adm/daemons/logindfrom a normal, properly-placed session (客店) succeeded ("重新编译 ... 成功!"), confirming real admin write access without going through the#include-simul_efun pitfall (this file is its own standalone compiled command object).
Fix: cold-start eval-cost exhaustion during make_body() — recurrence of AGENTS.md §7.90 ("xyj2000f" variant), 100% reproducible, not intermittent
Initially misdiagnosed this as a debug-build-speed test artifact to note
and leave alone. Checking AGENTS.md before drafting a new entry caught the
mistake: §7.90 already documents this exact class, including a variant
("harsher variant on xyj2000f") that matches this lib symptom-for-symptom
— eval-cost abort inside make_body() itself, 100% reproducible on every
attempt (not the more common "only some never-visited rooms" flavor), no
player-facing error, and §7.90's own prescribed and verified fix is to
raise the committed maximum evaluation cost, not leave it alone.
Applying that established fix here instead of inventing a new
rationalization.
It reproduced on every single fresh-boot first login attempt, every time,
across multiple independent boots, and did not cleanly self-heal the
way this session's other libs' cold-start eval-cost hits did: the character
that "succeeds" after the aborted make_body() lands in a session where
every command (look, who, update) returns the generic "什么?"
fallback — command dispatch never finished wiring up. Reconnecting doesn't
help; the very next login attempt hits the identical abort, every time.
- Root cause:
config.fluffossetmaximum evaluation cost : 300000(300ms) — already raised once from the shipped default of 100000 per an in-file comment ("Because of the heavy load on the Power PC, made it larger to pass the bad time"), i.e. hand-tuned once already for slow hardware, in the original 2000s-era production environment, but still far below the5000000this project's own §7.90 precedent established as the working value (already used by 30+ other libs in this project).make_body()'s cumulative interpreted LPC work (loading skill tables,dbase, etc.) reliably exceeds 300ms end-to-end on this repo'sbuild-debugdriver. - Fix:
config.fluffos:maximum evaluation cost : 300000→5000000. - Verified: fresh boot, first login attempt succeeded cleanly on the very first try (previously failed 100% of the time) — full room description, "目前权限:(admin)" shown, zero eval-cost errors in
debug.log. The "灰蒙蒙" void-room symptom seen while investigating theupdate.lpcbug above was a downstream artifact of this same thrashing, not a real saved-location bug — resolved by this same fix.
Re-verified still holding (code-inspection, not full live re-repro — see rationale)
All three round-two fixes remain present and unmodified in the current
code (adm/simul_efun/message.lpc's exclude || ({}), feature/skill.lpc's
four mapp()-guarded accessors plus the mingjiao.h/bojuan.lpc guards,
and cmds/std/go.lpc's catch(call_other(dest, "???")) plus its two
guest_room.lpc/wizard_room.lpc siblings) — confirmed by direct grep
against each fix's exact diff, not merely assumed. Bug 2 Instance A was
additionally re-verified live: walked to 北大街 where 萧峰/xiaofeng sits,
waited past his check_skills() call_out, debug.log showed zero errors
afterward (previously crashed with "Bad argument 1 to keys()" pre-fix).
Bug 1's full NET_DEAD_TIMEOUT reproduction and Bug 3's maze route were
not re-walked live this pass — both require multi-minute real-time or
multi-step scripted routes already thoroughly live-verified twice in the
round-two writeup above, and this pass's time budget went to the new
update.lpc bug investigation instead; noted honestly rather than silently
re-claimed as freshly live-tested.
Verification method
Two rapid consecutive reconnects as fluffos (both clean, both showing
"目前权限:(admin)"), update /adm/daemons/logind used as the real
privileged-action admin-rights check (not the login banner), driver killed
by exact PID after testing, incidental fluffos.o save-timestamp churn
reverted before commit.
Files modified this pass
work/cmds/wiz/update.lpc— new fix (environment(me)null-check).work/adm/simul_efun/file.lpc—cat()/log_file()hardening (assure_file()guard onlog_file(), null-guard oncat()'sread_file()).work/adm/daemons/logind.lpc— §8.9 fix (ob->query("age")→user->query("age")), removed oneprintf("%O\n", ob)debug leak.config.fluffos— §7.90 fix (maximum evaluation cost300000 → 5000000).
§7.100 扫描修复(ROOM 基类多余 replace_program())
#define ROOM "/inherit/room/room":删除 818 处多余的、独立成行的
replace_program(ROOM);(保留 inherit ROOM;),与 xkx2017/
jqxz2008 系列同一血统同一形状(本档案不是逐字节相同的手足档案,
独立验证)。clone/misc/roommaker.lpc 同样有两套模板——"造一间空
房间"的 heredoc 本来干净,"克隆我所在的房间"命令的字符串拼接模板
把同一枚多余的 replace_program(ROOM); 烤进了每一个新克隆的房间,
已同步修正。已用 build-debug 驱动干净启动验证(0 个新增编译错
误,端口正常监听);未做完整 §10.7 深度游玩测试。
``§7.112`` residual-gap closure (2026-08-20)
Corpus re-scan (grep -rl 'call_out("death_stage"' ... | filter for missing guard) found unguarded init()-scheduled death_stage() call_out chain(s) in d/death/npc/newgargoyle.lpc that the original two-wave sweep (see AGENTS.md §7.112) missed -- same reconnect-triggered duplicate-chain bug, different filename/lineage. Added the standard query_temp("death_stage_active")/set_temp/delete_temp re-entry guard, adapted per file's own exit points. Compile-verified via lpcc --batch.
深度功能测试第四轮 / Deep functional test round four (2026-08-20)
Resolved this lib's two remaining round-two/round-three "honesty notes — not verified live" gaps (sect apprenticeship completion and death/respawn) explicitly. Found and fixed one new, genuinely crash-causing bug in the process; the standard checklist re-confirm otherwise came back 100% clean (all prior fixes still present, matching the current code).
Item 1 — sect membership / xue skill learning: COMPLETED LIVE. New bug found and fixed: adm/daemons/chinesed.lpc's bare restore() (no local catch()) let a pre-existing corrupted save silently abort create() before its own mapping dict variable could ever be (re-)guarded, crashing every real xue/skills command
File:line: adm/daemons/chinesed.lpc:25-28 (pre-fix).
Re-examined the four 丐帮 teachers previously confirmed reachable without prior
sect membership (鲁有脚/彭有敬 at d/city/pomiao, 简长老 at
d/city/gbandao, 苏国盛 — actually 洪七公's man hong — at d/city/gbxiaowu/d/city/mishi,
though the latter two turn out to be gated behind d/city/gbandao's own
valid_leave() for non-members, so not actually reachable pre-membership either)
and confirmed none of the pomiao/gbandao-reachable teachers can currently
accept qinfengxia's actual roll (int 26): kungfu/class/gaibang/lu.lpc
rejects if int>=25 (inverted from what a skim of the file names suggests —
Lu wants *low*-int students), peng.lpc and jian.lpc are unconditional
non-recruiters. This matches the round-two writeup's own conclusion, not a
contradiction of it.
Read the full kungfu/class/gaibang/*.lpc roster instead of stopping at the
four already-tried teachers: yu-hx.lpc (余洪兴, 丐帮八袋弟子) accepts if
int>=25 — the *actual* NPC the original task brief's "int>=25 threshold"
was describing, not lu.lpc. qinfengxia's existing int 26 already
satisfies it — no stat grant was needed at all, only physical access.
yu-hx.lpc sits in d/gaibang/underxx, reachable in real play only via
d/city/pomiao's dong (树洞) passage, itself gated by
(fam=me->query("family")) && fam["family_name"]=="丐帮" — i.e. the whole
underground-tunnel teacher roster (li-sh, liang, yu-hx, zuo-quan,
ma-jw, he-bj) is circularly locked behind already having a sect, so
reaching it the "long way" isn't actually possible pre-membership either.
Used the sanctioned admin-shortcut path instead: admin goto
/d/gaibang/underxx (teleport the admin only, bypassing the tedious/circular
travel gate, not any stat/security check) then admin summon qinfengxia
(pulls the real character object to the admin's room via the ordinary
move() mechanism — the same summon any admin could use in a real support
situation) to physically place qinfengxia in the room without touching any
stat. bai yu/apprentice yu then ran yu-hx.lpc's real, unmodified
attempt_apprentice() check against the character's real, roll-derived
int, exactly like the four previously-tested teachers.
- Sect join itself worked correctly, first try, no bug:
attempt_apprentice()'sint>=25branch matched,recruit/class="beggar"ran,baicompleted — "恭喜您成为丐帮的第二十代弟子。",scorecorrectly showed "丐帮第二十代弟子" and "你的师父是余洪兴。". xue force from yucrashed, and so did the unrelatedskillscommand (which lists learned skills):
``
执行时段错误:*Value being indexed is zero.
程式:/adm/daemons/chinesed.lpc 第 94 行
呼叫来自:/feature/command.lpc 的 command_hook() 第 55 行,物件: /clone/user/user#12 ("沈秋白")
呼叫来自:/cmds/skill/xue.lpc 的 main() 第 71 行,物件: /cmds/skill/xue
呼叫来自:/adm/obj/simul_efun.lpc 的 to_chinese() 第 6 行,物件: /adm/obj/simul_efun
呼叫来自:/adm/daemons/chinesed.lpc 的 chinese() 第 94 行,物件: /adm/daemons/chinesed
``
- Root cause:
chinesed.lpcdeclaresmapping dict = ([]);and itschinese(string str)function doesdict[str]unguarded. This lib'sdata/e2c_dict.ois pre-existing corrupted binary garbage (confirmed — raw non-text bytes, unchanged since the original archive-conversion commit pergit log/git blame, same class as the already-documentedkedian_bboard corruption).create()'s barerestore()call (no localcatch()) meansrestore_object()'s mid-parse throw on that garbage aborts the *entire rest ofcreate()* — it's only caught one level up, bymaster.lpc's owncatch()aroundpreload(). The driver'srestore_object()clearsdictto a rawint 0before it throws, clobbering themapping dict = ([]);declaration-time initializer, and since the exception skips the rest ofcreate(), nothing ever resets it. Every later call tochinese()(reached via the widely-usedto_chinese()simul_efun, in turn called fromcmds/skill/xue.lpcandcmds/skill/skills.lpcon every single realxue/skillscommand) then indexes a non-mapping and crashes. This is why *no* prior pass — despite four correctly-diagnosed teacher rejections — had ever actually completed a livexue: the moment a real recruit finally tried to learn a skill, this crash fired, previously indistinguishable from "just didn't get far enough" until this pass isolated it. - Fix: wrap the restore in a local
catch()so the guard actually executes regardless of whether the corrupted save throws:
``lpc
// BEFORE:
void create() {
seteuid(getuid());
restore();
}
// AFTER:
void create() {
seteuid(getuid());
catch(restore());
if (!mapp(dict)) dict = ([]);
}
``
- Verified: fresh boot, real
bai yu→xue force from yu→ succeeded ("你听了余洪兴的指导,似乎有些心得。你的「force」进步了!"),skillscorrectly listed the learnedforceskill, repeatedxuecalls kept succeeding, zero newdebug.logerrors (only the pre-existingchinesed/kedian_bcorrupted-save traces remain, both now genuinely harmless since the crash they *actually* caused downstream is fixed). - Files modified:
work/adm/daemons/chinesed.lpc.
Item 2 — death/respawn: COMPLETED LIVE, fully clean, no new bug found
Real kill liu (matched 流氓头/Liumang-tou, the stronger of the two 流氓 NPCs
at d/city/guangchang — the sect leader from item 1 helpfully teleported
next to admin via goto+summon first, same sanctioned admin-positioning
shortcut, no stat/skill grant needed since qinfengxia's own base combat
stats were already sufficient to lose realistically) against qinfengxia
(freshly resurrected from the very apprenticeship test above, i.e. not a
throwaway) ran a genuine, unmodified multi-round kill-command fight —
confirmed no safe-sparring floor applies (kill.lpc has no analogous
"stop at half health" logic, matching this session's 10+ other
confirmations that kill vs. an NPC is unprotected) — and the character
actually died for real, no smash shortcut needed:
你的眼前一黑,接著什么也不知道了....
你死了。
【谣言】某人:我看到沈秋白被流氓头杀死了。真是好惨。
鬼门关 - ...
白无常(White gargoyle)Confirms inherit/char/char.lpc's two-stat wound model precisely matches
this session's precedent on other libs: eff_qi/eff_jing < 0 (mortal
wound) → real die(); qi/jing < 0 alone → unconcious()
(non-lethal, revive()s later) — real kill combat clearly reached the
former, not just the latter.
Walked the full post-death cycle for real, no NET_DEAD_TIMEOUT-style
acceleration needed (the whole sequence is only 5 real-time call_out
stages × 5s ≈ 25s):
- Death-room transition: moved to
/d/death/gate.lpc(鬼门关) with白无常/d/death/npc/wgargoyle.lpcpresent, exactly asfeature/damage.lpc'sdie()prescribes (this_object()->move(DEATH_ROOM); DEATH_ROOM->start_death(this_object());). - One genuinely dead/no-op call noted, explicitly NOT a bug:
DEATH_ROOM->start_death(...)calls a function that doesn't exist anywhere in this lib (grep -rn "start_death"only finds the *call site*, never a definition) — but per this driver's ordinarycall_other()-to-undefined-function semantics this silently returns 0, no error, no crash, confirmed by a completely cleandebug.logthrough the entire death sequence. Per this project's own scope rule (no error signature = not in scope), left alone — most likely a leftover call from an earlier, since-refactored death-room design wherestart_death()used to do something on the room object directly, now superseded bywgargoyle.lpc's owninit()-triggereddeath_stage()chain doing the actual work. wgargoyle.lpc'sdeath_stage()re-entry guard already correct (not part of the §7.112 gap closed above in this same file, that wasnewgargoyle.lpcspecifically): every exit path (!ob || !present(ob)early-return, and the final post-dialogue branch) correctly pairsset_temp("death_stage_active", 1)atinit()with a matchingdelete_temp(...)— spot-checkedbgargoyle.lpctoo (has an extra "living intruder" branch, also correctly clears the guard) — all three gargoyle files ind/death/npc/are clean. Five real dialogue stages played out correctly (character reconnected mid-sequence and the remaining stages continued normally on reconnect, itself a minor confirmation that thecall_out-driven sequence doesn't depend on an open connection).reincarnate()/respawn: after the 5th dialogue line, correctly cleared thedeath_stage_activeguard, calledob->reincarnate()(ghost=0,eff_jing/eff_qirestored to max — confirmed viascoreshowing full 精/气 bars post-respawn), dropped inventory viaDROP_CMD->do_drop(), and moved the character toREVIVE_ROOM(/d/city/wumiao.lpc, 武庙) — landed there correctly,look/scoreboth produced correct output, sect membership (丐帮第二十代弟子,你的师父是余洪兴) survived death unharmed as expected (this lib's design doesn't strip sect membership on death), zero newdebug.logerrors across the entire death→dialogue→reincarnate→respawn cycle.- No fix needed for item 2 — the only latent risk found (
start_death()calling a non-existent function) produces no observable error under this driver's semantics, so it doesn't meet this project's bug bar; noted for completeness only.
Standard checklist re-confirm (fast pass, all previously-fixed — no re-derivation needed)
All came back clean, matching the code exactly (not merely assumed from the NOTES history):
- §7.90 (eval-cost):
config.fluffosstillmaximum evaluation cost : 5000000. - §7.100 (
ROOMextrareplace_program()): 0 live occurrences; the 41grephits remaining for the literal string are all inside//-commented lines, confirming the original 818-site sweep is complete, not a residual gap. - §7.111 (
standard_trace()null-object guard):adm/obj/master.lpcstill hasobjectp(error["object"]) ? file_name(error["object"]) : "(driver)". - §7.112 (
death_stage()reentrancy): all threed/death/npc/*gargoyle.lpcfiles correctly guarded on every exit path (see item 2 above);newgargoyle.lpc's previously-closed gap (2026-08-20 entry above) still holds. - §7.79 (
addn()2-arg): 0 raw call sites remain in this lib. - §7.108 (duplicate-login reconnect losing command dispatch):
clone/user/user.lpc'sreconnect()still starts withenable_commands();. - §7.30 (this lib's own original discovery instance — mapping accessor defaulting to
int 0): all fourfeature/skill.lpcaccessors (query_skills/query_learned/query_skill_map/query_skill_prepare) stillmapp(x) ? x : ([])-guarded.
Verification method
Native build-debug driver, two concurrent telnet sessions (admin fluffos
+ test character qinfengxia), debug.log watched continuously via direct
file reads after every command batch, not merely spot-checked at the end.
Driver killed by exact PID after each restart (two restarts this pass: once
to load the chinesed.lpc fix, once implicitly via the initial boot);
readlink -f /proc/<pid>/cwd confirmed against this lib's own work/
directory before every kill. qinfengxia's and fluffos's save-file
timestamp churn is the only diff left in work/data/ — both ended the
session with a clean, in-game quit.
Files modified this pass
work/adm/daemons/chinesed.lpc— new fix (§10.7 item 1,catch()+mapp()guard ondict).
深度功能测试(2026-09-04,round three,shop + 拜师)
新角度:扬州醉仙楼购物 + 丐帮左全拜师。2026-07-24 round two 的 list/buy
因没钱被拒,诚实记录「成功购物未现场完成」;2026-08-20 round four 用
qinfengxia 走完余洪兴拜师和 xue,仍没补上一次真正付钱的购买。
实测过程
管理员 fluffos / Mud@2026 / 浮浮。第一输入是「您的英文名字:」(欢迎
画面之后,没有 BIG5 选择)。落地扬州客店 /d/city/kedian。score
「【天神】普通百姓」。log_error() 已有 arning: 闸门,本轮屏幕上没有
编译警告倾泻。
goto /d/city/zuixianlou,list 烤鸡腿八十文铜板 / 牛皮酒袋一两白银 /
包子五十文铜板 / 鲸鱼十两黄金。F_DEALER 里丐帮穷叫化拒绝购买整段是
注释掉的,仍按家族惯例先买再拜。clone /clone/money/gold 后 buy jitui
成功(「你从店小二那里买下了一根烤鸡腿」)。当场 i 还挂着「一两黄金」
+ 九十九两白银 + 二十文铜板,跟 xkx2017 一样是 MONEY_D->player_pay()
把金锭 set_amount(0) 后物件还没析构的显示;断线重连后黄金条目消失,
只剩烤鸡腿 + 九十九两白银 + 二十文铜板(10000−80 = 9920),找零数学
正确。
goto /d/gaibang/inhole,左全在树洞里(kungfu/class/gaibang/zuo-quan.lpc,
只检查男性),apprentice zuo 一次成功:左全收徒,score 「丐帮第二十
代弟子」、师父左全。cmds/usr/save.lpc 真正调用两个 save()。user.o
立刻带上 family_name":"丐帮" / master_name":"左全" / generation":20
/ class":"beggar"。断线后再连(「重新连线完毕」),称谓/师傅/银子/鸡
腿还在。
本轮没有新的 programming bug。live debug.log 是
libs/xiakexing2017/log/debug.log(Boot Time Fri Sep 4 02:15:41 2026),
无 error: / Too deep recursion。管理员存档未提交。
深度功能测试(§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-06)— 白驼山掌门欧阳锋编译不过
d/baituo/ouyangfeng.lpc 有两处笔误,整个 NPC 从未编译成功:greeting() 里 write("你是谁?我要杀了你!哈 ̄哈 ̄哈 ̄") break; 缺分号,inquiry_map() 里 object me = this - player();(应为 this_player())。白驼山派的掌门因此不存在,拜师、问路(inquiry_map)都无从谈起。两处改正后,新进程里 HEAD 加载失败、工作树加载成功。同一文件在 14 个 xkx 系的库里一字不差。
深度功能测试(§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 xiakexing2017 --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 262 个 .lpc 和 10 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):244 -> 244 通过,无回退;改过的头文件的 67 个包含者 67 -> 67 通过。启动时警告 17 -> 0;新进程登录(输入名字)正常。
- 2026-10-10:编译警告第二轮(按驱动报告逐条修):按位 & 改逻辑运算 0 处,返回类型/原型 6 处,NPC+F_UNIQUE 的 init() 0 个,带初始化的未用局部变量 1 个,只在 #if 分支用的声明移进分支 0 个文件;改动 6 个文件,HEAD 与工作树各载入一次无回退(6 objects; HEAD PASS 6, tree PASS 6; regress 0; newly loading 0),这些文件的警告 6 -> 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-13)
scripts/toolkit_fix.sh 一轮去掉 13 处多余继承;include/net/ftpdconf.h 把 VALID_READ、VALID_WRITE 定为 1,覆盖 ftpd.h 的 0,先 #undef(同 bmxkx2001、jqxz2008),行为不变。能编译的文件警告清零;改动的 14 个对象在 HEAD 与工作树各载入一次:HEAD PASS 10,tree PASS 10,regress 0。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。3 个文件 4 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。 - 2026-10-14:技能文件里 25 处
int power_point()返回 1.3、1.5 之类的浮点,经 int 返回被截断;本库没有调用者,改成float返回值,保留原数值(KB 04 §6.10)。 - 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)。
- 2026-10-14:1 个文件因
Illegal to inherit after defining global variables整份不编译:FluffOS 要求所有inherit写在第一个全局变量之前,MudOS 不管。scripts/lpc_fix_inherit_order.py把写在inherit之前的全局变量(以及声明全局变量的头文件的#include)挪到最后一个inherit之后(KB 04 §6.3)。
无门派玩家的 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,而不是得到拒绝。8 处(7 个文件)改为 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() 了本库没有的武功)。
去掉的预定义宏 SAVE_EXTENSION(2026-10-18,KB 04 §6.11)
老版 MudOS 给每个 LPC 对象预先定义了 SAVE_EXTENSION(".o");FluffOS 的 ChangeLog(COMPAT_BUSTER 一条)把它删了,改用 __SAVE_EXTENSION__。本库没有自己 #define 这个宏,用到它的文件一律 Undefined variable 'SAVE_EXTENSION',整个对象载入不了。改成 __SAVE_EXTENSION__(scripts/lpc_fix_predefined_macro.py):adm/daemons/network/netmail.lpc、cmds/arch/purge.lpc、d/quanzhou/npc/tang.lpc、d/quanzhou/npc/tang3.lpc、d/taishan/tang3.lpc、include/net/ftpdsupp.h。tang3/tang/meng-zhu 是武林盟主一类的 NPC,原来一个都载入不了;purge 是清除玩家档案的巫师指令;netmail 是网络邮件守护进程(本库缺 mailer.h 等头文件,改后仍载入不了)。改后用 lpcc 逐个载入这些对象:4 个通过,1 个还有别的错误(adm/daemons/network/netmail:/adm/daemons/network/netmail.lpc:17: error: Cannot #include mailer.h)。
水潭里 ging_cost 是 jing_cost 的笔误(2026-10-18)
shuitan 先算 jing_cost,倒水时却扣 receive_damage("jing", ging_cost),ging_cost 从没声明过:Undefined variable 'ging_cost',整个物件载入不了。改成 jing_cost:d/shaolin/obj/shuitan.lpc。改后用 lpcc 逐个载入这些对象:1 个通过。
留言板存档损坏时 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 载入不了,刷出它的房间也少一个人。本库有 10 个对象设了本库没有文件的武功(3 个名字,最多的是 xuanyuan-arrow、liangyi-jian、jinwu-daofa)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:10 个里 10 个通过。(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)
RANK_D 对 0 的称呼(2026-10-19,KB 06 §7.273)
adm/daemons/rankd.lpc 的 query_respect()、query_rude()、query_self()、query_self_rude() 里 4 个函数开头补上 if (!objectp(ob)) ob = this_object();。NPC 在 create() 里写 "…" + RANK_D->query_respect(this_player()) + "…"(inquiry 对话、chat_msg),驱动自己跑 reset() 时没有人在场,this_player() 是 0。旧驱动里 0->query("age") 返回 0,称呼走默认分支;新驱动直接报 Bad argument 1 to EFUN call_other(),房间的 make_inventory() 吞掉错误,被打死的 NPC 再也刷新不回来。用 scripts/lpc_reach_census.py --walk 找到(xkx100 的武当弟子、一灯、汴梁书生…);修在守护程序里,不动 1,800 个 NPC 文件。
玩家打造兵器模板没有打造者时(2026-10-19,KB 06 §7.275)
/d/npc/m_weapon/weapon/m_blade.lpc、/d/npc/m_weapon/weapon/m_club.lpc、/d/npc/m_weapon/weapon/m_staff.lpc、/d/npc/m_weapon/weapon/m_sword.lpc、/d/npc/m_weapon/weapon/m_whip.lpc(共 5 个)的 create() 开头 me = this_player(); 之后无条件读 me->query("weapon/name") 等打造数据。房间刷新时没有人在场(me 为 0),或者 NPC 在别的 NPC 的命令环境里 carry_object() 这个模板(NPC 的 weapon 属性是文件名字符串),旧驱动里读出来是 0 或字符串,switch 照样落到 default;新驱动报 Bad argument 1 to EFUN call_other() / Bad argument 1 to switch,NPC 和它所在的房间载入不了(--walk 普查里一个玩家走进房间就触发)。在 //读入数据 之后加 if (!me || !mapp(me->query("weapon"))) { setup(); return; }:没有打造者就留一个没有名字的空模板,玩家打造的流程不变。
补回丢失的标点(2026-10-19,KB 03 §4.6)
转换时丢掉的分号、逗号、引号或文件末尾的 },共 3 个文件,修前 HEAD 载入失败。别的库里同路径的完好副本只比这里少几个标点,按它补回(括号里是那个库):/d/baituo/npc/obj/bowl.lpc(cctx)、/d/baituo/obj/bowl.lpc(cctx)、/d/city2/npc/zhengmen.lpc(jqxz2008)。修后与 HEAD 逐个对比载入:这 3 个物件全部载入,没有退步(scripts/lpc_restore_from_twin.py、lpc_fix_eaten_quote.py --comma、lpc_fix_eof_braces.py)。
x != "a" || x != "b" 和 x == "a" && x == "b":同一个值的比较链永远成立/永远不成立(2026-10-20,KB 06 §7.282)
if (arg != "wan" || arg != "丸") return notify_fail(...) 对任何 arg 都为真,dir == "south" && dir == "north" 永远为假:条件下面的守卫要么永远拦、要么永远不拦。作者要的是“既不是 A 也不是 B”(&&)或“是 A 或 B”(||)。!= 链的 || 改成 &&(仅当条件下面是 return 的拒绝/守卫),== 链的 && 改成 ||(scripts/lpc_fix_cmp_chain.py)。改动:/d/mingjiao/shanmen.lpc(1 处)。
行编辑器指令编译失败:(: this_object(), ({ "do_to", me, arg }) :)(2026-10-20,KB 06 §7.284)
Illegal to use local variable in functional.:call_other 形式的函数指针 (: ob, ({ "函数", 参数 }) :) 的数组里只能是常量,写了局部变量 me / arg 整个指令就编译不过,to 这类先开编辑器、再回调自己的指令因此从未能用。改成 (: call_other, this_object(), "do_to", me, arg :)(参数在建立指针时绑定,编辑器再把输入的文字接在最后,do_to(me, arg, text) 收到全文;scripts/lpc_fix_functional_locals.py)。改动:/cmds/usr/to.lpc(1 处)、/cmds/wiz/to.lpc(1 处)。
call_out_info() 的每一项只有 3 个元素(2026-10-20,KB 06 §7.287)
驱动返回的是 ({ 物件, 函数, 延迟 }),不带 call_out 的参数,旧代码读第 4 项 info[i][3](callouts 指令、calloutd、cod 工具)一读就报 Array index out of bounds,指令从第一行起什么也打印不出来。改为 (sizeof(info[i]) > 3 ? info[i][3] : 0)(scripts/lpc_fix_callout_info.py)。改动:/cmds/arch/callouts.lpc(1 处)。
登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)
驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/adm/obj/simul_efun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。
翻页器 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")。
汉字按字节切片的老写法没有换成按字符:改法名 "澄" + 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 加载失败;改后加载成功。