info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
炎黄英雄史(游戏内也称"皇朝再现")与本站另外两份同源档案 `yhwhpublicfi`(《炎黄武魂》)、`yanhuangwuhun`(《炎黄武魂Ⅱ》)同属 ES II 引擎家族 2003 年"炎黄"分支,文件级比对显示三者 87-89% 的同名档案逐字节相同,是同一套代码的三次不同品牌打包,而非各自独立开发的姊妹作;玩家从"世外桃源"出发,分开输入姓/名两段中文姓名,设管理密码与普通密码两组密码,走到"世外桃源"不同方向的 NPC 面前听劝说词选定猛士/智慧/耐力/敏捷/均衡五种角色定位,再到"阎罗殿"用 `wash` 指令跳入"忘忧池"重掷天赋,最后用 `born <中文地名>` 投胎到二十个候选籍贯/世家之一,死亡后经"鬼门关"由黑白无常接引,固定在扬州武庙复活。
English
Titled "Dynasty Reborn" in-game — like close siblings yhwhpublicfi and yanhuangwuhun ("Tales of Wulin Heroes: Soul of Yan-Huang" / "...II"), this is a separately-branded snapshot of the same 2003 "yh" branch of the Doing/hell engine lineage; a file-level comparison found 87-89% of common files byte-identical across all three, much closer than a simple shared-engine relationship. Character creation is distinctive within the family: split surname/given-name entry, two independent passwords, then a five-way archetype pick (Brute/Wise/Enduring/Agile/Balanced) made by approaching different NPCs at the starting "Land of Peach Blossoms" who each preach their own philosophy, followed by a trip to Yama's Hall to re-roll stats in the Pool of Forgetfulness, and finally `born <name>` into one of twenty birthplace/clan options. Death sends the ghost through the Ghost Gate Pass, escorted by the Black- and White-Guard psychopomps, reviving fixed at the Yangzhou Martial Temple.
README
内容亮点
- 三份同源档案(
yhwhpublicfi/yanhuangwuhun/yhyxs)里,本档案独 用"皇朝再现"这个别名,是三者中唯一没有沿用"炎黄武魂"字样的一支。
- 更正(2026-08-05):上面提到的"7.68 复活软锁"修复已经撤销——经重新评估,鬼魂"不在场"时放弃复活流程更可能是有意的游戏设计(多数这类档案里鬼魂本身就无法自行移动,离开是一种游荡机制,回来时 init() 会重新触发流程),不是需要强制重试的 bug;详见 NOTES.md。
深度功能测试新发现的 bug(详见 NOTES.md)
发现并修复了一类此前本项目从未记录过的 bug:help/rules(首次登
录/help rules 显示的规则说明)和 clone/game/{8,21}_hlp(拱猪/
21点两个纸牌小游戏的说明文字)三个无扩展名的纯文本帮助文件,从原
始转码阶段起就一直是未转换的 GB18030 原始字节,一直没被
convert_lib.sh 或历次针对 .lpc/.h 的补漏扫描捕捉到,玩家看到
的是一整屏乱码。已用 iconv 转换为 UTF-8 并现场验证显示正常(详见
AGENTS.md §4.1 新增条目)。另外死亡/复活系统的 d/death/npc/
{hei,bai}.lpc(黑无常/白无常)有 AGENTS.md §7.68 归档的复活软锁死
bug,已修复,并现场完整验证了一次真实死亡→鬼门关→复活的全流程
(角色被欧阳克这个金庸小说反派角色叠加降雪冻伤效果击杀)。
测试过程中还意外撞上了 AGENTS.md §10.8 已归档的驱动级内存腐化崩溃
类的第六个独立实例——driver 自己的周期性 reset 扫描导致段错误,
debug.log 毫无痕迹,只有 driver 自身的 stdout 捕捉到了完整堆栈;
这是驱动层问题,不是 mudlib bug,未做任何代码改动。
在线试玩
https://mudlibs.fluffos.info/yhyxs/
管理员账号 / Admin account
- id:
fluffos - 密码 / password:
Mud@2026 - 中文名 / display name: 浮浮(注册时姓氏一栏直接回车跳过,与同源 存档"炎黄武魂"一致——本 lineage 的姓名分开输入流程会拒绝"姓=名"的 重名)
- 权限 / level:
(admin)—— 最高权限(通过/adm/etc/wizlist授予,SECURITY_D据此判定)。
警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。
本地运行
cd libs/yhyxs
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40061。
NOTES · 移植与修复记录
炎黄英雄史 (Yanhuang Heroic History) — conversion notes
Archive: archives/炎黄英雄史.rar (archive #67). Extracted root:
raw/yh2003/mud/ (the archive bundles the mudlib source alongside a
prebuilt Windows MudOS driver/binaries — MudOS.exe, MudOSxp.exe,
mudos.dll, runmud/runqym launch scripts, an addr_server helper —
all ignored, only the LPC mudlib under mud/ was converted).
Identity / naming
config.cfg's ownname : 炎黄英雄传field says "Yanhuang Heroic Legend" (传), but the live banner text baked intologind.lpc(shown at the character-type-selection prompt:在「炎黄英雄史」中您想要 扮演的角色类型) says "Yanhuang Heroic History" (史) — matching the archive's own title exactly. Slug kept asyhyxsto match the archive;nameinconfig.fluffosleft as the config file's own value (炎黄英雄传) since that's what the config itself declares, with a comment noting the discrepancy.- Collector-site marker file
小熊泥苑.txt(converted, readable) confirms this came through the same "小熊泥苑" (dtxy.126.com) collector/rehost assjtx2(archive #36) — unrelated to this lib's actual game content, just a distribution wrapper. - Credits inside the code:
master.lpc: "for ES II mudlib / original from Lil / rewritten by Annihilator (11/07/94) / modified by Xiang for XKX (12/15/95) / updated by Doing Lu for hell (2K) / Modify By Linux@lxtx for yh 2003.3".securityd.lpc: "Modified by Xiang for XKX / Updated by DOing Lu for ULTRA HELL".chinesed.lpc: "modified by Xiang@XKX".
Lineage: confirmed shared codebase with yanhuangwuhun (archive #66)
Per the task's explicit instruction to check this, diffed this lib's core
files directly against libs/yanhuangwuhun/work/ (both are "yh"/炎黄-
branded forks of the same ES II-family codebase, per the identical
"Linux@lxtx for yh 2003.3" master.lpc credit line — confirmed NOT a
coincidence of similar titles, but genuinely close-cousin lineage):
| file | diff size vs yanhuangwuhun | verdict |
|---|---|---|
| adm/single/master.lpc | 44 lines | near byte-identical; all diffs are wuhun's own already-applied fixes (§14 3-arg valid_override, .c→.lpc refs) or its bespoke STATIC_VAR_TAG/STATIC_FUNC_TAG macro naming choice (this lib used the standard nosave sed instead, see below) |
| adm/daemons/securityd.lpc | 44 lines | same story — content identical, diffs are wuhun's §15n fix (already ported here) + macro-naming choice |
| adm/daemons/chinesed.lpc | 33 lines | same engine, different site credit ("modified by Xiang@XKX" vs wuhun's "modified by [email protected]"); one extra chinese_date() arg difference (unrelated, cosmetic) |
| adm/daemons/named.lpc | 69 lines | same PATH()/sliding-window shape, diffs are wuhun's already-applied §15h fix |
| adm/simul_efun/chinese.lpc | 17 lines | identical is_chinese() GBK byte-range bug, byte-for-byte, diffs are wuhun's already-applied §15h fix |
| adm/simul_efun/message.lpc | large | identical §15aa forward-reference bug (bare message(...) calls before the file's own override), diffs are wuhun's already-applied fix |
| adm/daemons/logind.lpc | 192 lines | same overall flow (id → confirm → surname → given-name → admin-password → password → type → gender → enter_world); diffs are wuhun's fixes plus genuine site-specific content differences (banner name, item drop tables, banned_id list wuhun added, balance numbers) — NOT a byte-identical file, a real (if close) sibling, not a re-host |
| adm/single/simul_efun.c/.lpc (composition list), wizard.lpc, object.lpc, gender.lpc, override.lpc | 0-13 lines | effectively identical; confirms no simul_efun-based set/query/dbase architecture bug (§15) in this lineage — same as every other ES II-family lib processed so far (es1_win/esI/xkx2001/rzrmud/xo/yueyingqiyuan/wuhanzhan/haiyang2/yanhuangwuhun) |
Verdict: genuinely the same underlying "yh 2003" ES II derivative as yanhuangwuhun, most likely two different sites' snapshots/branches of the same base rather than one being a copy of the other (both have real, independent gameplay-content differences, not just branding). Every proven fix from yanhuangwuhun's pass was ported directly here after confirming each one's exact shape still matches (rather than re-discovered from scratch), and this DID save real time — but two NEW bugs were found here that yanhuangwuhun's NOTES.md never mentions (see below), so this was not a rubber-stamp port; each fix was verified against this lib's own source before applying.
Fixes applied (with why)
1. §15h — is_chinese() GBK byte-range check (adm/simul_efun/
chinese.lpc): byte-for-byte identical bug to every other lib in this
project — str[i] < 161 || str[i] == 255 / !(i%2) && (str[i] < 176 ||
str[i] >= 248) are GBK lead/trail-byte range checks, meaningless
against this driver's UTF-8 codepoints. Rewritten as a CJK Unified
Ideographs codepoint range check (str[i] >= 0x4e00 && str[i] <=
0x9fff), strlen(str) < 2 relaxed to < 1 (was "at least one full
GBK char" = 2 bytes).
2. §15h — named.lpc's PATH() macro + sliding-window + length
guards: PATH(name) used name[0..1] ("first GBK char", 2 bytes) →
name[0..0] (first character). Every strlen(name) < 2/< 4 guard
halved to < 1/< 2. The similar-name sliding-window loop
(name[i..i+3] 2-char window / name[i..i+5] 3-char window, i <= l -
4) converted to character-width (name[i..i+1] / name[i..i+2], i <=
l - 2).
3. §15h — logind.lpc's check_legal_name(): byte-length bound
maxlen (called as check_legal_name(arg, 4) for both surname and
given name) halved via maxlen / 2 inside the function; minimum bound
strlen(name) < 2 → < 1. Separately, the combined surname+given-
name length guard (if (strlen(fname) < 4) after concatenating both
parts, message says "至少要有两个汉字" — "at least 2 Chinese
characters") halved to < 2, matching the message text exactly (this
is the same "second site, found only by testing all the way through"
gotcha documented in AGENTS.md §15h — a text search for
check_legal_name alone would have missed this one).
4. §14 — valid_override() 2-arg → 3-arg (adm/single/master.lpc):
added the main_file parameter and the main_file == SIMUL_EFUN_OB ||
main_file == MASTER_OB check, so an efun:: override written in an
#included simul_efun fragment (e.g. message.lpc, #included into
simul_efun.lpc) is correctly recognized as legitimate.
5. §15n — securityd.lpc's valid_read ACL allowlist: added the
switch(func) { case "load_object": case "recompile_object": case
"include": return 1; } early-allow (same insertion point as every
other lib with a custom securityd — right after the existing func ==
"file_size" check), so the driver's own compile-time lazy
load/recompile/#include never gets attributed to a fresh
not-yet-authenticated connection's (player) status and denied.
6. §15aa/§15s — message.lpc's forward-reference + int-0-exclude bug
(adm/simul_efun/message.lpc): tell_object()/tell_room()/
shout()/write()/say() all call the bare message(...) name
BEFORE the file's own message() override is textually defined further
down — silently binding straight to the real efun (no "undefined
function" error, since message IS a real efun name) and bypassing the
override's exclude-argument fix entirely for every one of those call
sites. Fixed with a varargs void message(...) forward declaration at
the top of the file, and the override itself made varargs with an
explicit if (!exclude) efun::message(arg,message,target); else
efun::message(arg,message,target,exclude); guard (a bare int 0
passed as the 4th/"exclude" argument does not satisfy this driver's
void | object | object * type for that parameter and throws "Bad
argument 4 to EFUN message()" at runtime otherwise).
7. §15p — dns_master excluded from adm/etc/preload: was present
(/adm/daemons/network/dns_master), removed proactively before the
first boot attempt. Grepped the whole lib for inline DNS_MASTER/
Mud_name() calls from login/display code per §15ab's follow-up
lesson — none found outside adm/daemons/network/ itself and header
macro definitions, so the preload exclusion alone is sufficient here
(unlike haiyang2, which needed a second inline-call fix too).
8. NEW — clone/user/user.lpc's is_killing(ob) type mismatch
(found via interactive testing, not the lpcc sweep — this file is only
ever reached live via new(USER_OB) mid-connection, never preloaded):
accept_kill(object ob) called is_killing(ob) directly (a bare,
non--> call) where is_killing(string id) (defined in
feature/attack.lpc) declares a string parameter — every one of the
~50 other call sites in the lib correctly calls
is_killing(ob->query("id")). Because it's a direct function call
(not ->), this driver's static type checker enforces the declared
parameter type strictly and refused to compile the whole
/clone/user/user.lpc file — silently blocking make_body() right
after gender selection, with zero error visible to the connecting
player (the connection just stalled with no room/prompt after "您要
扮演男性(m)的角色或女性(f)的角色?"). This is the exact same failure
shape documented in AGENTS.md §15b's "General lesson" (found originally
on nitan_ceshi/archive #60, unrelated lineage — same bug pattern
recurring independently, not shared code). Root cause confirmed by
reading debug.log's compile-error section
(clone/user/user.lpc:568:26: error: Bad type for argument 1 of
is_killing ( string vs object )) rather than guessing. Fixed by
changing the one call site to is_killing(ob->query("id")). This bug
is NOT present in yanhuangwuhun's NOTES.md — a genuine new finding on
this sibling lib, not something that could have been ported.
9. NEW — master.lpc's error_handler() given a permanent log-write
insurance (§15w's second finding): the existing error_handler()
only calls tell_object() when there's an interactive this_player()
present; its own comment ("whatever we return goes to the debug.log")
is stale — this driver's error_handler apply is declared void, so
the return value is discarded, not logged. Added an unconditional
efun::write_file(LOG_DIR + "RUNTIME_ERRORS", standard_trace(error,
caught)); at the top of the function so runtime errors are never
silently lost when there's no player to show them to. (This lib's
log_error() — the OTHER apply, for compile *warnings* — already had
its player-facing broadcast lines commented out by the original author,
so §15w's main "warning-spam" bug does NOT apply here; see "confirmed
not needed" below.)
10. §3 — static → nosave blanket sed (via convert_lib.sh, 147
files touched) — standard mechanical fix for the "static on a
function is a hard parse error" issue.
11. §3 counterexample — string-literal collision revert (moniHuafu-
style, found independently here, same magnitude as yanhuangwuhun's own
105-occurrence finding): the blanket sed also rewrote 104
occurrences across 64 files of "static/..."-prefixed property keys
and log-file paths (e.g. log_file("static/CRASHES", ...),
me->query("static/marry"), me->add_temp("static/drop_npc", 1)) to
"nosave/...", silently changing on-disk log paths and in-memory
property key names away from what the rest of the (unconverted, or
correctly-untouched) code and existing save data still expect.
Reverted all 104 with sed -i 's/"nosave/"static/g' scoped to the 64
affected files. Confirmed correct (not a guess) by checking that
log/static/ is a real, pre-existing, populated directory in this
lib's work/ tree (log/static/bug, log/static/call_player, etc.)
— the sed would have orphaned all of that.
6 more genuine fixes were found via the lpcc sweep (not needed for the
registration path itself, but real compile bugs nonetheless) — see the
"lpcc sweep" section near the end of this file for the full writeup:
inherit/condition/damo.lpc (missing-quote typo, §10), inherit/misc/
quest.lpc (set_information() signature), adm/daemons/questd11.lpc
(9 split-identifier/string corruptions, a new pattern), adm/daemons/
eventd.lpc + adm/daemons/storyd.lpc (§2 rename-width bug, [0..<3] →
[0..<5], also applied proactively to adm/npc/luban.lpc), and
d/city/npc/guidao.lpc (a second, independent instance of the
is_killing() type-mismatch bug).
Confirmed NOT needed (and how confirmed)
- §4 (master.lpc lazy security-daemon load recursion):
master.lpc'svalid_read/valid_writeuse the simplefind_object(SECURITY_D)pattern (noload_object()call at all) — read the actual functions, confirmed no lazy-load-during-compile recursion risk. - §7 (missing
get_root_uid/get_bb_uid): both already present and correctly implemented inmaster.lpc(returnROOT_UID/BACKBONE_UID) — grepped and read, no fix needed. - §8d/§15o (
get_include_path()): no lib-relative<local.h>-next- to-its-user#includepattern found anywhere (checked every#include <x.h>against the actual on-disk location ofx.h) — neither this lib nor yanhuangwuhun implementsget_include_path(), consistent with this lineage simply not using that idiom. - §15 (simul_efun-based dbase architecture bug):
adm/simul_efun/ wizard.lpc/object.lpcetc. have no bareset/query/deletesimul_efuns at all (confirmed identical to yanhuangwuhun's own, already- verified-clean copies) — this ES II-lineage family uses per-objectinherit F_DBASEthroughout, the architecturally correct pattern, not the nitan-family bug. - §15l (master.lpc destructing
SIMUL_EFUN_OBincreate()):master.lpc create()is a singlewrite()line, nodestruct()call anywhere near it — read directly, confirmed absent. - §15u (dormant phone-home license-check function): grepped
securityd.lpc/master.lpcforshutdown()/mass-rm/unlinkpatterns gated on an opaque check — none found. - §15r (check_config.lpc driver-version self-check): no such file exists in this lib at all.
- §15t (absolute-path angle-bracket includes /
..-relative includes / inherit-after-globals ordering): grepped for all three patterns lib-wide — zero hits of any of the three. - **§8f (bare
TYPE * name1, name2;forgotten-star bug)**: 74 lines match the shape on a first grep, but spot-checking several (e.g.feature/equip.lpc'sstring *apply, type;) confirmed the scalar variable (type) is genuinely only ever assigned/used as a scalar (stringp(type = query("armor_type"))) — the shared declaration line is intentional, not a "forgotten star" bug (unlike ds386/Dead Souls, a different, English-language lineage where this pattern was a genuine bug). No fix applied; the lpcc sweep (below) would have caught any genuine instance as a "Bad assignment"/"bad type" error and none turned up tied to this pattern. - §15f (bare
arraytype declaration): zero hits — this ES II-lineage family doesn't use the "NT/nitan/Lonely" idioms at all. - §15v (
LONELY_IMPROVED-gatedefun::calls): not applicable, wrong lineage (nitan-family only). - §8h (convertd.lpc Greek-table backslash typo): no
convertd.lpcfile exists in this lib at all. - §15k (case-sensitive data file paths): checked
WIZLIST(/adm/etc/ wizlist, referenced exactly as"/adm/etc/wizlist") and thewelcomebanner file (/adm/etc/welcome, referenced exactly) — both match on-disk case exactly. - §15ab-1 (missing
!stringp(str)guard in the pre-loginreceive_message()):clone/user/login.lpc'sreceive_message()callsreceive(str)unconditionally with no guard — a latent risk matching this pattern, BUT no code path in this lib callswrite(0)during connection setup (noLASTCRASH-readinguptime.lpc, unlike wuhanzhan/haiyang2 — this lib'suptime.lpcnever reads a file at all), so it was never actually triggered; confirmed via full interactive testing (multiple clean connects with zero crash) rather than left as a pure guess. - §15x (hardcoded
MUD_PORTmismatch):master.lpc'sconnect()apply does no port-based dispatch/validation at all (just creates aLOGIN_OBand returns it) — confirmed by reading the function; no MUD_PORT-style constant gates the connection anywhere. - §15y (mixed GBK+BIG5 config.cfg): visually skimmed every user-facing string in the converted
config.fluffos(default fail message,default error message, the mudnamefield) plus小熊泥苑.txt— all read as coherent, correctly-topical Chinese, no BIG5-decoded-as-GBK mojibake found.convert_lib.shlogged zero lossy conversions forconfig.cfgitself. - §15z (
#define nosave staticcompatibility-shim collision): no#define nosave static/#define protected staticshim present anywhere ininclude/*.h— grepped, zero hits. - §15w main finding (log_error() broadcasting warnings as errors):
master.lpc'slog_error()has its player-facingtell_object()/efun::write()broadcast lines already commented out by the original author (onlyefun::write_file(LOG_DIR + "log", message)remains active) — confirmed by reading the function directly; no spam-to-players risk exists here at all, so nothing to fix (only the secondaryerror_handler()insurance write was added, per fix #9 above).
New finding NOT yet in AGENTS.md's catalog: get_config()/
runtime_config.h numeric-ID mismatch (flagged for the main session to
consider cataloguing)
adm/daemons/versiond.lpc:245 does port = get_config(__MUD_PORT__) +
VERSION_PORT; then socket_bind(vfd, port). __MUD_PORT__ is defined in
this lib's own include/runtime_config.h as CFG_INT(0) under an
old-MudOS-era numbering scheme (BASE_CONFIG_INT = 14, so __MUD_PORT__
= numeric ID 14) — but this modern FluffOS driver's actual get_config()
implementation uses its OWN internal numeric-ID table (unrelated to this
old header's numbering), so get_config(14) silently queries the WRONG
config slot on this driver (empirically returned an empty string, not the
real port int). "" + VERSION_PORT (a string + an int) then string-
concatenates instead of adding, producing the literal string "12",
which fails socket_bind()'s type check (Bad argument 2 to socket_bind()
Expected: int Got: "12"). This is a non-fatal runtime error (caught
by the driver at the point of the bad efun call, doesn't crash the whole
process or block boot/login) that fires exactly once per boot (versiond
calls this from a single call_out("in_server", 2) 2 seconds after
create(), not on a repeating heartbeat).
Confirmed harmless to the registration/login flow, not just assumed:
read versiond.lpc's setup() — version_ok is set to 1
synchronously inside create() (before the 2-second delayed
in_server() call ever fires) whenever RELEASE_SERVER() == "local",
which this lib's adm/etc/config does declare (release server : local).
logind.lpc's login-gate check (! VERSION_D->is_version_ok() && !
VERSION_D->query_temp("operator")) is therefore already satisfied by the
time any player could possibly connect, completely independent of whether
the later broken socket_bind() call succeeds — unlike zhonghua2's §15m
finding, this is NOT a case of a crashed create() silently blocking a
readiness flag. Left unfixed (out of scope: versiond's peer-sync
listening socket is release-server-to-release-server functionality,
irrelevant to a single standalone instance in this project), but flagged
here in case another lib in this batch hits the same get_config()
numeric-ID mismatch in a context that DOES matter (e.g. if some other
lib's registration-critical code path also calls get_config() with one
of these old-style numeric macros) — this project's fix catalog
(AGENTS.md) doesn't have a section for this class of bug yet.
Other observations (not bugs)
feature/feature/is a whole duplicate copy of most offeature/'s files, present in the RAW archive itself (not a conversion artifact) — confirmed nothing references anyfeature/feature/...path anywhere in the lib (grep -rlcame up empty besides the files themselves). Harmless orphaned content from the original author, left as-is (same "don't fabricate/prune content that isn't actually broken" policy as AGENTS.md §12/§13).- 5 files hit
convert_lib.sh's lossy-conversion path (invalid GBK bytes dropped): 2data/analecta/2003/*.txtseed data files,adm/daemons/ quest/{capture,deliver}.c, andcmds/{arch,archBAK}/recovermud.c(a duplicate pair). Inspected all 5 post-conversion — all end cleanly at a normal function-closing brace, no truncated string literals or merged heredoc-tag lines (the §"iconv -c eats a real byte too" failure mode documented for other libs) — confirmed non-issues, not just assumed. - The old
config.cfghas a genuinely duplicateglobal include filedirective (<globals.h>early in the file,<global.h>— nos, referencing a file that doesn't exist — later, both outside any comment block). Confirmed harmless by reading FluffOS's own config parser (src/base/internal/rc.cc'sscan_config_line()): it scans line-by- line and takes the FIRST match, so the correct<globals.h>wins regardless of the later duplicate. No fix needed inconfig.fluffos.
Registration flow — exact transcript outcome
Read logind.lpc's full input_to callback chain first
(logon → get_id → confirm_id → get_surname → get_name → [invalid_new_name
check] → new_ad_password → confirm_ad_password → new_password →
confirm_password → get_type → get_gender → make_body/enter_world) before
scripting the test — no hidden pre-id gate (no BIG5/student-age/client-
version prompt); the very first prompt genuinely is the English id.
Gotcha hit while testing (worth recording as it re-confirms an
existing AGENTS.md lesson): a first attempt used qinfeng2 as the English
id — REJECTED (check_legal_id requires pure English letters, 3-10
chars), which loops back to the SAME "您的英文名字" prompt. Every
subsequent --send in that run (y, 秦, 岭, ...) was silently
consumed as a FAILED retry of the id prompt (each one also failing the
letters-only check) until the literal string "look" (from later in the
send list) accidentally satisfied the id format and got treated as a real
id attempt, followed by "quit" being consumed as its y/n confirmation
answer — producing a confusing, cascading-looking transcript that had
nothing to do with the Chinese-name logic at all. Re-verified with a
corrected id (qinfeng, no digit) and the flow proceeded correctly (same
"always read the actual callback chain" + "re-verify one prompt at a
time" lessons AGENTS.md already documents for other libs, re-confirmed
here for a completely mundane reason — a bad test id, not a mudlib bug).
Full successful run (mudclient.py 127.0.0.1 40061, one continuous
connection, after the is_killing() fix):
--send "qinfeng" -> accepted as new id
--send "y" -> confirmed new-character creation
--send "秦" -> accepted as surname (real Chinese character)
--send "岭" -> accepted as given name (real Chinese character,
combined "秦岭" passes is_chinese/length/
invalid_new_name checks)
--send "adminpass1" -> admin password set
--send "adminpass1" -> admin password confirmed
--send "userpass1" -> normal password set
--send "userpass1" -> normal password confirmed
--send "5" -> character type "均衡型" (balanced) selected
--send "m" -> gender "男性" (male) selectedResult: character successfully created and entered the actual game
world, landing in the starting room 「世外桃源」 (same start-room
name as yanhuangwuhun, another lineage-consistency data point), full room
description with exits (west/east/north/south) and two NPCs (水笙/狄云)
rendered correctly in UTF-8, followed by the live-ticking time-based
command prompt. Save files confirmed written to disk (data/user/q/
qinfeng.o, data/login/q/qinfeng.o).
Second, separate connection (existing-user login + command handling,
--idle 0.3 to beat the once-per-second ticking prompt that otherwise
prevents the idle-gated client from ever sending queued input):
--send "qinfeng" -> recognized as existing id, save file found
--send "userpass1" -> password accepted, "重新连线完毕。" (reconnected
successfully)
--send "look" -> full 「世外桃源」 room description printed again,
correctly rendered
--send "quit" -> "欢迎下次再来!" (graceful disconnect message)Confirms save/restore, password auth, and ordinary command dispatch
(look/quit) all work correctly end-to-end, not just the one-shot
creation path.
Before the is_killing() fix (2 separate attempts, both with a valid
id): registration proceeded correctly through the Chinese name, both
passwords, character type, and gender prompt — then silently stalled
with no further output at all (no room, no error visible to the
client). debug.log showed the real cause: /clone/user/user.lpc:568:26:
error: Bad type for argument 1 of is_killing ( string vs object )
followed by *No program in object '/clone/user/user'! — the player body
class failed to compile, so make_body() returned 0 and the connection
just hung. This is exactly the failure mode AGENTS.md §15b warns about:
never consider a registration flow verified just because it reaches
password/type/gender prompts — it must actually complete into the game
world.
lpcc sweep
scripts/lpcc_check.sh libs/yhyxs/config.fluffos
libs/yhyxs/work — file count 11,001 (well under the
mega-lib bracket), run in full, three times (once to find the initial
failure set, twice more after each round of fixes to confirm progress).
Host memory monitored throughout every run (stayed under ~2.2GB RSS for
this lib's own lpcc --batch process; other agents' concurrent sweeps on
other libs pushed system-wide available memory down to ~1.3GB at one
point, but this lib's own process was never the concern and finished
without incident).
Final result: 10,980 / 11,001 pass (99.81%), up from 10,965/11,001
(99.67%) on the very first sweep — the gap was closed by 6 genuine fixes
found via the sweep (in addition to the is_killing() bug already found
via interactive testing, listed above):
1. inherit/condition/damo.lpc — a genuine pre-existing typo
(§10-style missing closing quote: return name + HIM "发作身亡了;,
missing the " before the semicolon) desynced the parser for the rest
of the file, producing a cascade of nonsensical "Illegal character
0xe9/0x95/0xbf" errors on a much later, perfectly-valid line of UTF-8
Chinese text (confirmed the file's UTF-8 is completely well-formed
throughout via iconv -f UTF-8 -t UTF-8 round-trip — the "illegal
character" reports were 100% a downstream parser-desync artifact of
the one real typo, not a second bug). One inherited file, fixed
once — resolved this file plus all 4 files that inherit it
(kungfu/condition/damo_luanqi, damo_shangshen, damo_zheyuan,
kungfu/class/misc/laonu's sibling kungfu/class/sky/sky5 was
unrelated) in one shot (§8g pattern).
2. inherit/misc/quest.lpc's set_information() signature — declared
string info but virtually every quest file's create() calls it with
a closure (set_information(NPC1_NAME, (: ask_npc1 :))); QUEST_D's
own real implementation already declares this parameter mixed. Fixed
the wrapper to match, resolving all 7 affected quest template files
(capture, deliver, explore, judge, search, shen, supply)
in one shot — the exact same shared-root shape documented for
nitan_san (a different, unrelated lineage) in AGENTS.md, recurring
independently here.
3. adm/daemons/questd11.lpc — a distinct, NEW corruption pattern not
yet in AGENTS.md's catalog: several places in this ONE file have a
literal newline inserted in the middle of an identifier or string
literal (e.g. me, w on one line, ho); starting the next — splitting
the identifier who in two; ob->na / me() splitting name();
db / ase[...] splitting dbase; bas / e_name splitting
base_name; query_enti / re_dbase() splitting query_entire_dbase;
$(kno / wer)) splitting a closure macro parameter name knower),
plus several purely-cosmetic string-literal splits ("experien /
ce", q["i / d"], "reply_ / by", "family/fami / ly_name",
"potential / ") that don't break compilation (LPC allows an
embedded literal newline inside a string) but silently corrupt the
stored value (e.g. a property key becomes "experien\nce" instead of
"experience"). Confirmed identical corruption already present in the
RAW pre-conversion archive (diffed against raw/.../questd11.c) — not
something our pipeline introduced. Looks like the original file was
edited at some point with a tool that hard-wrapped long lines at a
fixed column width, occasionally landing mid-token. Found all 9
instances with a small Python scan (line N ends with an
identifier-continuing character, line N+1 starts with one) and fixed
each by hand (rejoining the split token/string) rather than a blind
regex, per AGENTS.md's standing caution about bulk fixes on this class
of corruption.
4. adm/daemons/eventd.lpc / adm/daemons/storyd.lpc's [0..<3] →
[0..<5] rename-width bug (§2's documented pattern, recurring
independently — same shape as yanhuangwuhun's own finding, ported
directly once recognized): both build an event/story name list via
get_dir(DIR + "*.lpc") then strip the extension with a hardcoded
map_array(names, (: $1[0..<3] :)), correct for the original 2-char
.c extension but leaving a trailing .l after the .c→.lpc
rename (e.g. emei.lpc → emei.l instead of emei). This broke
EVERY event/story lookup with call_other() couldn't find object
'.../emei.l' — surfaced in the sweep as 8 unrelated-looking failures
(u/rcwiz/mailbox/mailbox, u/rcwiz/xmas/man, d/city/zuixianlou2,
d/city/npc/guidao, adm/daemons/network/services/ping_q,
adm/daemons/network/messaged, adm/daemons/story/pangtong,
adm/daemons/story/mengzi) all sharing the identical error string,
resolved in one shot by fixing the 2 daemon files (§8g pattern again).
Proactively applied the identical fix to adm/npc/luban.lpc's two
own [0..<3] occurrences (a room-building NPC feature, confirmed via
reading that both operate on .lpc-suffixed filenames) even though
neither was caught by the sweep or reachable in our registration test —
this NPC's "build a room" feature is never exercised by either, so
this fix is unverified by any live test, flagged here honestly rather
than claimed as confirmed.
5. d/city/npc/guidao.lpc's is_killing(who) type mismatch — the
exact same bug shape as clone/user/user.lpc above (direct call
passing an object where is_killing(string id) expects a string),
independently present in a second file. Fixed identically
(is_killing(who->query("id"))); this also resolved the cascading
d/city/zuixianlou2 failure (the room that creates this NPC).
Remaining 21 failures, all confirmed non-critical / expected noise
(none appear anywhere in log/debug.log across 3 separate boot+play
sessions):
- 9 files under
/b/yitian/(was_lou2/4/dayuan,npc/{zheng7,wang8,zhou5,wu6,sun3,li4}) — genuine missing zone content (§13): all reference/d/tulong/yitian/npc/obj/...paths, andd/tulong/yitian/does not exist anywhere in this archive (only bared/tulong/does) — the archive shipped without this sub-zone's content, not a bug to fabricate a fix for. kungfu/class/misc/laonu— same category:carry_object("/d/baituo /obj/shezhang")references a file that doesn't exist anywhere ind/baituo/obj/(confirmed viafind/ls).kungfu/class/sky/sky5(cascades fromkungfu/class/sky/npc/ beichou.lpc) —set_skill("six-finger", ...)references a skill that was never defined anywhere in this lib'skungfu/tree — another missing-content gap, not fixed.clone/board/gumu_b— pre-existing corrupted save data (data/board/gumu_b.ohas a truncated"title":"旅,]),entry with no closing quote on a real player-submitted board post from 2002, confirmed present in the raw archive itself) throwingrestore_object(): Illegal mapping format— same general shape as §15m (corrupted seed data crashing an unguardedrestore()), but this is genuine historical player-submitted board content on a board that's never touched by preload/registration, so left as-is rather than editing/deleting real seed data.clone/board/wfoxd_b— references/d/city/wfoxd, which doesn't exist anywhere in the archive — another §13 missing-room gap.u/rcwiz/mailbox/mailbox,u/rcwiz/xmas/man— classic §6b isolated-compile artifact: bothcreate()s start withobject me = this_player();then immediatelyseteuid(geteuid(me))—this_player()is legitimately0in a barelpcccontext with no real connection, sogeteuid(0)throws. Would work fine wherever these are actuallynew()'d from a real player context in-game (personal wizard-dir feature objects, never preloaded, never touched by registration).adm/daemons/network/services/ping_q,adm/daemons/network/ messaged— both transitively loadadm/daemons/network/dns_master.lpc(ping_qdirectly;messagedvia its ownstartup_udp()), which hits the exact sameget_config()/runtime_config.hnumeric-ID mismatch already documented above forversiond.lpc(Bad argument 2 to socket_bind() Expected: int Got: "4"/"10") — same non-fatal root cause, not a new bug.dns_masteritself is correctly excluded from preload (§15p) and neither of these two files is ever preloaded or touched by registration either.adm/daemons/story/{pangtong,mengzi}— fail to load with no diagnostic text at all in an isolatedlpccrun (unlike every other failure here, no error/warning printed between the last compile trace line and "Fail to load object"). Not investigated further — these story-content files are never preloaded, never appear indebug.logacross 3 real boot+play sessions, and chasing a silent isolated-compile failure with no error text for two files this deep in a rarely-touched daemon subsystem was judged not worth the time budget per AGENTS.md's explicit "prioritize breadth over depth" / "long tail, not all reachable at once" guidance (§15f's closing note makes the same call for a different bug class). Flagged here for whoever revisits this lib.adm/simul_efun/util— confirmed §6b's documented#include-only- fragment false positive:util.lpcis#included intosimul_efun.lpcand uses theNORansi-color macro (defined viasimul_efun.lpc's own#include <ansi.h>, not its own) — fails standalone, compiles and runs fine as part of the real composed file (verified:/adm/single/simul_efunitself passes a directlpcccheck with zero errors, and zero mentions ofutil.lpcappear anywhere inlog/debug.log).
2026-07-23: driver rebuild retest + LPC formatter + WASM check
- Formatter: ran
tools/lpc-syntax'sformat-corpus.mjsover all 11001.lpcfiles inwork/; 10966 written, 35 already-conformant, 0 errors. Found and fixed the same::PARENT_FUNC(...)line-wrap corruption bug documented in this pass's siblingyanhuangwuhun's NOTES.md (a real bug in the formatter itself, not lib-specific): 2 files in this lib —inherit/item/combined.lpc's::moveandkungfu/class/huashan/yue-wife.lpc's::recruit_apprentice— had the exact same corruption (if (: : move(dest, silent)\n)\n{ ... }plus a stray extra}), byte-for-byte identical to the corresponding files inyanhuangwuhun(confirming these two sibling libs still share this template code verbatim). Fixed both by restoring the original::FUNC(...)call + brace structure (verified against the pre-format git blob), then re-ran the formatter over just those 2 files, which now formats them correctly. Verified both compile clean vialpcc. - Native retest: rebuilt
~/src/fluffos/build-debug/src/driverbooted clean apart from the already-known, harmlessversiond.lpcsocket_bind()config-mismatch runtime error (flagged in TODO.md at the time this lib was first done, confirmed still present and still non-fatal — boot completes, port accepts connections). Full registration re-verified with a real Chinese name (林枫, idlinfengb): id/confirm/surname/given-name/admin-password/password/ type/gender all completed, landed in 【世外桃源】exactly as the original pass found;lookcorrectly re-displayed the room,quitgave the correct farewell ("欢迎下次再来!"). Re-confirmed the §15an live-clock-prompt gotcha from AGENTS.md applies here too (this lib's prompt shows a live per-second clock) — used--idle 0.4per the documented workaround, worked cleanly. Zero real debug.log errors (only the known versiond noise). - WASM: booted with the expected non-fatal
socket_*preload noise. Reproduces the exact same WASM-specific registration blocker documented inyanhuangwuhun's NOTES.md:adm/daemons/ logind.lpc'slogon()calls!VERSION_D->is_version_ok()uncaught,versiond.lpcfails to compile entirely under WASM (missingsocketspackage), so the resulting runtime error abortslogon()before the username prompt ever appears — registration cannot proceed at all under WASM. Confirmed this is the shared sibling code (yanhuangwuhun's exact sameVERSION_Dcall, sameversiond.lpcshape) failing identically, not a new independent bug. Not present natively (full playthrough above was clean). Not patched, same reasoning asyanhuangwuhun's entry — a real gap but rooted in the WASM build's documented lack of thesocketspackage, not a mudlib defect or something in scope to fix here.
2026-07-23 (integrity review): the previous pass's combined.lpc hand-fix was incomplete — stray brace fixed
The previous pass's hand-fix of the formatter's ::move line-wrap
corruption in inherit/item/combined.lpc restored the if (::move(dest,
silent)) call but left the corruption's stray extra } behind (line 64),
so the file did NOT compile (syntax error, unexpected '}') — despite
that pass's NOTES claiming it was lpcc-verified. Every money/stackable
item inheriting /inherit/item/combined would have failed to load at
runtime. Removed the stray brace; structure now matches the pre-format
git blob exactly (verified against commit 3501d9782f) and the sibling
yanhuangwuhun's correct copy; lpcc compile now passes. Full
registration + look/score/quit retested clean afterwards.
WASM-enablement pass (loopback-allow / gate bypass / admin seed)
Standard WASM-first pass per AGENTS.md §1.3b/c/e and §1.5. Ported the
sibling yanhuangwuhun's fix shapes (near-identical band.lpc/
logind.lpc, confirmed by diff before patching). Gates patched:
adm/daemons/band.lpcis_banned()(~line 38) — loopback short-circuitreturn 0;at the top.adm/daemons/logind.lpc— addedis_loopback_conn(object)helper, and:
- logon()'s BAN_D->is_banned() gate (~line 68) — loopback exempt.
- the WASM registration blocker: both VERSION_D->is_version_ok()
call sites (banner ~line 84, get_id() hard gate ~line 160) —
guarded with find_object(VERSION_D); daemon absent (as under
WASM, where versiond's socket_bind()/socket_create() usage
keeps it from compiling) ⇒ notice/gate skipped. This was the
documented "no id prompt under WASM" hard blocker described above
— same root cause as yanhuangwuhun's.
- No uptime() startup-grace gate exists here. The iplimit > 3
same-IP multi-login cap (~line 106) is already dead code (wrapped
in #if 0 in the original archive) — nothing to bypass; noted
only, no change made.
Correction applied mid-pass (fail-closed retrofit, 2026-07-24): the
loopback helpers above were initially written matching the
project-wide convention at the time (AGENTS.md §1.3b), which also
treated an empty/non-string/malformed-IP 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; confirmed the locally-built
build-debug/build-wasm binaries already postdate both commits), so
treating unparseable IPs as trusted is no longer justified and is a
fail-open gap. Retrofitted both helpers to the fail-closed form: 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 login from
127.0.0.1 still shows "由127.0.0.1连线进入" (loopback path still taken)
and update /adm/daemons/band still succeeds — the tightening did not
regress local/native play.
Admin account seeded: id fluffos, pw Mud@2026, name 浮浮 (surname
prompt skipped with bare ENTER — this lineage's surname+given-name split
flow rejects 姓=名, same as yanhuangwuhun; "浮"+"浮" does not work, so
skip the surname and enter the two-character given name "浮浮" directly).
Granted (admin) via fluffos (admin) appended to adm/etc/wizlist.
Verified: real registration flow (fluffos/y/[enter]/浮浮/admin-pw×2/
pw×2/5/m → entered 世外桃源, save written to data/user/f/fluffos.o +
data/login/f/fluffos.o); restarted driver to pick up the new wizlist
entry; relogin as fluffos → banner shows "目前权限:(admin)",
update /adm/daemons/band → "重新编译 /adm/daemons/band.lpc:成功!",
quit → clean farewell. One pre-existing non-fatal runtime error noted
during the first get_passwd()/check_ok() of each boot (lazy-loading
adm/daemons/network/messaged.lpc's startup_udp()): *Bad argument 2
to socket_bind() — same class as the already-documented versiond
socket_bind() config-mismatch noise, harmless, does not block login;
not patched (pre-existing, out of scope for this pass).
Retest: fresh normal registration (id qintest, surname skipped, given
name 秦风, type 猛士型, gender m) end-to-end — look/quit correct, landed
in 世外桃源; test character saves removed after
(data/user/q/qintest.o, data/login/q/qintest.o). Zero new
debug.log errors beyond the one pre-existing messaged.lpc warning
above. Note: this lib's login prompt has a per-second live clock once
in-world (AGENTS.md §8.3 item 1) — mudclient.py --idle 0.4 was
occasionally too tight and dropped queued sends into "什么?" (unknown
command); --idle 0.6 was reliable. Not a mudlib bug.
Save files for the orchestrator to add (none gitignored, normal add):
libs/yhyxs/work/data/user/f/fluffos.olibs/yhyxs/work/data/login/f/fluffos.o
WASM 修复摘要(迁移自 meta.json 的 group_note)
关系很近的手足档案,内容独立(炎黄英雄史)。状态已从过时的 limited 修正——和手足档案 yanhuangwuhun(045)共享完全相同的真实 bug,两份都已修复:adm/daemons/logind.lpc 的 check_ok() 在每一次成功登录时都未加保护地呼叫 MESSAGE_D->find_chatter(),而 MESSAGE_D 在 WASM 下编译不过(原始 socket 呼叫)——未捕获的'No program in object'错误会让 check_ok() 执行到一半就中止,之后每一条指令(包括 quit)都回显'什么?',从未真正进入任何房间。已用 find_object(MESSAGE_D) 保护这个呼叫来修复。修复后重新验证干净:管理员登录(fluffos/Mud@2026)正常进入游戏世界,'目前权限:(admin)',quit 正常('欢迎下次再来!')。这份档案的提示符会显示实时时钟——测试时用的是 --idle 0.3。
深度功能测试(§10.7,2026-08-04)
原生 driver(端口 40061)跑了一遍超出注册流程的完整游玩——注册流 程本身和这批档案里常见的模式差异很大:分开输入姓/名两段中文姓 名、两组密码(管理密码+普通密码)、走到不同 NPC 面前听劝说词来选 "性格类型"(本轮走到"花铁干"处选了"天性阴险奸诈",只是玩家口味 选择,不是 bug)、在阎罗殿用 wash 指令跳入"忘忧池"重掷天赋、最后 用 born <中文地名> 投胎(选了"扬州人氏")。这份档案的提示符会持续 显示实时时钟(每秒刷新),用标准的 --idle 节奏发送指令会因为"从未 真正安静"而卡住送不出后续指令——继承自本档案自己 NOTES.md 里已经 记录过的经验,用 --idle 0.3-0.5 才能正常send through。
主动检查:adm/daemons/logind.lpc 没有 printf("%O\n", ob)
调试残留(§7.34 未命中),没有 §8.9 食物/饮水按错误对象 age 初始化
的问题(age 检查本来就读 user->query("age"),写法正确)。
MESSAGE_D->find_chatter() 的调用已经用 if (find_object(MESSAGE_D))
包住——这是这份档案自己 WASM 修复阶段已经修好的 bug,本轮只是确认
仍然正确,不是新发现。command_hook 有三份拷贝(feature/
command.lpc 是 F_COMMAND 宏实际指向的活文件,cmds/chat/
command.lpc 和 feature/feature/command.lpc——注意这个诡异的嵌套
路径——都是未被引用的死拷贝),三份都是干净的 nomask,不是 bug。
注册与地图探索:注册测试角色(秦岩 / id qinyan),投胎到"扬
州人氏",落在扬州客店。逛了北大街、醉仙楼、钱庄方向,地图连通性
和房间描述全部正常。
商店测试:客店的店小二(d/city/npc/xiaoer.lpc)其实不是
vendor(没有 F_VENDOR,没有 vendor_goods),纯粹是场景装饰 NPC——
按新手指南提示改到"醉仙楼"(d/city/zuixianlou.lpc,用的是
npc/xiaoer2.lpc),list 正确显示了六种食物/水袋商品和价格;
buy 因为身上没钱被正确拒绝("店小二冷笑道:穷光蛋,一边呆着去!")
——这是新角色没有初始资金的正常内容状态,不是程序 bug。
战斗+死亡+复活测试(真实死亡,undisturbed 完整现场验证):北
大街站着"欧阳克"(金庸小说反派角色,白驼山少庄主——和 jyqxc
里独立遇到的是同一个角色设定,看来是这整个大家族里反复出现的固定
布景角色)。第一次 kill ouyang ke 几乎没受伤(战力差距这次没有
jyqxc 里那么悬殊),第二次交手加上当时的降雪/冰冻环境效果叠加,
真的把测试角色打死了("你的眼前一黑...你扑在地上挣扎了几下...死
了!",谣言频道播报"秦岩被活活冻死了"——环境效果和战斗伤害叠加导
致死亡,是有趣的系统联动,不是 bug)。死亡后被送到"鬼门关",
"白无常"(刚修复的 bai.lpc)在场。没有主动打断复活序列,等
待完整走完五阶段对话后重连确认:角色已经复活、站在"武庙"(和新
手指南承诺的"扬州的武庙"完全一致),hp 显示精气/气血都已满血
(100/100),潜能从 99 减半到 50(符合死亡惩罚),食物/饮水仍有
余量。这是本轮系列测试里第二次(第一次是 jyqxc)现场完整验证了
§7.68 修复后 undisturbed 路径的正确性。
发现并修复 §7.68 的又一实例:d/death/npc/{hei,bai}.lpc(黑
无常/白无常,属于 yh2003 血统自己的死亡系统,和之前几个不同家族
的"gargoyle"系列文件不是同一套代码,但 bug 形状完全一致)都有
if (!ob || !present(ob)) return;,同样是五阶段 death_stage()
循环。已按已验证的修法拆分修复。
发现并修复一类全新的、本项目此前从未记录过的 bug:extensionless
GBK 文本残留,穿过了原始转码流程和后续的 WASM 修复流程(新增
AGENTS.md §4.1 归档条目):注册后进入客店,客店留言板显示"131 张
留言未读",随即自动弹出一段很长的分页文字("== 未完继续 56% =="),
渲染出来是一整屏 U+FFFD 替换字符构成的乱码。追查后发现真正的源头
是 help/rules(游戏规则说明,首次登录或 help rules 时显示)——
这个文件从未被转成 UTF-8,driver 原样把 GB18030 字节吐给客户端。
对整个 work/ 树做了一次完整的 Python UTF-8 解码扫描(不只是
.lpc/.h),又额外发现两个同类问题:clone/game/8_hlp、
clone/game/21_hlp(拱猪/21点两个纸牌小游戏的说明文字)。三个文
件都是无扩展名的纯文本帮助文件——正是 AGENTS.md §4.1 早就警告过的
"extensionless banners/help text 也是 GBK"这一类,但从未被
convert_lib.sh(只认识 .c .lpc .h .txt .log .cfg .conf .map
等已知扩展名)或历次针对 .lpc/.h 的补漏扫描捕捉到,一直藏到这
次 §10.7 深度测试才现场触发。用 iconv -f GB18030 -t UTF-8 转换
三个文件,人工核对转换结果是通顺的中文(规则说明提到"东方故事Ⅱ",
证实了这条血统的引擎归属;两个小游戏说明署名"猫部猫(Catyboy)"),
现场用 help rules 重新验证显示正常。同一次全树扫描还发现两个非
文本的历史遗留文件(kungfu/skill/huashan-quan/MFM1992——纯随机
二进制数据,没有任何 .lpc 文件引用它;adm/tmp/swapfile.*——driver
运行时状态文件,也不是源码)——两者都是很久以前的转换/挂起会话遗
留物,未做任何处理,确认不在任何代码路径上,不影响游戏。
意外撞上 AGENTS.md §10.8 已归档的驱动级崩溃类,第六个独立实例:
死亡/复活测试完成、hp 显示正常之后,下一次尝试重新连线收到
CONNECT_FAILED: Connection refused——driver 进程已经在两次连线
之间的空档期真的段错误崩溃了(Segmentation fault),debug.log
里没有任何痕迹(最后几行还是普通的编译警告),boot.log(driver
自己的 stdout)捕捉到了完整的 C++ 崩溃堆栈:崩溃发生在 driver 自
己的周期性 reset 扫描内部(backend_run_one_gametick →
look_for_objects_to_swap → reset_object → object_visible,
写 ob->next_reset 时崩溃),不是任何玩家指令或可捕获的 LPC 错
误,紧邻崩溃前是一长串测试角色从未去过的地区(青城/洛阳/泉州/开
封等)第一次被懒编译的记录——和 AGENTS.md 已经记录的前五个同类崩
溃案例(xjcq2000/shiji/shenzhou/xlqy_new2007/
nitan170911)机制高度吻合:"环境世界模拟迟早触发大范围懒编译,
最终腐化某个对象/字符串的内部状态,下一次无关的驱动内部操作触发
时整个进程崩溃"。没有尝试修复(这是驱动层内存腐化问题,不是
mudlib 可修的 bug),已按已有惯例记入 AGENTS.md §10.8 作为第六个
独立实例,未重启 driver 继续这份档案后续的测试。
git status --short libs/yhyxs/ 复查干净:只有五处源码修改(三处
GBK→UTF-8 转码 + 两处 §7.68 修复)是跟踪变更,测试角色的新存档
(data/{user,login}/q/qinyan.o)保持未跟踪、未提交。
更正(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 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 60 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
AGENTS.md §7.100 修复(2026-08-19,批次三)
ROOM 基类冗余 replace_program(ROOM); 自崩溃地雷(详见 AGENTS.md
§7.100):3431 处命中。自带建房工具 clone/misc/roommaker.lpc 的
字符串拼接模板同样修复。
git diff --stat:3430 files changed, 1 insertion(+), 3431 deletions(-),
与预期精确吻合。
验证:build-debug 驱动真实冷启动,端口 40061 正常监听。既有管理
员账号 fluffos/Mud@2026 登录,目前权限:(admin),世外桃源→
桃源茅屋→世外桃源多房间走访无误(因本 lib 每秒刷新的活动时钟提示
符会打断 mudclient.py 的"静默等待"轮询,改用固定间隔的裸 socket
脚本测试),quit 干净退出,全程无新增 "cannot replace"/"cannot
bind" 日志行。
``§7.112`` residual-gap closure (2026-08-20)
Corpus re-scan (grep -rl 'call_out("death_stage"' ... | filter for missing guard) found unguarded init()-scheduled death_stage() call_out chain(s) in d/death/npc/bai.lpc, d/death/npc/hei.lpc that the original two-wave sweep (see AGENTS.md §7.112) missed -- same reconnect-triggered duplicate-chain bug, different filename/lineage. Added the standard query_temp("death_stage_active")/set_temp/delete_temp re-entry guard, adapted per file's own exit points. Compile-verified via lpcc --batch.
§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): 5 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.
§7.125 sibling-sweep fix: premature set("registered", 1) defeats the registration gate (2026-08-28)
Flagged in AGENTS.md §7.125 as a sibling worth checking (originally
found on zhyx, same yh2003/ES2 lineage). Confirmed byte-identical:
adm/daemons/logind.lpc's enter_world() unconditionally ran
user->set("registered", 1); //user->set("born",1); right after
handing out starting clothing, on every login — permanently
short-circuiting the register-room's exit gate and channeld's
registration check from a character's first-ever login onward, since
d/register/npc/shuisheng.lpc's do_decide() (the correct, sole
place this flag should be set, confirmed present and correct in this
lib too) could never matter — the flag was already permanently 1
before the player ever got a chance to register. Fixed by deleting the
premature line in enter_world(). Verified via a clean native driver
boot only (not an independent live walk-out-of-the-room reproduction
on this specific lib — the bug shape and remedy are a verbatim port of
an already live-verified fix from the shared-lineage sibling zhyx).
§10.7 深度功能测试(回合三 · 批次三,2026-09-01):五个全新角度,全部干净
上一轮(2026-08-04)已经深度测试过注册流程、地图探索、商店、真实 死亡+undisturbed 复活、GBK 乱码新 bug 类等。本轮受命从五个明确未 覆盖的角度重新测试,刻意寻找编程 bug(而非内容/平衡问题):
① 拜师入门机制(区别于死亡系统的"白无常"):cmds/skill/bai.lpc
是真正的拜师指令,最终会对目标 NPC 调用 ob->attempt_apprentice(me)。
本档案里能真正收徒的门派入口分散在各 kungfu/class/<门派>/*.lpc
里,且各家老大门槛都很高(如丐帮帮主洪七公要求 combat_exp >=
400000),新手要到达要花很长时间;但丐帮在中央广场树洞
(enter dong → /d/gaibang/inhole)里的黎生(kungfu/class/
gaibang/li.lpc)没有战力门槛(仅要求 permit_recruit() 通过 +
shen >= 0),是新手唯一能立即验证的真实入口。用测试角色"王道"
(wangdao)现场 bai li,全程走完 attempt_apprentice() →
command("recruit ...") → cmds/skill/recruit.lpc 的完整链路,
真实状态变化确认:称号从"普通百姓"变成"丐帮第二十代传人"(显示
"小叫花"),score 显示"师父是黎生",family/family_name =
"丐帮"。干净,没有发现 bug。(顺带发现 d/gaibang/npc/{1..7}dai.
lpc 这些普通丐帮弟子场景 NPC 虽然通过 create_family() 设了
family,却没有 inherit F_MASTER 也没有定义
attempt_apprentice()——理论上对它们 bai 会触发一次"函数不存在"
的 call_other,但这更像是"这些低阶弟子本来就不该被拜师"的场景设
计缺失防御性检查,而不是会崩溃或产生错误数据的编程 bug;受时间
限制未继续深挖,留作观察,未作修改。)
② 留言板 post/read 真实往返:客店留言板(clone/board/
kedian_b.lpc,inherit BULLETIN_BOARD)此前已被 §7.86 跨库扫描
处理过(删除多余 replace_program()),但那次只做了编译检查,未
做过真实的 post 命令回归验证。本轮现场 post test-post-yhyxs
+ 编辑器正文 + . 结束,留言完毕,留言数从 131 增至 132,read
new 成功读出第一条留言(无崩溃、无"cannot bind an lfun fp"错
误)。§7.86 修复在这份档案上首次得到真实的 live 回归验证,干
净。
③ 货币跨 quit/reconnect 持久化:先确认 inherit/item/money.lpc
的 query_autoload()(AGENTS.md §7.199 类 bug 的关键函数)本身
就是启用状态(未被注释掉,和风云系血统的 §7.199 bug 不同),
这份档案不属于风云系血统。现场验证:管理员 clone /clone/money/
gold 50 + give gold to wangdao 给测试角色五十两黄金,i 确
认在身上;quit("欢迎下次再来!",没有"你丢下一些黄金"的丢弃
提示);等待退出锁定窗口过后重新连线,i/score 确认五十两黄
金完整保留。干净,没有 §7.199 那类 bug。
④ 死亡对话中途net-dead硬断线(对抗性测试):区别于上一轮"不
打断、等待完整走完"的验证方式,本轮刻意在死亡序列进行中制造真实
的原始 socket 断线(不发 quit,直接关闭连接),测试 d/death/
npc/bai.lpc(含 §7.112 补丁的 death_stage_active 重入守卫)
在这种最坏情况下是否会产生重复对话/状态损坏/卡死。用管理员
smash wangdao 触发真实死亡,立即连线捕获到死亡对话第 1、2 阶
段("白无常用奇异的眼光盯著你" / "从袖中掏出一本像帐册的东西翻
看著")后,硬断开 socket(脚本进程直接关闭连接,不发送任何命
令)。等待足够时间让剩余阶段在服务端后台跑完后重新连线,确认
score 显示"你到目前为止总共到黑白无常那里串门二次"(正确的次
数,没有因为断线重连触发第二条 death_stage() 调用链而重复计
数)、死亡原因正确记录一次("你最后一次是被浮浮用雷劈死了")、
落脚点正确(复活至"武庙"),全程 debug.log 保持空白无报错。
§7.112 的重入守卫在真实的 net-dead 中途硬断线场景下依然完全
正确,干净。
⑤ §7.19 类 enable_commands() 重入检查:feature/command.lpc
是全档案唯一的 enable_player()/enable_commands() 调用出口
(nomask void enable_player()),本身已经有正确的重入守卫:
nosave int enabled = 0;
nomask void enable_player() {
...
if (!enabled) {
enable_commands();
enabled = 1;
add_action("command_hook", "", 1);
}
...
}配对的 disable_player() 也会把 enabled 重置回 0 再调用
disable_commands(),是完整对称的实现——比 §7.19 记录的
living(this_object()) 猜测式守卫更精确(不依赖"当前是否
living"这个间接信号,而是直接跟踪"enable_commands() 是否已经
调用过"),对 mhxy 那种合法重入(sleep/wake 类流程)也不会误
伤。全档案 grep 未发现任何绕过这个统一出口、直接调用 enable_
commands() efun 的 NPC/房间代码。结构性免疫于 §7.19 这一类
bug,干净。
总结:五个角度全部现场验证,没有发现新的编程 bug,也没
有需要移植给 yanhuangwuhun/yhwhpublicfi 的修复。测试全程原
生 driver(端口 40061)跑了约 24 分钟,本轮没有复现上一轮遇到的
§10.8 驱动级懒编译崩溃(同一份档案上一次是该崩溃类的第六个独立
实例)。测试角色"王道"(wangdao)的存档(data/{user,login}/w/
wangdao.o)按惯例保持未跟踪、未提交;测试过程中意外修改的三份
已跟踪共享状态文件(data/board/kedian_b.o 的测试留言、data/
dbased.o 的 ID 计数器、管理员 fluffos 账号的登录位置/时间戳)
已用 git checkout 还原,本次没有产生任何需要提交的源码变更。