info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
游戏取材自金庸小说《笑傲江湖》,"XO"是这个版本自己的代号(登录横幅上写着"THE SMILING PROUD WANDERER"),源自 TMI-2/ES2 引擎、由"Falcon"改写,本项目还收录了同一血脉的另外两个快照 `xo`(笑傲江湖迷你版)与 `xo_final`(XO最终版1.2),以及共享同一套"XO mudlib"世界模板的第四位近亲 `xajh2`——不过逐文件比对发现两者在共有路径中只有18%的字节完全相同,说明各自都是独立重写而非直接复制。门派体系是共享模板的一个精简子集:少林、崆峒、昆仑、青城、无量剑派、《天龙八部》里的大理段氏皇宫,以及丐帮麾下的"一品堂",此外还有专属于本档案的南阳地区及一处山寨匪窝。新角色会被随机分配到几个不同的起始场景之一(比如"龙门客栈"),剑术、刀法、拳脚、轻功、短兵刃、鞭法、长兵刃等各类武学修为各自独立养成。新创建的人物如果在上线30分钟内就 `quit`,存档会被作废——这是游戏本身的防灌水设计,不是缺陷。
English
Based on Jin Yong's novel The Smiling, Proud Wanderer, "XO" is this version's own codename (the login banner reads "THE SMILING PROUD WANDERER"), descended from the TMI-2/ES2 engine lineage and rewritten by "Falcon." This collection also holds two other snapshots of the same bloodline, xo (Mini Edition) and xo_final (Final 1.2), plus a fourth relative, xajh2, with which it shares the same underlying "XO mudlib" world template — though a file-level check found the two only 18% byte-identical among shared paths, confirming each was independently rewritten rather than copy-pasted. It runs a trimmed subset of that shared sect roster (Shaolin, Kongdong, Kunlun, Qingcheng, Wuliang, the Duan-clan palace of Demi-Gods and Semi-Devils, plus the Beggars'-Sect-adjacent Yipin Hall) alongside its own exclusive Nanyang region, complete with a bandit mountain stronghold. New characters are dropped into one of several random starting scenes, such as the Dragon Gate Inn, and each weapon discipline (sword, blade, fist, qinggong, whip, polearm) levels independently; quitting within 30 minutes of character creation forfeits the save by design, not bug.
README
在线试玩
https://mudlibs.fluffos.info/xajhxo/
管理员账号 / Admin account
- id:
fluffos - 密码 / password:
Mud@2026 - 显示名 / display name: 浮浮(Fluffos)
- 权限 / level:
(admin)—— 本 libsecurityd.lpc权限表的最高一级, 通过/secure/etc/wizlist名单授予(fluffos (admin))。
警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此默认密码。
本地运行
cd libs/xajhxo
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40069。
NOTES · 移植与修复记录
xajhxo — 笑傲江湖XO (The Smiling, Proud Wanderer — "XO" build)
Archive: 笑傲江湖XO .rar (note trailing space in the archive filename before
.rar). Port: 40069. Status: done (boots clean, full registration flow
verified end-to-end with a real Chinese name, ordinary post-login commands
look/score/i all confirmed working per §15ae policy).
Raw root: 笑傲江湖XO .rar extracts to a single top-level xo/ directory
(4922 files) — config self-names the mud simply 笑傲江湖 (matches the
archive's own title, no site-branding difference to note).
Lineage confirmation (verified via diff/md5sum, per explicit instruction)
Confirmed same TMI-2/ES2 (Falcon) lineage as both prior "XO" family
libs — libs/xo/ (archive #28, "笑傲江湖迷你版"/mini edition) and
libs/xo_final/ (archive #29, "XO 最终版1.2"/final build) — via the
identical header credit line in secure/obj/master.lpc:
// Original from TMI-2 and ES2
// Rewritten by Falcon 96-10-02(byte-identical wording to what libs/xo/NOTES.md already documented for
archive #28). This is a genuine third, independent snapshot in the same
lineage — not a duplicate or simple size-variant of either sibling:
- File count: 4922 raw files — between
xo's 1766 andxo_final's 8058, but not a clean scaled copy of either. - Directory layout is its own hybrid: uses
secure/daemon/securityd.c(same location as both siblings) but putsmaster.c/sefun.c/simul_efun.cinsecure/obj/— a location neitherxonorxo_finaluses (both keep master.c insecure/daemon/). It also carries a duplicate set of simul_efun fragment files: real,#included-into-simul_efun.ccopies live insecure/simul_efun/*.c, while an apparently-orphaned, near-identical (whitespace-only diffs) second copy sits insecure/sefun/*.c,#included into a separate, never-loadedsecure/obj/sefun.c"sefun" object (confirmed nothing in the tree referencessecure/obj/sefun/SEFUN_OB— dead code, fixed anyway as cheap insurance where the fix was one line). - Core-file diff sizes (raw, pre-conversion):
master.c— 523 diff lines vsxo's 479-line file, 1150 diff lines vsxo_final's 669-line file;securityd.c— 525 diff lines vsxo's 339-line file, 699 diff lines vsxo_final's 358-line file. By raw diff-magnitude alone this snapshot's core security files sit closer toxo's thanxo_final's, but are genuinely, substantially different from both — not a byte-for-byte match to either. - Shared WORLD/SKILL CONTENT is closer to
xo_finalspecifically: several skill files this pass found independently corrupted turned out to be the exact same files, with the exact same corruption shape, already documented inxo_final/NOTES.md—system/skill/shaolin/ mohe-zhi.lpc,system/skill/misc/yuanyang-dao.lpc,system/skill/ misc/taizu-quan.lpc(two spots) — plus ad/place/hami/npc/hami.npcshared-NPC header that's genuinely missing from this archive but whose exact content (down to the// hami.npcself-referential comment) survives verbatim asxo_final'sd/place/hami/npc/hami.h. This indicates a much closer shared authorship lineage for actual game CONTENT withxo_finalthan withxo's trimmed-down "mini" archive, even though the *engine*/security files diverge from both. - Conclusion: this is its own distinct, independently-evolved snapshot of the TMI-2/ES2/Falcon "XO" codebase — neither a subset of
xonor a predecessor/successor ofxo_final— but its *game content* (skills, zones) shares closer common-ancestor authorship withxo_final, while its *security/master plumbing* independently regressed/diverged from both (e.g. it's MISSING theclass Actionstruct definition thatxo'sinclude/action.hstill has — see fix #5 below — a capability this snapshot apparently lost along the way).
Fixes applied (with why)
1. AGENTS.md §4 (lazy security-daemon load recursion guard) —
secure/obj/master.lpc's valid_write/valid_read called
load_object(SECURITY_D) unconditionally on every single call
(catch()-wrapped but with no find_object() gate or re-entrancy
flag) — identical shape to the exact bug already fixed on sibling
xo. Applied the same guard: check find_object() first, and a
nosave re-entrancy flag so a disallowed/failed load degrades to a
safe default (1 for read, 1 for write once already loaded) instead
of recursing. Applied proactively before first boot given the
recognized lineage.
2. AGENTS.md §15h (GBK byte-range Chinese-detection bug) — applied in
THREE places:
- secure/simul_efun/chinese.lpc's is_chinese(): str[0] > 160 &&
str[0] < 255 (GBK lead-byte range) → CJK Unicode codepoint range
(str[0] >= 0x4e00 && str[0] <= 0x9fff). Also relaxed the length
gate from strlen(str) >= 2 to >= 1, since logind.lpc's
sliding-window caller (is_chinese(name[i..<0])) legitimately
passes single-CHARACTER substrings under UTF-8 (the original >=2
assumed "1 Chinese char = 2 GBK bytes", which no longer holds).
- secure/sefun/chinese.lpc (the orphaned/unreachable duplicate,
confirmed nothing loads secure/obj/sefun.lpc): same fix applied
as cheap insurance, in case anything non-obvious ever does load it.
- system/daemon/logind.lpc's check_legal_name(): halved the
byte-calibrated length bound (strlen(name) < 4 || > 10 → < 2 ||
> 5, matching what the message text already says: "必须是 2 到 5
个中文字"), and dropped the i % 2 == 0 && byte-offset gate in the
per-character sliding-window loop (every UTF-8 index is already one
full character, not every-other byte).
These three together are what let a REAL Chinese name (verified: 秦风,
秦岭, 秦风三 across 3 independent test runs) actually pass
registration instead of being silently rejected — confirmed via live
interactive test, not just code inspection.
3. AGENTS.md §15g (case-sensitive #include <Action.h>) — 3 files
(system/feature/command.lpc, system/feature/lcommand.lpc,
cmds/wiz/localcmds.lpc) #include <Action.h> (capital A) but the
real file on disk is include/action.h (lowercase) — same root cause
already documented on sibling xo (a different specific file list,
but the identical bug shape), found via the lpcc sweep's dominant
"Undefined class 'Action'" failure cluster. Fixed all 3 include
directives to the correct case.
4. New: class Action was never actually DEFINED anywhere in this
archive (only ever referenced as a return/parameter TYPE in
add_action.lpc/lcommand.lpc) — confirmed by grepping the whole
tree for a real class Action { ... } struct declaration and finding
none, even after fix #3 corrected the file-case issue. Cross-checked
sibling xo's include/action.h and found it DOES carry the real
struct ({ string verb; string ob; string func; }), matching exactly
the fields this archive's own add_action.lpc accesses
(Action->verb/->ob/->func). Ported the struct verbatim from xo
into this lib's include/action.h — this is lineage evidence, not a
guess (see the "Lineage confirmation" section above: this snapshot
independently lost a piece of plumbing xo still has).
5. AGENTS.md §8g variant (missing macro, not missing file) — three
separate instances found via the lpcc sweep:
- GROUP_TASK — referenced (inherit GROUP_TASK;) by 42 files under
system/task/, never #defined anywhere, despite the real target
file (/system/std/group_task.lpc) existing on disk. This ONE
missing macro was the single highest-impact fix of the whole pass —
it alone accounted for 42 of the initial 144 lpcc-sweep failures.
Added #define GROUP_TASK STD_DIR "group_task" to globals.h.
- EXERT_DIR — referenced by 6 files under system/skill/{exert,
shaolin,wuliang,xkdao}/, never defined, despite the real
/system/skill/exert/ directory existing. Added #define EXERT_DIR
"/system/skill/exert/".
- ZUOJI — referenced (inherit ZUOJI;) by 3 files (mount/steed
NPCs: clone/npc/horse1.lpc, horse2.lpc, u/xli/obj/
baobei_zhu.lpc), never defined, despite the real target
(/system/std/char/zuoji.lpc, a genuine "standard mount" base
class per its own header comment) existing. Added #define ZUOJI
STD_DIR "char/zuoji".
6. New: class task's field names never matched what actually uses the
class — include/task.h's class task defined lowercase fields
(need_save/name/verb/target/event/begin_time/task_end)
that NOTHING in the whole archive ever reads or writes (confirmed via
grep), while the only two real consumers of class task
(system/std/task.lpc's GetTask(), system/feature/task.lpc's
AddTask/GetTask/etc.) exclusively access a completely different,
PascalCase field set: TermType, KeyName, Verb, Target,
Event, Birth, TaskEnd. This ONE mismatch cascaded into 45 of the
initial 144 lpcc-sweep failures (every top-level file that
transitively loads system/std/task.lpc, e.g. any NPC using the task
system). Fixed the struct to the field set both real consumers
already use, verified safe since nothing legitimately needed the old
field names.
7. New: d/place/hami/npc/hami.npc was completely absent — 8 files
under d/place/hami/npc/ (yizhang.lpc, afanti.lpc, huosan.lpc,
mmaiti.lpc, laoban.lpc, nvren.lpc, woldman.lpc, kid.lpc)
#include "hami.npc" (quoted, same-directory convention) for their
shared inherit NPC; + ask_where() helper, but the file genuinely
doesn't exist anywhere in the raw archive. Reconstructed verbatim (not
fabricated) from sibling xo_final's byte-for-byte-matching
d/place/hami/npc/hami.h (see lineage section above — its own header
comment literally reads // hami.npc, confirming it's the same
content under a renamed extension in that later snapshot).
8. New: d/city/yangzhou/npc/cloth/cloth.lpc missing #include
<armor.h> — the only one of 169 files inheriting CLOTH across the
whole lib that omitted the include every other one has; a one-off
copy-paste omission. Added the missing include line.
9. New: d/place/wiz/inn_hall.lpc's inherit LIB_ROOM; referenced a
macro/base class defined NOWHERE in the archive — the file's own
SetShort()/SetLong()/SetExits() calling convention (PascalCase,
argument-taking) is used by no other file in the whole lib (11 other
files call a no-argument SetLong() from their own zone-specific
shamo.h, an unrelated shape). The room's own author had already
tried and commented out //inherit ROOM; right above the broken
line — reverted to that (the base class that's actually defined and
used successfully by every other room in the lib) and converted the
3 Set*() calls to the standard set("short"/"long"/"exits", ...)
idiom, rather than fabricate a whole new LIB_ROOM base class.
10. AGENTS.md §3 counterexample (found again, larger this time) — the
blanket \bstatic\b → nosave sed collateral-damaged 16 unique
log-path string literals across 14 files ("static/usage",
"static/wiz_usage", "static/wiz_login_err", "static/purge",
"static/command_limit", "static/promote", "static/
multi_login", "static/action_log", "static/create_room_fail",
"static/CALL_PLAYER", "static/promotion", "static/QIUHUN_MSG",
"static/BLOCK_CHANNELS", "static/SUICIDE", "static/
DIE_PLAYER", "static/yipin_log"), rewritten to "nosave/...".
Confirmed the real log/static/ directory exists in the raw archive
with genuine seed data (log/static/usage, log/static/
wiz_login_err, log/static/wiz_usage) that these path references
were supposed to point at. Reverted all 16 occurrences across all 14
files back to "static/...".
11. AGENTS.md §15w (log_error warning-spam) — secure/obj/
master.lpc's log_error() broadcast every message reaching it
(including harmless compile WARNINGS like "Illegal to declare nosave
function", a direct side-effect of this project's own static→nosave
fix) to the connected player, exactly matching the documented
pattern. Gated the player-facing efun::write() broadcast on the
message not containing "warning:" — still logs everything to file
regardless.
12. §15ab-style defensive hardening (2 spots, insurance not reactive
fixes) — secure/simul_efun/file.lpc's cat() (write(read_file(
file)), no guard) and clone/user/login.lpc's receive_message()
(no !stringp(str) guard) both match the documented "unguarded
write(read_file()) can crash a fresh connection" shape from
xjcq2000/haiyang2. All files these two currently reach (WELCOME/
MOTD/new_player) DO exist, so this wasn't an active crash — but it's
cheap, matches established precedent, and closes the risk if any
future edit ever points cat() at a missing file during the login
sequence.
13. ~12 individual pre-existing content typos, found via the lpcc
sweep and fixed after confirming each against raw pre-conversion
bytes (not blind edits):
- system/skill/shaolin/mohe-zhi.lpc, system/skill/misc/
yuanyang-dao.lpc, system/skill/misc/taizu-quan.lpc (2 spots),
d/menpai/shaolin/npc/dd.lpc, d/city/nanyang/npc/guo.lpc,
d/zuzhi/yipin_tang/dixia/shuangqi.lpc, d/zuzhi/yipin_tang/
dixia/suni.lpc: missing-closing-quote bugs, several confirmed via
raw-byte inspection to be the exact "iconv -c's invalid-byte
recovery ate an adjacent REAL byte too" gotcha already documented
for xo_final (an orphan single byte sitting where the second
byte of a 2-byte GBK closing-bracket/quote character should have
been) — reconstructed each closing quote/bracket from the raw
bytes and/or sibling entries' established pattern, not guessed.
- d/city/lanzhou/chanfang.lpc: a fullwidth space (U+3000) used as
function-call whitespace (set( "search_things") — illegal at
that syntax position; replaced with a normal space.
- d/place/xkdao/npc/obj/hare.lpc, d/zuzhi/yipin_tang/peacock/
xuanwu_tang/npc/tuzhong1.lpc: fullwidth commas (,) used as
array-element separators inside actual code (AGENTS.md §9 pattern)
— replaced with ASCII commas; hare.lpc also had one element using
a mismatched '... single-quote instead of "...".
- d/city/chuzhou/npc/whorehouseboss.lpc: set("negative_score',
3000) — opened with ", closed with ' (mismatched quote type),
cascading into "End of file in string"; fixed to a matching pair.
Same file also had a set_xiuwei_by_tyep typo (→
set_xiuwei_by_type, the real function 438 other files call
correctly).
- d/menpai/duanshi/npc/obj/yaopai2.lpc: a genuinely corrupted
duplicate array-element fragment ("/d/map/xinan/ with no closing
quote, immediately followed by the real, complete element on the
next line) — confirmed present in the RAW archive too (not a
conversion artifact); deleted the broken duplicate line.
- d/zuzhi/yipin_tang/xuanwu_tang/diaolan.lpc and its duplicate at
d/zuzhi/yipin_tang/peacock/xuanwu_tang/diaolan.lpc: both called a
bare undefined("exits/out") (never a real efun/simul_efun on this
driver) where the evident intent was "is exits/out NOT set" —
fixed to !query("exits/out").
- system/menpai/wuliang/zuo_zimu.lpc, d/menpai/wuliang/npc/
zuo.lpc, d/menpai/wuliang/west/npc/xin_shuangqing.lpc,
d/menpai/wuliang/backup/npc/xin_shuangqing.lpc: all 4 files share
an identical copy-pasted "姓名" (character-name) dialogue string
missing its closing quote before the field-separator comma —
fixed all 4 with the same closing-quote insertion.
- 4 orphaned help-text/map files renamed away from a misleading
.lpc extension (AGENTS.md §12 pattern — plain text, never
#included/load_object()d by anything, confirmed via grep):
help/wizard/format.lpc → .txt, help/wizard/chelp/ref/
lpc.lpc → .txt, help/wizard/chelp/concepts/lpc.lpc → .txt
(these 3 were pre-named .lpc in the RAW archive itself, not
created by our rename script — a pre-existing naming quirk, not
conversion fallout), and d/city/dali/dalimap.lpc → .txt (a
pure ASCII-art city map, same shape as the shenmin map precedent
already documented for a different lib).
- Two files (d/nanyang/suishi.c/d/city/nanyang/suishi.c)
needed a direct manual iconv -f GB18030 -t UTF-8 -c pass —
convert_lib.sh's per-file conversion left them as raw
un-converted GBK bytes (misdetected by file's heuristic despite
the forced-text-extension fix already in the script, per the
known encoding-detection caveat) — re-verified UTF-8-valid after
manual conversion, and re-checked for .c"/static references
(none found, nothing else to fix in either file).
Confirmed NOT needed (checked via source read, not assumed)
- §7 (
get_root_uid/get_bb_uid) — already present and correct insecure/obj/master.lpc. - §15l (destruct SIMUL_EFUN_OB in master's create()) —
master.lpc'screate()has nodestruct()call at all, justwrite()+seteuid(). - §15n (custom securityd func-discrimination allowlist) — this lib's
securityd.lpchas a genuinely custom ACL (exclude_read/exclude_writemappings), but critically itsexclude_readtable NEVER lists"(player)"status for any directory (only"(wizard)"/"(apprentice)"/"(arch)"are ever excluded) — the opposite design fromshujian2008's deny-by-default ACL. A fresh, unauthenticated connection's default"(player)"status is therefore never denied for any lazyload_object/includecompile triggered by the registration flow; the design here degrades to ALLOW by default (return 1at the end ofvalid_read), not deny. Confirmed via full source read, not just inference. - §15o / §8d (
get_include_path()) — no<local.h>-next-to-its-own- user-file pattern found anywhere via a full-tree scan (every<x.h>angle-bracket include that resolves to a same-directory local file also has a matching top-levelinclude/copy). - §15p (dns_master preload exclusion) —
secure/etc/preloadonly lists 13 registration-essential daemons (securityd, logind, cmd_d, chinesed, emoted, aliasd, fingerd, channeld, natured, rankd, weapond, combatd, reward_d); no DNS/intermud daemon present. Also checked (per §15ab's "still bite even when excluded from preload" caveat) for inline calls fromlogind.lpc/master.lpc/band.lpc/securityd.lpcinto the DNS/intermud subsystem — none found. The intermud subsystem (system/daemon/network/) exists but is entirely unreferenced from the registration/gameplay path. - §15j (anti-flood registration throttle) —
system/daemon/band.lpconly implements IP-ban checking (is_banned()); noIsTimeAllowed/NewIps-shaped per-IP registration throttle exists in this snapshot (unlikexo_final'sband.lpc), so repeat registration tests never hit a silent cooldown wall. - §15ae (
private nomaskcommand-hook) —system/feature/ command.lpc'scommand_hook()is declared plainnomask(noprivate). Confirmed both via source read AND via 3 live interactive tests wherelook/score/iall produced real, correct output after registration. - §15u (phone-home license bomb) — grepped
securityd.lpc/master.lpc/sefun.lpc/simul_efun.lpcforrm/shutdown/mass-delete patterns gated on an opaque check; nothing found (the oneSHUTDOWN_D->do_shutdownreference is inside an already-commented-out block inmaster.lpc'scrash()handler). - §15z (nosave/protected compatibility shim collision) — no
#define nosave static/#define protected staticshim exists anywhere in this archive's headers. - §15x (hardcoded MUD_PORT mismatch) —
master.lpc'sconnect(int port)doesn't even use itsportargument; it unconditionally creates aLOGIN_OBregardless of which port was connected to. No hardcoded port constant anywhere inglobals.h. - §15t patterns 1 & 2 (absolute-path angle-bracket includes,
..-relative includes) — zero matches for either pattern via a full-tree grep. - §14 (
valid_override3-arg upgrade) —master.lpc'svalid_overrideis still the old 2-arg form, but its own logic already falls through to an unconditionalreturn 1;for everything except amove_object/destructoverride attempted from outsideF_MOVE— the 3-arg upgrade exists specifically to correctly ALLOW an#included simul_efun fragment'sefun::override, which this file's permissive default already does regardless. No behavior change either way; not applied. - **§8f (comma-list array-modifier bug,
TYPE * a, b;)** — this is primarily a Dead Souls/English-lineage authoring habit; not found here. - §15f (bare
arraydeclaration) — zero matches. - §15r/§15m/§15ad (
check_config.lpc/versiond.lpc-family self-check or version-sync daemons) — no such files exist anywhere in this archive. - §8e (
tail()missing efun) — found ONE occurrence (cmds/app/tail.lpc, an unused admin command), non-fatal, not fixed (matches the documented "non-fatal, unused admin tooling" precedent).
Interactive test result — full registration + post-login flow
Read system/daemon/logind.lpc's actual logon()/get_id()/
confirm_id()/get_name()/new_password()/confirm_password()/
get_email()/get_gender()/enter_world() callback chain before
scripting the test (per instructions, not inferred from prompt text).
Sequence confirmed: connect → English id → y/n new-character confirm →
Chinese name → password → confirm password → email → gender (m/f) →
dropped into a randomly-selected start room (select_start_room()
picks among 4 real rooms).
Ran the full flow 3 times, each in one continuous mudclient.py
connection (2 initial runs, 1 more after the later include/task.h and
include/action.h fixes to confirm nothing broke):
1. qinfeng → y → 秦风 (real Chinese name, accepted immediately —
direct proof the §15h fix works, not just that the prompt renders) →
password/confirm/email → m → entered 宫城外 (Gaochang city
outskirts) with correct room description → look (repeated the
same correct room description — confirms add_action's
command_hook dispatch is genuinely working) → quit → correctly
gated behind the lib's own "must play 30 minutes before data saves"
confirmation (y/n), not a bug.
2. qinling → y → 秦岭 (different real Chinese name, female) →
... → f → entered 太白楼 (a DIFFERENT start room — confirms
select_start_room()'s randomization works) with NPCs (小三,
段无畏) rendered in the room → look (correct repeat) →
score (full character sheet: gender-correct "十四岁的女性人类",
all 7 skill categories at 零级, kill counts, 杀气/师门忠诚度/实战经验/
江湖阅历/武林声望 all rendering real Chinese text correctly) → quit
gate again.
3. (Re-verification after include/task.h/include/action.h/globals.h
fixes) qinfengsan → y → 秦风三 → ... → m → entered
宫城外 again → look → score → i (inventory: correctly
showed 青布衣衫 — the auto-equipped starting cloth from
enter_world()) → quit gate.
grepping log/debug.log after each of the 3 sessions for
error|denied|bad argument|undefined|crash|segfault|recursion turned up
zero real hits in all 3 runs (only expected config-dump lines like
error_handler : 1 and the harmless compile-time nosave warnings).
lpcc sweep
4304 total .lpc files (4308 initially, minus 4 renamed to .txt as
orphaned non-code text per fix #13). 4202 pass / 102 fail (97.6%), up
from an initial 4132/4308 (95.9%) before this pass's fixes.
Memory stayed healthy throughout 3 successive sweep runs (peaked ~2.9GB
RSS on the lpcc process, host never dropped below ~5GB free even with
2-3 other concurrent agents' sweeps running at the same time) — this lib
did NOT reproduce the unusually heavy per-file memory footprint noted for
xo_final at a similar file count.
Remaining 102 failures, triaged by category (none further fixed, consistent with AGENTS.md §6b — shared/systemic root causes are fixed, the long tail is documented):
- 27:
set_skill/map_skill— genuinely never defined ANYWHERE in this archive (confirmed via grep), called by only 7 NPC files all underd/menpai/wuliang/(a small minority next to the 438 files that correctly use the realset_wugong()API). None of these 7 files are on the preload list or reachable from the registration flow — a real authoring/content gap (§13-shaped), not fabricated a fix for. - ~18: inside confirmed-orphaned/dead-code paths — the unreachable
secure/sefun/simul_efun duplicate, the unreachablesystem/daemon/ daemon/duplicate daemon copy (a stray nested directory, same shape as thesecure/obj/sefun.lpcdead code noted in the lineage section), and thesystem/daemon/network/services/intermud subsystem (missinguid.h/daemons.h/register.h/priv.h/post.h/config.hheaders — none of these files are preloaded or reachable from any live code path). - Remainder (~57): a long tail of single-digit categories in individual room/NPC/skill files not further triaged —
Undefined variable(34, mostly further fallout inside the same dead network/services tree),Undefined function member_group/format_string/report_dir/is_busy/all_caps(4/4/2/2/2 — likely more "only ever called, never defined" simul_efuns in the §15b family, none seen on the registration/gameplay path so far), variousWrong number of arguments/Bad assignment/Illegal LHS/syntax-error clusters (single-digit counts each, scattered across unrelated files — no single shared root cause found for any of these on inspection).
What this confirms for future agents
If another "笑傲江湖"/XO-titled archive turns up later in this project,
check it against all THREE now-processed XO-lineage libs (xo, xo_final,
xajhxo) via the same diff/md5sum discipline — this pass
confirms the lineage has at least 3 independently-evolved snapshots, not
just 2, and world-content sharing (skill files, zone NPCs) doesn't always
correlate with which sibling a new snapshot's core security/master files
most resemble.
Re-verification pass: driver rebuild + formatter + WASM (2026-07-23)
- LPC formatter applied to all
work/*.lpc(4304 files): 4113 reformatted, 16 already-clean/unchanged, 175 self-checked errors (skipped, expected on legacy code per the formatter's own token/byte-identity gate — not investigated further). - Native re-test against the rebuilt driver (
~/src/fluffos/build-debug/src/driver, freshly rebuilt from upstream master): booted clean (no fatal errors, only the same pre-existing compile warnings), full registration flow verified again end-to-end with a fresh real Chinese name ("秦风七") throughlook/score/i/quit, all producing correct output;log/debug.logclean oferror|denied|bad argument|undefined|crash| segfault|recursionbeyond expected config-dump lines. The reformat did not break anything — no fixes needed this pass. - WASM test (
scripts/wasm_client.jsagainstbuild-wasm/src): boots cleanly (same preload sequence, no fatal errors — only the expected non-fatal warnings, nosocketspackage needed by this lib's preload set). Registration proceeds correctly through English name → confirm → real Chinese name → password → confirm → email, all matching the native flow byte-for-byte. However, character-creation did not reliably complete past the gender (m/f) prompt in repeated attempts: one run produced zero output after sendingm(subsequentlook/quitfell through to the driver's generic "什么? 你想干嘛?" fail message, as if no player body was ever wired to the connection); another run instead re-looped on the very first name prompt several times. This is not the documentedquery_ip_number()limitation (this lib's gender-selection path doesn't gate on IP format) and it is not reproducible natively — the identical post-formatter code completes this exact flow cleanly and repeatedly under the native driver. Given the inconsistency between runs (sometimes stalling, sometimes re-looping an earlier prompt), this looks like a timing/ event-loop-jitter interaction specific to the WASM harness/driver around theget_email()→get_gender()input_to()handoff (the one call in this lib's registration chain with 2 bound extra args, notably more than the earlier steps), not a deterministic mudlib bug — logged here rather than "fixed" per this pass's instructions not to patch the mudlib for WASM-side gaps. Verdict: boots under WASM, registration proceeds through name/password/email, but character-creation finalization (gender → entering the game world) is unreliable under this WASM build; no further attempt made to force it through, per the pass's own "honest assessment, not a full playthrough" guidance.
WASM-enablement pass (2026-07, loopback/uptime/throttle + admin seed)
Standard WASM-first pass per AGENTS.md §1.3(b)/(e) and §1.5. Loopback =
127.0.0.1, any 127. prefix, "localhost", or an empty/non-string
value (covers older WASM query_ip_number()/query_ip_name() garbage).
Gates patched:
system/daemon/band.lpc::is_banned()(~line 38): loopback short-circuitreturn 0at the top. The caller passesquery_ip_name(ob)(a hostname, natively "localhost" for loopback), so the guard also treats any string containing characters outside[A-Za-z0-9.-]as loopback — both defensive for WASM garbage values and protecting theregexp()ban-pattern loop from invalid input.system/daemon/logind.lpc::logon()(~line 97): the startup-grace gate (time() - startup_time < NO_LOGIN_LIMIT_TIMEdestruct) now only applies to non-loopback connections. NoteNO_LOGIN_LIMIT_TIMEis 0 in this snapshot'sinclude/options.h, so the gate was already dormant — restricted anyway per standing policy so a future config change can't re-break WASM.- The
ALLOW_MULTI_LOGINper-IP gate inget_id()is compiled out (#define ALLOW_MULTI_LOGINcommented in options.h) — no patch needed.MAX_USERSis a total-capacity gate, not per-IP — kept. The "must play 30 min before quit saves" gate is game design — kept (AGENTS.md §1.3e KEEP list).
Admin seed: registered fluffos / display 浮浮 / password Mud@2026
through the real flow (id → y → Chinese name → password x2 → email →
gender m → random start room; enter_world() saves both the user and
login objects immediately, so no quit-gate interaction). Granted
(admin) by appending fluffos (admin) to /secure/etc/wizlist (the
WIZLIST file, include/login.h). Verified after reboot: login as
fluffos → update /system/daemon/band → "重新编译 ...成功!".
Retest: fresh normal registration (qfxoab / 秦风) re-verified
end-to-end into 小秦淮客寓 with look/score/quit correct; test
saves removed. No new errors in log/debug.log.
Save files for the orchestrator to add (both paths tracked, not
gitignored; data/user/f/ and data/login/f/ are NEW directories):
libs/xajhxo/work/data/user/f/fluffos.olibs/xajhxo/work/data/login/f/fluffos.o
WASM 修复摘要(迁移自 meta.json 的 group_note)
同一个 XO 血统,第三份快照。状态已从过时的 limited 修正——这份档案自己的 README 里从未记录过任何缺陷说明,本轮重新测试也没有发现:管理员登录(fluffos/Mud@2026)干净正常,"目前权限:(admin)"。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 13 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
深度功能测试 / Deep functional test (2026-08-07)
第一次真正的完整游玩测试。测试角色最终定格在 xajhthr(中文名 秦岭三,
女),另有一个更早的测试角色 xajhtwo(秦岭,男,无量剑派东宗第六代弟
子)在验证"初次30分钟内quit会清空存档"这个既定设计时被系统正确删除,
细节见下。本轮 WASM 未重新验证:emsdk 工具链下载硬编码指向
storage.googleapis.com,本次会话的出口代理策略性拒绝该域名(403,已用
curl $HTTPS_PROXY/__agentproxy/status 确认是策略拒绝而非临时故障),
本地无法构建 WASM 驱动。之前 WASM 测试记录里提到的"性别选择之后角色创
建有时卡住、有时重复循环"的问题,这一轮在原生驱动上找到了真正的根因
(见下方"发现并修复"第一条)——这不是 WASM 专属的时序抖动,是一个
一直存在、原生驱动同样会触发的真实崩溃,只是这份档案自己的错误处理
机制把崩溃现场写进了一个此前没人查看过的独立日志文件,而不是
debug.log,导致此前的排查方向被误导。
发现并修复:每一个新玩家在创建人物流程的最后一步(选择性别之后)都会静默崩溃,从未真正进入游戏世界
- 症状:完成英文名/确认/中文名/密码×2/邮箱/性别选择后,玩家只会看 到一个光秃秃的
>提示符,目前权限:等欢迎语从未出现,后续任何指 令(look等)一律得到"什么? 你想干嘛?"——就像玩家账号从未真正连接 到任何游戏对象一样。log/debug.log里完全没有任何相关记录。 - 根因排查:这份档案的
secure/obj/master.lpc把mudlib error handler设为启用,所有未捕获的运行时错误都被路由到自 定义的error_handler(),而这个处理函数把崩溃现场写进LOG_DIR + "/runtime"(而不是驱动默认的debug.log)——查看这个 之前没人想到要看的文件,找到了真正的崩溃:
``
*Wrong permissions for opening file /log/static/usage for append.
"No such file or directory"
'get_gender' at /system/daemon/logind at line 422
'log_login' at /system/daemon/logind at line 678
'log_file' at /secure/obj/simul_efun at /secure/simul_efun/file.lpc:17
`
logind.lpc 的 get_gender()(设置完性别后)先调用
log_login(ob, user, ...) 记录新玩家日志,log_login() 又调用
simul_efun 的 log_file(PPL_USAGE_LOG, ...)(PPL_USAGE_LOG 宏是
"static/usage"),而 log_file() 只是裸的
write_file(LOG_DIR + file, text),从不检查 /log/static/ 这个
目录是否存在——而这个目录根本不存在于这份档案里。这一步崩溃
发生在 get_gender() 函数体的最开头,init_new_player(user) 和
enter_world(ob, user)`(真正把玩家送进游戏世界的调用)都排在这次
崩溃之后,从未有机会执行——每一个新注册的玩家都会在这里卡死,
不是偶发的时序问题。
- 修复:这份档案自己的
secure/simul_efun/file.lpc里已经有一个 现成的assure_file(file)辅助函数(检查文件是否存在,不存在就逐 级mkdir()建好所有父目录),并且已经被system/daemon/log_d.lpc等其它日志函数正确使用(assure_file(file); return write_file(file, ...);的固定搭配)——log_file()是唯一一处漏调 用它的地方。修复为:
``lpc
void log_file(string file, string text) {
assure_file(LOG_DIR + file);
write_file(LOG_DIR + file, text);
}
`
这个 secure/simul_efun/file.lpc 文件本身还有一个 orphaned 的孪生
副本 secure/sefun/file.lpc(确认没有任何代码 #include 或加载它,
与此档案自己 NOTES.md 之前记录的"重复 sefun 目录"是同一批遗留文件)
——同样的修法作为廉价保险也一并应用了。
修复过程中还发现:assure_file()在物理上定义在log_file()之
后,加上调用它需要一个"先声明后使用"的前置原型(这份档案的编译
器不支持在同一文件里调用尚未出现在源码前面的函数——logind.lpc
文件头部本身就有一长串private void xxx(...)这样的前置声明,是这
份代码库自己的既定写法):第一次只加 assure_file()调用而没加前置
声明,编译报错Undefined function assure_file——按同一惯例在文件
开头加了一行void assure_file(string file);`补上。
- 验证:修复前,用刚重启的驱动新注册一个角色,选择完性别后必现上 述崩溃(原生驱动,非 WASM),角色永远停留在光秃秃的
>。加上修复后 重启驱动,同样的注册流程(英文名/确认/中文名/密码/邮箱/性别)顺利 显示"目前权限:"并正确落地到随机分配的四个起始场景之一(本次是 〖龙门客栈〗);log/runtime(此次修复前的旧崩溃记录仍留在文件里, 但确认 mtime 早于本轮重启,本轮全程没有任何新增内容)和debug.log双双干净。
发现并修复:少林寺方丈(本派拜师环节的关键 NPC)因为一处技能名拼写错误,从存档诞生起就从未真正出现在他自己的房间里
- 症状:走到少林寺法堂想拜师,执事慧觉回答"未得方丈准许,贫僧不 敢擅收俗家弟子"——但走进方丈应该所在的〖方丈精舍书房〗,房间里空 无一人(正常应有的 NPC 完全缺席),
debug.log里能看到一条编译期 的房间物件创建失败信息:
``
*F_SKILL: No such skill (shalin-xinfa)
创建房间中的物体失败, 详见 room_log
``
- 根因:
d/menpai/shaolin/npc/xuanci.lpc(玄慈方丈,少林派第三十 六代掌门方丈)的create()里有
``lpc
set_wugong("shalin-xinfa", 200);
`
——技能名拼成了"shalin",少了一个"o"。system/skill/shaolin/
shaolin-xinfa.lpc(正确拼写)确实存在,证明这是纯粹的拼写笔误,
不是故意改名或缺失内容。set_wugong()对一个不存在的技能名会直接
抛出未捕获的错误,而这一行排在 create_family("少林派", 36, "掌门
方丈")之前——玄慈的整个create()`在能把自己注册为少林派掌门
之前就先崩溃了,导致这个对象连同"少林派"这个门派的掌门归属,从这
份档案诞生起就从未真正被创建过。全档案搜索确认这个拼写错误只出现
这一处。
- 修复:改成正确拼写
set_wugong("shaolin-xinfa", 200);。§9格式化自检通过,3 处格式化盲点检查干净。 - 验证:修复前用刚重启的驱动
goto到方丈的房间,房间描述正常但 玩家列表里没有玄慈;修复后同样的房间,玄慈正常出现,look能看到 完整的"少林派第三十六代掌门方丈「少林寺方丈」玄慈"称号,apprentice xuan ci也能触发他自己的attempt_apprentice()对话逻辑("小兄弟与 本派素无来往,不知此话从何谈起?"——这是他要求申请人必须已经是少 林弟子才会考虑收为徒的既定门规判断,属于正常游戏设计,不是本次要 修的问题)。
观察但未处理(超出"仅修程序 bug"范围)
F_MASTER(system/std/char/master.lpc)从未提供attempt_apprentice()的默认实现:cmds/verb/apprentice.lpc对任何门派师父都会调用ob->attempt_apprentice(me),但基类完全没有这个方法,只有部分师父 NPC(如huijue.lpc/xuanci.lpc/无量剑派的zuo_zimu.lpc)自己实 现了它。对没有实现这个方法的师父(比如大理段氏的fu_sigui.lpc、gao_shengtai.lpc)执行apprentice指令会完全静默——没有任何回应 也没有报错,call_other调用一个对象上不存在的函数在 LPC 里就是这 样静默返回 0。这更像是"这些 NPC 压根没打算走apprentice这条路线 (可能是走剧情任务由代码主动调用recruit_apprentice())"的设计不 一致,而不是一个能明确判断"应该怎么补"的程序 bug——要不要给F_MASTER加一个统一的默认回应,属于会影响全档案所有门派 NPC 行为 的设计决策,如实记录为观察,未做改动。d/zuzhi/yipin_tang/dixia/npc/weishi2.lpc第 20 行set_wugong("ny- bufa", ...)引用了一个全档案都找不到的技能名:搜遍system/skill/没有任何文件匹配"ny-bufa"(system/skill/kongdong/nieyun-bufa.lpc在拼写上有点像,但无法确定"ny"是否就是"nieyun"的缩写,猜测重命名 需要对这个组织的技能设定有更多背景知识才能确认)。这个 NPC 是"一 品堂"底下的普通杀手侍卫(JoinZuzhi加入的是反派组织,不是正统门 派拜师路线),不是任何关键剧情节点,严重程度远低于少林方丈那处— —如实记录为观察,未做改动。- "必须玩够 30 分钟才能保存"的退出门槛,实测会真的删除存档文件: 用第一个测试角色
xajhtwo(已经拜入无量剑派东宗、学会wuliang- jian)故意在 30 分钟内确认quit,data/login/x/、data/user/x/下对应的.o存档文件被系统直接删除(不只是"不保存这次的改动",是 整个账号一并清空,下次用同一个英文名登录会重新走"创建新角色"流 程)——这与 README 里"这是游戏本身的防灌水设计"的说明完全一致,也 与本项目其它同源档案(xo/xo_final)NOTES.md 里记录的同一机制 一致,是刻意为之,不是 bug。
测试内容与结果
- 注册:英文名(只能纯字母,3-12 位)→ 确认 y → 中文名(秦岭/秦 岭三,均一次通过)→ 密码 ×2 → 邮箱 → 性别(m/f 均测试过)→ 顺利落 地到随机四选一起始场景之一。
- 状态查看:
look/score/i在天赋随机分配、门派加入、学会技能 前后均正确刷新。 - 门派——组织路线(拜师):少林寺(修复后)能正常触发掌门对话逻 辑,但被"必须先是本派弟子"的门规正确拒绝(正常设计);大理段氏的
gao_shengtai/fu_sigui因为F_MASTER默认无实现而静默无反应(如 实记录为观察,见上);无量剑派东宗(zuo zimu/左子穆)走完整 流程成功:apprentice zuo zimu→ 掌门带去侧厅→ 讲门规→kneel跪拜 祖师像→ 正式收徒,score正确显示"无量剑派东宗第六代弟子"、师承 "左子穆",还获赠一把随身长剑。 - 门派/技能——快捷路线:全档案没有找到"赠礼 NPC"或
mygift风格的 新手引导任务链(不同于同血脉xo_final的mygiftd.lpc),改用管理 员call <玩家>->set_wugong(...)直接授予技能验证快捷路径确实可行 (call xajhthr->set_wugong("jiben-qinggong",30)成功,skills确 认)。 - 安全陪练/对练(duilian):
duilian gong gjie(无量剑派同门师 兄)——真实交锋但只有轻微划伤,stop指令能随时正常终止对练。 - 持久化:(1)刻意验证了"30 分钟内 quit 会清空存档"的既定设计, 与 README 描述完全一致,见上"观察"小节;(2)正常场景:管理员
call授予技能后显式->save(),直接断开连接(不发quit,模拟真 实网络掉线)等待约 15 秒后用同一账号重连,重新连线返回提示正确 出现,skills显示授予的技能完整保留。 - 管理员账号:
fluffos/Mud@2026登录正常,目前权限:(admin)正确显示;call指令(本档案没有单独的update风格热更新指令,call obj->func(args)承担了同样的诊断/管理功能)执行->set_wugong()/->save()均成功返回预期结果,确认写权限正常。
§7.100 扫描修复(ROOM 基类多余 replace_program())
#define ROOM STD_DIR "room"(STD_DIR = /system/std/,宏名本
身仍是字面量 ROOM,源码里的调用不受拼接影响):删除 439 处多余
的、独立成行的 replace_program(ROOM);(保留 inherit ROOM;),
438 处脚本自动删除;另 1 处在本库房间建造工具
clone/wizard/roommaker.lpc"克隆我所在的房间"命令的字符串拼接模
板里,已同步手动修正。help/wizard/es/help/roommaker 是纯文本帮助
文档,不含该 bug 模式,无需处理。work/data 下未发现额外 .lpc
源文件。修复后全库仅剩 52 处历史遗留的 //-注释掉实例,均确认无
害、未改动。已用 build-debug 驱动干净启动验证(0 个新增编译错误
,端口 40069 正常监听,debug.log 无新增 "cannot replace"/
"cannot bind" 行);未做完整 §10.7 深度游玩测试。
深度功能测试第四轮 / Round-four §10.7 deep test (2026-08-20):真实战斗致死/复活流程
补全上一轮(2026-08-07)从未真正测试过的一环:真实战斗导致角色死亡、
经过判官轮回流程、最终复活重新落地游戏世界的完整循环。全程同时监视
log/debug.log(驱动默认日志)和 log/runtime(这份档案自己的
error_handler() 改写落点,上一轮正是靠查看这个文件才找到注册崩溃
的真正根因,本轮同样全程盯着)。
死亡机制确认(两段式 kee/eff_kee 模型)
读 system/std/char.lpc 的 heart_beat() 确认了这份档案的死亡判定
逻辑:eff_kee/eff_sen/eff_gin 任一 < 0 时直接 die();kee/
sen/gin 任一 < 0 时(存活状态下)先 unconcious(),非存活状态才
die()。这是与本项目其它一些库(如 xyxy2)相同形状的两段式生命值
模型。用管理员 call qxverify->set("eff_kee", -1) 给测试角色注入一次
性负值(临时数值授予,未绕过真实 die()/heart_beat() 代码路径),
接下来完全依赖真实的、未修改的 heart_beat() 心跳循环触发 die()——
测试确认心跳只在角色有活跃在线连接时才会真正跳动(断线角色的心跳不
会推进死亡判定),角色保持连线在场时,等待几秒后角色真实死亡("你死
了","【谣言】秦验证莫名其妙的死了"),移动进入 DEATH_ROOM
(/d/place/death/yellow1,"黄泉路"),is_ghost() 变为 1。
发现并修复:查察司判官 chacha.lpc 的死亡轮回状态机有一条退出分支忘记清理重入守卫,导致携带任何物品死亡的鬼魂永久卡死、再也无法投胎——本轮任务重点排查对象,找到了真实缺陷
- 背景:
d/place/death/npc/chacha.lpc(查察司判官,阎罗大殿里负责 鬼魂轮回叙事的 NPC)用一个共享的临时属性death_stage_active作为 重入守卫,防止玩家重连触发的init()在一条death_stage()call_out链条还在跑的时候又叠加一条新链。这个守卫也同时被d/place/death/npc/mengpo.lpc(孟婆,另一个独立的投胎 NPC)复用同 一个属性名——两者的init()都以if (ob->query_temp( "death_stage_active")) return;开头。 - 症状:
death_stage(object ob, int stage)走到最后一段(stage == 4)时,如果deep_inventory(ob)(鬼魂身上还带着东西)非空,会 提示"判官说道:不过阴间的东西是不能带到阳间的,你先要把你身上的东 西放下来。"然后直接return——但这条分支唯独没有清理death_stage_active守卫,跟同一函数里另一条退出分支(!ob || !present(ob),第 69-72 行)以及mengpo.lpc自己整条链路的每一个退 出点相比,是唯一遗漏的一处。玩家扔掉身上的东西以后,守卫仍然锁死为 1,无论离开房间再回来(重新触发init())还是走到孟婆房间(她的init()检查的是同一个属性名),都会被同一个残留的守卫直接挡在return——永久卡死,两条投胎路线全部失效,只有管理员手动清空这 个临时属性才能救回来。 - 可达性验证(不是纸面假设):这份档案的
die()(system/feature/damage.lpc)本身不会清空鬼魂的随身物品——真正清 空发生在CHAR_D->make_corpse()(system/daemon/chard.lpc)里,只 在活人变鬼魂那一刻执行一次。但孟婆自己的投胎流程会主动往鬼魂身上 塞一碗"孟婆茶"(tea_give()最后一段,tea->move(ob))——茶碗要 靠玩家自己输入drink tea喝掉才会被destruct()清除。也就是说,一 个鬼魂只要先走到孟婆房间领了茶还没喝,就带着这碗茶继续往判官房间走 ,是完全正常、大概率会发生的真实游玩路径(孟婆房间就在必经之路上) ,不是刻意构造的边界情况。 - 实测复现:测试角色
qxverify(秦验证)死亡后按真实路线走 (黄泉路→黄泉路→孟婆房间取茶不喝→鬼门关→酆都城→大殿)到查察司判 官房间,等待完整的五段对话跑完,第五段(stage==4)确认收到"你先 要把你身上的东西放下来"提示;管理员call查询确认query_temp("death_stage_active")此时为1且再也不会归零——离开房 间再走回来(触发init()重试)确认没有任何新消息,验证卡死是真实、 永久的,不是时序错觉。 - 修复:在这条唯一遗漏的分支里补上同一守卫的清理,与函数里另一条 退出分支保持一致:
``lpc
if (sizeof(obs)) {
command("hmm");
tell_object(ob, HIW "判官说道:“不过阴间的东西是不能带到阳间的,你先要把你身上的东西放下来。”\n" NOR);
ob->delete_temp("death_stage_active");
return;
}
`
这是最小、行为一致的修复:清理守卫以后,玩家扔掉东西、离开房间再走
回来会重新触发init()→check_rein()→death_stage()整条链,这次
deep_inventory()`已经是空的,直接走向正常完成分支。
- 修复后验证(全新测试角色
qxverify,重启驱动加载修复后):完全 重复上面的复现步骤,第五段消息出现后立刻用管理员call确认death_stage_active已经变回0(此前是永久卡在1);然后drop tea丢掉孟婆茶,离开大殿再走回来,五段判官对话完整重新播放一 遍,最后一段成功触发ob->reincarnate()并随机传送到三个复活点之一 (本次落地"广场");is_ghost()确认变回0,score/look产出的 内容完全正常,角色成功回到阳间继续游戏。 - 日志核对:整个死亡→轮回卡死→修复→重新死亡→轮回完整走完的全程 (两轮驱动重启,多个测试角色),
log/debug.log稳定保持 392 行、log/runtime稳定保持 3034 行,与本轮测试开始前的基线完全一致, 没有任何新增内容(log/runtime里现存的历史崩溃记录全部是本项目 更早期会话遗留的,mtime 均早于本轮启动)——这个 bug 本身不产生任何 错误签名,纯粹是一次静默的、可复现的状态机遗漏,只能靠真正走一遍死 亡流程并检查每一个退出分支才能发现,符合本轮任务"检查death_stage()每一个退出分支是否都清理了重入守卫"的排查要求。
标准检查清单快速过一遍(预期多数已修,仅确认)
- §7.90(eval-cost):
config.fluffos里maximum evaluation cost已经是30000000(本项目通用的已修正默认值),确认早已修好。 - §7.100(
ROOM基类多余replace_program()):全库grep零处存活 实例(历史修复记录见上,本轮仅确认零回归)。 - §7.111(
standard_trace()对error["object"]为0时崩溃):这 份档案的master.lpc用独立的trace_line()辅助函数封装了这处逻辑, 本来就是obj ? file_name(obj) : "<none>"三元守卫写法(跟已知漏洞 形状不同,但效果等价、本来就安全)——确认不需要修。 - §7.112(死亡/重入
call_out守卫):全库唯二使用death_stage_active标志的两个文件是mengpo.lpc(已确认每条退出 分支都清理干净)和chacha.lpc(本轮发现并修复的那一处遗漏,见上) ——本项任务的重点排查目标,找到了真实缺陷。 - §7.79(
addn()二参数变体):全库grep零匹配,这份档案压根没 有addn()这个调用,确认不适用。 - §7.108(
reconnect()应调用enable_commands()):clone/user/ user.lpc的reconnect()本身不直接调用enable_commands(),但实测 确认重连后look/i/score等指令全部正常工作——原因是add_action绑定(enable_player()里注册的command_hook)挂在角色对象本身而不 是连接上,断线不会清除,logind.lpc的reconnect()只需要exec(user, ob)把新连接接到同一个未销毁的对象上即可恢复指令;本轮 测试中每一次断线重连(多次)指令都立即可用,确认不是 bug,是这份档 案的既有架构设计正常工作。 - §7.30(
mapp(x) ? x : ([])守卫):system/feature/skill.lpc的 五个mapping访问器(query_jibie_map()等)内部本身没有守卫,直接return原始实例变量——但实测一个全新、零技能角色执行skills指令 完全正常显示"你目前并没有学会任何技能。",未崩溃:三处真实调用点 (cmds/comm/skills.lpc、cmds/admin/makedoc.lpc、cmds/comm/ enable.lpc)全部先用sizeof()或mapp()把返回值判空之后才会继续 往下用,且经验证这份驱动上sizeof(0)本身不报错(返回0,不是抛异 常);另外system/std/char/npc.lpc的tell_skills()里my_family["generation"]这种嵌套取值也是安全的,因为只有is_tongmen_of()已经确认双方family字段都是合法mapping之后才会 走到这一步(is_tongmen_of()自己有!mapp()守卫)。全部三个真实调 用路径都实测/代码确认安全,无需改动。 - combatd.lpc 的
bounce除零 bug:全库(system/daemon/ combatd.lpc及其孤立副本system/daemon/daemon/combatd.lpc)grep零处出现bounce这个变量名,这个具体 bug 形状在本档案里不存 在,不适用。
结论
本轮任务明确要求的"真实战斗致死+复活"缺口已补全:找到并修复了一个
真实、可复现、静默的程序缺陷(chacha.lpc死亡轮回状态机的重入守
卫在物品检查分支遗漏清理,导致携带物品死亡的鬼魂永久卡死、两条投胎
路线同时失效),修复经过全新测试角色从死亡到复活的完整实测验证,两
份日志文件全程干净无新增。标准检查清单六项全部确认(一项已修实无需
再动、四项确认不适用/设计正常工作、一项就是本轮的核心发现)。
String corruption !\xee\x93\xa2n cleanup (2026-09-03)
Same convert-era byte corruption as the waiter !”\n / !\n →
! + private-use U+E4E2 + literal n shape. Fixed in: waiter.lpc (nanyang + city/nanyang), huilun.lpc, system/feature/command.lpc.
深度功能测试(§10.7,2026-10-04)— negative range ends
scripts/lpc_fix_negative_ranges.py: 4 line(s) in 1 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-07)— 兵器的 actions 函数指针
set("actions", (: WEAPON_D, "query_action" :));:旧 MudOS 把 (: 字符串, "函数" :) 当作 call_other 函数指针,本驱动把它当作表达式函数指针,求值只得到字符串 "query_action"。没有对应兵器技能时 feature/attack.lpc 直接用兵器的 actions,COMBAT_D 拿到的不是 mapping,reset_action() 后仍不是,记一条 bad action 就不出招——拿着这些兵器打不出攻击。改成别的兵器早已用的 (: call_other, WEAPON_D, "query_action" :)(KB 04 §6.10):d/menpai/wuliang/west/npc/obj/flower.h、d/menpai/wuliang/backup/npc/obj/flower.h。改动的文件在 HEAD 与工作树分别加载(新进程),无回退。
深度功能测试(§10.7,2026-10-07)— 编译警告整理
scripts/lpc_warnings.py xajhxo --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 245 个 .lpc 和 15 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):222 -> 222 通过,无回退;改过的头文件的 26 个包含者 24 -> 24 通过。
手改两处,把只在条件编译里用到的局部变量声明挪进同一个 #ifdef:system/daemon/logind.lpc get_id() 的 my_ip(ALLOW_MULTI_LOGIN),system/daemon/combatd.lpc 里与全局 debug 同名的局部 debug(DEBUG)。启动时警告 31 -> 0;新进程登录(新人物确认)正常。
深度功能测试(§10.7,2026-10-08)— .lpc 后缀的定宽切片
.c 改名 .lpc 时只改了字符串没改切片宽度:f[<2..] == ".lpc" 拿两个字符比四个字符,永远不成立(!= 永远成立),按后缀筛文件、去后缀的代码因此从不运行或截成 x.l(KB 06 §7.238)。scripts/lpc_lpc_suffix_slice.py --apply 改了 2 处比较、2 处去后缀(如 system/daemon/helpd.lpc);改动的文件 HEAD 与工作树分别加载(新进程),无回退。
- 2026-10-10:编译警告第二轮(按驱动报告逐条修):按位 & 改逻辑运算 0 处,返回类型/原型 1 处,NPC+F_UNIQUE 的 init() 0 个,带初始化的未用局部变量 4 个,只在 #if 分支用的声明移进分支 3 个文件;改动 7 个文件,HEAD 与工作树各载入一次无回退(7 objects; HEAD PASS 6, tree PASS 6; regress 0; newly loading 0),这些文件的警告 7 -> 0。
深度功能测试(§10.7,2026-10-11):“无副作用表达式”警告复查
system/std/sserver.lpc:else 0;改为return 0;(行为不变)。载入无回退。
编译警告收尾(2026-10-13)
手工清零(scripts/toolkit_fix.sh 去掉 clone/wear/finger/finger.lpc 一处多余继承);改动的 6 个对象在 HEAD 与工作树各载入一次,regress 0。
clone/user/templet.lpc(存档autoload转换器)继承F_DBASE后又自己声明dbase,存盘写出两条dbase(KB 06 §7.233,同 xo);删去自己的那个。- 左子穆
d/menpai/wuliang/npc/zuo.lpc声明了一份refuse_msg(“现在没什么事做,你先下去练功吧”,是任务 NPC 的拒绝语),可从没读过它,与npc.lpc的辈分拒绝语同名;改名zuo_refuse_msg,仍不接入,行为不变。 cmds/comm/who.lpc外层没用的your_id(内层另有声明)、d/zuzhi/yipin_tang/peacock/xuanwu_tang/npc/heiyiren.lpc没用的last_time删去;cmds/wiz/allow.lpc与globals.h相同的ALLOW_D删去。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。3 个文件 3 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断);x op= 浮点先把浮点转成整数再运算(系数小于 1 时 x 直接变 0),改为x = to_int(x op ...):/cmds/skill/xiulian.lpc(jilei_gain +=)、/system/daemon/combatd.lpc(power +=)、/system/daemon/daemon/combatd.lpc(power +=)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。
- 2026-10-14:2 个文件因
Illegal to inherit after defining global variables整份不编译:FluffOS 要求所有inherit写在第一个全局变量之前,MudOS 不管。scripts/lpc_fix_inherit_order.py把写在inherit之前的全局变量(以及声明全局变量的头文件的#include)挪到最后一个inherit之后(KB 04 §6.3)。
- 2026-10-14:2 个文件的函数声明为
int(或void)却返回字符串、对象(int query_seal() { return "…"; }),旧驱动照样把值交给调用者;改为实际返回的类型(scripts/lpc_fix_void_return.py)(KB 04 §6.1、§6.3)。新编译出来的文件露出的警告一并清掉(删去未用局部变量,有返回值函数里的空return;改为return 0;)。新进程比对 HEAD 与工作树:4 个对象,新增载入 2,回退 0。
转码吃掉的引号(2026-10-15,KB 03)
原档在右引号前有一个孤立的 GBK 前导字节,转 UTF-8 时连引号一起丢了(如 return name + HIM "发作身亡了;),字符串一直延续到后面,文件编译失败:/d/menpai/shaolin/npc/master。补回引号(scripts/lpc_fix_eaten_quote.py)。
补(同日):这处修改当时漏提交了(提交时只取了 .lpc),现补上;这个头文件目前没有文件引用。
family 映射(再补)(2026-10-15,KB 05 §7.30)
mapping fam; fam = (mapping)ob->query("family");(先声明后赋值)和泥潭式的 query("family", me) 同样直接取下标:无门派的新人拜师(attempt_apprentice/recognize_apprentice)、NPC 的 greeting、房间的 valid_leave、ask_me 等报 Value being indexed is zero。3 处补上 if (!mapp(fam)) fam = ([ ]);(刚 recruit_apprentice() 之后的除外)。
NPC 带着本库没有的物品,载入失败(2026-10-15,KB 06 §7.73)
11 个文件、20 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/city/lanzhou/clone/obj/cloth1、/d/menpai/wuliang/obj/cloth、/d/city/capital/obj/wuyun_xue)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 8 个,改后 8 个(0 个原来载入不了),没有退步;其余 4 个另有原因(多为 set_skill() 了本库没有的武功)。
通向不存在房间的出口,按对面房间的回头出口改正(2026-10-18,KB 06 §7.257)
房间出口 "west": __DIR__"xxx" 指向的文件本库没有,玩家走过去就被卡住(笔误、改过名的房间,或者没带进档案的编号房间)。对面的房间存在、并且它相反方向的出口正好指回这个房间,说明出口原本就是通向它:/d/zuzhi/yipin_tang/peacock/xuanwu_tang/midao 的 down:yandong1.lpc → yandong。改动后每个出口都能解析到本库的文件(scripts/lpc_fix_dead_exits.py;目标在运行时本来就能载入的库——虚拟房间——不动)。
调用了父类没有的函数的对象(2026-10-19,KB 06 §7.259)
::create();、::init();、return ::buy_object(who, item); 这类调用写的是父类里有这个函数的版本;本库的父类没有,旧驱动把它当空操作(返回 0),现在的驱动在编译时就报 Unable to find the inherited function,整个对象载入不了。单独成句的调用删掉,return ::f(...) 改成 return 0;(void 函数里是 return;),与旧驱动的行为一致。共 1 处、1 个文件:::valid_teach()(1 处)。只改了出错原因全是这一类的文件;还带着别的错误(多半是父类本身没了)的文件没动,等父类补回后再看。(scripts/lpc_fix_dangling_super.py)
全局头文件丢了 1 个宏(2026-10-19,KB 04 §6.16)
/include/globals.h 里没有定义 YIPIN_D,用到它们的物件(名单里 2 个)编译时报 Undefined variable/Undefined function,整个物件载入不了(编译器在第一个未定义的名字上停下,后面的错误和警告都看不到)。同系的库在各自的头文件里都有这几行:照其中和本库共有文件最多的那个库,原样补到 /include/globals.h 最后一个 #define 之后,宏体里出现的路径在本库里都存在。补完后名单里有 2 个载入,其余还差别的东西。(scripts/lpc_restore_defines.py)
手工收尾(2026-10-19,KB 04 §6.10)
system/std/char/questnpc.lpc、d/nanyang/npc/guo.lpc:string *refuse_msg盖掉 NPC 基类system/std/char/npc.lpc里同名的nosave string *refuse_msg(Redeclaration of global variable 'refuse_msg'),驱动给了它们第二个变量,基类的函数和它们自己的函数从不共用;改名questnpc_refuse_msg、guo_refuse_msg(scripts/lpc_rename_ident.py),行为不变。
逐个与 HEAD 对比载入,没有退步。
补回丢失的标点(2026-10-19,KB 03 §4.6)
转换时丢掉的分号、逗号、引号或文件末尾的 },共 6 个文件,修前 HEAD 载入失败。别的库里同路径的完好副本只比这里少几个标点,按它补回(括号里是那个库):/d/city/capital/dxgs_yuan1.lpc(xajh2)、/d/menpai/wuliang/npc/obj/xing.lpc(xajh2)、/system/condition/deadlife.lpc(xajh2)、/system/condition/deadlife.lpc(xo_final)。文件末尾少了 }(最后一个或两个字节丢了),补上:/d/city/capital/obj/food/gourou_baozi.lpc(2 个)、/d/place/xkdao/npc/tu.lpc。修后与 HEAD 逐个对比载入:这 6 个物件全部载入,没有退步(scripts/lpc_restore_from_twin.py、lpc_fix_eaten_quote.py --comma、lpc_fix_eof_braces.py)。
旧驱动的类型宽松:缺省参数、无类型形参、数组初始化、class 变量(2026-10-19,KB 04 §6.19、§6.20)
原来的 MudOS 把调用少传的参数补 0、把没写类型的形参当 mixed、不检查变量的声明类型与初始值,也允许变量叫 class;这个驱动在编译时全部拒绝,整个物件载入失败。修了 2 个文件:/d/zuzhi/yipin_tang/peacock/xuanwu_tang/develope/boat.lpc、/d/zuzhi/yipin_tang/xuanwu_tang/develope/boat.lpc(scripts/lpc_round_files.py 的修复轮:调用处补写 0、无类型形参补 mixed、string X = ({...}) 补星号、void 函数的返回值,以及 class 变量改名 pick_class)。逐个文件在 HEAD 和改后载入对比:这些文件里 0 个原来载入失败、现在载入,没有退步。
无量剑派掌门:wl-shenfa 改成 wuliang-shenfa(2026-10-19,KB 06 §7.258)
system/menpai/wuliang/ 里 zuo_zimu.lpc、qu_lianxing.lpc、rong_ziju.lpc 授的 wl-shenfa 本库没有这个技能(只有 system/skill/wuliang/wuliang-shenfa.lpc,同目录 shang_yuqing.lpc 和 wuliang_d.lpc 写的都是它),create() 里 F_SKILL: No such skill 把物件中止。改成 wuliang-shenfa 后 zuo_zimu.lpc 载入(修前失败)。qu_lianxing.lpc、rong_ziju.lpc、shang_yuqing.lpc 往后一步因 parry(本库没有这个技能文件)失败:system/feature/skill.lpc 的 set_wugong() 对没有文件的技能 error()。set_wugong() 开头加 if (!valid_skill(skill)) return 0;(同文件的 set_xiuwei() 早已这样处理),物件照常载入,只缺这一项技能(KB 06 §7.256)。授了缺失技能的物件(活目录里)共 9 个,修后在 system/menpai/ 和这些物件上对比 HEAD 与工作树:载入 29 → 34(zuo_zimu、qu_lianxing、rong_ziju、shang_yuqing 和 weishi2),没有回归,没有警告;system/menpai/biaoju/ke.lpc 仍失败(另一个原因,没有动)。
if (x = 常量):条件里写成赋值的比较(2026-10-20,KB 06 §7.290)
if (arg = "button" || arg = "按钮")、if (!arg || arg = "") return 0;、else if (check = 0 && tempB != ""):把 == 写成了 =,条件的值就是那个常量,分支对所有非零数和任何字符串(连 "" 都算)永远成立、对 0 永远不成立,变量还被覆盖;驱动对此没有任何警告。每一处 = 改成 ==(scripts/lpc_fix_cond_assign.py,共 1 处)。改动:/d/city/yangzhou/shufeng_guan.lpc(1 处)。
tail 指令:tail(file) 驱动里没有(2026-10-20,KB 06 §7.291)
cmds/*/tail.lpc 调用 MudOS 的 tail() efun,FluffOS 没有,指令编译不过。模拟 efun 文件里补了 tail(file)(Discworld 家族的兼容版:文件最后约 20 行,从完整的一行开始,存在返回 1;scripts/lpc_add_sefun.py tail)。
登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)
驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/secure/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)。改动:/system/feature/more.lpc。
巫师命令不带参数时对 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() 之后的 2 个文件补了原型 int help(object me);,否则编译报 Undefined function help)。改动(2 个文件,括号里是接住 0 的 efun):/cmds/admin/changebody.lpc(present)、/cmds/admin/replace.lpc(get_dir)。加载检查 HEAD 与工作树对照,没有退步。
注册到不存在函数的 add_action,第二轮:宏路径父类和静态解析不了的对象(2026-10-21,KB 06 §7.349)
第一轮的静态解析读不了经宏路径继承父类的对象(#define ROOM STD_DIR "room/room" 这类定义的正文以另一个宏开头)和它解析不了的继承,这些对象当作 UNSURE 没有扫;第二轮修了解析器,重扫并对这些对象在运行时问 function_exists()。add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 17 条注册(8 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/city/yangzhou/yuelao_ci.lpc(do_divorce)、/d/place/xkdao/npc/haidao.lpc(do_kill)、/d/place/xkdao/npc/tu.lpc(do_kill)、/d/zuzhi/yipin_tang/peacock/xuanwu_tang/xuanya1.lpc(do_droop、do_pull)、/d/zuzhi/yipin_tang/xuanwu_tang/xuanya1.lpc(do_droop、do_pull)、/system/std/room/hockshop.lpc(DoBuy、DoList、DoPawn、DoPawnlist等)、/system/std/room/kezhan.lpc(do_check、do_load)、/system/std/room/shop.lpc(do_list)。最多的函数名:do_kill 2 处、do_droop 2 处、do_pull 2 处、do_divorce 1 处、DoBuy 1 处。
(: 函数 :) 指针指向对象自己没有定义的函数(2026-10-22,KB 06 §7.355)
item_desc / inquiry / chat_msg_combat 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:编译不报错,用到时驱动报 *Undefined lfun pointer called: 函数名(look 某物、ask NPC about 话题 直接出错,战斗 / 闲聊里的指针在 NPC 的 chat() 里出错,这一拍心跳的后半段被跳过)。scripts/lpc_scan_callout_targets.py --kinds=lfun。 还剩 4 个函数没有补(inquiry 4):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/d/menpai/shaolin/npc/xuwen.lpc:ask_armor2、/d/menpai/shaolin/npc/xuwen.lpc:ask_armor3、/d/menpai/shaolin/npc2/xuwen.lpc:ask_armor2、/d/menpai/shaolin/npc2/xuwen.lpc:ask_armor3。
不带前导斜杠的物品路径:缺失物品的 new() / carry_object() 调用链加了保护(2026-10-22,KB 06 §7.73 补充)
carry_object("d/city/obj/jiasha")->wear(); 这类路径不带前导斜杠的写法在这个驱动里按根目录解析(new("d/city/obj/changjian") 能加载 /d/city/obj/changjian),所以和带斜杠的一样:文件不在压缩包里时 new() 返回 0,->wear() 在 0 上报 Bad argument 1 to EFUN call_other(),整个 NPC 加载不了。此前的保护工具把这种写法当作「相对当前目录,没法判断」跳过了。1 个文件里 1 处(obj/cloth5)改成带保护的块(scripts/lpc_guard_carry_object.py)。 对这 1 个改动文件做加载检查(HEAD 与工作区对比):HEAD 能加载 1 个,现在 1 个(新增可加载 0 个,回归 0 个)。
(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)
inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 2 个文件里 4 行只含这个指针的行("僧鞋": (: ask_armor2 :), "绑腿": (: ask_armor3 :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。
汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 2 处(2026-10-22,KB 06 §7.360)
原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字);各派掌门的辈分判断(name[0..1] == "澄")永远不成立。
本库 2 处(2 个文件):拼名字的赋值 1 处、与汉字常量比较 1 处。例:name[2..3] → name[1..1](/d/menpai/shaolin/npc/huijue.lpc:126);name[0..1] == "澄" → name[0..0] == "澄"(/d/menpai/shaolin/npc/xuanci.lpc:100)。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,不要重跑。
长度检查按字节写,旁边的提示按汉字写:7 处(2026-10-22,KB 06 §7.362)
老档的 strlen() 数字节(一个汉字 2 字节),转成 UTF-8 后它数字符:提示写「必须是 2 到 6 个中文字」的检查还是 strlen(name) < 4 || strlen(name) > 12,于是两三个字的房名、帮会名、群名、聊天室名、称号被提示自己说允许的那句话拒绝,上限却是提示的两倍;留言板的标题检查 i < 4 || i > 24 || i % 2 还会拒绝所有奇数个字的标题(post 你好吗)。本库 7 处(4 个文件):if (strlen(msg) > 20)(/clone/misc/chatroom.lpc:69);if (strlen(msg) > 30)(/clone/misc/chatroom.lpc:75)。scripts/lpc_fix_strlen_gate.py 以提示为准:条件里每个 strlen 门槛都恰是提示所说数字的两倍才折半,存长度的变量(i = strlen(arg);)的门槛和 || i % 2 奇偶项、其后循环的 i % 2 == 0 && 一并处理;提示里有英文 / 字母 / 字节的不动。证明(scripts/lpcc_eval.py,bxsj 的留言板,求值对象继承 NPC 并重写 edit() 记录调用,command("post 标题")):HEAD 拒绝「你好」和「你好吗」、放行 23 个字的标题;改后接受前两个、拒绝第三个;shenmo 的 named.lpc invalid_new_name("张三"):HEAD 回 名字只能是2-4个中文字。,改后回 0。抽样 24 + 15 个库的 HEAD / 工作树加载对比 0 退步。
按字节写的汉字循环、茶馆猜诗和同盟频道标题(2026-10-23,KB 06 §7.363)
老档按字节做「一个汉字」:while (i--) if (i % 2 == 0 && !is_chinese(name[i..<0])) 只检查偶数下标,奇数位上的拉丁字母(张a)会过;茶馆猜诗 mixup() 用 strlen(str)/2 再 j=j+j、str[k..k+1] 交换两字节;泥潭系 channeld 的 switch (strlen(fname)) case 4/6/8/10 和 strlen(family)==4/6 按字节长度给同盟 / 门派名加空格。本驱动的 strlen() 和区间都按字符算。scripts/lpc_fix_byte_char.py:去掉奇偶项、把区间改成 name[i..i],按字符重写 mixup() 并把 strlen(first)>=14 折成 7,频道标题的 case / 门派长度改成字符数(对照 aoxiangtianji)。本库 汉字循环 / is_chinese 区间 4 个文件。证明(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(一格两个空格)。本库 2 处(1 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。
无参数时命令对 0 的调用(2026-10-27,KB 06 §7.336 Also)
巫师/管理员命令在通过权限门之后仍会把 0 交给 sscanf / read_file / find_living 等。lpc_fix_bare_arg.py 在首次使用参数前加上 if (!arg)(或 help(me) / GetHelp())。本库本批改动见该提交。
estate ROOM_HEAD 宏引号 / #inherit / dir / retrun(2026-10-27,KB 04 §6.25 Also)
system/daemon/estate 的 ROOM_HEAD/EXIT_STRING 宏里裸双引号打断 #define,模板写成 #inherit AREA,CreateRoom 用未定义的 dir,AddItem 写 retrun 且参数 class Location 本库无此类型。改为转义引号、inherit AREA、out_dir、return、mixed location。证明:HEAD 失败;改后加载。
深度功能测试(2026-10-27,§6.25 mooncake2 do_write 截断)
u/xli/mooncake2 同 xo:do_write 截断。用本库 mooncake 覆盖。证明:HEAD 失败;改后加载。
深度功能测试(2026-10-27,§6.25 tell_object 字符串目标)
xinfang/chatroom:tell_object("ob", …)→tell_object(ob, …)。
深度功能测试(2026-10-27,§6.25 one-arg message_vision)
message_vision("…") 补第二参(me / TP / this_player())。
深度功能测试(2026-10-27,§6.10 未知 pragma save_binary/optimize)
删除本库中 FluffOS 不认的 #pragma save_binary / #pragma optimize… 行。