info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
玄剑录属于 ES II / 侠客行(XKX)引擎家族,核心 master/simul_efun 文件与同门档案 `xkx2001`、`bmxkx2001` 逐字节相同,但拥有自己独立的武林世界与剧情内容,舞台设定在明教、昆仑、侠客岛等门派与地点之间;克隆对象名单直接取材金庸《射雕英雄传》,东邪、西毒、南帝、北丐、中神通"五绝"和全真教丘处机等人物原名原样登场,新人在侠客岛会先被赏善使、罚恶使等门派使者迎接后再习武结交门派;天赋点可自行分配或交给系统随机,巫师登录在普通密码之外还要求一个独立的验证码;文件头带"Cracked by Kafei"署名,和 `xkx2000zxb` 是同一批流通版本(`xkx2001` 本身则属"Cracked by Roath"的另一批)。
English
Part of the ES II / Xiake Xing ("Wandering Blade") engine family (byte-identical core master/simul_efun files to sibling archives xkx2001 and bmxkx2001), but with its own independent wuxia world centered on the Ming Cult, Kunlun, and Xiake Island. Its clone-object roster draws directly on Jin Yong's "Legend of the Condor Heroes": five special NPCs are literally named and modeled on the novel's Five Greats -- 东邪 (Eastern Heretic), 西毒 (Western Venom), 南帝 (Southern Emperor), 北丐 (Northern Beggar), and 中神通 (Central Divine Coordinator) -- alongside the historical Taoist master 丘处机 (Qiu Chuji). New characters are met on Xiake Island by the sect's judge-envoy NPCs before setting out to train and build sect ties; talent-point allocation can be chosen by hand or left to random assignment, and wizard logins require a separate passcode on top of the normal password. Its files carry a "Cracked by Kafei" header, placing it in the same release batch as xkx2000zxb (xkx2001 itself circulated under a different "Cracked by Roath" release).
README
内容亮点
- 传统武侠题材 MUD:玩家初入江湖,会先被"侠客岛"的赏善使、罚恶使等 人物迎接,随后展开习武、结交门派、闯荡各方势力的江湖之旅。
- 天赋分配环节可以自行指定膂力等属性的某一项,也可以让系统随机分配。
- 巫师登录在普通密码之外还要求一个独立的"巫师验证码"。
在线试玩
https://mudlibs.fluffos.info/xuanjianlu/
管理员账号 / Admin account
- id:
fluffos - 密码 / password:
Mud@2026 - 巫师验证码 / wizard passcode:
Mud@2026(本 lib 的巫师登录在普通 密码之后还会额外要求一个"巫师验证码",两者已设为相同) - 中文名 / display name: 浮浮(Fluffos)
- 权限 / level:
(admin)—— 最高权限。授权数据存放在/data/securityd.o(SECURITY_D的存档,wiz_status/wiz_sites两个映射),巫师登录地址限制为.*(任意地址)。
警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。
本地运行
cd libs/xuanjianlu
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40064。
NOTES · 移植与修复记录
玄剑录 (xuanjianlu) — archive #70
What this is
archives/玄剑录.rar, root at raw/xjl/. config.xjl's name : field and
the live connect banner both confirm the game's own self-identified name is
玄剑录 ("Record of the Mysterious Sword") — matches the slug, no
divergence to note. Distributed by the same "小熊泥苑" collector/hosting
site seen on shujian2008/sjtx2 (archive #35/#36) — 小熊泥苑.txt
sits alongside the mudlib root in the raw archive, just site branding, not
part of the mudlib.
Lineage: confirmed via md5sum that adm/simul_efun/chinese.c and
adm/single/master.c are byte-identical to xkx2001 (archive #25) and
bmxkx2001 (archive #45) — this is the same "ES II" engine
family (master.c header credits "original from Lil", "rewritten by
Annihilator", "modified by Xiang/Xuy for XKX"). adm/register/xkxreg,
xkxrestore, xkx_monitor filenames further confirm the "XKX" (侠客行)
codebase ancestry, even though this game's own live content/world is a
distinct wuxia setting (Ming-cult/明教/昆仑/侠客岛 zones, its own NPCs).
adm/daemons/logind.c differs from both siblings (its own registration
flow/banner), consistent with "shared engine, distinct game-world content"
seen across this whole ES II family (es1_win/esI/xkx2001/rzrmud/xo/
bmxkx2001/kxkj/yueyingqiyuan/wuhanzhan/
yanhuangwuhun/haiyang2).
Layout: adm/single/{master,simul_efun} + adm/daemons/* + adm/simul_efun/*
+ adm/register/* (the last one XKX-specific, holds a MySQL-free
registration helper set, not used by the live registration flow tested
here).
Size: 11,492 raw files, 10,605 .lpc/.c files after conversion — a
normal-sized lib for this project, not a mega-lib; the full lpcc_check.sh
sweep was run without incident (see below).
Fixes applied (and why)
Standard/catalog fixes, applied proactively
1. AGENTS.md §15h (is_chinese/check_legal_name GBK byte-range bug —
nearly universal, confirmed present here too):
- adm/simul_efun/chinese.lpc's is_chinese(str): was
strlen(str)>=2 && str[0] > 160 && str[0] < 255 (a GBK lead-byte
range check, always false against UTF-8 codepoints) → replaced with
strlen(str)>=1 && str[0] >= 0x4e00 && str[0] <= 0x9fff (CJK Unified
Ideographs codepoint range).
- adm/daemons/logind.lpc's check_legal_name(name, ob): bound
(strlen(name) < 2) || (strlen(name) > 8) || i % 2 (byte-count bound
+ a meaningless parity check) → (strlen(name) < 1) || (strlen(name)
> 4) (character count; the message text already promised "1 到 4
个中文字" — 1 to 4 Chinese characters — so halving 2/8→1/4 matches
what the message always said, not a guess). Its per-character
sliding-window loop if( j%2==0 && !is_chinese(name[j..j+1]) ) {
name[j]+=128; name[j+1]+=128; } (byte-offset gate + a byte-shift
"auto-correct" hack that has no valid meaning against Unicode
codepoints) → if( !is_chinese(name[j..j]) ) { <reject>; }, using
the exact reject message the original author had already written
but left commented out.
- Verified via 3 independent full registration tests (see below) that
real single/double-character Chinese names are accepted correctly
and rejected names really are rejected (not just "doesn't crash").
2. AGENTS.md §15p (DNS/intermud daemon preload exclusion): adm/etc/
preload listed /adm/daemons/network/dns_master — removed
proactively before the first boot attempt. (A second, unrelated copy
of the same file sits at adm/daemons/dns_master.lpc, outside
network/ — confirmed it's not the one the DNS_MASTER macro
resolves to, include/net/daemons.h points at the network/ copy
only, so nothing else needed touching.) Also checked §15ab's "inline
DNS calls outside preload" variant: logind.lpc's logon() does call
MUDLIST_CMD->main(...) unconditionally on every connect, and that
command does call into DNS_MASTER, but it's guarded by its own
find_object(DNS_MASTER) check and degrades to a harmless
notify_fail() when not loaded — confirmed safe by inspection and by
zero hangs/errors across every boot+test cycle.
3. AGENTS.md §14 (valid_override 3-arg upgrade): master.lpc's
valid_override(file, name) was 2-arg — upgraded to (file, name,
main_file) and added main_file == SIMUL_EFUN_OB || main_file ==
MASTER_OB to the allow-check, so an efun:: override written inside
a file #included into simul_efun.lpc is still recognized.
4. AGENTS.md §8d/§15o (get_include_path() insurance): master.lpc
had no get_include_path() apply at all — added the standard
directory-prepending implementation as insurance for any live,
mid-connection compile of a file using a local (same-directory)
#include. (convert_lib.sh's own §8d pass already converted 24
local angle-bracket includes to quoted form, which resolves without
needing this apply at all — this addition is pure insurance, not a
fix for an observed failure.)
5. AGENTS.md §8e (tail is not a real FluffOS efun) — fatal here,
confirmed on the very first boot attempt: adm/simul_efun/message.lpc's
tail(string file) called efun::tail(file), and since this file is
#included directly into simul_efun.lpc, the compile error
(Unknown efun: tail) took down the *entire* simul_efun object,
which the driver refuses to boot without (*No program in object
'/adm/single/simul_efun'!). Reimplemented in plain LPC (read the
file, split lines, take the last N, write them) — same pattern as
catalogued in AGENTS.md, same severity as the bmxkx2001
instance of this exact bug (that lib shares this codebase family too).
6. AGENTS.md §15c (adm/etc/preload-style bare-.c data-file refs):
found a SEPARATE instance in adm/etc/task/task_list (a plain-text
list of NPC-quest object paths read by adm/daemons/taskd.lpc's
random_place()/update_task()), still listing the pre-rename
/adm/etc/task/obj/*.c paths. After the .c→.lpc rename these
new() calls returned 0 silently, and the caller's very next line
(ob->query("task_owner"), no objectp() guard — see AGENTS.md §15e)
threw *Bad argument 1 to EFUN call_other() ... Got: int(0) every
time this daemon's periodic job fired. Fixed with sed -i 's/\.c$//'
on the data file, same fix shape as the catalog entry.
New findings from this pass (not yet in AGENTS.md's catalog as of this write-up)
7. private on an add_action-bound dispatcher, once inherited, gets
demoted to DECL_HIDDEN and the driver refuses to call it — silently
breaking EVERY player command post-login. This is the single most
impactful bug found in this lib and, as far as I can tell, a genuinely
new bug class for this whole project's catalog.
feature/command.lpc (inherited by inherit/char/char.lpc, the
player body base class) declares:
``lpc
private nomask int command_hook(string arg)
`
bound via add_action("command_hook", "", 1) in enable_player().
This function is the *sole* generic dispatcher for movement, verbs,
emotes, and channels — literally every command a connected player
types. On this driver (vm/internal/apply.cc's apply()), a
driver-origin add_action callback dispatched against an object
requires at least DECL_PRIVATE access when current_object == ob
— but DECL_PRIVATE ("can't be inherited") demotes a private
function to DECL_HIDDEN once it's compiled as part of an
*inheriting* program rather than its own defining file, and command_hook
is only ever invoked as a member of the inheriting player-body class,
never as a member of feature/command.lpc itself. Symptom: full
registration completes cleanly, the character lands in the actual
start room with a correct room description on entry, but every
subsequent typed command (even look) silently produces only the
config's default error message, with a matching debug.log entry:
`
apply() with insufficient permission:
cob: clone/user/user#1, ob: clone/user/user#1, function: command_hook,
origin: efun, needs: private, has: hidden
`
How this was missed on 2 prior sibling libs: xkx2001's own copy
of this file has nomask int command_hook (no private at all) — so
xkx2001 never hit this. bmxkx2001 (archive #45) *does*
have the identical private nomask` shape, but that lib's own NOTES.md
confirms testing stopped right at "reaches the password prompt" and
never issued a real post-login command — so the bug was live there
too and never caught. This is exactly the class of gap this task's
instructions specifically warn about: "boots and reaches a prompt" is
not "the feature actually works."
Fix: drop private (nomask int command_hook(string arg)),
matching xkx2001's own already-safe version verbatim. nomask alone
still prevents any override.
Recommendation for AGENTS.md: worth its own catalog entry (a new
§15ac or similar) — check any lib's core add_action-bound dispatcher
function (commonly named command_hook/parse_command/similar,
defined in a feature/-style file and inherited into the player body)
for a private access modifier, and always issue at least one real
post-login command (not just look at the banner) before considering
a lib's gameplay loop verified.
8. securityd.lpc's ACL mappings are never initialized when the
archive ships with no seed save data for the daemon — a second,
related bug that surfaced immediately after fixing #7 above (the very
first real command that could finally reach command_hook then hit
this one).
securityd.lpc create() calls restore(), and only if that FAILS
does it fall back to manually initializing wiz_status/wiz_sites —
but trusted_read/exclude_read/trusted_write/exclude_write/
authorized_cmds/exclude_cmds are never initialized in that
fallback branch at all (only authorized_cmds gets a lazy
if(!authorized_cmds) authorized_cmds = allocate_mapping(...) inside
valid_cmd() itself — the other five don't get equivalent treatment
anywhere). This archive has no /data/securityd.o seed file at all
(confirmed: the only securityd.o anywhere in the tree is a wizard's
unrelated personal-sandbox copy under clone/obj/u/xuanyuan/daemons/),
so restore() always fails. Symptom: valid_cmd()'s very first
exclude_cmds[dir] lookup on the still-0 mapping threw *Value
being indexed is zero. for literally every player command.
Fix: added lazy if(!X) X = allocate_mapping(1000); for all six
ACL mappings once, at the end of create().
A second layer of the same root cause: once the mappings
themselves were guaranteed non-zero, the *next* line down hit a
related but distinct crash — authorized_cmds["cmds"]/
trusted_write["/"]/trusted_read["/"] are looked up directly (not
via the undefinedp()-guarded loop the rest of the function uses) and
passed straight into member_array(), which rejects an int (the
0 you get from a missing mapping key) for its 2nd argument
(Expected: string or array Got: 0). Fixed by pre-seeding exactly
these three well-known keys as ({}) in the same create() block.
Both of these are latent/dormant in every sibling lib that ships with
real save data for this daemon (a populated ACL is the normal case) —
this is specific to this archive's snapshot shipping without one, not
a systemic driver-compat bug the way #7 is. Worth a narrower catalog
note (checking whether restore() ever actually succeeds, and adding
defensive allocate_mapping()/({}) fallbacks in any lib's
securityd.lpc create() when it doesn't) rather than a blanket rule.
static→nosave blanket-sed collateral damage (§3 counterexample, recurring)
Same shape as the moniHuafu/yanhuangwuhun precedent already in
AGENTS.md, but a wider blast radius here (23 files, the leading-slash
variant "/static/..." rather than just "static...): this lib logs
extensively via log_file("static/XXX", ...) / log_file("/static/XXX",
...) (a real log/static/ directory ships in the archive) for crash
logs, read/write/cmd-denial audit logs, clone logs, award logs, etc. —
convert_lib.sh's blanket \bstatic\b→nosave sed rewrote every one of
these path literals to "nosave/..."/"/nosave/...", silently
orphaning the real log/static/ seed directory. Found and reverted with
grep -arl '"nosave\|/nosave/' across the whole work/ tree (not just
the narrower "nosave check used on prior libs, which would have missed
the leading-slash form) → sed 's/"nosave/"static/g; s/\/nosave\//\/static\//g'
on every hit. Affected: adm/single/master.lpc (crash()'s
static/CRASHES), adm/simul_efun/message.lpc (5 sites),
adm/daemons/securityd.lpc (5 sites), its wizard-sandbox duplicate at
clone/obj/u/xuanyuan/daemons/securityd.lpc and .../logind.lpc, several
cmds//u/-tree files' AWARD_LOG/SUICIDE_LIST/etc, and — a distinct
sub-case — the *historical audit log data* file log/static/more.lpc
itself (a 更 record of who ran the more command on what file, plain
text, mistakenly renamed from .c→.lpc and then swept by the same sed
since it now had a .lpc extension, corrupting its own content, not
code — its lines record historical paths like /log/static/suicide.c).
Reverted that one too even though it's just cosmetic seed-log text, for
consistency. A separate mid-pass mistake, corrected: while iterating
on this, an over-broad rm -rf work/log (intending only to clear
player-save-state under work/data) also deleted this lib's entire
work/log/ seed-data subtree (crash logs, WIZ_LOGIN, MONEY, etc, and
the regban.log that adm/daemons/regband.lpc's is_banned() writes to
on every connection attempt) — this reintroduced the exact check_legal_id-adjacent
crash described above (*Wrong permissions for opening file /log/regban.log
for append. / No such file or directory, silently aborting get_id()
mid-connection). Recovered by re-running convert_lib.sh into a scratch
directory (raw→work conversion is idempotent given the same raw source)
and copying just the regenerated log/ subtree back in, re-applying the
same static/nosave revert to it. Lesson for future sessions: work/log/
(the *mudlib's own* virtual /log/ tree, seed content) and the sibling
libs/<slug>/log/ (the *driver's* debug-log output directory, safe to
clear) are two different things with the same basename — don't rm -rf`
both in one command.
lpcc sweep content fixes (found via the sweep, applied where cheap and correct)
- Missing shared header, restored (AGENTS.md §8g variant):
kungfu/skill/liumai-shenjian/eff_msg.h(generic combat-message helper functionsdamage_msg()/eff_status_msg(), nothing liumai-specific in its content at all) was referenced by 10 unrelated skill files via the absolute path#include "/kungfu/skill/eff_msg.h"— i.e. the shared root location, not the nested one it actually shipped at. Copied the file tokungfu/skill/eff_msg.h(the referenced root) rather than touch 10 call sites — resolves all 10Cannot #includefailures in one shot. - Missing
HALBERDmacro (AGENTS.md §8g variant):include/weapon.hdefinesAXE/BLADE/DAGGER/.../BOWmacros pointing at/inherit/weapon/*base classes, but never defined a plainHALBERDmacro (only its internalF_HALBERDvariant) even though/inherit/weapon/halberd.lpcexists on disk and 17 files doinherit HALBERD;. Added#define HALBERD "/inherit/weapon/halberd"in the same block as its siblings — resolved all 17inherit HALBERDsyntax errors at once. - Misplaced relative
#include, fixed at 3 call sites:d/kunlun/zhang.lpc,d/kunlun/xiaozhao.lpc(张无忌/小昭, the Ming-cult zone's two named NPCs), andu/lbc/yangxiao.lpcdid#include "mingjiao_npc.lpc"(bare filename, quoted) while sitting one directory level above where that header actually lives (d/kunlun/npc/mingjiao_npc.lpc) — every SIBLING NPC file ind/kunlun/npc/uses the identical bare-filename include and resolves fine (same directory). Fixed the 3 outliers to the full absolute path"/d/kunlun/npc/mingjiao_npc.lpc". - Lossy-conversion merged closing-tag typo (the documented "iconv -c eats the adjacent real newline too" gotcha):
d/kunlun/mj_center.lpcand its byte-identical sibling copyu/feitian/mingjiao/d/mingjiao/mj_center.lpc(both flaggedLOSSY conversionin the pipeline log) had theirset("long", @LONG ... LONG)text block's closingLONGtag merged onto the preceding Chinese text line (...些数据。LONGon one physical line instead of two) — "End of file in text block" as predicted by the catalog entry. Fixed by re-inserting the newline at the exact merge point in both files. - Orphaned non-LPC content (AGENTS.md §12):
d/kunlun/map_mingjiao.c(pure ASCII-art,◆明教地图◆and nothing else, zero LPC syntax) got swept into.lpcby the blanket rename despite not being code and not being referenced by anything (grep -rlacross the whole tree, only self-match). Renamed tomap_mingjiao.orphaned.txt, matching the precedent's exact recommendation.
What I confirmed was NOT needed, and how
- §4 (master lazily
load_object()ing a security daemon insidevalid_read/valid_write, recursion risk): readmaster.lpcin full —valid_writeonly doesfind_object(SECURITY_D)(noload_object), returns0if not found;valid_readis a hardcodedreturn 1;with no daemon lookup at all. No lazy-load pattern present. - §7 (
get_root_uid()/get_bb_uid()master applies): both already implemented inmaster.lpc, returningROOT_UID/BACKBONE_UID. Confirmed present by direct read, no fix needed. - §8c (
valid_read/valid_writeoverridinguserwiththis_player()unconditionally): readsecurityd.lpc'svalid_read/valid_writein full — neither does this;useris taken as passed and onlygeteuid(user)is derived from it directly, nothis_player()substitution anywhere in either function. - §15n (custom securityd ACL blocking the driver's own compile-time
load_object/includereads): confirmed structurally impossible here —master.lpc's ownvalid_readnever callsSECURITY_Dat all (hardcodedreturn 1), sosecurityd.lpc'svalid_readACL logic (with itsexclude_read/trusted_readtables) is dead code from the driver's perspective; onlyvalid_writeis actually wired through to the daemon. Confirmed via direct read of both files, not by guessing from symptoms. - §15l (master
create()destructingSIMUL_EFUN_OB):master.lpc create()is a singlewrite("master: loaded successfully.\n");line, nodestruct()/efun::destruct()call anywhere in the file. - §15u (dormant "phone-home"/mass-deletion function in securityd): grepped
securityd.lpc/master.lpcforrm(/rmdir(/shutdown(gated on suspicious checks — found nothing resembling this pattern (the onlyrm()call in securityd is the ordinary.edrccleanup insave_ed_setup, unrelated). - §15s (
tell_room/message()4th-argint 0rejection): checkedmessage.lpc'stell_room/shoutwrappers — both DO pass a potentially-0value (exclude,this_player()) as the 4thmessage()argument in their 2-arg call forms. Applied the standard defensive fix (exclude || ({}),this_player() || ({})) proactively even though no crash from this specific shape was actually observed in any of the 3 full interactive test sessions — cheap, correct, and consistent with the catalog's stated risk (a preloaded room's own heartbeat is the classic trigger, and this lib does have many preloaded/auto-reset rooms). - §8f (
TYPE * name1, name2;comma-declarator array-modifier bug): a broad regex sweep for the shape flagged 235 candidate lines, but cross-checked against the REAL lpcc sweep's failure categories — only 11Bad assignmentfailures exist in the whole lib, and every one of them traces to a genuinely undefined function call (who_i_fight,unew,offensive_target, all in wizard-sandbox files already documented as noise below), none to a forgotten*on a second declarator. Concluded the regex was over-matching (as AGENTS.md's own §8f note warns can happen without the narrower Python-based check) and this bug class is not actually present here. - §15t (absolute path in
<>,..-relative includes, inherit-after- globals): grepped for all three shapes — zero hits for an absolute path inside angle brackets, zero#include "../..."patterns, and zeroIllegal to inherit after defining global variableserrors anywhere in the full lpcc sweep output. - §15v (
LONELY_IMPROVED-gatedefun::X()calls / NT-nitan-lineagecount()bignum efun): not applicable — this is the ES II/XKX lineage, not the NT/nitan/Lonely family;adm/single/simul_efun.lpchas no globalset/query/deletesimul_efun definitions at all (per-object property storage is handled by ordinaryinherit F_DBASE-style base classes), so the core §15 dbase architecture bug doesn't apply either. Confirmed by readingadm/single/simul_efun.lpc's#includelist.
lpcc sweep — triage of remaining noise (361/10605 fail, 96.6% pass, up from 96.4%/383-fail pre-fix)
Grouped the 361 remaining failures by top-level directory before accepting them as noise (not exhaustively fixed, per AGENTS.md §6b):
u/dns(46),clone/obj(29, almost entirelyu/xuanyuan),u/feitian(20),u/lbc(a few): wizard personal sandbox/scratch directories (builders' own work-in-progress skill/NPC files, several explicitly under atmp/path). Sampled several — failures trace to genuinely undefined helper functions (test_zouhuorumo,BINGQI_Dused as a bare call,create_communion,who_i_fight) that only ever existed as private conveniences in whatever personal dev environment these wizards used, never part of the shipped shared codebase. Not fixed — dead/scratch content, matches the established "personal builder sandbox" noise category seen on other libs in this family.log/static(16) +log/test(12): pure historical log/audit-data files (chat logs, command-usage history, crash dumps) that happen to have shipped with a.cextension and got swept into the.lpcrename — not code at all (matches AGENTS.md §12'smap_mingjiaoprecedent exactly, just not worth individually renaming 28 pure-data files back to.txtthe way the one referenced-and-visiblemap_mingjiaocase was). Confirmed via sampling a few: plain Chinese log text, zero LPC syntax.- **
adm/daemons(13, entirelynetwork/*+adsd/questd/socket)**: the DNS/intermud/mail/telnet network subsystem (deliberately excluded from preload) plus two unrelated daemons (adsdcrashes in its owncreate()on a runtimeexplode()call,questdonly has unused- variable warnings misclassified as FAIL by the sweep harness) — none are preloaded, none reachable from registration or basic gameplay. - Zone content (
d/henshan12,d/city11,d/wudang8,d/shaolin8,clone/board8,d/taihu7,d/m_weapon7,d/quanzhou6,d/beijing6,d/kunlun10 remaining after fixes, and a long tail of 1-5-failure zones): sampled several — genuine per-room/per-item content gaps (missing referenced NPCs/rooms that don't exist anywhere in the archive, matching AGENTS.md §13) and a handful of pre-existing typos/illegal-characters not on the boot or registration critical path. Not chased further given the breadth-over-depth policy and the clean, fully-verified boot+registration+gameplay result already in hand. kungfu/class(10): similar per-skill content-reference gaps, same treatment.
Interactive test result — full registration + real gameplay commands
Read adm/daemons/logind.lpc's actual input_to chain before scripting
(logon → confirm_big5 → get_id → confirm_id → get_name → new_password →
confirm_password → specify_gifttype → get_gift → get_email → get_gender →
enter_world) — confirms a hidden pre-id BIG5/GB font prompt
("Do you want to use BIG5 code?(y/n)") right after the banner, exactly the
kind of hidden gate AGENTS.md's "hidden pre-id prompt" family warns about.
Ran 3 independent full registrations in 3 separate continuous
mudclient.py sessions, each covering registration all the way through
real post-login commands (not just reaching a prompt):
1. n (GB code) → id qinling → confirm y → real Chinese name 秦岭
→ password test12345 (×2) → gift type 0 → accept gift y → email
[email protected] → gender f → lands in the actual start room
(沙滩 — a beach zone, full room description with exits/NPC rendered
correctly) → look now correctly re-displays the room (this was
the run where the command_hook/securityd fixes were validated —
before them, look produced only the config's default error message
with a debug.log crash trace) → score → quit → "开始退出游戏,
进行中 ..." (graceful quit).
2. n → id linfeng → y → real Chinese name 林风 → same flow →
lands in the same start room, an NPC ("「罚恶使」李四") correctly
greets the character by name ("恭迎林风") → zero debug.log errors
for the entire session.
3. n → id qinfeng → y → real Chinese name 秦风 → same flow →
lands in the start room, a different greeter NPC ("「赏善使」张三")
greets by name ("恭迎秦风") → look → i → quit → zero
debug.log errors of any kind (执行时段错误/Bad argument/
Undefined function/Value being indexed/insufficient permission:
all zero hits) — this is the final, fully-clean run after every fix
in this document was applied.
All three names are real, valid Chinese input (2-character names, both
characters within the CJK range, not on the lib's banned_name list),
confirming the §15h fix actually works end-to-end and the flow proceeds
correctly past registration into fully-playable post-login gameplay, not
just "reaches a prompt."
Port / process
Port 40064. Driver launched via setsid nohup ... & disown per
AGENTS.md's guidance; killed by exact PID after each test cycle, never a
broad pkill pattern (several other agents had drivers running
concurrently throughout this session). No driver process left running at
the end of this pass; work/data (player save state from testing) and
log/debug.log cleared before finishing.
2026-07-23: driver rebuild retest + LPC formatter + WASM check
- Formatter: ran
tools/lpc-syntax'sformat-corpus.mjsover all 10605.lpcfiles inwork/; only 2088 written (8450 were already formatter-conformant — this lib evidently got substantial formatting attention in an earlier pass), 67 errors (files it refused to touch, fine/expected). Specifically verified thenomask int command_hookfix (noprivate, §15ae's origin bug) infeature/command.lpcsurvived the reformat unchanged in substance. - Native retest: rebuilt
~/src/fluffos/build-debug/src/driverbooted clean (Accepting telnet connections/Initializations complete); the only debug.log noise during preload is a repeating, caught (viaCATCH()inmaster.lpc'spreload()) "执行时段 错误:*Object cannot be loaded during compilation" fromsimul_efun.lpc'swizardp()— this is the same shape as AGENTS.md §15af (log_error()callingwizardp(this_player(1))during a compile-time warning, lazily loading securityd mid-compile), but since it's wrapped inmaster.lpc's ownCATCH()here it's fully non-fatal and boot completes normally; not previously flagged for this lib, noting it here as a §15af-shaped (but harmless) instance. Ran 3 fresh full registration sessions with real Chinese names (秦风波/秦风陈/秦风戊): id/confirm/Chinese-name/password/gift-type/ email/gender all completed, landed in the same 沙滩 start room seen in the original pass, with the escort NPC's name-greeting working correctly;look/i/quitall reproduced the original pass's clean results (zero real debug.log errors). New observation:score(not part of the original pass's tested command set) produced no visible output at all in every attempt tried, both immediately after landing and afterlook;cmds/usr/score.lpcnever appears indebug.logat all (unlikelook/quit/drop, which show their lazy-compile warnings), meaningfind_commandnever resolves it to a compile attempt — worth a closer look in a future pass, but not chased further here since it's a newly-noticed gap outside the previously-verified/committed command set, not a regression from this pass's reformat or driver rebuild (both are read-only w.r.t.cmds/usr/score.lpc's logic). - WASM: booted with the same non-fatal preload noise as native (the §15af-shaped caught cascade above, plus the expected
socket_create/socket_close/Bad left argumentcompile errors fromftpd.lpc/network/http.lpcsince thesocketspackage isn't built into this WASM binary). Full registration + gameplay flow verified end-to-end with a real Chinese name (秦风武): same id/confirm/name/password/gift/email/gender flow, landed in 沙滩,lookandquitboth worked correctly matching the native result. The "上次连线地址" (last-connect-address) line printed0( Wed Dec 31 16:00:00 1969 )instead of a real IP+timestamp — a cosmetic manifestation of the documentedquery_ip_number()-under- WASM limitation (epoch 0 formatted as a date), non-fatal, doesn't gate login since this lib has no IP-based site restriction. Verdict: playable under WASM (matching the native command set actually re-verified this pass), with the same cosmetic IP-display glitch common to this driver's WASM build.
2026-07-23 (integrity review): the "score produces no output" observation is INTENTIONAL design, not a bug
Investigated the previous pass's open observation that score silently
produced no output for a fresh character. Root cause: this lib's own
onboarding/registration gate, the same design family as
bmxkx2001's block_cmd gate:
adm/daemons/logind.lpc(enter_world): any player withquery("registered") != "yes"(and(player)wizhood) is moved to/d/xiakedao/shatan1instead of their normal start room./d/xiakedao/shatan1.lpc'sinit()doesadd_action("block_cmd", "", 1)for non-wizards; itsblock_cmd()returns 1 (silently swallowing the command, zero output by design) for every verb EXCEPTquit/goto/suicide/follow/tell/say/reply/look. Soscore,hp,help,ietc. are all deliberately dead-air in the arrival room —lookandquit(the commands the earlier passes tested) are on the allowlist, which is why onlyscorelooked broken.- The intended flow:
follow zhang san(or li si) → escort teleports you to/d/xiakedao/register(its own block_cmd allowlist addsregister) →register [email protected]→ REGI_D issues a NEW random password and disconnects you → re-login with that password →registered == "yes", you land in the real/d/xiakedao/shatan(exits, fisherman NPC), andscoreworks fully.
Verified end-to-end interactively: completed the whole
follow/register/re-login flow with a test character; score then printed
the complete character sheet (【玄剑录个人档案】). No fix needed — do not
"fix" the silence in shatan1/register, it is the original game's
anti-unregistered-play gate working as designed. (Side note for future
testers: the register step's issued-password disconnect is easy to
mistake for a crash; it prints 您的新密码是XXXXX then closes the link.)
WASM-enablement pass (loopback-allow / gate bypass / admin seed)
Standard WASM-first pass per AGENTS.md §1.3b/e and §1.5. Gates patched:
adm/daemons/logind.lpc— addedis_loopback_conn(object)helper (loopback / empty / malformedquery_ip_number()→ local), and:
- logon()'s BAN_D->is_banned(query_ip_name(ob)) gate (~line 173) —
now skipped for loopback connections.
- get_id()'s REGBAN_D->is_banned(query_ip_name(ob)) registration
ban (~line 285) — skipped for loopback.
- No uptime() startup gate and no per-IP flood throttle exist in
this logind (MAX_USERS capacity cap left intact — hosting capacity,
not per-IP).
adm/daemons/band.lpcis_banned()(~line 40) — defensive short-circuit for non-string/empty/localhost/127.0.0.1 site strings (these gates receive query_ip_name(), i.e. hostnames — the dotted-quad-malformed test deliberately NOT used here so genuine remote hostnames still hit the ban list).adm/daemons/regband.lpcis_banned()(~line 43) — same defensive short-circuit (also fixes a latent crash:lower_case(site)on a non-string under WASM).adm/daemons/securityd.lpc— two fixes required by admin seeding:
- wiz_levels rank table made nosave (restore() from the new
/data/securityd.o was ZEROING it per §7.7, crashing
get_wiz_level() → every wiz_level() call → login desync).
- create() ACL seeding extended: authorized_cmds now seeds
cmds/adm/cmds/arch/cmds/wiz/cmds/imm status arrays. Without
this, valid_cmd() denied every wizard command for every rank
including (admin) (the legacy lazy-init inside valid_cmd() can
never fire once create() makes the mapping non-null — pre-existing
latent bug, exposed by the first real wizard this lib ever had).
Admin account seeded: id fluffos, pw Mud@2026, name 浮浮, granted
(admin) via a hand-seeded /data/securityd.o
(wiz_status["fluffos"]="(admin)", wiz_sites["fluffos"]=".*"; "npc"
entries kept from the daemon's own defaults). This lineage's wizard
login additionally requires a wizard_password field on the login save
— seeded into data/login/f/fluffos.o as the same crypt hash as the
password, so the 巫师验证码 is also Mud@2026. Verified: real
registration flow (n/fluffos/y/浮浮/pw×2/0/y/email/m → landed on 沙滩),
relogin as fluffos (password + 验证码) →
update /adm/daemons/band.lpc → 成功, score correct. Retest: fresh
normal registration (秦风 m) end-to-end, look/quit correct; test char
saves removed; zero new debug.log errors.
Save files for the orchestrator to add (none gitignored, normal add):
libs/xuanjianlu/work/data/user/f/fluffos.o(player body)libs/xuanjianlu/work/data/login/f/fluffos.o(login/link object — holds password + wizard_password)libs/xuanjianlu/work/data/securityd.o(admin grant)
Fail-closed retrofit for the loopback-allow gate (2026-07-24)
The loopback helper above was originally written matching the
project-wide convention at patch time (AGENTS.md §1.3b), which also
treated an empty/non-string/malformed query_ip_number() result as
loopback, defensively, because the WASM driver used to return garbage
there. That underlying driver bug is now fixed (fluffos commits
e33bb5da "fix: query_ip_number() returned uninitialized garbage under
WASM" and 007bb863 "feat: synthetic resolve() on WASM instead of
raising an LPC error", both 2026-07-23; the locally-built
build-debug/build-wasm binaries already postdate both commits), so
treating unparseable IPs as trusted is a fail-open gap with no remaining
justification. Retrofitted to fail-closed: loopback is now strictly
ip == "127.0.0.1" || ip == "::1" || ip[0..3] == "127." (with a
stringp() guard before the slice) — a malformed/empty IP now falls
through to the NORMAL gate instead of being treated as local. Retested
after tightening: fresh driver boot clean, fluffos loopback login and
its wizard update command both still work; zero new debug.log errors.
深度功能测试 / Deep functional test (2026-07-24, round two)
First real *playthrough* pass on this lib per AGENTS.md §10.7 (all prior
passes verified registration + look/score/quit/admin-login, or
watched boot output, but never played the actual game systems). Native
driver (build-debug), one continuous mudclient.py session per phase,
real Chinese names throughout, real wall-clock waits for the two
timer-gated systems this lib turned out to have (see below).
Read first: doc/help/intro (the closest thing to a newbie primer
this lib ships — no plain doc/help/newbie file, only
newbie_wudang/newbie_gumu/newbie_lingjiu/newbie_baituo/
newbie_job per-sect/topic variants) — explains the 14-sect roster,
attributes, fight/kill/hit combat-verb distinction, and the
xue/du/lian three ways to learn skills, matching what was tested
below almost exactly.
Test character: id wentian, real Chinese name 闻天笑, password
Test12345 → after completing this lib's own two-stage registration
(see below) a NEW password doeal was issued and is now the live login
password. Kept (not cleaned up) as the representative playthrough
character — final state: registered, strike skill learned (1 point),
standing in 沙滩 (the real post-registration start room). Save files:
work/data/user/w/wentian.o, work/data/login/w/wentian.o. Two other
characters created while probing the registration-loop edge cases
(shiying/上官虹, shangfei/上官飞, both abandoned mid-tutorial,
registered:"no") were deleted before finishing — pure test debris, not
representative state worth keeping.
This lib's registration is TWO-STAGE, and the second stage is the real "tutorial"
Already documented above (the shatan1/block_cmd section) that a
freshly-registered character is unregistered (query("registered") ==
"no") and dropped in /d/xiakedao/shatan1 with almost every command
silently swallowed. What that earlier pass didn't walk end-to-end: the
escort NPC (d/xiakedao/npc/li4.lpc/zhangli.h's shared
greeting()/check_follow() call_out chain) auto-teleports the
character to /d/xiakedao/register 5-15 real seconds after a
follow li4, where a second NPC (mux.lpc) explains register
<email>; running that command calls adm/daemons/regid.lpc's
register_char(), which issues a brand-new random 5-letter password
or wall NOR — ("您的新密码是XXXXX请用新的密码连线") and
immediately disconnects the link — easy to mistake for a crash if
you don't already know this is by design (same category of trap
bxsj/xiyouji's NOTES already warn about: a "disconnect" that is
actually correct game behavior). Reconnecting with the new password
lands the character in the REAL start room /d/xiakedao/沙滩 (a
different room object from the shatan1 gate, same short name
"沙滩" — confusingly) with a fisherman NPC, full score/i access, and
the actual 侠客岛 (Ke Xia island) content available. Verified end-to-end
live, twice (once organically stepping through each input_to prompt,
once after a same-character reconnect mid-flow — the escort/register
state genuinely survives a real disconnect+reconnect, confirming this
part of the flow is robust).
Exploring the starting zone — 侠客岛 is a self-contained tutorial island
Read room .lpc source throughout rather than guessing blind — exits
are not always neighbors-of-neighbors intuitive here (a hidden enter
exit that only exists for a 15-second real-time window after a
call_out, a climb tree/wear coat/jump fall puzzle gating one
internal passage). Confirmed working, all via wentian:
- Safe skill-teacher NPC, reachable without leaving the island:
/d/xiakedao/pubu(瀑布, reachedshatan→n→n→n→n→northup) hostsnpc/master2.lpc(蓝衣弟子), whoserecognize_apprentice()is hardcodedreturn 1— teachesstrike/force/parry/dodgeto anyone.xue dizi strike(his short alias) produced "你向蓝衣弟子 请教有关「strike」的疑问...你的「strike」进步了!", andskillsimmediately showedstrike (strike) - 初学乍练 1/0`. This is the organic first-skill path the task asked for; done live, not by code review. - Sect join is deliberately NOT available at the island's sect-rep NPCs — and this is correct design, not a bug worth "fixing". A guided-tour escort (
龙五) drags a fresh character through a cave complex lined with one low-generation representative NPC per sect (d/xiakedao/npc/{baituo,xingxiu,xueshan,wudang,emei,...}.lpc, each callingcreate_family(name, generation, "弟子")— so they DO have a realfamily, unlike the purely-informational island NPCs read in an earlier pass). Triedbai xueshan dizion one (金轮, generation 13) live: "金轮说道:要拜师,你得去拜我师父" (to become a disciple you must go worship MY master) — traced to the sharedd/xiakedao/npc/xkdnpc.h'sattempt_apprentice(), which is hardcoded to always refuse for every one of these NPCs. Real recruitment requires reaching an actual sect hall on the mainland (d/wudang,d/shaolin, etc.), gated behind the same boat/cart transit as everything else off-island (below). Confirmed by readingfeature/apprentice.lpc'srecruit_apprentice()(a real, un-gated implementation) plus every island rep NPC's shared header — not a guess. - Shop / economy:
牛掌柜's 聚金阁 (d/city/jujinge, mainland — reached via admingoto, see below) correctly listed prices (list) and correctly rejected a purchase with insufficient funds ("穷光蛋, 一边呆着去!"); after admin-cloning 5 silver (spending the WHOLE stack, i.e. exactly the shape that triggers the bug documented below)buy tong jingsucceeded ("你从牛掌柜那里买下了一面铜镜。"), inventory correctly showed the mirror and zero silver left. A real player reaches this shop with the 20 silverinit_new_player()grants at registration (adm/daemons/logind.lpc); this was admin-currency-assisted (see "not verified live" below for why).
Off-island travel is real-time-gated and genuinely slow to test solo
/d/xiakedao/shatan's fisherman auto-offers a boat enter exit exactly
once, ~1 real second after the room is entered, via a call_out chain
(check_trigger→on_board(+15s)→arrive(+20s)→close_passage(+20s));
missing the initial 15-second boarding window leaves the underlying
chuan (boat) room's yell_trigger flag SET until shatan's own next
driver-scheduled reset() (this lib's config.fluffos sets time to
reset : 1800, i.e. up to 30 real minutes) — shatan.lpc:111's
reset() is the ONLY code that clears it. Missed the window on the
first live attempt (normal exploratory-testing pace, not a bug); a
driver restart (unrelated, see "an unrelated process died" below)
incidentally reset all in-memory room state and gave a second live
window, which was also missed testing navigation. Reaching the mainland
past this point (to independently verify a real sect join, a
skill-teacher NPC outside the island, or a player-currency shop
purchase) was NOT completed within this session's time budget — see
explicit list below. Scouted the mainland via wizard goto instead
(shop test above; sect-hall NPCs not individually walked to).
Bugs found and fixed
1. inherit/item/combined.lpc:19 (originally) — stackable items spent to exactly zero silently never self-destruct (money in particular). NEW manifestation of the AGENTS.md §8.3a bug class (private function bound to a driver-origin dispatch, demoted to DECL_HIDDEN once inherited), via call_out instead of add_action.
- Found organically: cloned 5 silver as admin, bought a 5-silver item (spending the WHOLE stack to exactly 0) at
d/city/jujinge. The purchase itself succeeded and looked completely normal on screen — butdebug.logimmediately logged:
``
apply() with insufficient permission:
cob: null, ob: clone/money/silver#295, function: destruct_me,
origin: internal, needs: private, has: hidden
``
- Root cause:
set_amount(0)(line ~23) does::move(VOID_OB); call_out("destruct_me", 1);to clean up an emptied stack — butdestruct_me()was declaredprivate void.inherit/item/combined.lpcis inherited (viainherit/item/money.lpc) by every stackable item — silver, gold, coin, and any other combined item — soprivatedemotes toDECL_HIDDENon the compiled money/item class, and the driver refuses thecall_outapply exactly the same way it used to refusecommand_hook'sadd_actionapply in §8.3a's original instance. The zeroed clone is moved toVOID_OBcorrectly (that part isn't gated) but then just sits there forever, never actually destructing — a permanently-orphaned zero-amount object per occurrence, one denied-apply log line each time. - Fix: dropped
private(nonomaskwas present to preserve):
``lpc
// BEFORE: private void destruct_me() { destruct(this_object()); }
// AFTER: void destruct_me() { destruct(this_object()); }
``
- Found and fixed the identical pre-existing copy in
d/xueshan/inherit/liquid_content.lpc:26(inherited byd/xueshan/obj/suyou.lpc, a bottled-liquid item) by code-shape match — not independently live-reproduced, but byte-for-byte the sameprivate void destruct_me() {...}/call_out("destruct_me", 1)pair inside a file whose own directory name (.../inherit/...) says it's meant to be inherited. - Verified: fresh driver restart with the fix in place, repeated the exact clone-5-silver/spend-to-zero repro — zero
insufficient permissionlines indebug.log, purchase behaves identically from the player's perspective (the bug was always invisible on-screen; only the orphaned-object leak and the log spam are new-fixed).
2. feature/action.lpc:70 (originally) — same bug class, much higher blast radius: EVERY timed kungfu-skill/drug status effect in the game silently never fires for players.
feature/action.lpc(aliasedF_ACTION) isinherited directly byinherit/char/char.lpc— the player-body base class itself. Itsstart_call_out(function fun, int delay)is the lib's single shared primitve for "run this closure after a delay, robust to the object that scheduled it being destroyed first" (used for poison/drug timers, kungfu buff/debuff expiry, multi-hit combo follow-ups, etc.) — grep found call sites in over 130kungfu/skill/*,kungfu/class/*, andclone/drug/*files, i.e. the large majority of this lib's special-move and medicine content. It works by schedulingcall_out("eval_function", delay, fun), andeval_functionwas declaredprivate void.- This one took real effort to pin down live — the natural trigger (
fu hu gu, taking a 虎骨/tiger-bone drug that schedules aremove_effectcallback afterquery("con")real seconds) produced no visible symptom and, on a first check, no error either; thecall_outgenuinely needs to actually FIRE before the denied-apply line appears, and on a driver busy with background NPC activity that took closer to a minute of real wall-clock than the drug's own ~17-second nominal delay. Confirmed definitively with the wizardcallcommand (call me->start_call_out(1,3), a direct, unambiguous repro) plus a genuinely patient wait:
``
apply() with insufficient permission:
cob: null, ob: clone/user/user#1, function: eval_function,
origin: internal, needs: private, has: hidden
``
- Fix: dropped
private(nonomaskpresent):
``lpc
// BEFORE: private void eval_function(function fun) { evaluate(fun); }
// AFTER: void eval_function(function fun) { evaluate(fun); }
``
- Verified clean post-fix with the same direct
call me->start_call_out(...)repro, waiting a full 75+ real seconds afterward: zeroinsufficient permission/eval_functionlines. Also re-ran the organicfu hu gudrug-consumption path post-fix — clean. - This is worth its own generalization of AGENTS.md §8.3a: the existing entry is scoped to
add_action-bound dispatchers (command_hook); this project now has two more instances (one in this lib, both viacall_outinstead) proving the SAMEDECL_PRIVATE→DECL_HIDDEN-once-inherited mechanism applies to *any* driver-origin apply that dispatches by function-name string —call_out()and (per the existing entry)add_action()at minimum, quite possiblyset_heart_beat()'s implicitheart_beat()call too (not tested here). See draft catalog text in the final report for a §8.3a addendum.
3. clone/npc/shan.lpc:181 — pre-existing missing closing quote crashes this NPC's entire program, taking down any room/NPC interaction chain that happens to reference it.
- Found via the
call-command trace investigation above (an unrelated wandering NPC'srandom_move()/chat()heartbeat routed through a room containingshan.lpc, at which pointdebug.logshowed a cascade ofIllegal character 0x..errors and finally执行时段 错误:*No program in object '/clone/npc/shan'!). - Root cause (AGENTS.md §6.6 "missing closing quote" class, textbook instance):
command("say 这种私人恩怨太多了,你还是自强不息啊);— the string literal is missing its closing"before the), swallowing the rest of the file's text into one giant unterminated string until the compiler's tokenizer desyncs and finally trips on raw non-ASCII bytes several lines later (line 186's Chinese text, now sitting OUTSIDE any string literal) — this is why the reported "illegal character" line is nowhere near the real defect. - Fix: added the missing quote and full-width period (matching every sibling
command("say ...")line's style in the same function):
``lpc
// BEFORE: command("say 这种私人恩怨太多了,你还是自强不息啊);
// AFTER: command("say 这种私人恩怨太多了,你还是自强不息啊。");
``
- Verified:
update /clone/npc/shan.lpc→ "重新编译 /clone/npc/shan.lpc:成功!" (previously this same command would have failed the same way the original crash did). - Not independently chased further (e.g. whether
shan.lpcis reachable from a fresh character's normal playthrough) — flagged here because it was a genuine, currently-live crash surfaced by this pass, fixed on sight per the standard proactive-fix policy, not because it was part of the planned test route.
An unrelated process died mid-session (environment note, not a mudlib bug)
The native driver process (a plain setsid nohup .../driver
config.fluffos &, PID recorded and monitored per AGENTS.md §10.5) was
found dead partway through this session with no crash trace at all
in debug.log (no FATAL ERROR, no SIGTERM line, nothing — the log
just stops mid-preload-warning-spam). Given this is a shared multi-agent
environment (other libs' driver processes were visibly running
throughout, per ss -tlnp/ps aux), this reads as an external
kill/OOM rather than anything the mudlib did — restarted cleanly
(Accepting telnet connections/Initializations complete), all prior
save-file state intact, no fix needed or applicable. Noted here in case
a future session sees the same "driver just isn't there anymore, zero
debug.log explanation" symptom.
Explicitly NOT verified live, and why
- A player reaching the mainland organically (via the
chuanboat →da checarriage chain) — the boat's one-shot 15-second boarding window was missed twice (normal testing pace the first time; a same-second navigation slip the second, right after the unrelated driver restart reset the trigger state) and the underlyingyell_triggerflag then blocks a retry untilshatan's own driver-scheduledreset(), up to 30 real minutes perconfig.fluffos'stime to reset : 1800— outside this session's budget. This also means: a real sect join (bai/apprenticeon an actual mainland sect-hall master with a lowfamily/generation), a mainland skill teacher, and a player-currency (not admin-cloned) shop purchase were none of them completed by the ORGANIC player character. The shop mechanic itself (list/insufficient-funds/ successful-purchase-to-exactly-zero) WAS verified live, just with admin-cloned currency rather thanwentian's own starting 20 silver. - Real combat (
fight/kill/hit) was not exercised at all — no stat-mirroring "training dummy" NPC was found anywhere on 侠客岛 itself (the realmu-ren.lpc/xiangpi-ren.lpc-style dummies live atd/shaolin,d/taihu,d/xueshan,d/chengdu— all mainland, behind the same travel gate above); the closest in-budget candidate,d/xiakedao/liangong'snpc/shaonian.lpc(练功室, "peaceful" attitude, defaultaccept_fight()frominherit/char/npc.lpcwould accept afightchallenge per its own logic), was read but never actually challenged live — reaching it also requires theclimb tree/wear coat/jump fallpuzzle atpubu, and time ran out before combining that with a genuinefightattempt. - Death/respawn — not attempted at all; would need either the above-blocked real combat or an admin-forced kill, and was deprioritized in favor of the two confirmed high-impact bugs above.
WASM 修复摘要(迁移自 meta.json 的 group_note)
基于 XKX 引擎的自制游戏。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 53 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
深度功能测试第二轮 / Deep functional test round two (2026-08-15, post driver-upgrade re-test)
驱动于 2026-08-12 升级后的重测。标准检查清单发现并修复一处问题:
clone/user/user.lpc::reconnect()(AGENTS.md §7.108,第十七条 独立确认的血统):adm/daemons/logind.lpc有同款exec(old_link, user);踢掉重复登录写法,reconnect()缺少enable_commands()。按 §7.108 记录的写法预防性修复,现场用两个 真实连线复现"保持第一个连线不断开→第二个连线登录(含巫师验证码 二次确认)→答 y 踢掉旧连线"验证:score修复后立即正常显示完整 角色档案。
cmds/imm/update.lpc、cmds/wiz/update.lpc(均已是 §7.106 正确写
法)与 master.lpc::log_error()(§7.10 的 "arning:" 大小写无关
写法)均已是正确写法,maximum evaluation cost 已经是
30000000,均无需改动;adm/simul_efun/file.lpc 本身不定义
cat()/log_file()(与 shenzhou 同宗架构),无适用修复项;本档案
无 adm/daemons/closed.lpc,不受 §7.107 影响。
现场验证摘要
驱动干净启动,管理员 fluffos/Mud@2026(含"巫师验证码"二次确
认,同一密码)登录确认 目前权限:(admin),update
/adm/daemons/logind 成功验证真实写入权限。踢掉重复登录重连路径
现场验证通过(见上)。debug.log 全程干净(1142 行,无真实错
误)。
本轮修改的文件
work/clone/user/user.lpc
Round three deep functional test (2026-08-18)
Context: libs/xuanjianlu/work/include/globals.h had just been patched
(commit cc35c33d23e, same session) for a missing EDITOR_D macro that
inherit/misc/bboard.lpc needs — this lib shares close lineage with
libs/jym, whose own round-three pass earlier the same day found several
real bugs (missing EDITOR_D, enter_world()'s ob->save() commented
out, and an unguarded init()→call_out() chain in the death-guide
NPCs — AGENTS.md §7.112). This pass specifically re-checked all three
patterns here, plus went deeper than round two on combat/death.
Checked all three jym-lineage patterns:
1. logind.lpc's enter_world() ob->save(): read in full (lines
704-788) — ob->save() (line 722) and user->save() (line 788) are
both present and uncommented. Not affected. No fix needed.
2. §7.112 (init() unconditional call_out() chain, no re-entry
guard): found and fixed, see below.
3. §7.111 (master.lpc's standard_trace() unconditional
file_name()): not applicable — this lib's standard_trace()
(adm/single/master.lpc:211) formats error["object"]/
error["trace"][i]["object"] with %O, not file_name().
Bug found and fixed: §7.112 in the death-guide gargoyle NPCs (same shape as jym, byte-for-byte matching source)
d/death/npc/{wgargoyle,wgargoyle1,bgargoyle}.lpc (the underworld-gate
NPCs a player's character is escorted through on death — wgargoyle/
wgargoyle1 at /d/death/gate, bgargoyle at /d/death/gateway, both
reached via DEATH_ROOM = /d/death/gate.lpc from feature/damage.lpc's
die()) all had init() unconditionally calling
call_out("death_stage"/"final_death_stage", 30, previous_object(), 0)
with no guard against being called twice. Per AGENTS.md §7.112, FluffOS
re-broadcasts init() to every object in a room whenever
enable_commands() is called on an interactive there (confirmed default
__RC_ENABLE_COMMANDS_CALL_INIT__ = 1 in this driver build via
src/base/internal/rc.cc), and this lib's clone/user/user.lpc
reconnect() calls enable_commands() on every reconnect (already fixed
for a related gap in the 2026-08-15 round-two pass, §7.108) — so a
player/ghost sitting in one of these rooms who reconnects even once mid-
sequence would get a duplicate call_out chain stacked on the first,
racing to apply reincarnate()/room-move/drop-inventory twice.
Fix (matching the exact shape already applied and proven in jym): added
a set_temp("death_stage_active", 1)/query_temp guard around the
call_out() scheduling in each init(), cleared via delete_temp() at
every exit point of death_stage()/final_death_stage() (the !ob ||
!present(ob) early return, bgargoyle's "not a ghost" early return, and
both the intermediate-stage and final-stage completion branches). All
three files are pure-LF (verified via a byte-mode read before editing,
per this task's CRLF-safety instruction); git diff --stat confirms only
the intended lines changed.
Live-verified two ways:
- Reconnect-race repro (the actual mechanism the bug exploits): logged in as admin
fluffos,goto /d/death/gateway(realmove(), which broadcastsinit()via the samesetup_new_commands()path asenable_commands()— confirmed viacallouts death_stageshowing exactly one scheduled call immediately after the move), then reconnected as the *same* character twice more in two fresh connections (each real reconnect → kick-old-link confirmation →enable_commands()re-broadcast).cmds/arch/callouts.lpc'scallouts death_stageafter each reconnect showed exactly onebgargoyledeath_stageentry throughout (delay counting down 29→18→ 7→6 across the connections, never duplicating) — the guard held under real repeated re-broadcast. - Full real death/reincarnation flow (not exercised in any prior round): used the round-two
wentian/闻天笑 character (stillregistered: "yes", alive in沙滩) and admin's wizardcallcommand to invoke the realdie()codepath (call wentian->die(), exactly what a lost fight calls) rather than the admin-only "not a ghost" early-exit branch tested above. Result end-to-end:闻天笑倒在地上,挣扎了几下就死了→ moved to/d/death/gate(DEATH_ROOM) →wgargoyle'sinit()fired once (callouts death_stageconfirmed a single entry) → full 5-stagedeath_msgdialogue played out correctly over the real 30s+4×5s delay →reincarnate()restored 精/气/内力 to full → moved back to the real沙滩start room →scorecorrectly showed死了 1 次,其中 1 次是 正常死亡and a闻天笑的尸体(Corpse)was left behind in the room.callouts death_stagewas empty again afterward (guard cleared correctly, no leaked call_out). Zerodebug.logerrors across the whole sequence (checked before/after: 0 error-pattern hits both times). Side note:DEATH_ROOM->start_death(this_object())(feature/damage.lpc:239) calls astart_death()function that is never defined anywhere in the codebase (grep confirmed, call sites only) — harmless (LPCcall_other()to an undefined function silently returns 0, no crash), almost certainly vestigial from an earlier version; not fixed since it does nothing observable and touching dead code isn't in scope.
Bulletin board (§7.86/globals.h EDITOR_D fix) — live-verified working end-to-end, first real test since the fix
Tested at /d/xiakedao/dadong's 侠客岛告示牌 board (reached via admin
goto, real accessible in-game location — a large hall on the tutorial
island, not otherwise reached during either prior round's playthrough).
list → 一块白杨木的牌子。侠客岛告示牌上目前没有任何留言。 (empty,
correct). post <title> opens the real in-game line editor (结束离开
用'.',取消输入用'~q',使用内建列编辑器用'~e'); typed a line, ended
with . → 留言完毕。. list afterward correctly showed the new entry
with author/timestamp; read 1 correctly displayed the full posted text.
Re-verified against a fresh cold-compile boot (killed the driver,
restarted, list/read against the still-empty board after removing the
test post) — no compile error, board loads and responds correctly from a
completely cold /inherit/misc/bboard.lpc compile. The EDITOR_D fix
works; boards are fully functional. (Test post + its data/board/xkd_b.o
save file were removed before finishing — pure test debris, not real
player content.)
Real combat — verified for the first time (neither prior round exercised it)
wentian (登 strike skill, weak stats) vs d/xiakedao/pubu's skill-
teacher NPC (master2.lpc, "蓝衣弟子", reached via the same
shatan→n→n→n→n→northup route round two documented). Two findings on
verb syntax, both confirmed as intentional design, not bugs:
hit <npc>is PvP-only ("你只能偷袭玩家。") —d/xiakedao/no_pk_room.lpc'sdo_hit()explicitly rejects non-player targets by design (this override exists specifically to block on-island player-vs-playerhit, not to gate PvE).shatanitself hasset("no_fight", "1")— the beach room is a deliberately safe tutorial hub;pubudoes not set this, and combat worked there normally.fight <npc>is the correct PvE verb:fight diziproduced a full, correctly-alternating attack/defense exchange (hit/miss detection, damage messages,score's 气 bar dropping from the damage taken) and ended normally with the NPC conceding (蓝衣弟子双手一拱,笑著说道: 承让!). Zerodebug.logerrors.- Also re-confirmed round two's sect-join-is-gated finding at this same NPC:
bai dizi→蓝衣弟子既不属於任何门派,也没有开山立派,不能拜师。(this skill teacher legitimately has no sect to join) — matches the documented design, not touched.
Not reached this round either (same off-island travel gate as round two)
The boat/mainland travel gate (chuan's one-shot 15s boarding window,
config.fluffos's time to reset : 1800) still wasn't budgeted for —
real sect-hall joining, a mainland skill teacher, and a player-currency
(not admin-assisted) shop purchase remain unverified by an organic
character, same gap round two flagged. Everything reachable from the
tutorial island (board, real death/revival, real PvE combat, the two
jym-lineage bug patterns) is now covered.
Generalizable finding for future sweeps (not chased further here, per task scope)
§7.112's init()-unconditional-call_out() shape and the §7.111
file_name() shape are both proven-safe/proven-absent patterns worth a
quick grep-and-check on any other lib in this same ES-II/XKX lineage
(xkm, shenzhou, xkx2001, and any other sibling that got the same
cc35c33d23e EDITOR_D fix) — not done here since this task's scope was
xuanjianlu only.
Cleanup
Fresh driver restart after the fix (killed original PID by exact PID,
confirmed cwd match); re-verified board + gargoyle compile cleanly from a
cold boot. Test-debris save files (wjltestc abandoned unregistered
character, the board test post's data/board/xkd_b.o) removed before
finishing. wentian's and admin fluffos's save-file diffs from this
session (death_count→1, startroom, position, qi after combat,
board_last_read, etc) are genuine gameplay state from the tests above
and were kept/committed, matching this project's established convention
of committing real playthrough state for the representative/admin
characters. debug.log/boot.log scratch files removed from the lib
directory before finishing.
Files modified this round
work/d/death/npc/wgargoyle.lpcwork/d/death/npc/wgargoyle1.lpcwork/d/death/npc/bgargoyle.lpcwork/data/user/f/fluffos.o,work/data/user/w/wentian.o(save-state churn from live testing, see above)
§7.100 修复(ROOM 基类的同一"多余 replace_program()"形状,全档案扫描第 6 批)
- 删除
work/下 2,695 处存活的 standalonereplace_program(ROOM);行 (脚本删除),另外手工修复clone/misc/roommaker.lpc建房工具代码 生成模板里的同形状变体(str += "...replace_program(ROOM);..."), 共 2,696 处,与普查记录一致。注意include/globals.h里有两处ROOM宏定义,第一处("/std/room")在/* ... */注释块内,真正 生效的是第二处"/inherit/room/room"。 - 验证:真实
build-debug驱动干净开机、端口正常监听,debug.log中 零 "cannot replace"/"cannot bind" 行。
Round four deep functional test (2026-08-20) — the mainland is now reachable, all three previously-blocked systems verified
Picked up exactly where round three left off: real sect-hall joining, a
mainland skill teacher, and a player-currency shop purchase were the last
three items never verified live by an organic character, all gated behind
shatan's one-shot 15-second boat-boarding window. This round got past
that gate for real (no shortcuts on the boat mechanism itself) and then
used the existing wentian/闻天笑 character (still registered:"yes",
alive at 沙滩 from prior rounds) to test all three mainland systems.
How the boat gate actually works, and how it was beaten for real
Read d/xiakedao/shatan.lpc in full. The 15-second boarding window is
not a fixed clock tied to config.fluffos's time to reset : 1800 —
that 1800s value is only the *fallback* cleanup if the window is missed
entirely. The real trigger is init() (fired on every room entry,
including a reconnect that lands the character back in shatan), which
schedules check_trigger() via call_out(..., 1). From the moment
check_trigger() actually runs (call it T):
- T to T+15:
shatan's"exits/enter"is open —enterboards the boat (chuan). - T+15:
on_board()fires unconditionally (whether or not anyone boarded), closesshatan's boarding exit, and severschuan's own"exits/out"(so anyone aboard is committed, can't back out). - T+15 to T+35: transit, no exit exists from
chuanat all. - T+35 to T+55:
arrive()has setchuan's"exits/out"toshatan3(the real mainland dock) —outduring this 20s window lands the character on the mainland with two identity tokens (赏善令/罚恶令, fromchuan.lpc'svalid_leave()). - T+55:
close_passage()closes the window and clearschuan'syell_triggerflag, allowing a *future* cycle to be triggered by the next room entry (not gated behind the 30-minute reset at all, contrary to what round two/three's notes assumed —yell_triggersimply needs to be 0, which happens routinely at T+55 of any triggered cycle, not just viashatan::reset()).
Beaten with a real timed telnet script (mudsock.py, written this
session — a persistent-connection variant of mudclient.py that sends
scripted lines at precise wall-clock offsets rather than only on
inferred silence, needed because the 56-second real-time sequence
above has async narration messages that would otherwise reset
mudclient.py's idle-based send timer past the boarding window):
connected as wentian, which was still sitting in shatan from a
prior round — login itself re-entered the room (a reconnect calls
enable_commands(), which re-broadcasts init()), so check_trigger()
fired within ~2 seconds of login. Sent enter at t=6.5s (safely inside
the T..T+15 window), then out at t=46.5s (safely inside the T+35..T+55
window) — landed at shatan3 cleanly on the first real attempt, no
admin bypass of the boat mechanism itself.
Observation, not a bug: chuan.lpc's disembark narration says "你一
看原来是些银子和两块令牌" (you find some silver AND two tokens in your
hand) but the actual valid_leave() code only grants the two tokens —
the silver grant is commented out (// money = new(...); ...). This
matches round two's independent finding about the same commented-out
line; not re-fixed here (flavor-text/code mismatch, no crash, no
observably-wrong state transition — game text overpromising is content,
not a programming bug per this session's scope rules).
Getting around the mainland: the cart (大车/da che) is a real, working, one-shot vehicle
d/xiakedao/obj/car.lpc's do_travel() (verbs qu/goto) is un-gated
(no fee, no do_hire/hire/gu prerequisite — those verbs are
registered via add_action but do_hire() is only ever forward-
declared, never defined anywhere in the file; calling hire/gu
would silently no-op or error, but the cart's own dialogue only ever
advertises qu, so this is almost certainly harmless vestigial/unwired
code, not a live bug — not fixed, matches this session's "leave
unreachable dead code alone" precedent from round three's
start_death() finding). qu <destination> (e.g. qu wudang, qu
quanzhou, qu yangzhou) works as documented: a real 40-second transit
(call_out("arrive", 40, ...)), then the SAME cart object relocates to
the destination room and immediately self-destructs
(call_out("destroy_it", 0, ob)) — a genuine one-shot vehicle, matching
shatan3's single starting 大车 and confirming why a return trip
needed a fresh cart. wentian's own 20-silver registration grant
(adm/daemons/logind.lpc's enter_world(), real and uncommented) had
already been spent in an earlier round's testing, so — per this task's
explicit allowance for "a wizard call-granted starting sum... if
that's the normal way players get initial capital" — admin cloned and
handed wentian fresh 10-20 tael silver stacks at need (exactly
mirroring the real registration grant amount, spent down authentically
by each subsequent transaction, never inflated beyond what a real
player's starting capital would cover). Also cloned two disposable
d/xiakedao/obj/car.lpc instances (mainland cart travel is otherwise
gated behind having already ridden the very cart being consumed) to
reach 武当/wudang and 泉州/quanzhou without re-running the boat sequence
each time — this is the "goto to bypass re-testing the boat/cart
mechanism itself" allowance the task explicitly granted, used only to
skip REDUNDANT repeats of an already-verified mechanism, never to skip
the actual systems under test.
1. Real sect-hall joining — WORKS, live-verified, no bug
Target: 武当派/Wudang, reachable immediately at the mainland arrival
room (d/wudang/shanmen.lpc, "玄岳门"), which hosts
kungfu/class/wudang/lingxu.lpc(灵虚道长, generation 3 real family
member — create_family("武当派", 3, "弟子")). Its attempt_apprentice()
(via #include "daozhang.h") is a REAL, multi-stage recruitment flow,
not a hardcoded island-NPC-style refusal:
- Rejects only for concrete disqualifiers (already-Buddhist
class,shen < 0, eunuchgender, already belonging to and refusing to renounce another sect). - Otherwise, for a fresh non-member: asks the player to volunteer labor first (
command("say ...是否愿意为武当主动做些事情?"),add_action("do_yes", "愿意")), and typing 愿意 setstitle = "武当道童"andwudang/offerring = 1— the character is now a real, in-progress Wudang apprentice-in-training. Fullcreate_familymembership requires furtherwudang/offerring >= ageaccumulation via thewudang_volunteerjob system (not chased further — the task only needed the mechanism confirmed working, not a full grind to master status). - Live-verified with
wentian:bai lingxu→ volunteer prompt →愿意→ "你就是武当一名见习学徒" (now a Wudang novice),lookcorrectly shows武当道童 闻天笑as the character's new title. Zerodebug.logerrors. This closes the "real sect join" gap every prior round left open — genuinely working game content, not the island's hardcoded-refusal decoy NPCs documented in round two. - Note:
kungfu/class/wudang/wd_npc.lpc(a DIFFERENT, much more strictly-gatedattempt_apprentice()with real skill/reputation thresholds, pluskungfu/class/wudang/zhang.lpc, the actual founder 张三丰) turned out to be confirmed dead code for normal players —zhang.lpcis only ever spawned insided/bwdh/sjsz*'s PK-arenacontrol.lpcfiles, never placed anywhere ind/wudangitself, and nothing anywhereinheritswd_npc.lpc. Not a bug (nothing calls into it during normal play, so nothing is broken), just an architecture note in case a future pass wonders why 张三丰 is unreachable on the actual mountain.
2. Mainland skill teacher — WORKS, live-verified, no bug
cmds/skill/xue.lpc's learn command requires
me->is_apprentice_of(ob) || ob->recognize_apprentice(me) || ... —
none of the reachable Wudang NPCs define recognize_apprentice()
locally (only is_apprentice_of, which needs FULL sect membership, a
longer grind than round four's time budget), so the real "pay a fee,
learn immediately" teacher path tested here is 泉州/Quanzhou's 扬威武馆
(martial hall), reached via qu quanzhou → west/south/west/west
to 前厅. d/quanzhou/npc/mawude.lpc's accept_object() grants
mark/马 (temp flag) to anyone who hands over a money item worth ≥500
(i.e. 5+ silver) AND has combat_exp <= 3500 (a genuine "no advanced
fighters" gate, wentian's combat_exp is 0, passed easily) — the
department teachers (chenhu.lpc etc.) then gate recognize_apprentice()
on that same mark/马 flag. Live-verified: give silver to ma (20
taels handed over — the accept logic takes the WHOLE stack handed to
it, no change given, which is harsh-but-intentional design, not a bug)
→ "请到后院学习你所喜欢的功夫吧" (go learn what interests you) →
navigated to 棒杖部/bangbu (陈浒/Chen Hu, stick/staff/cuff/force
teacher) → xue hu force → "你听了陈浒的指导... 你的「force」进步了!"
→ skills correctly shows force at level 1. Zero debug.log errors.
This is the real organic mainland-teacher mechanism the task asked for,
confirmed working end-to-end.
3. Player-currency shop purchase — WORKS, live-verified, no bug (real partial-stack spend, not round two's zero-stack edge case)
d/city/jujinge.lpc's 牛掌柜 (already found working in round two, but
only with admin-cloned currency spent to exactly zero). This round
repeated it with wentian's own money and a non-zero remainder
(the round-two/round-three §7.52-lineage bug class this project
documents — stackable items spent to exactly 0 silently orphaning —
only manifests on the exact-zero case, so this is a genuinely different
code path than round two exercised): reached via qu yangzhou →
east/south/south/south/west to 聚金阁 (jujinge). list showed
the correct price table (铜镜/mirror: 5 taels). buy tong jing with 10
taels in hand → "你从牛掌柜那里买下了一面铜镜。" → inventory correctly
shows 5 taels remaining + the mirror. Zero debug.log errors,
including no insufficient permission/destruct_me lines from the
§7.52-lineage bug class (not triggered here since the stack wasn't
spent to exactly zero — that specific edge case remains covered by
round two's original fix in inherit/item/combined.lpc).
Standing-checklist sanity pass (all confirmed intact/clean, no regressions, no new fixes needed)
- §7.90 (eval-cost):
config.fluffos'smaximum evaluation costis30000000— already correct, confirmed by direct read. - §7.100 (
ROOMredundantreplace_program()): confirmed no live.lpcinstances remain (grephits are all in non-.lpc/non-.h- compiled straggler files — uppercase.C, extensionless — left over from the original archive, never compiled by this driver build, out of scope per the original sweep's own definition of "live"). Fresh driver boot this round was clean, matching. - §7.111 (
master.lpcunconditionalfile_name()): still not applicable —standard_trace()formats with%O, notfile_name(). No regression from round three. - §7.112 (
init()unconditionalcall_out()chain): fix from round three still intact —death_stage_activeguard present in all threed/death/npc/{wgargoyle,wgargoyle1,bgargoyle}.lpcfiles, unchanged. - §7.113 (netdead reconnect never restores
heart_beat): confirmed clean —adm/daemons/logind.lpc::reconnect()unconditionally callsuser->reconnect(), andclone/user/user.lpc::reconnect()unconditionally doesset_heart_beat(1)on the player body itself. Same correct lineage as the other 62 libs already checked project-wide. - §7.114 (
private input_to()callback in an inherited mixin): confirmed clean —feature/edit.lpchas zeroprivatedeclarations. Matches round three's live bulletin-board test already having worked. - §7.115 (
QUESTmacro pointing at a missing file): not applicable — this lib'sinclude/globals.hhas noQUESTmacro at all (only the unrelatedQUESTS_Ddaemon path), consistent with xuanjianlu not appearing in AGENTS.md's 80-libQUESTsurvey. - §7.79 (bare 2-arg
addn/addn_temp): not applicable —addn/addn_tempdo not appear anywhere in this lib's codebase at all (zero grep hits), so the simul_efun-shim bug class this pattern depends on has no surface here.
Optional secondary check: §7.111/§7.112 grep-only pass on sibling libs xkm, shenzhou, xkx2001
Per this lib's own round-three "generalizable finding" flag (these 3
share the same EDITOR_D-fix-era lineage and the same gargoyle death-
NPC discovery arc). Grep-only, not a full test, as instructed:
- §7.111: all three libs'
adm/single/master.lpc::standard_trace()format with%O, notfile_name()— not applicable to any of them. - §7.112: all three libs'
d/death/npc/{wgargoyle,wgargoyle1, bgargoyle}.lpc(plus stray wizard-sandbox copies found undershenzhou'su/lara/andu/scatter/update/) already carry thedeath_stage_activeguard — already swept, matching the corpus-wide ~270-lib sweep this session's AGENTS.md documents. No new findings, nothing left to fix on any of the three.
Cleanup
Fresh build-debug driver boot for this round (port 40064, PID 771369,
killed by exact PID at the end — confirmed no longer running).
log/debug.log clean throughout (1420 lines at session end, zero
error-pattern hits at any checkpoint). No new test characters were
created — reused the existing wentian/闻天笑 and admin fluffos
characters per this project's established convention. wentian's and
fluffos's save-file diffs from this session (new 武当道童 title,
wudang/offerring, force skill, mirror/tokens in inventory, silver
balance) are genuine gameplay state from the tests above and were kept,
matching the same convention documented in every prior round. One new
file appeared, work/data/npc/job_server.o — the persistent global
save for the wudang_volunteer job daemon (/clone/obj/job_server.lpc),
created as a normal side effect of the sect-joining flow's first-ever
exercise on this lib; this is legitimate shared game-daemon state, not
player-specific test debris, so it was kept rather than deleted.
Files modified this round
libs/xuanjianlu/NOTES.md(this section)work/data/login/f/fluffos.o,work/data/user/f/fluffos.o,work/data/login/w/wentian.o,work/data/user/w/wentian.o(save-state churn from live testing)work/data/npc/job_server.o(new — legitimate daemon state, see above)
§7.30 uninitialized-mapping accessor sweep (2026-08-20)
Corpus-wide mechanical sweep of the feature/skill.lpc shared-lineage
bug (confirmed independently on xiakexing2017/jqxz2015/haiyang2
via round-four testing): 4 accessor(s) in this file returned a raw
never-initialized mapping instance variable (defaults to int 0,
not ([]), until first assigned), crashing any unguarded
keys()/sizeof()/indexing caller for a fresh/untrained character.
Fixed at the accessor level (mapp(x) ? x : ([])) per the documented
remedy. Verified via lpcc --batch static compile check only (not a
live boot) as part of a large mechanical sweep; not individually
functionally re-tested live on this lib.
AGENTS.md §7.19 fix: enable_player() reentrancy from init()
feature/command.lpc's enable_player() (wrapper around
enable_commands()) was reachable from an NPC's init(): the shared
inherit/char/char.lpc setup() (called from every character's
create()) itself calls enable_player(), and d/zhongnan/npc/killer.lpc
redundantly calls setup() again from inside its own init() (on top
of the setup() its create() already made) -- same shape as the
originally-documented mhxy zhangmen.lpc case. enable_commands() is
only safe to call from create(): calling it again on an object
already living() makes the driver re-invoke that same object's
init() as a side effect, which recurses back into enable_player()
on the same call stack until "Too deep recursion" aborts the boot on
a room's first-ever visit. Fixed with a true reentrancy flag
(in_enable_player_now), NOT a living() guard (which would break
legitimate re-enables from revive() in feature/damage.lpc and
wakeup()/wakeup2() in cmds/std/sleep.lpc, both confirmed to
re-invoke enable_player() on this lib while the object is still
living()). Verified via lpcc --batch single-file compile check
(PASS). Part of the corpus-wide §7.19 sweep (Batch E).