info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
2002 年版本,"青岛站"分线,取材自《西游记》的神话/仙侠题材 MUD,与本站收录的 "mhxy" 是同一套 2002 年代码库(仅站点名称品牌与巫师名单不同,文件级比对确认 14,563 个文件中有 14,561 个逐字节相同),属于西游记题材、ES II 引擎家族的一支(与 xyj2000f 等同宗)。新手从"南城客栈"起步,途中会遇到黄飞鸿、孙悟空等耳熟能详的角色;游戏内的成就体系紧扣原著——"西天取经""大闹天宫"等都是角色档案里会记录的经历;新闻公告、 门派、帮会系统一应俱全,整体偏休闲。巫师账号每次登录都会重新进入天赋分配室(`/d/wiz/init`),这是本库自己的既有设计,非巫师账号只在首次注册时经过这里。
English
A near-byte-identical 2002 sibling of this collection's `mhxy` archive — file-level diffing confirmed 14,561 of 14,563 files match exactly, with the only real differences being the "(Qingdao Station)" branding string spliced into the login banner and each site's own wizard roster. Same Journey to the West-themed ES II engine world shared with xyj2000f: new characters set out from the South City Inn (南城客栈), greeted by classic figures Huang Feihong and Sun Wukong, with an achievement record that tracks canon milestones like "journeying west for the scriptures" and "wreaking havoc in heaven," plus news, sects, and guilds.
README
内容亮点
- 取材自《西游记》的神话/仙侠题材 MUD:新手从"南城客栈"起步,途中会 遇到黄飞鸿、孙悟空等耳熟能详的角色。
- 游戏内的成就体系紧扣原著——"西天取经""大闹天宫"等都是角色档案里 会记录的经历。
- 新闻公告、门派、帮会系统一应俱全,整体偏休闲。
- 巫师账号每次登录都会重新进入天赋分配室(
/d/wiz/init)——这是本库 自己的既有设计,非巫师账号只在首次注册时经过这里。
在线试玩
https://mudlibs.fluffos.info/mhxyqd/
管理员账号 / Admin account
- id:
fluffos - 密码 / password:
Mud@2026 - 中文名 / display name: 浮浮
- 权限 / level:
(admin)— 本 lineage 最高权限(游戏内头衔显示为 【天神】)。 - 授权方式:
work/adm/etc/wizlist加入fluffos (admin)。
警告:正式对外开放前请务必修改此密码。
本地运行
cd libs/mhxyqd
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40050。世界数据量较大,启动后大约还需要 1-2 分钟在后台 继续加载全图 NPC/场景,但这期间已经可以连线注册、游玩。
NOTES · 移植与修复记录
梦幻西游2002版.rar → mhxyqd
- Archive:
archives/梦幻西游2002版.rar(15.8MB — "梦幻西游(青岛站)" / "Fantasy Journey to the West (Qingdao Station)"). Self-identified name perconfig.mhxy:name : 梦幻西游; the in-game banner/welcome text brands it梦幻西游(青岛站). - Mudlib root: nested at
mhxy/world/(same layout convention as archive #19). Config atmhxy/config.mhxy(MudOS 0.9.20 format, jjgod's Chinese localization of the standard MudOS sample config — same template as archive #19'smhxy). - Port: 40050 (per TODO.md's next-free-port ledger at the time this archive was picked up; 40046-40049 reserved for archives #52-55 being processed concurrently).
Lineage: SAME codebase as mhxy (archive #19) — confirmed via diff, not assumed from the title
The task brief called for verifying this explicitly rather than trusting
similar Chinese titles (per the project's repeated "similar titles ≠
same lineage" lesson) — and repeated for the opposite conclusion this
time: this archive and archive #19 (mhxy.rar) are the same release,
not just the same lineage family.
Diff method: extracted both archives' raw mhxy/world/ trees and
compared file-by-file.
- File tree structure: byte-identical (both 14,563 files, identical path listing, zero diff in
find -type f | sort). - Content: md5sum every file in both trees and joined on path — only 2 real differences out of 14,563 files: 1.
adm/daemons/logind.c— this archive's copy adds the branding string(青岛站)into two banner lines ("梦幻西游现有注册玩家..."→"梦幻西游(青岛站)现有注册玩家..."); everything else in the file (includingcheck_legal_name,check_legal_id, the whole registration callback chain) is byte-identical to #19's copy. 2.adm/etc/wizlist— differs (expected: reflects each site's actual wizard roster/admin state at snapshot time, not mudlib code). 3. (A handful of duplicate-named复件 (n).cbackup files under one NPC directory showed up as differing in a naive per-path md5 compare, but this was an artifact ofawkfield-splitting on the literal space in those filenames, not a real content difference — the files' multiset of hashes across that directory matched between the two trees.)
- adm/obj/master.c and adm/simul_efun/chinese.c (the two files
AGENTS.md specifically calls out to check): byte-identical to
archive #19's copies, confirmed via md5sum before touching either.
Conclusion: this is effectively the SAME 2002-era mhxy release
re-uploaded/re-shared as its own archive, with only the site-branding
string edited. Every fix already proven on mhxy (#19) was ported
directly rather than rediscovered — confirmed applicable file-by-file
since the underlying source (aside from that one cosmetic string) is
identical.
Status: DONE — boots clean, full registration flow verified end-to-end into the game world
Fixes applied (all ported directly from mhxy/#19's proven fixes, confirmed applicable via the diff above)
1. Standard conversion (scripts/convert_lib.sh): GB18030→UTF-8 on
every text file, .c→.lpc rename (11,785 .lpc files), literal
".c" reference fixes, angle-bracket #include <x.c> fixes, local
angle-bracket #include <x.h>→"x.h" conversion (same "west of
xiyouji.org lineage" pattern noted as unusually common on #19), and
static→nosave (confirmed 0 remaining static on functions/vars
after the pass). A handful of files (d/lingtai/obj/yuanpinzhu.lpc,
u/tianlin/tianlin/*.lpc, d/qujing/start/24/12.lpc,
daemon/class/yaomo/xueshan/cuixin-zhang/cuixin.lpc) are flagged by
file as "data" post-conversion but verified to be valid UTF-8 via
iconv -f UTF-8 -t UTF-8 — same known file-heuristic false-positive
documented in #19's NOTES.md, not actually broken.
2. AGENTS.md §15h (is_chinese / check_legal_name GBK byte-range bug)
— applied proactively to adm/simul_efun/chinese.lpc's is_chinese()
(GBK lead-byte range str[0]>160 && str[0]<255 → CJK Unicode block
str[0]>=0x4e00 && str[0]<=0x9fff) and adm/daemons/logind.lpc's
check_legal_name() (byte-count bounds <2 || >12 → character-count
bounds <1 || >6; dropped the i%2==0 && byte-lead-position gate).
Both functions were confirmed byte-for-byte identical to #19's
PRE-fix originals before editing, so #19's already-proven fix was
applied verbatim rather than re-derived.
3. adm/daemons/convertd.lpc's Greek-table stray-trailing-backslash
typo (§8h) — same 45 occurrences as #19 (confirmed: file is
byte-identical), same CRLF gotcha (s/\\"(,)?$/"\1\r/ — the naive
$-anchored sed silently matches 0 without the \r? since the file
has CRLF line endings). Fixed all 45 with the CRLF-aware pattern.
4. /u/feizei/log missing file — adm/obj/master.lpc's
log_error() does efun::write_file(home + "log", message) where
home = user_path("feizei") = "/u/feizei/" (NOT /u/tianlin/feizei/,
despite the wizard's actual home directory being nested under
tianlin/ — user_path() resolves independently of the real
directory layout). The archive never shipped /u/feizei/log as a
file, causing repeated Wrong permissions for opening file
/u/feizei/log for append / "No such file or directory" errors
every time choose_feizei() (a baoshi-daemon treasure-placement
routine) ran during preload. Gotcha hit while porting this fix:
the first attempt created /u/feizei/log as a DIRECTORY (mirroring
the wrong mental model from the task description's phrasing "log
directory"), which does NOT fix the problem — write_file() wants a
plain FILE at that exact path, and a directory there causes the same
class of open failure. Verified against #19's actual on-disk state
(libs/mhxy/work/u/feizei/log is a 9.8KB plain text file, not a
directory) before correcting: removed the directory, touched an
empty file instead. Confirmed fixed by rebooting and diffing the
error-count before/after: 14 occurrences → 0.
5. §15p (DNS/intermud daemon preload exclusion) — applied
proactively before the first boot attempt: removed
/adm/daemons/network/dns_master from adm/etc/preload (identical
preload list to #19's, same standing project policy).
What was checked and confirmed NOT needed (verified by reading the actual source, not assumed from #19's precedent)
- §4 (master.lpc
load_object()-in-valid_read/valid_writerecursion):master.lpc'svalid_read/valid_writeonly callfind_object(SECURITY_D), neverload_object()— no lazy-load recursion risk.securitydis preloaded up front. Confirmed by reading both functions directly; matches #19 (which also needed no §4 fix). - §7 (
get_root_uid/get_bb_uidmaster applies): both present inmaster.lpc(lines 205/210 in this copy). No stub needed. - §15l (master.lpc
create()destructingSIMUL_EFUN_OB): greppedmaster.lpcfordestruct/SIMUL_EFUN_OB— no such bootstrap-reload pattern present. - §8c / §15n (custom
securityd.lpcthis_player()-override / func-discrimination gap): this lib DOES have a genuinely customsecurityd.lpcACL (exclude_read/trusted_readtables keyed by euid/status), but itsvalid_read()already has the correct shape natively — it early-returns1(allow) for anyfuncOTHER thanread_file/file_size/stat/read_bytes/tail/ed_start, which meansload_object/recompile_object/include(the driver's own compile-time file accesses) are never subjected to the ACL table at all. This is architecturally the *inverse* structure of the shujian2008-family bug (§15n) that needs an explicit allowlist added — here the allow-by-default-except-these-specific-data-read-funcs shape already has the correct behavior built in. Nothis_player()-override footgun (§8c) present either — greppedsecurityd.lpcforthis_player(), zero hits. Confirmed empirically too: the real boot + full registration test produced zero "Read access denied" errors. - §8d/§15o (
get_include_path()master apply for<local.h>-style includes):master.lpcdoes not defineget_include_path()at all, and the lib has ~7,168 angle-bracket#include <x.h>directives, but the real boot + full registration test produced zeroCannot #includeerrors —convert_lib.sh's automatic local-angle-bracket detection already converted every include that actually needed to resolve against its own directory into quote-form during conversion (matches #19's experience, where this was likewise not needed as a master.lpc-level fix). - §15 (nitan/NT-family simul_efun-based generic dbase architecture bug):
adm/simul_efun/wizard.lpc(this lineage's simul_efun aggregate) defines no bareset/query/delete/addfunctions at all — grepped for the defining signatures, zero hits. This lineage's actual generic-property engine lives infeature/dbase.lpc, which already implements real LOCALset/query/delete/addmethods (confirmed by reading the file) — the architecturally-correct pattern, not the simul-efun-shared-storage bug. Not applicable to this lineage, same as #19. - named.lpc PATH()/sliding-window bug (part of the §15h family, common in the nitan lineage):
adm/daemons/named.lpcin this lib is a Chinese-surname→pinyin lookup table (mapping surname = (["赵":"zhao", ...])), a completely different implementation from the nitan-family's byte-indexednamed.lpc— noPATH()macro, nostrlen/byte-offset logic present. Not applicable. - §8e (missing
tail()efun): found exactly one call site,cmds/wiz/tail.lpc:15(tail(file)), a wizard admin command — same as lib #1's non-fatal instance, not lib #6's fatal compiled-into-simul_efun instance. This file is never preloaded or#included elsewhere; it only shows up as onelpcc-sweep failure, confirmed absent from the real boot/registration debug.log. Not fixed (dead-unless-invoked admin tool, out of scope per the "breadth over depth" policy).
Boot verification
cd libs/mhxyqd && ~/src/fluffos/build-debug/src/driver config.fluffos
— accepts telnet connections within ~1 second of start; preload of the
full NPC/room tree (hundreds of /d/qujing/... treasure-placement NPCs
via adm/daemons/baoshi.lpc) continues for roughly another 1-2 minutes
in the background after the socket opens. Zero FATAL errors, zero
SIGSEGV, zero "Read access denied", zero "Cannot #include", zero
"Undefined function" across the full boot. The only recurring
non-fatal error categories seen in log/debug.log across a full boot
(all confirmed non-blocking, all caught by the preload loop's own
CATCH()):
- 5-6×
*Too long evaluation. Execution aborted.— abaoshi.lpctreasure-placement routine occasionally exceeds the 400,000usec eval budget while placing a large batch of preload NPCs/items; aborts that one placement call, preload continues normally afterward. - 1×
*restore_object(): Illegal mapping format while restoring emote.—adm/daemons/emoted.lpc's save data (/data/emoted) has a corrupted entry in the ORIGINAL archive (same known category as archive #1's seed-data corruption, not introduced by this conversion pass); caught by master's preloadCATCH(), daemon continues with an empty emote-history mapping. - 1×
*Bad argument 1 to EFUN call_other()inadm/daemons/questd.lpc:153(spread_quest()) — acreate_objectstyle factory call returning0for one quest-room path with noobjectp()guard at the call site (§15e-shaped pattern); caught by preload'sCATCH(), does not block boot or any other daemon. - 1×
*Can't catch eval cost too big error.
Registration flow — read from source before testing
Read adm/daemons/logind.lpc's full input_to callback chain before
scripting the test (per the task's explicit instruction — hidden
pre-id gates have bitten other libs in this project):
logon() → (GB/BIG5 encoding prompt, conditionally compiled under
#ifdef GB_AND_BIG5) → encoding() → age-gate if_young() ("are you a
student", any non-"no" answer ends the session) → get_id() (English
id; typing the literal new branches to new-player registration) →
get_new_id() (pick an English id) → confirm_id() (auto-invoked with
"Yes", no separate y/n prompt) → get_name() (the Chinese name
prompt, gated by check_legal_name/is_chinese — the fix under
test) → new_password() → confirm_password() → get_new_superpasswd()
(a SEPARATE "identity token" password, must contain both upper- and
lower-case letters plus a non-letter character, ≥6 chars) →
confirm_new_superpasswd() → get_email() → get_webpage() →
get_icq() → get_gender() → confirm_gift() (auto-invoked with
default "n", sets up starting stats) → enter_world(). No hidden
client-version/BIG5-literal gate found in this chain (unlike
xyzx3's §15q case) — the only pre-id gate is the visible
GB/BIG5 encoding choice and the age-gate question, both confirmed by
reading the code, not inferred from prompt text.
Also checked banned_name/banned_id lists (logind.lpc's
banned_name array, adm/etc/banned_id file) to pick a real test name
that wasn't pre-rejected: "秦风" / id "qinfeng" — neither is banned
in this lib's lists (banned names here: 你/我/他/她/它/江泽民/邓小平/
李鹏/朱榕基/自己/某人/尸体/我们/你们/他们/大家/他妈的/去你的/毛泽东/巫师/
他奶奶的/田林/田/杜剑锋/杜剑/杜/尹/尹斌/晕/雲/暈/李).
Registration flow — actual transcript outcome (ONE continuous mudclient.py connection)
python3 scripts/mudclient.py 127.0.0.1 40050 --timeout 20 \
--send "gb" --send "no" --send "new" --send "qinfeng" \
--send "秦风" --send "test1234" --send "test1234" \
--send "Qinfeng123" --send "Qinfeng123" \
--send "[email protected]" --send "" --send "12345" --send "m" \
--send "look" --send "quit"Outcome, in order, all confirmed from the actual transcript (not
inferred):
1. "gb" → GB banner accepted, full MOTD/stats/uptime banner rendered
correctly in UTF-8 Chinese, no mojibake.
2. "no" → student age-gate passed, advanced past it.
3. "new" at the English-id prompt → "欢迎您,新玩家!" (welcome new
player) banner shown, advanced to get_new_id.
4. "qinfeng" → accepted as a legal, unused English id (3-8 lowercase
letters, not in banned_id, no existing save file) — advanced
straight to the Chinese-name prompt with NO rejection message.
5. "秦风" (the critical test) → accepted with NO "对不起,
请您用「中文」取名字" / "至少要..." rejection message — advanced
directly to "请设定您的密码:" (set your password). This is the
§15h fix working correctly, confirmed by the flow actually
advancing to the next stage, not just reaching the prompt.
6. Password set (test1234, ≥5 chars) and confirmed — no mismatch
rejection, advanced to the superpasswd prompt.
7. Superpasswd set (Qinfeng123 — has uppercase Q, lowercase, and
digits, ≥6 chars) and confirmed — no rejection, advanced through
email/webpage/icq prompts.
8. Gender "m" selected → auto-flowed through confirm_gift() and
enter_world() with zero crash — reached the actual gift/stat
allocation screen ("请选择你想重新设置哪一项天赋的取值[0-3]") and the
full in-game welcome banner/MOTD/news feed, i.e. the real game
world, not just a login-adjacent prompt.
9. One pre-existing cosmetic wart (present verbatim in #19 too, not
introduced by this conversion): get_name() has a leftover debug
printf("%O\n", ob); right before ob->set("name", arg), which
leaks the object's default string representation
(/obj/login#81-shaped text) into the player's own terminal for one
line. Harmless, not fixed (pre-existing in the original archive,
confirmed identical in #19's copy).
10. Some default error message ("你发现事情不大对了,但是又说不上来。")
lines appeared interleaved in the transcript during registration —
traced to master.lpc log_error()'s behavior of broadcasting the
generic error message to this_player(1) for ANY preload-time error
that happens to fire while a player connection exists (this test ran
while the background NPC-tree preload was still active) — i.e. this
is preload noise bleeding into the only connected session's screen,
not a registration-flow defect. Confirmed non-blocking: the flow
still advanced correctly at every single prompt with no re-prompt or
rejection.
Definitive independent confirmation (beyond the transcript): the completed registration was actually persisted to disk with the correct Chinese name, in valid UTF-8:
$ grep -a name libs/mhxyqd/work/data/login/q/qinfeng.o
dbase (["email":"[email protected]",...,"name":"秦风","icq":"12345",...])
$ grep -a name libs/mhxyqd/work/data/user/q/qinfeng.o
dbase (["...,"gender":"男性",...,"race":"人类",...,"name":"秦风",...])Both the login record and the full player-body record (race, gender, starting stats, channels) were created and saved correctly with the real Chinese name intact.
lpcc_check.sh sweep result
11,468 / 11,785 pass (97.31%) — essentially identical to #19's 97.3% (expected, given the near-identical codebase). 317 failures triaged by category (not all individually fixed, consistent with AGENTS.md §6b's "triage by category, fix genuine bugs, note the rest" policy — none of the categories below appear anywhere in the real boot log):
- ~40+ failures: NPC/room files under
u/tianlin/d/shaolin/,d/qujing/kusongbak/,d/guzhanchang/,u/redrain/menpai/d/tianpeng/etc. referencing zone/NPC paths that genuinely don't exist anywhere in this archive (e.g./daemon/class/shaolin/cheng-shi— confirmed viafindthat neither the directory nor the file exists anywhere in the tree). This is AGENTS.md §13's "missing zone content is a real archive gap, not a bug" category — these rooms/NPCs are never on any preload list and never touched by normal play. - 22 failures:
error: Unable to find the inherited function 'setup'and related "Undefined function query" cascades, all rooted in quest-definition fragment files (e.g.d/kaifeng/npc/quest.lpc,#included byquest_wr.lpcand siblings) that are#include-only data/logic fragments with noinheritof their own — meant to be pulled into a real NPC file that already inherits the right base class, not compiled standalone. Confirmed via source read (noinheritstatement in the fragment) and confirmed absent from the real boot log — classic §6b fragment-file sweep artifact. - 13 failures:
www/cgi-bin/*.lpcfiles using an undefinedF_REMOVEANSImacro (inherit F_REMOVEANSI;with no corresponding#defineanywhere ininclude/). This is a legacy web-CGI interface subsystem, entirely separate from and never touched by the telnet mudlib (confirmed absent from the boot log) — a real but low-priority content gap in a feature area out of scope for this conversion. - Remainder (~240 failures): a long tail of one-off syntax errors, undefined variables (
SAVE_EXTENSION,F_DISASTER,WELCOMEN,ID), and "Illegal character" hits scattered across individual, rarely- reached NPC/item/room files — consistent with genuine pre-existing authoring-era typos in content that was never exercised during original play either (the same long-tail pattern documented across most other libs in this project). Not fixed given the "breadth over depth" policy and the fact that none of them touch the boot or registration path.
How to re-run
cd libs/mhxyqd
~/src/fluffos/build-debug/src/driver config.fluffos
python3 ../../scripts/mudclient.py 127.0.0.1 40050 --timeout 20 \
--send "gb" --send "no" --send "look" --send "quit"(Use a fresh, never-before-used id/name if re-testing full registration
— qinfeng/秦风 is already registered from this pass's test, saved
under data/login/q/qinfeng.o and data/user/q/qinfeng.o.)
Driver process was killed after testing — confirmed no lingering
driver config.fluffos process left running on port 40050 or
otherwise.
QA re-verification pass (2026-07-23) — found and fixed a real, intermittent bug plus two minor robustness gaps
Bug found: ~1-in-6 chance every connection silently died with zero output
at the very first prompt. adm/daemons/logind.lpc's logon() picks a
random banner via switch(random(6)) between BANNER/BANNER1..BANNER5
(include/login.h: /adm/etc/banner, /adm/etc/banner1..banner5). Only
banner1..banner5 existed on disk — the base /adm/etc/banner (no
numeric suffix) did not, only a differently-cased /adm/etc/Banner (a
Windows-origin case-sensitivity mismatch, AGENTS.md §15k's pattern). Since
cat() is write(read_file(file)), a missing file makes read_file()
return 0, and write(0) then threw inside the player object's
receive_message(), which the driver's new_conn_handler catches by
silently disconnecting the connection with zero output at all — exactly
matching an apparently "hung"/dead server, but only on the random(6)==0
branch (~16.7% of connections). Confirmed by direct read of logon() and by
reproducing it live (my first test connection hit exactly this branch).
Fixed by cp adm/etc/Banner adm/etc/banner (matching AGENTS.md §15k's
established fix pattern — copy to the exact case-sensitive path the code
expects, leave the original in place too). Re-verified with 8 quick
connection attempts plus a full registration — no further silent
disconnects, and the previously-missing banner variant now renders correctly
when selected.
Second fix: adm/obj/master.lpc's log_error() broadcast every message
reaching it — including ordinary compile *warnings*, not just real errors —
to a connected non-wizard player as the alarming generic default error
message. A real registration session showed several of these lines
interleaved with the registration prompts (this was already noted as
"preload noise" in the original pass's NOTES above, but not fixed). Applied
the standard AGENTS.md §15w fix: gate the broadcast on the message NOT
containing "warning:", still logging everything to file regardless.
Verified: a post-fix registration session showed zero such lines.
Third, minor fix: the gift-finalization step (d/wiz/init.lpc's
do_finish()) triggers a log_error()-adjacent write to
/u/npc/log (a lazily-compiled file's file_owner() resolves to wizard
"npc", whose home directory was never shipped in this archive) — every new
character's registration hit *Wrong permissions for opening file
/u/npc/log for append. "No such file or directory" (caught, non-fatal, same
class as the already-documented /u/feizei/log fix from the original pass,
just a different user). Fixed the same way: created /u/npc/ and an empty
/u/npc/log file (not a directory).
Re-verified after all three fixes: full registration (gb → no → new →
id → 中文姓名 → password ×2 → 身份标识 ×2 → email → webpage → icq → gender →
gift-accept(9,y)) followed by look/score/quit, with both genders
(male "秦岭十三" / female "秦岭十四" — the female run also showed correct
gender-specific NPC dialogue "这位小姑娘"). Both landed correctly in the real
starting room (南城客栈), look and score rendered correctly, zero
"你发现事情不大对了" spam and zero new runtime errors beyond the
already-documented pre-existing preload noise (corrupted emote save data,
one questd.lpc factory-return-0 gap, a few baoshi.lpc eval-cost
timeouts during large treasure-placement batches — all non-fatal, all
pre-existing, none on the registration/gameplay path).
Re-verification pass: driver rebuild + LPC formatter + WASM build
- LPC formatter: ran
format-corpus.mjsover all 11,785.lpcfiles — 11,635 reformatted in place, 112 already-idempotent, 38 refused (nonzeroerrorsexpected/fine per the tool's own contract). Double-checked the §15kadm/etc/bannercase-fix (cp adm/etc/Banner adm/etc/banner) survived — both files still present post-reformat. - Native retest against rebuilt driver (
~/src/fluffos/build-debug/ src/driver, freshly rebuilt from upstream master): booted clean, zero fatal errors. Full registration re-verified with fresh real names (秦风田, plus 秦风齐/秦风水 during flow discovery) via the documentedgb → no → new → id → 中文姓名 → password×2 → 身份标识×2 → email → webpage → icq → gender → gift(9,y)flow; arrived correctly in 南城客栈 (banner rendered correctly, confirming §15k still holds),look/score/quitall correct. One single non-fatal, self-recoveringToo deep recursion.(/feature/name.lpc:14, triggered by a lazily- compiled/d/moon/npc/zhangmenNPC during one run) was observed — the driver's recursion guard caught it and continued, the connection was unaffected (registration/look/score/quit all still succeeded in that same run) — noted as a pre-existing quirk, not chased further per this lib's breadth-over-depth policy. No regressions from the rebuilt driver or the reformat pass; nothing required fixing. - WASM build test (
scripts/wasm_client.jsagainstbuild-wasm/src): boots cleanly in-process (the sockets-requiringftpd.lpc/httpd.lpcdaemons correctly throwUndefined function socket_createetc. under WASM's no-sockets-package build, caught non-fatally bymaster.lpc's preloadcatch(), same as natively when the package is absent). Login is blocked, but via a different concrete mechanism than the simple ban-check case: this lib'slogind.lpcencoding()step calls intoadm/daemons/ipd.lpc'sseek_ip_address(query_ip_number(ob))to do a geographic ISP lookup, andseek_ip_address()doesexplode(ip, ".")then indexesuser_ip[1]assuming a real dotted-quad; under WASM's malformed/emptyquery_ip_number()return value this throws an uncaughtArray index out of bounds(not even wrapped incatch()), which unwinds far enough to leave the connection stuck emitting "什么?" (unknown command) for every further input instead of ever reaching the id/name prompts. Root cause is the same documentedquery_ip_number()WASM limitation as the simpler ban-check case seen elsewhere in this batch — confirmed by the clean native run above using the identical code path on127.0.0.1— so no mudlib fix was attempted. Classified as: boots under WASM; login blocked by the driver's knownquery_ip_number()limitation (this lib's manifestation: an uncaught array-bounds error in its IP-geolocation daemon, rather than an explicit ban check).
WASM-enablement pass (loopback-allow / uptime / throttle / admin seed)
Standard pass per AGENTS.md §1.3(b)/(e), §1.5.
Gates patched (all tightened to strict loopback -- see fail-closed correction below):
adm/daemons/band.lpc_is_loopback()helper (~line 148), shared byis_banned()(~155),create_char_banned()(~170), andis_strict_banned()(~185): strict loopback short-circuitsreturn 0before the regexp site-list scan.adm/daemons/logind.lpcencoding()(~line 156,is_loopbacklocal): gates the "no IP name" kick (~175), the non-numeric-IP kick (~181), and the 40-characters-per-host cap (~215) — all three now apply to everything except strict loopback.adm/daemons/logind.lpcget_id()(~line 413): the per-IP multi-login cap (NowLogin >= MaxLimit) now applies to everything except strict loopback.adm/daemons/securityd.lpcmatch_wiz_site()(~line 117): strict loopback always matches (wizard per-site login restriction).adm/daemons/ipd.lpcseek_ip_address()(~line 13): NOT a security gate — this is the crash-prevention guard already documented above (§ "Re-verification pass") against theexplode()/array-index crash on a malformed IP; kept as-is (returns"本地"cosmetic placeholder), no fail-closed retrofit needed since it grants no access.- Uptime startup gate: none found.
- Anti-flood throttle: covered by the
is_loopback/NowLogingates above (this lineage's throttles are per-IP character-count caps, not a separate connect-time gate).
Fail-closed correction (retrofit): every gate above (except
ipd.lpc, which is not a security gate) was initially written with a
!stringp(ip)/ip == "" "treat malformed IP as loopback" fallback per
the original pre-driver-fix instructions. Since query_ip_number() is
now fixed upstream and always returns a clean 127.0.0.1 for loopback,
that fallback was tightened to strict 127.0.0.1/localhost/::1/
127.* matching only — a malformed/non-string IP now falls through to
the ORIGINAL gate logic (fail closed) instead of being waved through.
Retested after tightening: fluffos loopback login + update still
works (native and WASM).
Admin account: id fluffos, pw Mud@2026, name 浮浮, (admin) via
adm/etc/wizlist (already registered by save-file evidence on disk when
this pass started; wizlist entry present). Verified: relogin as
fluffos shows 仙衔 【天神】; update /adm/daemons/band → 重新编译
:成功. Note: as a wizard, fluffos transits /d/wiz/init (the gift/
attribute-allocation room) on EVERY login, not just first registration —
this is init.lpc's own init() logic (the "skip if already has
combat_exp/daoxing/no_gift" early-return only applies to non-wizards),
not a bug; just press 9 then y each time.
Save files (untracked, NOT gitignored — orchestrator must add):
work/data/user/f/fluffos.o, work/data/login/f/fluffos.o.
Retest: fresh normal registration (id ceshiliu, name 秦风寅)
end-to-end OK (look/score/quit correct); test character saves removed
afterward — ceshiliu (this pass) and ceshiwu (an untracked leftover
from an earlier interrupted pass, confirmed via git ls-files to NOT be
committed, safe to delete). Caution for future passes: qinfeng
(data/{user,login}/q/qinfeng.o) LOOKS like a similar test-registration
leftover (it's documented above at "Registration flow — actual
transcript outcome" as this project's own earlier test character) but IS
committed to git (git log -- ...qinfeng.o → part of the original
Add mhxyqd commit) — it was deleted once during this pass by
mistake and had to be restored via git show HEAD:path > path; do NOT
delete it as "test-character cleanup" without first checking
git ls-files/git log on the exact path. No new errors in debug.log.
WASM retest (wasm_client.js): fluffos
login + score + quit also verified working post-ipd.lpc-guard —
this lib's WASM-login-blocked verdict in the "Re-verification pass"
section above is now superseded; WASM is fully playable.
WASM 修复摘要(迁移自 meta.json 的 group_note)
同一套代码库,14561/14563 个档案完全相同。状态已从过时的 limited 修正——这份档案自己的 README 里从未记录过任何缺陷说明,本轮重新测试也没有发现:完整流程(GB/BIG5 选择→未成年人关卡(否)→管理员 id+密码→如果存档数据还没经历过,登录成功后会立即在 /d/wiz/init.lpc 触发一次仅限首次的天赋/礼物定制画面,用'9'接受再用'y'确认)全程无错误地进入"南城客栈",quit 也干净("浮浮(Fluffos)离开游戏了")。
深度功能测试(§10.7,本轮):发现并修复一处真实的 printf 调试残留 leak,加上 3 处 §7.68 死亡软锁
这份档案此前只做过注册流程验证,没有真正玩到移动/战斗/死亡。这次先
核对了最近几轮在 jqxz2008/xkx2017/xkx100 系列上确认过的 5 个 bug——
虽然本档案自己的 README 说和 xyj2000f 同属"ES II 引擎家族/西游题材",
但实测下来这几个 bug 一个都不存在:adm/daemons/logind.lpc 没有
ob->query("age") == 14 这种错对象的年龄检查、chinesed.lpc 存在但
自己的 data/e2c_dict.o 根本不存在(同 xkx100,restore() 对着不存
在的档案不会抛异常)、combatd.lpc/kill.lpc 完全没有往
/log/nosave/ 写任何东西、bai.lpc/apprentice.lpc 也没有那处括号
错误。说明这份档案和 jqxz2008/xkx 那条分支实际上是不同的、独立演化
的 ES II 变体,不能想当然地认为"同宗必有同 bug"。
不过还是找到了一个真实、独立的 bug:adm/daemons/logind.lpc 的
get_name() 函式在成功接受玩家的中文名字后,紧接着有一行明显的调试
残留 printf("%O\n", ob);——每一个新玩家输入完中文名字、正要进入下
一步设定密码的那一刻,都会在屏幕上看到一行 /obj/login#123 这样的
原始物件引用字串,纯属调试遗留、没有清理干净。已经删除这一行。用真
实驱动分两次完整注册验证:删除前能重现("您的中文名字:/obj/login#123"
后紧接着"请设定您的密码:"),删除后干净地直接进入密码设定,不再出
现物件引用。(在 www/、adm/daemons/network/dns_master.lpc、
cmds/eld/where.lpc 里也发现了同款 printf("%O\n", ...),但这些都
不在真人玩家会走到的注册/游戏路径上——网页端专用副本、DNS 精灵、巫
师专属指令——本轮判断不在范围内,未处理。)
另外发现并修复了 3 处 §7.68 死亡/复活软锁:d/death/npc/{b,
bgargoyle,wgargoyle}.lpc 的 death_stage() 都有
if (!ob || !present(ob)) return; 这种把"角色永久离开"和"角色只是
暂时不在场"混在一起处理的写法,按各文件自己的重试间隔(均为 5 秒)
拆分成"暂时不在场则重试"。
用真实驱动完整验证了两次全新注册(走完英文 id→new→中文名字→登录密
码→身份标识/超级密码→email→个人主页→ICQ→性别→天赋赠礼确认的完整
流程,比 jqxz2008/xkx 系列多了身份标识和个人主页/ICQ 三个额外步骤),
成功进入"南城客栈",NPC"黄飞鸿""孙悟空""店小二"都正确打招呼;
score 显示食物/饮水状态均为"正常"(不是空的,本来就没有 §8.9 那
个 bug);在"南城客栈"尝试攻击"孙悟空"被正确挡下("这里不准战斗。",
合理设计不是 bug);移动到"朱雀大街"实际攻击了游荡 NPC"疥顶小僧",
角色被打死,干净地触发"你死了"→"阴阳界"死亡场景→NPC"崔判官"自动
(不需要玩家任何操作)念白+翻生死簿+还阳,把角色送到复活点"荒郊小
店"——debug.log 全程保持空白,确认没有任何遗留问题。
未继续测试的部分
时间关系,没有测试拜师、购物、留言板(虽然已经看到留言板存在且有历 史留言)。
更正(2026-08-05):§7.68 复活软锁"修复"已撤销
上面提到的"鬼魂离开/不在场时被永久放弃复活流程"曾被当作 AGENTS.md
§7.68 记录的一类 bug 修复(把单次判定改成每 5 秒重试)。经用户指出并
重新审视:这更可能是有意的游戏设计,不是 bug——大多数这类档案里
鬼魂根本无法自行移动,所以"不在场"要么从未真正发生,要么是"离开去
在阴间游荡,想回来时再走回这个房间、流程会通过 init() 重新从头开始"
这种有意为之的宽松机制,而不是需要强制追上玩家的错误。强行重试还可能
引入新问题:如果鬼魂之后又走回这个房间,旧的重试和 init() 重新触发的
新一轮流程可能同时运行,导致对话重叠错乱。已把这处改动撤销,恢复成
原始的 if (!ob || !present(ob)) return; 单次判定写法(bmxkx2001
除外——那份档案里这确实是一个真实存在、经过实际复现验证的 bug:鬼魂
本身完全无法移动,是另一个不相关的 NPC 强行把鬼魂拖走导致的)。详见
AGENTS.md §7.68 顶部的撤销说明。
§7.86 跨库扫描修复(留言板 post 崩溃)
BBS_BOARD、BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 47 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
深度功能测试(2026-08-14,post driver-upgrade re-test)
Round-two re-verification pass against the rebuilt fluffos driver
(picks up upstream PRs #1343/#1344) plus the corpus-wide quest_times
%-operator fix — neither of which touches this lib's own code paths,
but a live re-play was still warranted since this lib was last tested
before the rebuild.
All previously-documented fixes independently re-verified still in
place (grepped/read the actual source, not just NOTES.md prose):
adm/etc/banner exists as a real file alongside Banner (§15k case
fix), adm/obj/master.lpc's log_error() still gates the broadcast on
strsrch(message, "arning:") == -1 (§15w), /u/npc/log and
/u/feizei/log are both plain files (not directories), logind.lpc's
get_name() debug-leak printf("%O\n", ob); is gone,
is_chinese()/check_legal_name() still use the CJK-codepoint /
character-count logic (not the old GBK byte-range check), and the
d/death/npc/{b,bgargoyle,wgargoyle}.lpc death files still carry the
ORIGINAL single-check if (!ob || !present(ob)) return; shape — the
2026-08-05 §7.68 revocation held, no regression crept back in. The
§7.86 board replace_program() sweep fix also spot-checked clean (0
files remaining with both a BBS_BOARD/BULLETIN_BOARD inherit and
a redundant replace_program()).
New standard-checklist items checked this round (not previously checked in this lib's history):
1. adm/simul_efun/file.lpc: log_file() (line 7) textually
precedes assure_file() (line 11), but log_file() doesn't call
assure_file() at all — no forward-declaration risk here, unlike
the sibling mhxy's finding this session. No fix needed.
2. config.fluffos's maximum evaluation cost: was 400000,
matching the exact value that caused 100%-reproducible
enter_world()/make_body() aborts on the sibling mhxy this
session (and matching this lib's own previously-documented
*Too long evaluation./*Can't catch eval cost too big error.
preload noise). Raised to 5000000 (matching mhxy's fix).
Re-verified live: the preload's previously-documented eval-cost
error categories (Too long evaluation, Can't catch eval cost too
big error) are now zero across a full boot, vs. the
previously-documented handful per boot.
3. §7.106 (cmds/wiz/update.lpc's present(file, environment(me))
crash): already fixed (environment(me) && (obj = present(file,
environment(me))) && interactive(obj)) — this lib was one of the
135 libs covered by the corpus-wide mechanical sweep referenced in
AGENTS.md §7.106. Live-verified: update /adm/daemons/band as
admin succeeded cleanly (重新编译 /adm/daemons/band.lpc:成功!).
New bug found and fixed (not from the standard checklist, found
while executing this round's required "two rapid reconnects" step):
the admin account (a wizard) got blocked by the 40-second "刚退出就想
进来?为了降低系统负荷,还是等一小会再连入吧!" quick-reconnect
throttle in adm/daemons/logind.lpc's get_passwd(), even though the
guard is written as !wizardp(user) && ... (i.e. intended to exempt
wizards). wizardp(user) evaluates false at this point in the login
sequence (before enter_world() grants full wizard status to the
freshly-restored body), so the exemption never actually fires for
anyone, admin included — reproduced live twice. The byte-identical
sibling mhxy hit and fixed this exact gap earlier this session by
adding an explicit loopback-IP exemption alongside the (non-functional
at this point) wizardp() check; ported that fix verbatim: a new
reconn_ip local captures query_ip_number(ob), and the throttle is
skipped when it's "127.0.0.1", "::1", or a "127."-prefixed
string. Verified live: recompiled logind.lpc via update, then did
two back-to-back quit→reconnect cycles within a few seconds each —
both landed straight back into the game with no throttle message
(previously reproduced the block twice before the fix). This gap was
present in the archive from the original conversion; the
WASM-enablement pass's loopback carve-outs (band.lpc, encoding()'s
IP/host-cap gates, get_id()'s multi-login cap, securityd.lpc's
match_wiz_site()) evidently didn't cover this specific throttle site
when mhxy and mhxyqd diverged in prior passes — now aligned with
mhxy.
Live re-play (real driver, scripts/tmux_mud.sh, existing admin
account, logging in via gb → no → id → password, transiting
/d/wiz/init with 9 then y each login as documented): confirmed
look/score render correctly (food/water both "正常", no §8.9
issue), update gives real write access, and re-walked the exact
death/resurrection path this lib's original §10.7 pass documented —
moved 南城客栈 → 朱雀大街 (west), attacked 疥顶小僧 (kill seng; the
kill jieding alias from the original NOTES no longer resolves since
the NPC's id list is ({"jieding xiaoseng", "xiaoseng", "seng"}), not
a bare "jieding" token — a client/id quirk, not a mudlib bug), died,
landed at 「阴阳界」, and 崔判官 auto-ran the full unattended dialogue
→ judgment → "送你还阳" sequence, landing cleanly at 「荒郊小店」 —
identical to the pass documented before the driver upgrade.
log/debug.log grew only with expected lazy-compile warnings (unused
locals, a couple of pre-existing signature mismatches) across the
entire session; zero FATAL, zero SIGSEGV, zero new error classes.
Process/repo hygiene: driver killed by exact PID (kill 1638278,
confirmed dead via ps), never pkill -f. git status --short
libs/mhxyqd/ post-test showed only the two genuine fixes
(config.fluffos, adm/daemons/logind.lpc) plus the admin's own
data/{login,user}/f/fluffos.o saves (real gameplay state from this
session — timestamp, kill/death record, stat drain from the death
tested above) — all kept. Six untracked data/{login,user}/c/chen{ba,
shi,wu}.o files predate this session (timestamped 2026-08-05, not in
git ls-files) and were left untouched, consistent with this lib's own
caution about not deleting unfamiliar test-character residue without
checking history first; qinfeng's committed save files were not
touched.
§7.100 sweep fix (ROOM base-class redundant replace_program())
#define ROOM "/std/room": deleted 550 redundant, live, standalone
replace_program(ROOM); lines (kept inherit ROOM;) — 548 caught by
the scripted sweep, plus 2 hand-fixed instances baked into the two
copies of this lib's room-building tool (obj/roommaker.lpc,
clone/misc/roommaker.lpc), same shape as sibling mhxy. 8
pre-existing //-commented instances remain untouched, confirmed
harmless. Verified via a clean build-debug driver boot (0 new
compile errors, port 40050 listening, zero new "cannot replace"/
"cannot bind" debug.log lines) plus a raw-socket connectivity check
(login banner rendered correctly) — a handful of transient "Too deep
recursion" errors during a weapon object's lazy compile
(d/obj/weapon/mace/copperjian.lpc, /adm/obj/master.lpc:433)
appeared well after "Initializations complete" and are unrelated to
this fix (no room/ROOM involvement); boot log growth briefly looked
stalled but was confirmed to be stdio buffering, not a real hang, via
the same connectivity check. No full §10.7 gameplay walkthrough this
pass. The 6 pre-existing untracked data/{login,user}/c/chen{ba,shi,
wu}.o files (timestamped 2026-08-05, predating this session) were
left untouched.
§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): 3 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 sweep (2026-09-01): enable_player() reentrancy from init()
Same corpus-wide bug class as mhxy/wuhanzhan (AGENTS.md §7.19): this
lib's feature/command.lpc enable_player() wrapper (around the raw
enable_commands() efun) is reachable from an NPC's init() via a
redundant create()-then-init()-calls-setup() (or reset_me()
calling setup()) chain -- confirmed live via a static scan of every
init() body in this lib: 55 NPC/item files call setup() directly
or via reset_me() from init(), after create() already called
setup() once (which already made the object living()). Calling
enable_commands() a second time on an already-living() object makes
the driver re-invoke that object's own init() as a side effect, which
re-enters this same chain while the original call is still on the
stack -- genuine reentrancy, crashing with "Too deep recursion" (most
likely to surface on an NPC's first-ever preload/compile).
feature/damage.lpc's revive() and cmds/std/sleep.lpc's wakeup1()/wakeup2() call enable_player() again while the object is still living(). This confirms a bare if (living(this_object())) return;
guard would be the WRONG fix (it would silently break that legitimate
re-enable) -- used the same true reentrancy-flag fix as mhxy instead:
a nosave private int in_enable_player_now; set for the duration of the
wrapper's body, guarding only genuine same-call-stack reentrancy while
leaving every legitimate re-enable (revive/wakeup/disguise) unaffected.
feature/command.lpc's enable_player() had a single fall-through exit
(no early returns), so one guard-at-top + one clear-at-bottom pair was
sufficient. Verified via a single-file lpcc --batch compile check
(PASS) -- not individually live-boot-tested.