info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
游戏登录画面自称"虚拟人生",属于"XO mudlib"引擎家族(源自 TMI-2/ES2,由 Falcon 于1996年重写),与本项目另收录的《笑傲江湖迷你版》《XO最终版1.2》同源但独立演化、开站更晚的一个站点快照(站内公告显示自2003年4月1日开站);虽然打着《笑傲江湖》的旗号,继承自共同 XO 世界模板的门派体系其实是金庸多部小说的大杂烩——《倚天屠龙记》里的少林、武当、峨嵋、昆仑、崆峒、华山六大派,与《天龙八部》大理段氏皇宫、神农帮、无量剑派并存,此外还有丐帮及其对立的情报机构"一品堂";角色成长分拳脚、轻功、剑术、刀法等相互独立的修为体系。新建角色对同一 IP 地址限流以防刷屏,退出游戏(quit)需先玩到10点经验才能存档,均为刻意的反滥用设计而非缺陷。
English
Branded at login as "Virtual Life," this is a member of the "XO mudlib" engine family (descended from TMI-2/ES2, rewritten by "Falcon" in 1996) and a later, independently-evolved snapshot of the same bloodline as this collection's Xiao Ao Jiang Hu XO and XO Final 1.2 — in-game notices show it opened April 1, 2003. Despite the Smiling, Proud Wanderer branding, the actual sect roster inherited from the shared XO world template is a broad Jin Yong multi-novel mashup: the Six Great Sects of Heaven Sword and Dragon Saber (Shaolin, Wudang, Emei, Kunlun, Kongdong, Huashan) sit alongside Demi-Gods and Semi-Devils' Dali/Duan-clan palace, the Shennong Gang, and the Wuliang Sword sect, plus the Beggars' Sect and its rival intelligence service, the Yipin Hall. Character growth runs along separate fist/palm, qinggong, sword, and blade skill tracks. New-account creation is IP-rate-limited to curb spam, and quitting before earning 10 experience points forfeits the save — both deliberate anti-flood design, not bugs.
README
内容亮点
- 整体偏正统门派养成向武侠 MUD,拳脚、轻功、剑术、刀法等修为体系 齐全,适合喜欢门派归属感与传统武侠数值成长的玩家。
- 新角色的新手村场景在本存档中缺失,系统会优雅地把角色放到 一个空白过渡地带而非报错崩溃,不影响其余指令正常使用。
在线试玩
https://mudlibs.fluffos.info/xajh2/
管理员账号 / Admin account
- ID:
fluffos - 密码 / Password:
Mud@2026 - 显示名 / Display name: 浮浮
- 权限 / Level:
(admin)—— securitydwiz_levels中的最高级,通过secure/etc/wizlist授予(fluffos (admin),启动时读入)。巫师身份 还使其免除"初次需玩满10分钟才能存档"的新手退出限制。
警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。
本地运行
cd libs/xajh2
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40068。
NOTES · 移植与修复记录
xajh2 — 笑傲江湖 (XO/TMI-2/ES2 lineage, NOT the nitan family)
Archive: 笑傲江湖II.rar. Port: 40068. Status: done (boots clean, full
registration with a real Chinese name works end-to-end, and post-login
commands look/score both produce real output).
Lineage check (done first, per instructions) — related to xo/xo_final, NOT nitan
Despite sharing the "笑傲江湖" (Laughing in the Wind) title with nitan6
(archive #22), this is a completely different codebase — confirmed by
evidence, not assumed either way:
- Raw archive layout:
lib/secure/daemon/{master,sefun,securityd}.c,lib/system/daemon/*.c,config.xo,startxo.bat, a bundled Cygwin driver (cygwin1.dll,driver.exe.stackdump) — this is thesecure/daemon/path convention used byxo(archive #28, 笑傲江湖迷你版) andxo_final(archive #29, xo最终版1.2), not theadm/obj/+adm/kernel/simul_efun/layout of the nitan family. secure/daemon/master.c's header comment: *"Original from TMI-2 and ES2, Rewritten by Falcon 96-10-02"* and itsconnect()references *"The XO mudlib www server"* — identical branding lineage toxo/xo_final.md5sumagainst both confirms this is a distinct, non-identical snapshot (not a duplicate of either):master.c/sefun.c/chinesed.c/logind.call differ byte-for-byte from bothxoandxo_final, while sharing the same directory layout and header style. File counts also place it as its own build: 5079 raw / 4344.lpc(betweenxo's 1395 andxo_final's ~7174).- Architecturally:
secure/daemon/sefun.lpcdefines no globalset/query/deleteat all — every object gets real, localset/query/deletemethods fromsystem/feature/base/dbase.lpc(inherit F_TREEMAP, genuine per-objectdbasemapping). This is the architecturally-correct pattern (same asxo/rzrmud/xkx2001), not the nitan-family bare-simul_efun-dbase bug (AGENTS.md §15) — confirmed by readingdbase.lpc's actual implementation, not inferred from the title. - The in-game banner text itself says *"笑傲江湖从二⊙⊙三年四月一日以来已经 有...人次登录"* ("since April 1, 2003") — a later snapshot than
xo(1998-99 era) orxo_final, consistent with this being a distinct, later "XO mudlib" build, plausibly the "II" the archive name refers to.
Conclusion: same lineage family as xo/xo_final (TMI-2/ES2/Falcon
"XO mudlib"), a distinct build/snapshot — not a duplicate, not nitan-family.
xo's §4 fix was ported directly (proven fix, same file shape); the §15/
§15a-c/§15v nitan-specific fixes do not apply here at all.
What this is (per the archive's own README, 说明.txt)
Actual game name is 笑傲江湖 (config.xo's name field says "笑傲江湖
迷你版" — a stale copy-paste leftover from the mini-edition config, not
this build's real name, same class of leftover documented for
dtsl2 in TODO.md). Per 说明.txt: official address
xo.27net.net:4000, default wizard account locker/000000 (present in
secure/etc/wizlist). Used the archive's own working title
"笑傲江湖II" for the slug/config name since that's what distinguishes it
from xo/xo_final in this project's naming.
Fixes applied (with why)
1. AGENTS.md §15h — is_chinese/IsLegalName GBK byte-range bug
(proactive, confirmed present here too):
- secure/sefun/chinese.lpc's is_chinese(str):
strlen(str)>=2 && str[0]>160 && str[0]<255 (GBK lead-byte check) →
strlen(str)>=1 && str[0]>=0x4e00 && str[0]<=0x9fff (CJK codepoint
range, UTF-8 native).
- system/daemon/band.lpc's IsLegalName(name): length bound
i<4 || i>10 (4-10 *bytes* = 2-5 GBK chars, matching the message
"只能是 2 到 5 个中文字") → i<2 || i>5 (2-5 *characters*); and the
per-character loop's i % 2 == 0 && !is_chinese(name[i..<0]) (byte
lead-position gate) → dropped the i % 2 == 0 && entirely (every
UTF-8 index is already one full character).
- Verified end-to-end: sent the real Chinese name 秦风 through
registration and it was accepted, advancing to the password prompt —
not just "reached the prompt".
2. AGENTS.md §4 — master.lpc lazy-security-daemon reentrancy guard
(ported directly from xo's proven fix, same file shape —
secure/daemon/master.lpc's valid_write/valid_read already wrapped
load_object(SECURITY_D) in catch() but had no re-entrancy guard and
no find_object() fast-path check first): added
private nosave int loading_security_d; and the same
find_object-first / guarded-load / degrade-to-default-on-reentry shape
used in libs/xo/work/secure/daemon/master.lpc.
3. New bug (not previously cataloged): combatd.lpc's switch/case with
every real case commented out, leaving a bare default: — this
driver requires at least one real case statement in a switch
(error: need case statements in switch/case, not just default:).
Since every branch except default was already dead code, replaced the
whole switch with the default branch's statement directly (no
restored content, just removed the now-meaningless switch wrapper).
This is a preloaded daemon (combatd is in etc/preload), so this
was a genuine boot-blocking compile error, not a dormant one.
4. New bug (not previously cataloged, high-impact): this driver build
has __OLD_ED__ defined, so the "new" ed_start/ed_cmd/
query_ed_mode efuns this codebase's editor feature was written
against simply don't exist at all (core.spec's #ifdef OLD_ED /
#else split — only one side is ever compiled in). This broke TWO
files:
- system/feature/user/editor.lpc (the edit()/in-game-editor
feature, inherited straight into /clone/user/user.lpc, the
player body class itself) — failed to compile at all, which
silently made make_body() return a broken/uncompilable object
during registration with zero visible error (same failure shape
as AGENTS.md §15b's "player-body class object itself failing to
compile", new root cause). Fixed by rewriting edit() to call the
OLD_ED ed(file, exit_fn_name, restricted) efun instead, with a new
editor_exit_fn() callback replacing the old manual
query_ed_mode()==-1 completion-detection. Confirmed via
~/src/fluffos/src/comm.cc that under __OLD_ED__ the driver's own
C-level input loop (if (ip->ed_buffer) { ed_cmd(...); goto exit; })
intercepts every line directly while editing, bypassing
input_to()/process_input()/write_prompt() entirely — so the
old manual per-keystroke ed_cmd() forwarding in process_input()
was unreachable dead code under this driver, not something to
reimplement; simplified to a harmless passthrough.
- system/feature/user/message.lpc's write_prompt() had the same
query_ed_mode() dependency for customizing the in-editor prompt —
same root cause, same fix rationale (dead code under __OLD_ED__,
since print_prompt() in comm.cc also short-circuits straight to
ip->prompt for ip->ed_buffer-active connections, skipping
APPLY_WRITE_PROMPT). Removed the branch.
- This is likely to recur on other libs — worth a new AGENTS.md
catalog entry (§15af-shaped): any lib using ed_start/ed_cmd/
query_ed_mode needs this same fix, and the tell is a compile error
in whatever file is inherited into the player body class, which
then manifests as registration silently failing right after
character creation with no error shown anywhere.
5. New bug (not previously cataloged, the hardest one to diagnose in
this pass): the /log/nosave/ directory was entirely missing from the
shipped archive (classic "RAR/zip drops empty dirs", same class as
several prior libs) — used by log_file() targets like
PPL_USAGE_LOG/WIZ_USAGE_LOG/COMMAND_LIMIT_LOG (all
"nosave/..."-prefixed in include/globals.h). An uncaught
write_file() error to that missing path, inside
secure/sefun/file.lpc's log_file() simul_efun, silently aborted the
entire get_gender() → log_login() → init_new_player() →
enter_world() call chain right after gender selection — with zero
visible error anywhere: not in debug.log, not shown to the
connecting player. Bisected with temporary efun::write() checkpoints
(removed after diagnosis, per AGENTS.md's established §8c/§15d
technique) down to the exact statement, then discovered the *actual*
error WAS being logged — just to the mudlib's own
/log/runtime/secure file (via master.lpc's custom error_handler()
apply), not debug.log. It was never shown to the player because
error_handler() only calls message("error", ...) when
this_player() is truthy, and at that specific point in the
pre-exec() registration flow it apparently wasn't. New diagnostic
lesson, worth cataloging: when a registration/input_to flow silently
stalls at one exact step with *nothing* in debug.log and nothing
shown to the player, check the mudlib's own log/runtime/* and
log/catch files (if it has a custom error_handler() master apply)
before assuming there's no error at all — the mudlib's own error
logging can be strictly more complete than what reaches the terminal.
Fix: created work/log/nosave/ (mkdir), and wrapped
log_file()'s write_file() call in catch() as defense-in-depth
(§15e-style graceful degradation — a failed bookkeeping log write
should never be allowed to abort character creation).
6. §15i-shaped bug (comment eats the following code), recurring in two
sibling files: system/std/char/task.lpc and
system/std/char/special_quest.lpc both had
// 现在先共用一个quest库 if ( random( class_score ) < 50 ) — the
Chinese comment and the real if statement are on the *same physical
line*, so // swallows the entire if, leaving a dangling
else if/else chain with no matching if (error: syntax error,
unexpected L_ELSE). Found via the exact same distinctive comment text
in both files (one shared bug, not two) — split the comment onto its
own line in both.
7. clone/user/immortal.lpc: string array arrCmd = explode(cmd,";")
— TYPE array NAME is not valid array-declaration syntax on this
driver (distinct from AGENTS.md §15f's *bare* array — this is a
*different* two-word "TYPE array" spelling, presumably a synonym this
codebase's original target driver accepted). Fixed to string *arrCmd.
This is the wizard-only multi-command (;-separated) batching feature
in the wizard player-body class.
8. system/daemon/poisond.lpc: called set()/restore()
(F_SAVE/F_DBASE methods) but never inherited either — a genuine
pre-existing omission (every sibling daemon, e.g. emoted.lpc, does
inherit F_SAVE;). Added inherit F_SAVE;. Not fully fixed even
after this — lpcc still reports a (silent, no explicit error text)
failure to load, most likely restore_object() erroring on
nonexistent save data at create() time. Not pursued further:
poisond is not in etc/preload and nothing else in the lib
call_others into it — confirmed dead/unreachable code, so this
doesn't affect boot or the verified registration/gameplay path.
Confirmed NOT needed (checked by reading the actual source)
- §7
get_root_uid()/get_bb_uid(): already implemented inmaster.lpc, returningROOT_UID/BACKBONE_UID. - §15g case-sensitivity
#include <Action.h>: the exact same<Action.h>(capital A) include that was an *active* bug inxo(archive #28) exists here too, insystem/feature/char/command.lpc— but it's already commented out (//#include <Action.h>) in this build, so it's inert, not an active bug. (Theclass Actionstruct it would have defined is separately, genuinely missing from this whole archive — see the lpcc-sweep section below.) - §15n custom securityd blocking driver compile-time loads:
secure/daemon/securityd.lpc'svalid_readalready hasif (!euid) return 1;(explicit allow when the caller's euid is unset) *before* the exclude-list check — this alone sidesteps the whole bug class (a never-preloaded object's first lazy compile, with the loading object's euid not yet set, would otherwise hit the exclude list and get denied). Confirmed by reading the source, not by hitting the crash. - §15o
master.lpc get_include_path(): not implemented, butconvert_lib.sh's automatic local-angle-include→quotes rewrite (13 instances converted) already resolved every actual<local.h>-next-to- its-user case in this lib before the first boot attempt — nothing left needing this fix. - §15p DNS/intermud preload exclusion:
etc/preloadnever contained a DNS/intermud/mudlist daemon in the first place (only/system/daemon/network/smtp, whosecreate()only sets a socket type — no eagereventCreateSocket()call, confirmed by reading it). Nothing to exclude. - §15v
LONELY_IMPROVED-style efun gating: not applicable (not nitan-family). - §15w
log_error()funneling warnings to players:master.lpc's customerror_handler()only shows a message whenthis_player()is truthy, and only for genuine runtime errors (not compile warnings) — no evidence of spurious warning-spam during a clean boot. - §15x hardcoded
MUD_PORTmismatch:master.lpc'sconnect(port)only special-casesport==4004(an alternate httpd port, unconfigured here) and falls through to normal login for any other port — our assigned 40068 hits the normal path correctly. - §15z
nosave/staticcompatibility shim collision: no#define nosave static/#define protected staticshim found anywhere. - §15ae
private nomaskcommand-hook bug — the CRITICAL standing-policy check:system/feature/char/command.lpc'scommand_hook(string arg)is declarednomask int command_hook(...)— notprivate. Verified by reading the source *before* testing, and confirmed empirically:score(a real post-login command) produces a full, correct character-sheet output, not the generic fail message. - §15/§15a-c/§15v (nitan-family dbase architecture bug): definitively not applicable — see the Lineage section above.
- **§8f (
TYPE * a, bcomma-list array-modifier bug, Dead-Souls-lineage specific)**: 49 candidate lines found via the same grep shape, spot- checked a sample — all are genuinely-intentional mixed array/scalar declarations (e.g.string *id, my_id;wheremy_idreally is used as a scalar elsewhere), not the Dead Souls bug. Not touched. - §15ac bare
SAVE_EXTENSION: one hit (include/net/ftpdsupp.h), butftpdis commented out ofmaster.lpc'sconnect()and never loaded — inert, not fixed.
New bug classes found here, worth AGENTS.md entries (flagged for the
main session to catalog)
1. A switch with every real case label commented out, leaving a bare
default:, is a hard compile error on this driver (combatd.lpc).
2. __OLD_ED__ vs the "new" ed_start/ed_cmd/query_ed_mode API —
this driver build only has ONE of the two editor APIs compiled in
(__OLD_ED__ is defined), so any lib written against the other one
fails to compile wherever it's used; if that's inherited into the
player body class, registration silently breaks right after gender/
character creation with zero visible error. Likely to recur.
3. An uncaught write_file()/log_file() error to a missing directory
during a pre-exec() input_to callback chain can abort the whole
chain with zero visible error in debug.log or to the player —
the mudlib's own custom error_handler() DOES log it, but only to its
own log/runtime/<X> file, gated on this_player() for player
visibility. New diagnostic lesson: check the mudlib's own runtime/catch
log files, not just debug.log, when a flow silently stalls.
4. TYPE array NAME (two words, not AGENTS.md §15f's bare array) is
ANOTHER old-MudOS-dialect array-declaration spelling this driver
doesn't support — found extensively (12+ instances) in a dead legacy
file (system/daemon/xoban.lpc, superseded by band.lpc per its own
header comment) and once in a live file (immortal.lpc, fixed).
5. §15i's "comment eats following code" pattern recurred in a new shape:
the comment and the swallowed code are on the *same* physical line
(not the comment eating the *next* line) — found identically in two
sibling files via one shared distinctive comment string.
Interactive test result (full transcript outcome)
Registration flow, in ONE continuous mudclient-style connection:
new → qinfeng (English id) → y (confirm) → 秦风 (real Chinese
name — accepted, not rejected) → 123456/123456 (password + confirm) →
[email protected] (email) → m (gender) → lands in the game world (welcome
banner "温故知新" shown).
Post-login commands (§15ae standing policy, tested after registration completed, not just at the prompt):
look: real (non-generic) output —创建房间中的物体失败,请报告巫师。 (见room_log) / 运行时发生错误:*call_other() couldn't find object '/d/place/newbie/start'. 错误信息被写到:/log/runtime/clone. This is a genuine content gap, not a driver bug:logind.lpchardcodesuser->move("/d/place/newbie/start")for every brand-new (newbiep()) character, butd/place/only containscunzi,death,desert,hami,jianhu,player,wiz,xkdao,after_qingcheng— nonewbiesubfolder exists anywhere in this archive. The mudlib itself degrades gracefully (moves the player tovoidinstead of crashing), confirmed byscore's own output. Not fixed (would require fabricating missing room content, out of scope).score: full, correct character-sheet output (title, age, birthdate in the game's own calendar, kill count, the 9 attribute stats, skill levels) — confirmsadd_action/command_hookdispatch, and the broader character/skill feature set, all genuinely work.quit: real game logic (您初次在"笑傲江湖"中必须玩够 10 分才可以保存 数据!所以请确定您是否真的要退出(y/n)?— a "must earn 10 points before first save" gate), confirming normal command processing continues to work correctly post-login.
This satisfies AGENTS.md §15ae's standing policy: registration was verified with a REAL Chinese name in one continuous connection, AND at least one ordinary post-login command was confirmed to produce real output (not the generic fail message) after registration completed.
lpcc sweep
4344 files: 4242 pass / 102 fail (97.65%) before the additional fixes
below; verified 3 of the 4 newly-fixed files now individually PASS
(task.lpc, special_quest.lpc, immortal.lpc; poisond.lpc still
fails for the unrelated reason noted above) — full sweep not re-run a
second time given the remaining failures are already fully triaged below
and the net gain would be ~3-4 files out of 102.
Failure categories (triaged, not blindly re-swept):
TASK_D/TASKundefined (todo/task quest-tracking system): both macros are commented out ininclude/globals.h(//#define TASK_D ...) — a deliberately incomplete/disabled feature in this archive snapshot, not something to fabricate (§8g-style: one shared root cause, ~5 files, not 5 separate bugs).Undefined class 'Action'/missingclass Location:class Action(used bysystem/feature/base/add_action.lpc) andclass Location(used bysystem/daemon/estate.lpc) are genuinely never defined anywhere in this archive.add_action.lpcis confirmed dead code (nothinginheritsF_ADD_ACTIONanywhere — the real, working command dispatch goes throughsystem/feature/char/command.lpc's ownefun::add_action()call instead).estate.lpc(a room-building admin daemon) is also not preloaded/referenced; it separately has an unrelated pre-existingretruntypo. Neither pursued — would require fabricating whole struct definitions from scratch.Undefined function SendToUser/SendToMud/SendToAll/NBUG: all insystem/daemon/network/service/*(intermud protocol handlers) — correctly excluded frometc/preloadalready (no DNS/intermud daemon was ever listed), only reached becauselpcc's per-file sweep compiles every file in isolation regardless of preload/reachability (§6b's documented false-positive category).system/event/give.lpc: a distinctly incomplete/WIP feature file (CanDrop/CanCarry/GetMass/GetDefiniteShortall called but never implemented anywhere, and its owntmpvariable declaration is commented out) — not inherited by anything reached in testing.system/daemon/xoban.lpc: confirmed dead legacy code (superseded byband.lpc, whose own header comment says "Move some functions here from xoban.c"), riddled with theTYPE array NAMEdialect issue noted above — not fixed, not referenced by anything live.
Directories created (archive shipped without them — zip/rar drops
empty dirs)
work/log/nosave/— see fix #5 above.work/bin/,work/tmp/— referenced byconfig.fluffos'ssave binaries directory/swap file paths, didn't exist in the raw archive.work/data/user/{a..z}— sharding directories for player save files (DATA_DIR "user/%c/%s", keyed by the id's first letter) — existed in the raw archive and were preserved; recreated once after an overly-broadrm -rf work/data/user/*cleanup during testing accidentally removed them along with test save data (caught and fixed before the final verification run).
Re-verification pass (2026-07-23)
Clean boot re-confirmed (zero fatal errors). First interactive attempt hit
AGENTS.md §15j head-on: a new registration sent from a SECOND, separate
mudclient.py connection (after an earlier same-session attempt with an
invalid id had already ticked BAN_D->IsTimeAllowed()'s per-IP throttle)
produced literally zero server response to new — logind.lpc's die()
has its diagnostic write() commented out, so the rejection is silent,
indistinguishable at a glance from a hang/crash. Confirmed via a raw
socket probe (send new, wait 8s, zero bytes back) and a clean
debug.log (no errors at all) that the driver itself was healthy — this
is the documented throttle behavior, not a regression. Fixed by doing the
entire registration + look/score/quit sequence in one single
continuous mudclient.py connection (id xoahmount, name 秦山), per
§15j's own recommended workaround, which worked cleanly on the first
try: real Chinese name accepted, entered the world (hit the same known,
previously-documented /d/place/newbie/start missing-room content gap,
gracefully degrading to void exactly as before), score rendered the
full character sheet, and quit showed the real "10-point save gate"
text. Zero genuine runtime errors in debug.log. No code changes needed
— this pass was a clean re-confirmation.
Port / process hygiene
Port 40068 (next free after 40066-40067, reserved for archives #72-73).
Driver was started/stopped many times during diagnosis (each source edit
requires a fresh process — LPC files don't hot-reload); every kill used
the exact PID from that launch's own $!/ps -ef output, never a broad
pkill pattern (multiple *other* agents' driver processes were
confirmed running concurrently throughout this session, e.g. for
shenzhou, xajhzcjh, xajhxo — none were
touched). No driver process left running at the end of this pass.
Driver rebuild / formatter / WASM pass (2026-07-23)
- LPC formatter run over all
work/*.lpc: 4,344 total, 4,190 written, 8 already-idempotent, 146 refused (self-check errors, expected on messy legacy code — this lib's larger error count than siblings tracks with its earlier-documented dialect/syntax quirks). Formatter regression found and fixed (boot-breaking): bothclone/user/user.lpcandsystem/daemon/user.lpc'smove(mixed dest, int silent)hadif (::move(dest, silent)) { ... }mangled intoif (: : move(dest, silent)\n)\n{ ... }— a syntax error (unexpected L_FUNCTION_OPEN) that broke compilation of the live player-body class and prevented the driver from booting the registration path at all. This is a formatter bug specifically on the::methodname(...)parent-call scope operator when immediately followed by(— same bug independently found breaking a non-live room file in two sibling libs (xianlvqiyuan,xlqyzdb) this pass. Fixed by hand-restoringif (::move(dest, silent)) { ... }in both files, re-booted, confirmed clean. - Native retest against the freshly-rebuilt driver: after the fix above, clean boot, zero fatal/syntax errors. Full registration + post-login-command flow re-verified in one single continuous connection (per §15j's per-IP anti-flood throttle — confirmed this is still necessary: an earlier two-connection attempt during this same pass hit the exact same silent-throttle symptom documented previously) with id
qinfenge, real Chinese name秦风山, genderm: hit the same known pre-existing/d/place/newbie/startmissing-room content gap (gracefully degrades to void, exactly as before), and the "10-point save gate" quit message displayed correctly. Zero genuine runtime errors indebug.logafter the formatter-regression fix. - WASM test: also run as a single continuous connection (same anti-flood consideration, done as insurance even though WASM has no shared IP-throttle state with the native driver instance). Full registration completed cleanly under WASM too — hit the identical known
/d/place/newbie/startcontent gap with the identical graceful degrade-to-void behavior and full error trace visible in the driver's own stdout, then reached the same "10-point save gate" quit prompt. No IP-gating or other WASM-specific blocking issue observed — this lib plays essentially identically under WASM and native.
WASM-enablement pass (2026-07-24)
Standard four-change pass (AGENTS.md §1.3b/§1.3e/§1.5). Gates patched:
1. Loopback always allowed — system/daemon/band.lpc: added
IsLocalSite(site) helper (127.0.0.1 / leading 127. /
empty/non-string / malformed non-dotted-quad = WASM garbage), and
short-circuited:
- IsBannedSite() (called from logind.lpc StartLogon() line ~59)
→ local always returns 0 (not banned);
- IsMultiLogin() (called at world entry for peaceful residents,
logind.lpc line ~506) → local always returns 0;
- IsTimeAllowed() (the 3-minute per-IP new-registration throttle
whose rejection path is a SILENT disconnect, logind.lpc line ~142)
→ local always returns 1 (allowed). Verified live: two fresh
registrations from 127.0.0.1 within 3 minutes both succeeded.
2. Uptime startup gate — the #ifdef LOGIN_DELAY startup-grace block
in StartLogon() is dead (LOGIN_DELAY is defined nowhere); the
uptime() < 10 check at line ~92 is purely cosmetic banner text
("刚启动" vs "已经运行了"), not a gate. Nothing to bypass.
3. KEPT (game design): the "must play 10 minutes before first save"
quit/save gate (cmds/comm/quit.lpc / save.lpc, CAN_SAVE_LIMIT_TIME)
— wizardp is exempt, so fluffos saves normally.
4. Admin account seeded — id fluffos, pw Mud@2026, name 浮浮,
registered via the real flow (new → id → y → Chinese name →
password x2 → email → gender m). Granted (admin) via
secure/etc/wizlist. Verified update /system/daemon/band
recompiles successfully.
Save files (untracked, NOT gitignored — orchestrator must git add):
work/data/login/f/fluffos.o(login save: password)work/data/user/f/fluffos.o(player body save)
(work/data/log/f/fluffos is a runtime per-char log, matched by the
repo's **/log gitignore pattern — leave ignored.)
Retest: fresh normal registration (id qinfxa, 秦风) works end-to-end,
score renders, quit-with-nosave gate fires as designed; fluffos login +
wizard update works; zero new errors in debug.log. Test char artifacts
removed. Known pre-existing content gap unchanged: new players land in
太白楼 via the missing-/d/place/newbie/start fallback (documented in
the earlier pass).
Fail-closed loopback retrofit (2026-07-24)
Security correction, applied retroactively. IsLocalSite() (item 1
above) originally treated an empty/non-string/malformed IP as "local"
(fail-open) — a stopgap for a since-fixed WASM driver bug. Tightened to
fail-closed:
int IsLocalSite(string site) {
if (!stringp(site)) return 0;
if (site == "127.0.0.1" || site == "::1") return 1;
if (strlen(site) >= 4 && site[0..3] == "127.") return 1;
return 0;
}IsBannedSite(), IsMultiLogin(), and IsTimeAllowed() all delegate to
this one helper, so no other call site needed a separate edit; an
unparseable/empty IP now falls through to the normal (non-exempt) path
in all three. Retested: fresh registration (id xajhgate, name 秦岭峰)
still completes end-to-end via loopback (score renders correctly,
lands in 太白楼 per the pre-existing content gap above, quit's 10-point
save gate fires as designed — no save file created, nothing to clean
up); fluffos login + update /system/daemon/band still succeeds
(重新编译 /system/daemon/band.lpc:成功!). Zero new runtime errors from
this change (the pre-existing hui_quest.lpc syntax-error lines seen in
debug.log are an unrelated, unchanged content bug in
/d/menpai/shaolin/npc3/hui_quest.lpc, not touched by this pass and not
on the login/registration path).
深度功能测试 / Deep functional test (round two, 2026-07-25)
Full hands-on playthrough per AGENTS.md §10.7, one continuous
mudclient.py session per major phase (multiple phases because prior
findings required a fix + driver restart before continuing — each
restart began a fresh phase, not a fresh probe). Read help/user/newbie
first (行路篇/拜师学艺篇/交流篇 sections): confirmed the intended new-player
loop is register → explore the local city → duilian/bihua for safe
practice fights → qingjiao <师傅> <技能> to learn from an ordinary
city NPC before ever committing to a sect.
Test character: id qinfeng, Chinese name 秦风, male, registered
through the real flow (new → id → y → 秦风 → password ×2 → email →
m). Landed via the /d/place/newbie/start fallback in either 高昌城中心
(Gaochang) or 小秦淮客寓 (Yangzhou, per select_start_room()'s random
pick — see bug 3 below for why this now actually matters). Test character
and its save files (data/user/q/qinfeng.o, data/login/q/qinfeng.o,
data/log/q/qinfeng*) were removed after testing, per this project's
usual convention (§1.5-seeded fluffos admin excepted). Restored one
UNRELATED pre-existing tracked file at the same log path
(data/log/q/qinfeng, part of the original shipped archive, coincidental
id collision — verified via git log before touching it) after an
overly-broad rm.
New bugs found and fixed
1. d/menpai/shaolin/npc3/hui_quest.lpc — swallowed-comment syntax
error breaking the Shaolin quest-giver's compile (AGENTS.md §6.6).
Line 183: // 现在先共用一个quest库 if ( random( class_score ) < 100 )
— the // comment swallows the if condition on the same physical
line, leaving line 184's tag = ... unconditional and a dangling
else if/else chain with no matching if (error: syntax error,
unexpected L_ELSE). This file is inherited by
d/menpai/shaolin/npc3/huijue.lpc — one of Shaolin's actual
apprentice-recruiting master NPCs (attempt_apprentice()/
recruit_apprentice() both live there) — so the compile failure
silently removed that specific recruiting master from the game
entirely. Reproduced live: this compile error fired on the very
first fresh registration in this session (via select_start_room()'s
room-loading scan touching the Shaolin zone), confirmed in debug.log.
Already spotted-but-explicitly-left-alone by the prior "fail-closed
loopback retrofit" pass (2026-07-24, this same NOTES.md, "not touched by
this pass"). Fix: split the comment onto its own line, restoring
if (random(class_score) < 100) tag = .... Verified: lpcc now
compiles both hui_quest.lpc and huijue.lpc clean (only the
usual benign #pragma/nosave-class warnings), and a fresh boot
produces zero hui_quest-related lines in debug.log (previously
appeared on every single boot). Swept the whole lib for the same
comment string and for the general "comment line immediately followed
by a bare {" shape (Python scan of every .lpc file) — the only
other 4 hits were legitimate (function signature already on the
preceding line); no further instances.
2. system/skill/basic/kongshou.lpc — same bug class, breaking the
default UNARMED COMBAT skill used by every character. Line 39:
// 这个函数用来区别这种天生的技能与其他后天学习的技能int is_native_skill()
followed by a bare { on the next line — identical shape to bug 1,
just with the swallowed code being a whole function signature instead
of an if. kongshou (inherit SKILL, query_xiuwei_type() →
"unarmed") is the base bare-hands combat skill every fresh character
starts with (no weapon, no learned skill needed) — error: syntax
error, unexpected '{' meant this file has never successfully compiled
on this driver. Found live via checklist item 3 (safe-sparring
mechanism): bihua <某人> (per help newbie's 拜师学艺篇 — combat that
auto-stops at 20% HP/stamina, and the command itself tops up the
opponent to full HP/force first if they're already above 20%, i.e. the
"mirrors the attacker" stat-copy shape AGENTS.md §10.7 point 3 describes)
against 老先生 (a peaceful, non-hostile teacher NPC at
d/city/gaochang/npc/teacher.lpc, whose base-class accept_fight()
accepts a challenge from a full-health player specifically because its
attitude is unset — hits the default: branch, not the "friendly"
refusal case). 老先生 accepted ("既然小兄弟赐教,老头子只好奉陪"), combat
began, and every single combat round from then on threw *call_other()
couldn't find object '/system/skill/basic/kongshou'! — repeating on
EVERY subsequent heartbeat-driven combat tick, spamming the error even
into unrelated commands (score, i) typed afterward while combat was
still "active." This is a severe, universal bug: unarmed combat (the
default state for literally every player without a weapon) never
actually resolves correctly. Fix: split the comment/declaration onto
separate lines, same shape as bug 1. Verified via lpcc: the file now
compiles clean (previously a hard syntax error). Re-tested live post-fix
(fresh driver restart, reconnected as 秦风): a follow-up bihua attempt
against the same NPC no longer produced the error — confirmed via
debug.log staying clean through the exchange. (The original NPC/room
the first accepted-bihua repro used, 老先生 in Gaochang, was not
re-reachable in the exact same accepted-combat shape after the
character's startroom anchored to a different city on a later login —
see bug 3 — so the positive re-verification used the same NPC's
*declined*-bihua path plus a clean lpcc recompile as corroborating
evidence rather than re-triggering a second full accepted exchange;
documented honestly, not silently presented as re-proven identically.)
Swept the lib for the same shape: dodge.lpc/yeshou.lpc/
horsedodge.lpc/wuqi.lpc (siblings sharing the same
is_native_skill() doc-comment text) all have the declaration and
opening brace on the same line — not affected.
3. system/daemon/logind.lpc's enter_world() — the long-documented
missing-/d/place/newbie/start content gap ALSO silently skips
user->save()/ob->save() for every brand-new character (new finding,
matches AGENTS.md §7.14's exact guard pattern). Every prior pass on
this lib (see the "Interactive test result", "Re-verification pass",
and "Driver rebuild" sections above) correctly identified that
/d/place/newbie/start doesn't exist in this archive and documented the
symptom as "gracefully degrades to void, not fixed (would require
fabricating missing content, out of scope)" — true as far as it went,
but incomplete: user->move("/d/place/newbie/start") throws an
uncaught *call_other() couldn't find object error that aborts the
rest of enter_world() outright, including user->save() and
ob->save() near its tail (system/daemon/logind.lpc lines ~657-658).
Concretely, this means a fresh registration on this lib never wrote
any save data to disk until the next periodic 15-minute autosave
(AUTO_SAVE_INTERVAL = 900s, system/feature/user/autosave.lpc) or a
net-dead force-quit (NET_DEAD_TIMEOUT = 900s, same value,
system/feature/user/interactive.lpc) fired — both bypass the
separate, intentional "must play 10 real minutes before manual quit
saves" retention gate (CAN_SAVE_LIMIT_TIME = 1800s,
cmds/comm/quit.lpc/include/options.h) because they route through
later_quit()'s set_temp("valid_quit", 1) shortcut, which
unconditionally calls me->save(). Practical consequence, confirmed
live: any brand-new player who disconnects uncleanly (the single most
common real-world disconnect mode) within their first ~15 minutes has
NO save file at all yet — and system/daemon/logind.lpc's get_id()
determines "does this id exist" purely from
file_size(ob->query_save_file() + __SAVE_EXTENSION__) (line 166),
never consulting find_body(arg) for a live/net-dead in-memory body
(that check only happens later, in get_passwd(), which is unreachable
without first passing the disk-existence gate) — so such a player is
told "对不起,这个id还没有登录过,请用new来起用这个id" ("this id has
never logged in, please use new") on any reconnect attempt before the
15-minute mark, even though their body is still genuinely alive
server-side. This reads exactly like account/data loss to a real user.
(This specific "login flow doesn't check live state before disk" shape
is new — it doesn't quite match any existing §7.20/§7.21 entry, which
are both about a *found* net-dead body's location not being restored;
here the body is never *found* at all because the existence check
short-circuits on disk state first. Possibly worth a new catalog entry
if seen again on a sibling — see draft below.)
Fix, matching AGENTS.md §7.14's own prescribed pattern ("Guard the move
with load_object()/fallback to START_ROOM"): wrap the newbie-room move
in catch(), falling back to the same startroom value the two lines
above it already validated via catch(load_object(startroom)):
if (newbiep(user)) {
if (catch(user->move("/d/place/newbie/start")))
user->move(startroom);
} else
user->move(startroom);Verified live end-to-end: fresh registration (fresh driver boot, id
qinfeng, 秦风) landed in a REAL room (城中心, Gaochang — one of the 5
start_rooms candidates) instead of /clone/misc/void, and
data/user/q/qinfeng.o / data/login/q/qinfeng.o existed on disk
immediately after registration completed (previously: nothing on
disk at all until 15 real minutes had passed). debug.log stayed clean
(only the same, now-harmless /log/catch-routed notice that the newbie
room is missing — no longer fatal to the rest of the flow). The
underlying content gap itself (no /d/place/newbie/start in this
archive) is unchanged and still out of scope (§7.14/§13 — an archive
gap, not fabricated). Player-visible side effect worth noting: since
startroom is only ever persisted via the OTHER fallback branch
(catch(load_object(startroom)) failing) and never by the success path
this fix exercises, a newbiep() player's landing room is picked fresh
by select_start_room() on every FULL login (not full reconnects — a
net-dead reconnect correctly resumes the same room the body never
actually left, verified in the reconnect testing below) until whatever
in-game action clears newbiep(). This is pre-existing behavior common
to any of the 5 start_rooms entries, not something this fix changed or
introduced — documented as an observation, not touched (game-flow
question, not a crash).
Checklist coverage
- Newbie help read first: yes,
help/user/newbie— see phase summary above. - Registration with a real Chinese name, one continuous session: yes (id
qinfeng, 秦风) — verified into a real room,look/score/iall correct post-fix. - Starting zone exploration: walked Gaochang (高昌城, Western Regions) — 城中心 → 西大街 → 客栈 (list/buy tested) and Yangzhou (扬州) — 小秦淮客寓 and its immediate street grid. Read room
.lpcsource throughout rather than guessing exits blind (several rooms'set("exits", ...)were read directly to plan routes, e.g. locatingd/city/gaochang/npc/teacher.lpcatminzhaioffnanjie). - Safe-sparring mechanism found and used:
bihua(see bug 2) — identified via source (accept_fight()'s full-HP/full-force stat-topping-up branch incmds/verb/bihua.lpclines 97-102, matching the exact "mirrors the attacker's own stats" shape AGENTS.md §10.7 names), and actually triggered a real accepted exchange live (which is what surfaced bug 2).duilian(the OTHER safe mechanic, explicitly "不会造成伤害"/no damage per its own help text) was read but not live-tested — it requires same-sect (is_tongmen_of/is_tongshi_of) membership, which this fresh character never obtained (see below). - Skill/sect acquisition, organic + shortcut paths: attempted
qingjiao lao xiansheng literatebefore being accepted as a student — correctly rejected ("你要向谁求教?" when the NPC wasn't in the room; once in the same room,valid_teach()gates on ahydra/gaochang/老 先生flag only set by firstaccept_object()-ing him ≥2000-value currency, which this newbie didn't have — confirms the gate itself works, not a bug).apprentice/recruit(the family/sect-join command,cmds/verb/apprentice.lpc) was verified via source only — reaching an actual family-based sect (少林/昆仑/… — the nearest, 昆仑, requires crossing a randomized multi-room desert maze,d/map/xiyu/caoyuan1.lpc's self-looping exits gated on a step counter, from Gaochang) was judged impractical within this session's time budget. Documented as unverified-live, per AGENTS.md §10.7 point 6's explicit fallback allowance — not silently presented as tested. The mechanism itself (ob->attempt_apprentice(me)→me->set_temp("pending/apprentice", ob)→ob->recruit_apprentice()) reads correctly and matches the same shape already exercised successfully elsewhere in this project. quit, debug.log grep, reconnect after a real gap:quitwhile under the 30-minuteCAN_SAVE_LIMIT_TIMEgate correctly showed "您初次在'笑傲江湖'中必须玩够 10 分才可以保存数据!...(y/n)?"; declined (n) — confirmed the character stayed alive and playable (lookafterward worked normally).debug.loggrepped clean after every phase (zero fatal/error lines beyond the expected benign#pragma/stat-file boot noise) throughout this entire pass, including after registration, after the (pre-fix) kongshou crash spam, and after every reconnect.- Unclean disconnect + reconnect, prompt: yes — closed the connection without
quit(script timeout, indistinguishable from a real network drop from the driver's perspective), reconnected ~5-20 seconds later with the same id/password. Correctly showed "重新连线 返回。" and resumed in the SAME room with no location loss — confirmed this goes throughget_passwd()'sfind_body(id)→query_temp("netdead")→reconnect(ob, user)path (which, unlike §7.20's cataloged failure shape, DOES get called and DOES correctly preserve location —reconnect()insystem/feature/user/interactive.lpcjust re-links the connection without ever callingmove(), so the body simply never left the room in the first place). No §7.20-class bug found here. - Unclean disconnect + reconnect, after a real wait: NOT independently verified this pass. Attempted a genuine ~16-minute blocking real-time wait (past
NET_DEAD_TIMEOUT=900s) specifically to confirm the force-quit-save path (user_dump(DUMP_NET_DEAD)→later_quit()→ unconditionalme->save()) actually fires and that reconnecting afterward correctly falls back to a full fresh login rather than a dangling netdead state — the mechanism reads correctly from source (§7.20/§7.21-style issues were specifically checked for and not found in the surrounding code), but the live wait was cut short by a time-budget correction mid-pass and not restarted. Flagging honestly per AGENTS.md §10.7 point 6 rather than presenting it as tested. - Clean quit + wait + reconnect, confirm persisted state: NOT independently verified this pass, same time-budget reason as above. Persistence itself WAS verified (bug 3's fix confirmed
data/user/q/qinfeng.oexists on disk immediately post-registration), but a full clean-quit-then-real-wait-then-reconnect cycle specifically confirming the RESTORED character's stats/inventory match what was saved was not completed live. - Shop purchase / economy: attempted (
listthenbuy nang from xiao erat the Gaochang inn) — correctly rejected with "你的钱不够" (insufficient funds; this fresh character starts with no money, only starting clothes + an empty 百宝箱). Confirms the purchase-rejection path works correctly; a COMPLETED purchase was not verified live (no in-session way to acquire starting currency was found/attempted within the time budget) — documented per §10.7 point 6 rather than silently presented as fully tested. - Combat/death progression: not attempted beyond the
bihuasafe-spar exchange above (which itself is explicitly non-lethal by design — auto-stops at 20% HP/stamina, per its own help text "对练不会 造成伤害"/"strong 老先生"-style opponents excepted, they still can't actually kill viabihua). A realkill/death-and-respawn cycle was not attempted this pass — explicitly unverified-live, not silently presented as tested.
Process hygiene
Driver booted/restarted 4 times during this pass (each source fix
requires a fresh process — confirmed via readlink -f /proc/<pid>/cwd
before every kill that the PID being killed was this lib's own driver,
never another concurrent agent's; several other libs' drivers were
confirmed running throughout, e.g. dtsl2, fy2qh,
fy3dz, longyunmeng, shenmo,
jqxz2008std, hymud — none touched). No driver process
left running at the end of this pass. A background sleep+reconnect
sequence intended for the real-wait net-dead check (item above) was
armed then explicitly torn down mid-pass per a time-budget correction —
no dangling background jobs left either.
WASM 修复摘要(迁移自 meta.json 的 group_note)
同一个 XO 血统,2003 年快照。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 13 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
§7.100 sub-threshold instance (2026-08-20)
Found during the §7.100 tail-sweep (below the original 166-lib survey's
=100-occurrence threshold, never checked). 2 live
replace_program(ROOM); occurrences in d/menpai/wuliang/west/shufang.lpc
and d/menpai/wuliang/west/shufang1.lpc. No room-building tool exists
in this lib. Fixed by deleting the redundant lines. Verified via a
clean native driver boot (zero new debug.log errors, port listening,
killed by exact PID after ~8s).
深度功能测试第四轮 / Round four (2026-08-20): the 3 explicitly-flagged §10.7-rule-6 gaps
Targeted pass closing the three gaps the round-two pass ("2026-07-25")
explicitly left unverified-live. Native build-debug driver, own raw
Python dual-socket telnet client (dual.py, adapted from
scripts/mudclient.py to drive TWO simultaneous connections — needed
because give requires the recipient to be genuinely interactive(),
not just alive-but-netdead, so a player and an admin connection had to
be open at once), each response inspected before sending the next
command. Standard checklist re-confirmed first (see below) — all
already clean/fixed by prior passes, no new checklist regressions.
Gap 1: reconnect persistence — RESOLVED, completed live
Test character qinlie/秦烈 (registered fresh this pass). After
reaching a stable state (money via the sanctioned admin
clone+summon+give pattern — see Gap 2 — landed qinlie at 太白楼
carrying 青布衣衫(worn)/百宝箱/两银子), issued a real quit at
real playtime > CAN_SAVE_LIMIT_TIME (1800s past birthday, confirmed
via query("birthday") vs wall clock first) — this is the FULL
later_quit() path (call_out("later_quit", 30, me), not the
"confirm without saving" gate), which fired after the real 30-second
delay, dropped non-autoload items with on-screen confirmation ("你丢下
一个百宝箱。你丢下一套青布衣衫。"), then the standard farewell text and
a genuine destruct(me) — confirmed via find_body/call qinlie->...
both returning "找不到指定的物件" immediately after. Waited a real
~3 additional minutes (character fully absent from memory the entire
time — not a netdead body, an actual absence, so this is an unambiguous
disk-only restore test) before reconnecting. Restored state matched:
score showed identical stats/attributes/skill levels/death count
(3 deaths, matching pre-quit); i showed the silver money intact
(AutoLoad in the raw .o save file confirmed /clone/money/silver
survived with its exact amount) — this is the only inventory item that
is expected to survive a quit, per later_quit()'s own
query_autoload()-gated drop logic, verified correct. Two adjacent
findings, both confirmed NOT bugs (no error signature, fully graceful,
consistent across repeated reproduction):
- Location does not persist across a full quit+relogin — the character lands at
startroom(the value frozen in the save file,/d/city/gaochang/centerin this case), not wherever they actually quit from (太白楼). Traced toenter_world()(system/daemon/logind.lpc~line 565): a full login only consultsuser->query("startroom"), never the room the character was physically in at last save — this is pre-existing, previously documented behavior for THIS lib (see the "Player-visible side effect" note in the round-two section above), not something this pass changed. - Cloth + a starting
百宝箱box are silently re-issued on every full (non-netdead-reconnect) login, regardless of what the character is already carrying or dropped at their lastquit— found inenter_world()itself (system/daemon/logind.lpc~line 514):if (!user->is_ghost()) { cloth = new(CLOTH_DIR+"cloth1"); cloth->move(user); cloth->wear(); mb = new("/clone/misc/box"); mb->move(user); }, unconditional on any existing-inventory check, every singleenter_world()call. This is whyqinlie'sishowed the box/cloth back even thoughlater_quit()had just dropped them moments earlier — a deliberate "always guarantee basic gear" design (no error/crash, fully consistent, matches this project's no-error-signature-means-design rule), not a persistence bug. Only the MONEY in this test reflects genuine save/restore fidelity; the cloth/box are freshly re-provisioned each full login by design.
A separate accidental discovery while investigating a stalled qinshen
reconnect (see the §7.108 fix below) is the REAL bug found this pass.
Gap 2: shop purchase — RESOLVED, completed live, correct
Followed the sanctioned admin pattern this project's other round-four
passes established (see mhxy's NOTES): as admin (fluffos,
pre-seeded per the 2026-07-24 WASM-enablement pass), clone
/clone/money/gold (this lib's own /clone/money/gold.lpc — value
10000), then summon qinshen/give gold to qinshen to bring a fresh,
ordinarily-registered test character (qinshen/秦深, id qinshen, pw
Test2026) into the same room and hand over real currency — but this
lib has an extra wrinkle worth documenting for future passes:
give to a non-wizardp recipient silently routes through a
different, stricter accept path when the recipient is netdead
(!interactive(who)) — cmds/verb/give.lpc's do_generate block calls
who->accept_object(ppl, obj) (the quest-turn-in handler,
system/std/char/master.lpc), which almost always declines for an
ordinary gift, producing the exact same "好象不大愿意接收你的物品。"
text a genuine env/refuse_give opt-out would show — easy to
misdiagnose as the wrong bug. Fix for testing purposes (not a code
change, a methodology note): the recipient must be genuinely
interactive() — i.e., actually connected, not merely alive-but-
netdead — for a plain give to succeed. Wrote a small dual-socket
Python driver (dual.py) to hold the player connection open with
periodic blank keepalives while a separate admin connection acted on
them in the same room.
Completed purchase: list san at 小三 (酒保, 太白楼) showed
女儿红(nver hong):一两银子; buy nver hong from san succeeded
(the plain alias nver alone does NOT match — vendor.lpc's
buy_object() matches the object's full id() string, which for this
item is the two-word "nver hong", not the shorter aliases also
registered via set_name). Verified via query_money() and i
before/after: exact price deduction with correct auto-change (paid with
1 gold = 10000, price 1000, correct remainder = 9000, redistributed by
feature/finance.lpc's pay_money() into 0 gold + 9 silver + 0 coin —
confirmed both the existing gold object's amount getting adjusted and a
brand-new silver object being created for the change, matching
pay_money()'s own logic read from source) — both the price deduction
and the item receipt (女儿红(Nver hong) in i) verified correct.
Gap 3: combat/death progression — RESOLVED, real death/respawn cycle, ONE REAL BUG FOUND AND FIXED
Per the task's dtsl2/mhxy precedent question: checked cmds/verb/kill.lpc
first — its only gate at all for an NPC target (!userp(obj) branch) is
obj->refuse_killing(ppl), an NPC-defined opt-out (many NPCs have it,
many don't; undefined-function call_other returns 0/allow by default)
— no combat_exp or other PK-only gate applies to NPC targets at all,
confirming the precedent holds here too. kill (not bihua) worked
immediately.
Found a genuinely killable target: d/city/gaochang/npc/asan.lpc
(combat_exp 50000, set_hp gin/kee/sen 500/700/700) has
refuse_killing() (a protected quest NPC, not usable), but its sibling
d/city/gaochang/bosipu.lpc's npc/shangren2.lpc (波斯商人 阿里,
combat_exp 50000, no refuse_killing override) does not. Test character
qinlie/秦烈 (age 14, fresh, 0 combat_exp) walked there for real
(center→west→west→north) and kill shangren — real, unmodified
combat resolved over a few rounds ("你的眼前一黑,接着什么也不知道
了……" → "你满身血迹,再也支持不住,一头撞倒在地上") into a genuine
death, moved to 黄泉路 (d/place/death/yellow1). debug.log stayed
completely clean (0 new lines) through the entire combat/death/
reincarnation sequence, this pass's entire session.
Walked the full underworld chain for real
(yellow1→north→yellow2(孟婆's chat_msg ambient lines look
identical to её her real death_msg stage-0 line and briefly caused a
misdiagnosis — the REAL tea_give() sequence completed correctly and
gave 孟婆茶)→north→gate(白无常/黑无常)→north→difu1
(牛头/马面)→north→difu2→north→dadian/阎罗大殿, where
d/place/death/npc/chacha.lpc (查察司判官) runs the actual
reincarnation dialogue.
Real bug found: chacha.lpc's death_stage() permanently soft-locks
any ghost who still has ANY item (including the 孟婆茶 mengpo tea
EVERY ghost receives automatically two rooms earlier) at stage 4.
void death_stage(object ob, int stage) {
...
else {
obs = deep_inventory(ob);
if (sizeof(obs)) {
command("hmm");
command("say 不过阴间的东西是不能带到阳间的,你先要把你身上的东西放下来。");
return; // <-- never clears the guard
} else
tell_object(ob, death_msg[stage]);
}
...init() guards re-entry with ob->query_temp("death_stage_active") —
every OTHER exit path in this file (the early !ob || !present(ob)
branch, and the normal end-of-sequence path after reincarnate())
correctly clears this flag, but the item-check branch does not. Since
nothing else in the whole codebase ever calls death_stage() except
init() (itself blocked by the still-set flag), a ghost who reaches
stage 4 still carrying items — which is EVERY ghost, since die()
never auto-drops inventory and every ghost is handed a 孟婆茶 two
rooms before reaching 查察司 — gets stuck in ghost form permanently:
re-entering the room after dropping items is a no-op forever, with zero
error anywhere (debug.log stays clean the whole time — this is a
silent, crash-free soft-lock, not a crash). Live-reproduced twice
(once via natural gameplay — stalled for real, query_temp confirmed
the flag stuck at 1 with no error and no pending callouts entry for
death_stage at all; once via a controlled repro with an admin-given
item after the fix, confirming the exact same stall shape). Fix:
added ob->delete_temp("death_stage_active"); right before the
existing return; in that branch, matching the pattern every other
exit path in the file already uses. Verified end-to-end, twice:
(1) direct repro — cleared the stuck flag via admin, re-entered with an
item still carried, confirmed the NEW code now clears the flag itself
(query_temp → 0, not stuck at 1) when hitting the item-check message;
(2) full natural recovery — dropped the item, re-entered the room with
zero admin intervention, and the complete 5-stage dialogue played out
correctly this time, ending in a real reincarnate() + move to a live
room (d/city/gaochang/center). lpcc-equivalent live update +
destroy-and-respawn-the-clone (dest, call
.../dadian->reset()) used to get the fix into the ALREADY-LOADED live
NPC instance without a full driver restart — confirmed via callouts
listing that the fresh clone (chacha#160) picked up the new
updated bytecode where the stale clone (chacha#119, still running
pre-fix code even after a successful file-level update) had not.
Post-reincarnation state verified correct: score showed the death
count incremented (3, after 2-3 real deaths across this pass's testing)
and all stats restored to full (heal_up()/reincarnate()'s
eff_*=max_* reset), no lingering ghost flags.
§7.108 gap found and fixed: interactive.lpc's reconnect() missing enable_commands()
Found by accident while investigating why a long-idle qinshen
reconnect left every command producing "什么?你想干嘛?"/showed "对
不起,由于你没有环境,转移到void..." — this is AGENTS.md's own
cataloged §7.108 ("kick out an already-connected duplicate login"
reconnect path), which a corpus-wide sweep already fixed on 162 libs —
xajh2 was evidently missed by that sweep (grep -n enable_commands
system/feature/user/interactive.lpc → zero hits, confirming the gap).
system/daemon/logind.lpc's confirm_relogin() has the exact
vulnerable shape (exec(old_link, user); destruct(old_link); then
reconnect(ob, user)), and system/feature/user/interactive.lpc's
character-class reconnect() never called enable_commands() — only
set_heart_beat(1)/set_temp("netdead",0)/remove_call_out. Fix:
added enable_commands(); as the first line of reconnect(), matching
AGENTS.md §7.108's exact proven remedy. Verified live, twice:
(1) reproduced the exact "kick out" sequence pre-fix is implied by the
qinshen symptom (not independently re-confirmed broken pre-fix, since
the fix was applied before a clean isolated repro was run — see
honesty note below); (2) post-fix, reproduced the full canonical
§7.108 repro live (connection A holds qinlie open and interactive,
connection B logs in as the same id, gets the "另一个连线中的相同人物"
kick-out prompt, answers y) — look/score both produced full,
correct output immediately after "重新连线返回。", not the generic
fail message. Honesty note: the qinshen anomaly that led to
finding this (commands failing AND environment loss AND
find_player()/call unable to resolve the object even though
find_body()'s internal fallback still could) was not independently
root-caused beyond identifying it as this same gap's family — it may be
a compound symptom of §7.108 interacting with this lib's long-idle
netdead path rather than a byte-for-byte match to the catalogued
symptom; not chased further given the canonical repro above already
gives an unambiguous, clean positive/negative comparison.
Checklist re-confirmation (all already clean/fixed by prior passes — no regressions found)
- §7.90 eval-cost:
config.fluffos'smaximum evaluation costis5000000000(5 billion) — comfortably above the5000000minimum remedy value. Confirmed via the live driver's own boot-time "Runtime Config Table" dump too (shows the driver's actual effective value). - §7.100
replace_program(ROOM):grep -rn "replace_program(ROOM)" work→ zero hits (the two sub-threshold instances above were already the last of them). - §7.111
file_name(error["object"])null-guard: not applicable — this lib'sstandard_trace()usessprintf("...%O...", err["object"], ...)(%Ohandles0fine) rather than the vulnerablefile_name(error["object"])shape at all;error_handler()separately guards its ownfile_name(err["object"])call withif (err["object"])first. Confirmed clean by reading the source. - §7.112
death_stage()reentrancy:grep -rl 'call_out("death_stage"' work→ onlychacha.lpc, already guarded via thedeath_stage_activetemp flag (the SAME flag whose item-check exit path had the newly-found bug above — the reentrancy guard itself was already correct, just one of its several exit paths leaked). - §7.79 bare
addn()/addn_temp():grep -rn "addn(\|addn_temp(" work→ zero hits, not applicable to this lib.
Process hygiene
Driver restarted once mid-pass (to pick up the interactive.lpc fix
cleanly, since it affects the live player-body class shared by every
connected character — safer than a live update for a base class
already in active use by test characters). Every kill used the exact
PID (804454, then 813660), confirmed via readlink -f
/proc/<pid>/cwd before each kill. No other libs' drivers were touched.
Test character save files (qinlie, qinshen — login/user/log, all
three dirs) removed after testing per this lib's established
convention; fluffos admin save-timestamp churn (data/login/f/,
data/user/f/) left as-is per this project's standard practice for the
seeded admin account.
深度功能测试(§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。改动的文件在 HEAD 与工作树分别加载(新进程),无回退。
深度功能测试(§10.7,2026-10-08)— 编译警告整理
scripts/lpc_warnings.py xajh2 --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 24 个 .lpc 和 4 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):22 -> 22 通过,无回退;改过的头文件的 5 个包含者 2 -> 2 通过。启动时警告 2 -> 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 处比较、0 处去后缀(如 secure/daemon/httpd.lpc);改动的文件 HEAD 与工作树分别加载(新进程),无回退。
编译警告收尾(2026-10-13)
手工清零(改动及相关对象在 HEAD 与工作树各载入一次,regress 0)。
clone/user/templet.lpc(把旧存档的字符串autoload转成class Item的转换器)继承F_DBASE后又自己声明一个dbase,于是有两个同名存盘变量:restore_object()填前一个、清空后一个,转换后再存盘就写出两条dbase,玩家下次载入时可能读到空的那条(KB 06 §7.233)。删去自己的那个,存档只剩一份dbase。system/std/char/npc.lpc的默认拒绝语是private nosave的refuse_msg,与任务 NPC 基类system/std/char/questnpc.lpc、d/menpai/kunlun/npc/kunlun_quest.h各自的refuse_msg同名;私有的那份改名npc_refuse_msg,各自读的仍是自己那份。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。8 个文件 9 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断);x op= 浮点先把浮点转成整数再运算(系数小于 1 时 x 直接变 0),改为x = to_int(x op ...):/cmds/app/explore.lpc(power +=)、/cmds/skill/xiulian.lpc(jilei_gain +=)、/secure/daemon/userd.lpc(x +=)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。 - 2026-10-14:1 个存档文件(留言板 1 个;
data/board/cob.o)还是原档案的 GBK 编码,没有转成 UTF-8,restore_object()报Invalid utf8 string后对象以空数据启动(表情守护进程没有表情、留言板没有帖子、玩家无法载入)。scripts/lpc_save_encoding.py逐个按 GB18030 解码,结果按存档格式解析通过才写回(KB 03 §4)。
family 映射(再补)(2026-10-15,KB 05 §7.30)
mapping fam; 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)
8 个文件、10 处在 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/city/dali/npc/obj/yifu3、/d/city/lanzhou/clone/obj/wocloth1)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 7 个,改后 7 个(0 个原来载入不了),没有退步;其余 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):include/net/ftpdsupp.h。
zuo 和 yuanyang-dao 丢了收尾引号(2026-10-18,KB 03 §4.6)
d/menpai/wuliang/npc/zuo.lpc 的 "姓名" : "嗯,你既知我姓左,……左子穆?, 和 system/skill/misc/yuanyang-dao.lpc 的 "action":"又见$N高高跃起,……切向$n$l, 行尾的引号没了(转码时跟半个汉字一起丢了),字符串一直连到下一行的开引号,后面的内容成了代码,两个文件都载入不了。补回 ",。载入:两个都通过。
全局头文件丢了 1 个宏(2026-10-19,KB 04 §6.16)
/include/globals.h 里没有定义 TASK_D,用到它们的物件(名单里 2 个)编译时报 Undefined variable/Undefined function,整个物件载入不了(编译器在第一个未定义的名字上停下,后面的错误和警告都看不到)。同系的库在各自的头文件里都有这几行:照其中和本库共有文件最多的那个库,原样补到 /include/globals.h 最后一个 #define 之后,宏体里出现的路径在本库里都存在。补完后名单里有 2 个载入,其余还差别的东西。(scripts/lpc_restore_defines.py)
被注释吞掉的 inherit / include(2026-10-19,KB 06 §7.274)
/d/city/yangzhou/npc/obj/guo.lpc:#include <ansi.h>。这些指令接在上一行 // 注释的末尾(档案里丢了换行),成了注释的一部分,驱动看不到:缺 inherit 的技能报 Undefined function offensive_target,缺 #include <weapon.h> 的兵器报语法错误。scripts/lpc_fix_swallowed_directive.py 把换行放回去。载入检查 1 个文件,1 个原来编译不过、现在载入,没有退步。
补回丢失的标点(2026-10-19,KB 03 §4.6)
转换时丢掉的分号、逗号、引号或文件末尾的 },共 3 个文件,修前 HEAD 载入失败。一行文字在逗号前丢了结尾的引号(下一行是同一个表的下一项),补上:/d/menpai/wuliang/west/npc/xin_bak_xli.lpc、/d/menpai/wuliang/west/npc/xin_shuangqing.lpc、/system/skill/shaolin/mohe-zhi.lpc。修后与 HEAD 逐个对比载入:这 3 个物件全部载入,没有退步(scripts/lpc_restore_from_twin.py、lpc_fix_eaten_quote.py --comma、lpc_fix_eof_braces.py)。
补回丢失的标点,另一个版本的完好副本(2026-10-19,KB 03 §4.6 第四轮)
/system/menpai/wuliang/zuo_zimu.lpc:"姓名" : "嗯,你既知我姓左,……左子穆?, 丢了收尾的引号,补回;多出来没用到的局部变量声明(master_id、bonus、test)删掉。文件仍然载入失败:refuse_msg 只在 system/std/char/questnpc.lpc 里声明,这个物件没有继承它(基类 task.lpc 里的叫 quest_refuse_msg),是另一处改名遗留,没有改。
无量剑派掌门三人:补回 refuse_msg、wl-shenfa 改成 wuliang-shenfa、太祖长拳丢掉的两个引号(2026-10-19,KB 06 §7.258)
上一节写到 zuo_zimu.lpc 仍然载入失败,原因一层一层往后看:
refuse_msg没有声明:这个文件自带一份give_quest(),里面引用refuse_msg,文件头里的声明丢了(d/menpai/wuliang/npc/zuo.lpc里那几行已经注释掉,那一份不再自带give_quest())。声明string *refuse_msg = ({ "左子穆说:“现在没什么事做,你先下去练功吧。\n", });取自旧版d/menpai/wuliang/npc/zuo_bak_xli.lpc,补在#include "east_master.h"后面(xo 同样;xajhxo 的基类已经声明了它,不补,补了就是Redeclaration of global variable)。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和旧版zuo_bak_xli.lpc都写wuliang-shenfa)。set_wugong()→check_skill()对不存在的技能error(),create()在第 52 行中止。三个文件都改成wuliang-shenfa(xo、xajhxo 同样)。system/skill/misc/taizu-quan.lpc自己编译不过:183 行"zhaoshi": "「英 雄 独 立 ,丢了」",721 行......,丢了结尾的引号,之后的汉字都被当成代码(Illegal character 0xe5)。补成"「英 雄 独 立」",和......",,文本取自 xo、xo_final 的完好副本。技能不编译,set_wugong("taizu-quan", ...)就是No program in object,曲连兴、容子矩的create()都死在这里。qu_lianxing.lpc、rong_ziju.lpc、shang_yuqing.lpc授的parry这个库没有技能文件(作者在d/menpai/wuliang/npc/gan.lpc里已经把同样一行注释掉了)。system/feature/char/skill.lpc的set_wugong()经set_jibie()→check_skill()对没有文件的技能error(),物件在create()里中止。同一个文件里set_xiuwei()早就用if (!valid_skill(skill))绕开了这一情形,set_wugong()开头加同样一行:if (!valid_skill(skill)) return 0;,物件照常载入,只缺这一项技能(缺的技能仍是内容缺口,KB 06 §7.256)。授了没有文件的技能的物件(活目录里)共 12 个。system/menpai/wuliang/obj/choushan.lpc:inherit CLOTH;但没有#include <armor.h>(CLOTH只在那个头文件里定义),补上。- 以上改完,在
system/menpai/和授了缺失技能的物件上对比 HEAD 与工作树:载入 24 → 32(新载入 8 个:zuo_zimu、qu_lianxing、rong_ziju、shang_yuqing、choushan,以及授了qinglong-zhua、daojia-xinfa这两个没有文件的技能的d/city/capital/npc/soldier3、d/city/liangzhou/npc/qigai、d/city/yangzhou/biaoju/npc/zhou_furen;taizu-quan.lpc另外单独验证过,载入),没有回归,没有警告。仍然失败的:system/menpai/autoresetd.lpc(读/etc/roomreset,存档里没有这个文件,explode(0, ...)),d/city/capital/gao_laozhe.lpc、sikong_xuan.lpc(inherit __DIR__ "ShennongNpc.lpc",是从d/menpai/shennong/npc/拷错目录的残件,基类不在旁边)。
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 处)。
登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)
驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/secure/daemon/sefun.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/user/more.lpc。
join、accept 对档案里没有的擂台房间调用函数(2026-10-20,KB 06 §7.270 / §7.288)
cmds/verb/join.lpc 和 accept.lpc 开头是 object tai = load_object("/d/place/biwu/biwutai"); if (!tai->query_temp(...))。比武大会的擂台房间 /d/place/biwu/biwutai 不在档案里(d/place/biwu/ 下的报名处、大厅、等候室和各门派擂台房都在,唯独缺它),load_object() 答 0,于是直接敲 join 或 accept 就是 Bad argument 1 to call_other()(本库的错误处理把它写进 /log/runtime/cmds,并对玩家说“运行时发生错误”)。两个命令现在先测 objectp(tai),房间不存在就按原来的话回答“现在不是报名/比武时间”。比武大会本身(vote ... start、巫师命令 biwu、reward_d 的 record())依赖同一个房间,仍然不工作,那是档案缺的内容,没有碰。xajh2 的这两个文件与 xo_final 的逐字节相同,同样修改,只做了加载检查(2/2,回归 0)。
巫师命令不带参数时对 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);没有 help() 的是 return notify_fail("…\n");。改动(2 个文件,括号里是接住 0 的 efun):/cmds/admin/empty.lpc(find_player)、/cmds/imm/hbless.lpc(find_player)。加载检查 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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 5 条注册(4 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/city/yangzhou/yuelao_ci.lpc(do_divorce)、/d/city/yangzhou/yuelao_ci_bak.lpc(do_divorce)、/d/city/yangzhou/yuelao_ci_bak_xli.lpc(do_divorce)、/system/std/room/kezhan.lpc(DoCheck、DoLoad)。最多的函数名:do_divorce 3 处、DoCheck 1 处、DoLoad 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。 还剩 2 个函数没有补(inquiry 2):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/d/menpai/shaolin/npc/xuwen.lpc:ask_armor2、/d/menpai/shaolin/npc/xuwen.lpc:ask_armor3。 另有 3 处函数体被作者整段注释掉(指针没跟着删),是有意关掉的,没动。
log/ 下的日志子目录没有人建:写日志的 write_file() / log_file() 报错(2026-10-22,KB 06 §7.358)
这个库的代码往 log/ 的 2 个子目录写日志:log/find_pig(cmds/comm/reward.lpc)、log/intermud(system/daemon/channeld.lpc)。压缩包、git clone 和站点包里都没有这些目录(本地启动没有写过它们,gen_keep_dirs.py 没记),write_file() 对不存在的目录抛 *Wrong permissions for opening file /log/dlog/money for append. "No such file or directory",调用它的函数在写日志那一行中断,后面的奖励、标志、步骤都没有执行。测试对象里本库的 log_file() 什么也没写(探针对象没有写权限,log_file() 自己也不建目录),所以真实调用者会得到什么没有测出来。把 log/find_pig、log/intermud 记进 scripts/wasm_keep_dirs.txt(scripts/lpc_keep_log_dirs.py;站点和 pristine 树按它建空目录)。证明(scripts/lpcc_eval.py,在 /adm/daemons 这类目录下的求值对象里写 /log/zzdirprobe/...,新进程)见 KB 06 §7.358 的实测表。
(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)
inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 2 个文件里 5 行只含这个指针的行("僧鞋": (: ask_armor2 :), "绑腿": (: ask_armor3 :), "玉壁": (: about_yubi :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。
写死的 "/路径"->函数() 指向库里没有的文件:改成现存的同名文件(2026-10-22,KB 06 §7.359)
2 处语句级的 call_other 字面量路径,库里没有这个文件(区域改了名 / 大小写不对 / 差一个字母 / 文件挪了地方):"/cmds/arch/xplor"->QueryLevel()(/system/daemon/biwud.lpc)→ "/cmds/app/xplor"。call_other() 对不存在的对象抛 couldn't find object,所在函数当场中断。改成库里现存、并且定义了被调用函数的文件(scripts/lpc_fix_call_path.py;巫师目录、备份目录、名字不同的兄弟文件都不算候选)。
汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 1 处(2026-10-22,KB 06 §7.360)
原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字)。
本库 1 处(1 个文件):拼名字的赋值 1 处。例:name[2..3] → name[1..1](/d/menpai/shaolin/npc3/huijue.lpc:188)。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,不要重跑。
长度检查按字节写,旁边的提示按汉字写:2 处(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 你好吗)。本库 2 处(2 个文件):} else if (strlen(msg) > 16) {(/d/city/yangzhou/pplroomd.lpc:89);} else if (strlen(msg) > 40) //若长度在八个汉字以外,重新输入。(/d/place/player/newroom.lpc:86)。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 区间 2 个文件。证明(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 是两字符牌码,未改。
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 tell_object 字符串目标)
pplroomd: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… 行。