Reborn in Blood

✅ 可玩

浴血重生

yxcs

🔑 fluffos / Mud@2026 更新 014de28 2026-09-01 源码 下载 ZIP

▶ 开始游玩 · Play Now

浴血重生登录横幅自称 "1.YXCS"/"浴血重生",是一款围绕"浴血重生"这一江湖恩怨、洗心革面主题展开的传统武侠 MUD,新角色要经历标准的门派拜师与完整的功夫技能体系,创角时还能选择"国际挑战者"式的比武活动;血统比较特殊:既有 ES II 引擎系列的痕迹(`chinesed.lpc` 等文件署名可追溯到 "ES"→"XKX" 一支),又混入了"Lonely"("nitan/NT" 系列的作者)后期改写的部分模块,属于一个 ES 系底子上被二次深度魔改的分支,不是纯血统的任何一支;地图上"世界之巅"跳崖场景与"天涯"家族(`xysylmhb`/`xyzxiiylzymh`/`yzxiiizylfy`/`xyzx3`/`tianxiawuxue`)逐字节相同,"Lonely"这条线索也和 `tianxiawuxue` 鸣谢名单里的同一位作者相呼应;另有一处防盗号保护,已在 wizlist 中登记为高权限的 ID 无法在注册流程中直接设置密码,需先从名单移除、注册完成后再加回去。

English

A traditional wuxia revenge story built around its "Reborn in Blood" premise — the login banner self-identifies as "1.YXCS" / "Reborn in Blood." New characters go through standard sect apprenticeship and a full kungfu/skill system, plus an "international challenger"-style tournament option at character creation; a wizlist-based anti-account-theft protection strips password-setting rights from any ID already registered as a high-privilege wizard until it's temporarily removed from the list. Its lineage is unusual: it carries traces of the ES II engine series (files like chinesed.lpc trace authorship back through the "ES"-to-"XKX" branch) mixed with modules later rewritten by "Lonely," the author behind the Nitan/NT series — making it a deeply modified hybrid rather than a pure descendant of any single lineage. Its map can be pinned down more precisely: the "Peak of the World" cliff-jump scene is byte-identical to the unrelated "Tianya" family shared by several other archives in this collection (xysylmhb/xyzxiiylzymh/yzxiiizylfy/xyzx3/tianxiawuxue).

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/yxcs/

管理员账号 / Admin account

警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。

本地运行

cd libs/yxcs
~/src/fluffos/build-debug/src/driver config.fluffos

游戏端口:40056

NOTES · 移植与修复记录

yxcs — 浴血重生 (archive #62)

Archive: archives/浴血重生MUD.rar. Port: 40056. Status: done (boots clean, full registration flow verified end-to-end with real Chinese names, reaching an actual game room; lpcc sweep at ~98%+ pass).

What this is

The mudlib's own config.cfg self-identifies name : 1.YXCS — "YXCS" is the pinyin initialism of 浴血重生 ("Bathed in Blood, Reborn"), matching the archive's own title exactly. The live in-game banner also prints "浴血重生(2.YXCS)". No slug discrepancy worth noting.

Archive layout: the actual mudlib root is nested one level down at raw/socket/yxcs/raw/socket/ also bundles several unrelated Windows binaries alongside it (MudOS.exe, serv-u/ — a cracked FTP server installer + keygen, superscan/ — a 2000s-era network port scanner). These are contemporary "toolbox" bundling from whoever originally distributed this archive, not part of the mudlib at all — ignored entirely, only raw/socket/yxcs/ was copied into work/.

13,452 raw files / 11,995 .lpc after conversion — a normal-to-large-sized lib, well below the "mega-lib" bracket (AGENTS.md §6b) that would call for skipping the full lpcc sweep.

Lineage

A genuine hybrid of two lineages already seen elsewhere in this project, not a clean match to either:

Fixes applied, and why

1. NEW bug class: #define nosave static / #define protected static

guarded by an LPC macro this driver never predefines — silently converts nearly every nosave function in the lib into static function (a hard parse error, AGENTS.md §3)

include/globals.h (and identical copies in adm/obj/master.lpc, adm/obj/simul_efun.lpc, and 4 files under a u/lonely/ backup tree) had:

#ifndef __SENSIBLE_MODIFIERS__
#define nosave static
#define protected static
#endif

__SENSIBLE_MODIFIERS__ (double-underscore, LPC-visible) is never predefined by this driver to LPC code (confirmed: only mentioned in ~/src/fluffos/src/local_options.README as a suggestion, never actually #define'd anywhere reachable from LPC) — so this #ifndef branch always fires. Since this driver DOES support nosave as a real (soft-warning-only) function modifier and rejects static on functions with a hard parse error (empirically verified: static int foo(){} → hard error, nosave void foo(){} → soft "Illegal to declare nosave function" warning only), this macro would have converted the vast majority of this lib's own nosave function declarations into an immediate, near-universal boot-blocking parse error.

Caught by luck, then fixed properly: convert_lib.sh's blanket \bstatic\bnosave sed (§3's standard fix) also fired on the word "static" *inside this macro's own replacement text*, turning #define nosave static into #define nosave nosave — a self-referential macro that the C preprocessor's "blue paint" rule resolves as a no-op (leaves nosave as itself), which accidentally neutralized exactly half the problem. But it also turned #define protected static into #define protected nosave, silently changing protected's semantics (DECL_PROTECTEDDECL_NOSAVE) instead of leaving it alone. Verified protected is a fully legal, real modifier on this driver on its own (protected int foo(){} compiles clean, no warning) — so the correct fix is to let BOTH pass through completely unchanged, not alias one to the other. Rewrote all 7 files' macro block to:

#define nosave nosave
#define protected protected

Spot-checked afterward: grep -rn '\bstatic\b' across the whole work/ tree now only matches my own new comments, confirming no other lingering static-as-function-modifier collateral.

2. §15h (is_chinese / check_legal_name GBK byte-range bugs)

3. §14 (valid_override needs the 3-arg signature)

adm/obj/master.lpc's valid_override(file, name) upgraded to valid_override(file, name, main_file), checking both file and main_file against SIMUL_EFUN_OB/MASTER_OB.

4. §15w (log_error() conflates warnings with real errors)

adm/obj/master.lpc's log_error() unconditionally showed the scary "编译时段错误:..." message to whatever player happened to be connected for EVERY compile diagnostic reaching the apply, including harmless soft warnings (e.g. this driver's own nosave-function warning, #pragma warnings, unused-variable warnings — all very common in this lib). Gated the player-facing write on strsrch(message, "warning:") == -1, matching the wuhanzhan (#58) precedent. Everything still logged to the log file regardless.

Confirmed NOT needed: error_handler() already unconditionally does efun::write_file(LOG_DIR + "debug.log", standard_trace(...)) at the end of its own body (no "falls through automatically" assumption relying on a non-existent return-value path) — the §15w "add a permanent RUNTIME_ERRORS insurance write" follow-up wasn't needed here.

5. NEW: #8e (tail) — fatal here since it's inside the composed

simul_efun compilation unit

adm/simul_efun/file.lpc's tail(string file) called efun::tail(file) — never a real FluffOS efun. Reimplemented in plain LPC (read_file + explode on \n + slice last 20 lines + write), same pattern as every other lib. Also fixed an identical copy in u/lonely/sec/file.lpc (an unused wizard-homedir backup copy, not on the live compile path, fixed for consistency). Confirmed dead/rarely-used admin tooling — no live callers found anywhere in the tree.

6. §15s (tell_room()'s bare message() 4th-arg bug)

adm/simul_efun/message.lpc's varargs void tell_room(mixed ob, string str, object *exclude) { if(ob) message("tell_room", str, ob, exclude); } — when called in the common 2-arg form, exclude defaults to bare int 0, which this driver's message() (4th arg typed void | object | object *) can reject. Fixed to exclude || ({}).

7. §11-style copy-paste bug: clone/weapon/panguanbi.lpc

inherit PEN; / init_pen(25)PEN was never a defined macro anywhere (PIN exists, a *different* weapon type meaning "针"/needle) and init_pen() was never a real function. The item's own content (id "panguanbi", long description "这是一柄普通的精钢剑" — "an ordinary fine steel sword") plus a working sibling copy at /d/obj/weapon/sword/panguanbi.lpc (correctly inherit SWORD; init_sword(20);) confirm this was a copy-paste-and-half-rename artifact. Fixed to inherit SWORD; ... init_sword(25); (kept the original numeric argument).

8. §8g-style missing #defines: SSERVER, F_NOCLONE

Two bare macros used by inherit SSERVER; (5 skill files) and inherit F_NOCLONE; (4 boss-NPC files under kungfu/class/ultra/ — 南海神尼/黄裳/ 独孤/葵花太监, all major named Jin Yong characters) were never #define'd anywhere, a hard parse error (inherit <undefined identifier> parses as "expecting L_STRING", not silently OK). Both had an obvious, already- existing target to alias to rather than fabricate:

Follow-on: the 4 F_NOCLONE boss files each call a bare check_clone() explicitly from their OWN create() (which fully overrides feature/unique.lpc's own automatic-on-create uniqueness check without ever calling ::create()), so the automatic check never actually ran for them. Added check_clone() to feature/unique.lpc itself (same body as its own create()), which — since it's inherited via the new F_NOCLONEF_UNIQUE alias — became available to all 4 files at once.

9. §15b (never-defined-but-called globals): add_light, keep_heart_beat

10. NEW: whole "competitor" (international-challenger event) subsystem

silently dropped from feature/attack.lpc

feature/attack.lpc had a .bak sibling (feature/attack.bak, 555 lines vs the live file's 342) — diffing them showed the ENTIRE competitor variable + query_competitor()/set_competitor()/competition_with()/ win()/lost() subsystem existed in the backup but had been completely removed from the live file, while 9+ other files (kungfu/class/generate/ challenger|indian|european|japanese|japanese1.lpc + u/lonely/ copies, adm/daemons/story/challenge.lpc, inherit/char/challenger.lpc, u/lonely/obj/user/quit.lpc) still call it — a real, wide-reaching "only ever called, never defined" gap (§15b), not a typo. Restored verbatim from attack.bak (not a guess) into feature/attack.lpc: the competitor variable + query_competitor()/set_competitor() near the top (no forward-call issue), and competition_with()/win()/lost() moved to just AFTER fight_ob()'s own definition later in the file — per §8b, these three call fight_ob()/run_override() and calling a same-file function before its definition fails to resolve on this driver; the backup's original ordering (which put all 5 functions in one place, before fight_ob()) would have hit exactly that. This was the single highest-impact fix in this pass by file count (resolved compile errors across the whole challenger-NPC family in one shot).

Follow-on fix needed once query_competitor resolved: inherit/char/ challenger.lpc's own competition_with() override had killertemp=ob; with no local declaration anywhere in the function — a genuine pre-existing typo (not something the restore introduced). Every sibling file using the identical killertemp=ob; idiom elsewhere in this lib (japanese.lpc, questguai.lpc, chinese.lpc) declares it as a plain local object; matched here (object killertemp; added to the function).

11. Pre-existing content typos fixed via the lpcc sweep

Confirmed NOT needed (and how confirmed)

Known residual issues (documented, not fixed — non-critical content)

Registration flow

Read adm/daemons/logind.lpc's actual callback chain before testing (per AGENTS.md's standing warning about hidden pre-id prompts): logon() prints a "Select 国标码 GB or 繁体 BIG5 (gb/big5):" line, but this is a soft prompt, not a gate — input_to(get_id, ...) fires next regardless, and get_id() only special-cases the literal input "big5" (sets a flag and re-shows the whole banner+prompt); ANY other input (including a normal English id) falls straight through to the real id-validation logic. So the very first --send can just be the real login id directly, no separate blank/gb answer needed.

Full chain confirmed by reading the source: get_id → (existing user) get_passwd or (new user) confirm_id (y/n) → get_name (Chinese name, no separate y/n confirmation when a real name is typed — only random-name generation goes through get_resp) → new_passwordconfirm_password → gift-attribute selection (select_gift 0-4/set_gift/get_gift y/n) → get_emailget_gender (m/f) → make_body()/init_new_player()/enter_world().

Verified twice, in two separate continuous mudclient.py sessions (before AND after restarting the driver to pick up the feature/ attack.lpc competitor-subsystem restore, to make sure that change didn't regress the core player-body class every character depends on):

1. qinfengy秦风test12345test123450y[email protected]mlookquit 2. linfengy林风test12345test123450y[email protected]flookquit

Both runs: Chinese name accepted with no rejection, no separate confirmation prompt (matches the source), gift/email/gender flow completed normally, and the character landed in an actual room ("世外桃源" / Peach Blossom Spring — the real starting room, not a void/error state), with a correct room description, exits, NPCs (水笙/狄云) listed, and the news system firing normally. A third quick test confirmed the *existing-user* path (re-entering linfeng) correctly routes to a password prompt instead of character creation, and 3 wrong-password attempts correctly disconnect with the expected message. debug.log was clean of any error/access-denied/crash lines across all three sessions (only the usual first-boot *Warning: unable to open stat file domain_stats/author_stats lines, and ordinary compile warnings — no 执行时段错误, no Segmentation, no Fail to load, no access denied).

lpcc sweep

Ran twice (once before the deeper §15b/copy-paste-content fixes, once after, to measure improvement): 11995 total files both times.

Memory stayed healthy throughout both sweeps on this 23GB host (never dropped below ~16GB available, well clear of the OOM risk threshold in AGENTS.md §6b) despite a concurrent agent running another lib's sweep at the same time.

Process notes

Re-verification pass: driver rebuild + formatter + WASM (2026-07)

WASM-enablement pass (2026-07): loopback gates + admin seeding

Standard pass per AGENTS.md §1.3b/§1.3e/§1.5:

- logon(): ban_cnt > 12 per-IP pending-connection cap — loopback exempt. - get_id(): the netclub/public-ip per-IP multi-login caps (ip_cnt > 12 / > 4) — loopback exempt. - get_passwd(): 30-second "你刚退出游戏" relogin load throttle — loopback exempt (the 600s kickout penalty is punishment/game design, kept). Also removed a leftover debug printf("%O\n", ob) in get_name() that printed /clone/user/login#N at every new player.

WASM long-sit boot-watch pass (2026-07)

200s scripts/wasm_boot_watch.sh sit: completely clean, zero grep hits beyond the known-benign early Unable to open log file: "log/debug.log" line — no new findings, no fix needed. (This lib's own adm/simul_efun/object.lpc file_owner() uses a different, already- correct 2-arg sscanf(file, "/u/%s/%s", name, rest) shape that doesn't have the 3-arg misattribution bug found live on sibling zhonghua2 and ported to 7 other libs in this batch — checked and confirmed fine, nothing to port here.)

WASM 修复摘要(迁移自 meta.json 的 group_note)

状态已从过时的 limited 修正——这份档案自己的 README 和 group_note 里从未记录过任何缺陷说明,本轮重新测试也没有发现:管理员登录(fluffos/Mud@2026)干净正常,权限已通过这份档案自己的最高阶层字符串确认('★ 您目前权限:〖 巫师协会懂事 〗(boss)'),quit 正常。

§7.86 跨库扫描修复(留言板 post 崩溃)

深度功能测试 / Deep functional test (AGENTS.md §10.7)

Read doc/help/newbie in full first (5-part in-game newbie guide, credited "By 黑客(lonely)"): covers stat-roll ranges, personality/character choice (光明磊落/狡黠多变/心狠手辣/阴险奸诈, each with a mechanical effect), sect recommendations by weapon type, and a Q&A section explicitly naming gc e n + learn zhu literate as the free literacy-training route (gc = 扬州中央广场).

NEW bug found and fixed: every single connection crashed inside logon(), before the id prompt, on a genuinely fresh checkout

First boot attempt (driver launched clean from libs/yxcs, port up in ~1s) produced, for every connection attempt: new_conn_handler: logon() on object clone/user/login#N has failed, the user is disconnected in the driver's own debug.log — a total, silent connection block with zero player-visible banner text (matches the exact symptom class in AGENTS.md §7.9/§7.11: the driver's own crash-summary line carries no LPC stack trace).

Root cause, found via the second, LPC-level debug.log (work/log/debug.log, written by error_handler()'s own write_file(LOG_DIR "debug.log", ...) — see the log_error() discussion above; check BOTH log locations per AGENTS.md's standing instruction, this bug was invisible in the first one):

执行时段错误:*Wrong permissions for opening file /log/file/cat_file for append.
"No such file or directory"
呼叫来自:/clone/user/login.lpc 的 logon() 第 7 行
呼叫来自:/adm/daemons/logind.lpc 的 logon() 第 115 行
呼叫来自:/adm/obj/simul_efun.lpc 的 cat() 第 291 行
呼叫来自:/adm/obj/simul_efun.lpc 的 log_file() 第 11 行

adm/daemons/logind.lpc's logon() — the very first thing every new connection's login.lpc calls — does cat(WELCOME) to print the login banner. adm/simul_efun/file.lpc's cat() unconditionally calls log_file("file/cat_file", ...) as its own audit-log side effect *before* actually printing anything, and log_file() itself was a bare write_file(LOG_DIR + file, text) with no directory guard at all — a fresh checkout never ships work/log/file/, so the write throws, the throw is never caught anywhere in the logon() call chain, and the entire apply aborts before the banner (or the id prompt after it) ever gets sent to the connecting socket. This is a new confirmed instance of AGENTS.md §7.11 ("missing runtime directories and the silent write_file abort") — same shape as the xajhxo precedent (log_login() → uncaught log_file() write into a never-shipped directory, killing registration one step earlier than the visible symptom), except here it fires on every connection, not just at one step of character creation, because cat(WELCOME) sits at the very top of logon() itself.

Fix: patched log_file() itself (adm/simul_efun/file.lpc), not just the one call site, since it's a shared simul_efun used by dozens of call sites across the whole tree — added the missing assure_file() guard (this file already has its own assure_file() helper, same pattern as the xajhxo fix; needed a one-line forward declaration since assure_file() is defined later in the same file and this compiler doesn't resolve same-file forward references without one):

void assure_file(string file);
void log_file(string file, string text) {
  assure_file(LOG_DIR + file);
  write_file(LOG_DIR + file, text);
}

Verified: killed the driver, restarted clean, reconnected — work/log/file/cat_file now gets created on first write, banner prints, id prompt reached, zero logon() ... has failed lines in either debug.log for the rest of the session.

Also required (pure operational setup, not a code bug, but a fresh-checkout prerequisite worth recording): the archive shipped with neither libs/yxcs/log/ (the driver's own real debug.log, §5.2 — CWD-relative, not mudlib-relative) nor work/log/ (the mudlib-virtual LOG_DIR target used by the LPC code above) on disk at all. mkdir -p log work/log/nosave work/log/wiz before boot; without it, debug.log itself never gets written (silent, not even the crash-summary line makes it to disk) and log_error()'s own write additionally throws during preload compile-warning logging (self-healing since it's already inside preload()'s outer catch(), but pollutes preload output).

Defensive fix applied alongside (not confirmed as a live crash this pass, but matches the exact AGENTS.md §7.9 shape and the underlying data files are explicitly gitignored — libs/*/work/adm/daemons/iduser / .../users, per this project's own .gitignore comment "user-id counters... not meaningful shipped content"): logind.lpc's logon() did i_user = atoi(read_file(__DIR__"users", 1)) (and the same shape for iduser and maxonline) with no stringp() guard — on a fresh checkout where those counter files don't exist yet, read_file() returns int 0, and atoi() on a non-string argument is exactly the §7.9 "fresh-checkout crash bomb" pattern. Guarded all three with stringp(...) ? atoi(...) : 0. (maxonline already existed on this checkout so this one wasn't empirically confirmed as live here, but users/iduser were both genuinely absent and this guard is cheap insurance regardless of which exact site would have thrown first.)

Registration — real Chinese name, full flow

Continuous session via a small custom Python telnet-IAC-stripping client in tmux (this environment has no telnet or nc-with-negotiation binary available, so scripts/tmux_mud.sh — which shells out to telnet — doesn't work here; wrote a ~90-line drop-in replacement using the same IAC-strip logic as scripts/mudclient.py, kept in the scratchpad, not committed).

qinfengb (rejected once first as qftest2 — digits not allowed, "你的 英文名字只能用英文字母") → y秦风波 (real Chinese name, accepted immediately, no separate confirmation prompt — matches the logind.lpc source read in the earlier pass) → test12345 ×2 → gift-attribute 0 (random) → y[email protected]m → landed in 世外桃源 (/d/register/entry), correct room description, water笙/狄云 present, news system fired. look/score/i all correct at this state.

Then the personality-choice sub-flow (not covered by the earlier registration-only pass): east → 陆天抒 (光明磊落 branch) → out → 「阎罗殿」(reincarnation hall, 地藏王) → born 扬州人氏 → landed in /d/city/kedian ("有间客栈"). score afterward correctly shows 性格:光明磊落, correct stats, food/water bars full (§8.9 pattern: confirmed NOT present here, bars start full).

Combat — safe sparring via the training-dummy stat-mirror pattern

cmds/std/fight.lpc's actual logic read first (per AGENTS.md's standing warning against assuming a "safe" NPC without checking the real accept_fight gate): a can_speak-having NPC's accept_fight(me) must return 1 for fight to proceed. d/shaolin/npc/mu-ren.lpc (and an apparently-orphaned identical copy at d/city/npc/mu-ren.lpc — see "observations" below) matches the exact §10.7-documented dummy shape: set("no_die", 1), and accept_fight() deletes its own skills/stats and copies the attacker's own skill map + str/int/con/dex/qi/jing/neili onto itself before the fight starts (a genuine mirror match, not a scripted-weak opponent). Admin-cloned one into the player's current room (clone /d/shaolin/npc/mu-ren) since none of this lib's dummy instances happen to be placed along the 世外桃源→扬州 walking path used above. fight mu ren in a no_fight-clear outdoor room ran a full multi-round exchange (both sides landing and missing hits, HP-state narration escalating normally) and resolved with the player conceding ("这场比试算我输了,佩服,佩服!") — no crash, no stuck state, score/look immediately afterward both correct.

Skill acquisition — organic path AND admin shortcut, both verified

Organic: per the newbie doc's own "gc e n" hint and the board item_desc text in d/city/shuyuan.lpc ("学文化的格式是 learn zhu literate!"), gave money to 朱丹臣 (d/city/npc/zhu.lpc, aliased zhu) via give 10 silver to zhu (money supplied by admin clone /clone/money/silver 20 + give, since a level-14 newbie starts with no cash) → NPC's own accept_object() credited a mark/朱 balance → learn zhu literate → "你听了朱丹臣的指导,似乎有些心得。你的「读书写字」 进步了!" — real accept-logic path, no shortcut, works end to end.

Admin shortcut: cmds/wiz/copyskill.lpc (copyskill <target>) — copies a target's entire skill map + skill_prepare + combat stats onto the caller. Ran copyskill qinfengb from the admin account: "你口中念 念有词,只见一道红光笼罩了你和秦风波。" — no dedicated single-skill grant command exists in cmds/wiz/ (checked the whole directory; the closest is this bulk stat/skill copy), documented honestly rather than assumed.

quit, debug.log, reconnect after a real gap

No new-account quit-deletes-account grace period found in this lineage (unlike the nitan family): logind.lpc's registration chain only calls ob->save()/user->save() once, inside enter_world(), at the very end of the full flow (after gender selection) — by the time a player can even type quit, the account is already durably saved, so there is no partial-registration window to worry about. cmds/usr/quit.lpc read in full: no lockout for wizardp(), and for non-wizards a pker_starttime window is punitive-PK-cooldown design, not a fresh-account grace period.

quit from 「东大街」 (outdoors, mid-conversation with 朱丹臣): dropped one worthless Cloth item as expected content-consistency (§7-adjacent but plainly intentional — "因为这样东西并不值钱,所以人们并不会注意到 它的存在" is a normal drop-message, not an error), printed "欢迎下次再 来!", connection closed cleanly. Both debug.log (driver-level) and work/log/debug.log (LPC-level) checked immediately after — zero new error/crash/access-denied lines beyond the already-documented, pre-existing d/city2/npc/wizer.lpc:22 exert_function() type-mismatch content bug (unrelated to this session, confirmed via grep this bug's own earlier pre-existing note in this same file).

Used the post-quit interval productively (reverted incidental resave noise on data/{login,user}/f/fluffos.o, spot-checked earlier prior-pass fixes — the nosave nosave/protected protected macro rewrite, F_NOCLONEF_UNIQUE, SSERVERF_SSERVER — all still intact, grepped 6 sibling same-lineage libs for the same log_file()-missing-assure_file() shape found above (see "cross-lib observation" below), and drafted this NOTES.md section) — then registered a second throwaway character, persisttest/李持久 (female, so the §8.9-adjacent gender-conditional init_new_player() money/combat_exp bonus path got exercised too), specifically to redo the persistence check cleanly (the first attempt, on qinfengb, was invalidated by deleting its own .o save files as part of the resave-noise cleanup *before* actually reconnecting to confirm — logged that mistake so a future pass doesn't repeat it: do the persistence reconnect BEFORE any save-file cleanup, not after).

persisttest quited from 世外桃源, then reconnected after a genuine ~1-2 minute additional wall-clock gap (on top of the several minutes already spent on the interval work above) via a fresh telnet session (real disconnect+reconnect, not a resumed connection): banner printed clean, persisttest + password → recognized as an existing account (no "creates a new person" prompt this time, confirming the id correctly round-tripped through save/reload) → "☆ 您现在是第 二 次光临浴血重生" / "☆ 您上次连线的地址是 127.0.0.1" / "☆ 您上次退出本游戏的时间是:[ 2026年8月7日21点36分 ]" (matches the exact prior quit time) → score showed the exact same character: 中文姓名 李持久, 性别 女性, stats 膂力25/悟性22/根骨18/身法15 (the exact roll accepted at registration), 钱庄存款 二十两白银 and 实战经验 50 (both from the female-branch init_new_player() bonus, confirming user->save()/reload round-trips that data correctly too). Full state persistence confirmed. quited again cleanly, debug.log/work/log/ debug.log both still clean (same pre-existing wizer.lpc content bug only, no new errors).

发现但判定为既有设计、未改动的现象

Cross-lib observation (not fixed this pass — out of scope, yxcs-only pass)

The log_file()-missing-assure_file() shape fixed above in adm/simul_efun/file.lpc is present, unfixed, in every other checked sibling of this lib's ES/XKX lineage (§11): xkx2001, rzrmud, bmxkx2001, kxkj, yueyingqiyuan, wuhanzhan all ship the identical unguarded void log_file(string file, string text) { write_file(LOG_DIR + file, text); }. Whether any of them actually hits the crash live depends on whether their own logon()/registration chain routes through an equivalent cat()-audit-log or other log_file() call before the id prompt (not checked here — that's each lib's own §10.7 pass to make). Flagging for whichever future dive picks one of these up: check work/log/debug.log (not just the driver's own debug.log) for a Wrong permissions ... for append trace through log_file() before assuming registration is clean.

§9 formatter

Ran format-corpus.mjs on both edited files (adm/daemons/logind.lpc, adm/simul_efun/file.lpc) — both reformatted clean, 0 errors. Checked all 3 documented blind spots: grep -rnE ':\s:\s*[a-zA-Z_]+\(' — zero hits in either file; no case-label lines touched by either diff (no manual diff-review needed); grep -rl '\\ n' — zero hits in either file. Re-booted and re-tested the full flow above against the reformatted files (this NOTES entry's own registration/combat/skill/quit walkthrough was run AFTER formatting, not before).

WASM 未验证说明

Per this project's build state this pass: WASM build permanently blocked (emsdk hardcodes storage.googleapis.com, denied by the proxy — confirmed via curl -sS $HTTPS_PROXY/__agentproxy/status). The fixes above (both the new log_file()/assure_file() fix and the logind.lpc counter-file guard) are pure LPC-level runtime-error guards with no WASM-specific code path — expected to apply identically under WASM once a build becomes available, but genuinely unverified under WASM this pass; the earlier WASM-enablement pass's own findings (loopback ban-bypass, admin seeding) are untouched and still apply.

深度功能测试(2026-08-13,round two,新驱动重测)

针对驱动升级(quest_times/win_times %-operator 修复 + Warning/warning 大小写回退兼容)做的重测。核对上面记录的所有修复(新连线 logon() 崩溃、log_file()、counter-file 防护、宏重写等)逐项确认代码仍然 生效;win_times%-operator 也已用 to_int(query("win_times")) % 5d/city2/npc/refereew.lpc:146d/taoyuan/npc/{jinyong,gulong}.lpc)。

本轮新发现并修复的 PROGRAMMING bug(上一轮 §10.7 深挖未覆盖到)

1. feature/dbase.lpcset() 密码写保护是 tybxjh/wlhd 那一类 已确认的 bug 形状if (prop == "password") if (wizhood(this_object()->query("id")) == "(boss)") if (this_player()) if (geteuid(this_player()) != this_object()->query("id")) return;——注释明确写着"Add by Lonely to fix a steal-power bug",本意是防止别人冒充覆盖既有 (boss) 级账号的密码,但 wizhood() 纯粹读 wizlist 文件,不检 查 dbase 里是否真的已经存在密码。本轮实测 fluffos 的注册/重 连都正常(这份档案里 this_player() 在密码 set() 那一刻的 euid 恰好已经和目标 id 一致,所以没有触发拦截——和 tybxjh 的具 体触发时机不同,但代码形状完全一致,属于潜在缺陷,不是已经在 本档案上实测复现的活跃崩溃)。按已确立的标准修法加上 mapp(dbase) && dbase[prop],只在密码已经存在时才拦截,不影响 首次创建。 2. §8.9 食物/饮水初始化判断的对象错了——上一轮深挖遗漏adm/daemons/logind.lpcenter_world()ob->query("age") == 14(应为 user)。已改成 user->query("age") == 14。 3. get_resp()(系统随机中文名接受分支)一处调试残留 printf("%O\n", ob)(AGENTS.md §7.34-class)——上一轮深挖用的是 手动输入中文名分支,没有触发这条泄漏:紧跟在中文名字确认之 后,把连线桩物件的原始引用直接回显给正在注册的新玩家。已删除; get_name()(手动输入分支)本来就没有这个残留,核对确认。

实测过程

管理员 fluffos/Mud@2026(此前深挖已注册并提交存档)用真实密码 重新连线两次(先重启驱动应用上面三处修复,再各做一次独立的登录+重 连验证),落地此前保存的地点,score 显示食物/饮水满格。两个 debug.log 位置(驱动级 log/debug.log 和 LPC 级 work/log/debug.log,本档案自己此前记录过必须都查)全程干净,无 运行时错误。驱动按精确 PID 结束;测试期间产生的存档时间戳增量已 git checkout -- 还原。启动前按此前记录的前置条件手动创建了 log/work/log/{nosave,wiz} 目录(均已确认被 .gitignore 正确 忽略,非代码问题)。

§7.100 修复(ROOM 基类的同一"多余 replace_program()"形状,全档案扫描第 6 批)

``§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/bgargoyle.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): 4 accessor(s) in this file returned a raw never-initialized mapping instance variable (defaults to int 0, not ([]), until first assigned), crashing any unguarded keys()/sizeof()/indexing caller for a fresh/untrained character. Fixed at the accessor level (mapp(x) ? x : ([])) per the documented remedy. Verified via lpcc --batch static compile check only (not a live boot) as part of a large mechanical sweep; not individually functionally re-tested live on this lib.

深度功能测试补充 (2026-08-24): shop buy and 拜师 sect apprenticeship — the two flows every prior §10.7 pass on this lib had skipped

Audited the full history above and confirmed neither a shop purchase nor a completed sect apprenticeship had ever been exercised on this lib, despite the newbie doc's own §3 sect-recommendation text implying a real system existed. Closed both gaps this pass, via two concurrent scripts/tmux_mud.sh sessions (admin + a fresh throwaway player qinmaiwu/秦买物, registered end-to-end same as the original pass) so the admin could goto <player> + clone/give money without needing quit-and-restore round-trips.

Shop purchase — found and fixed a real crash bug (vendor.lpc §-class, list/buy at ANY shop with a stale vendor_goods entry)

d/city/zahuopu.lpc (扬州 杂货铺, reachable in 3 moves from the newbie start room: kedian → west → beidajie1 → south → guangchang → east → dongdajie1 → south → zahuopu), NPC 杨永福 (d/city/npc/yang.lpc, inherit F_VENDOR_SALE). First list command thrown by a real player crashed outright: 这里发现了BUG,请用SOS告诉巫师。

work/log/debug.log:

执行时段错误:*call_other() couldn't find object '/u/xiha/fd'.
程式:/feature/vendor_sale.lpc 第 149 行
物件: /d/city/npc/yang#294
呼叫来自:/feature/vendor_sale.lpc 的 do_vendor_list() 第 149 行

yang.lpc's vendor_goods array lists "/u/xiha/fd" as its first entry — confirmed via the raw archive (raw/socket/yxcs/u/xiha/ only ever shipped workroom.c, never an fd.c) that this file never existed at all, not a casing mismatch. feature/vendor_sale.lpc's is_vendor_good(), do_vendor_list(), and (transitively, via is_vendor_good()) do_vendor_buy() all called methods directly on vendor_goods string entries with no existence guard, so this one dead reference broke list (and would have broken buy too, for anyone who managed to name it) for every player at this shop, every time — the exact class already cataloged in AGENTS.md's mohuanshiji precedent ("worth checking as a standing step on any future lib's feature/vendor.lpc-style shop test").

Fix followed this codebase's own existing idiom for the identical problem in the sibling feature/dealer.lpc (file_size(ob[i] + ".lpc") > 1, log the stale entry, skip it rather than crash) — added the same guard to all three loops/lookups in feature/vendor_sale.lpc (is_vendor_good(), and both loops inside do_vendor_list()), logging a vendor_error line instead of throwing. This is a missing-defensive-check programming bug (no existence check before an unconditional -> call), not a content restoration — the dead /u/xiha/fd entry itself was left in yang.lpc's vendor_goods array untouched, matching the give.lpc/ask.lpc QUEST-daemon precedent's remedy of guarding the call site rather than fabricating the missing target.

Verified live after a driver restart: list now prints all 16 real items (skipping the dead entry silently), and buy budai (麻布袋,一 两白银) correctly deducted exactly 1 tael from the player's 20-tael starting balance (二十两银子 → 十九两银子) and added 布袋(Budai) to inventory — real purchase, correct change/inventory math confirmed end-to-end.

Sect apprenticeship (拜师) — clean pass, no bug found

Full recruit/apprentice two-command handshake (cmds/skill/ recruit.lpc + cmds/skill/apprentice.lpc + feature/apprentice.lpc's recruit_apprentice()) read in full first. Picked 丐帮 (newbie doc's own §3 recommendation for 拳脚/unarmed fighters) — specifically 左全 (kungfu/class/gaibang/zuo-qu.lpc, one of the 七袋弟子, reachable from guangchang via enter shudong/d/gaibang/inhole) since his attempt_apprentice() only gates on gender == "男性" (the simplest of the gaibang roster's per-NPC stat/gender gates — siblings he-bj/ li-sh/ma-jw/yu-hx/liang each gate on a different stat threshold, jian/peng refuse everyone outright).

apprentice zuo (test char already male, 光明磊落 personality) → "你想要拜左全为师" → 左全 accepted immediately (no stat gate hit) → "你跪了下来向左全恭恭敬敬地磕了四个响头,叫道:「师父!」恭喜您成为 丐帮的第二十代弟子。" score before vs. after: 头衔 changed 【布衣平 民】→【叫 化 子】, 称谓 changed 普通百姓→丐帮第二十代弟子, 你的师傅 changed 目前还没有→左全. Full apprenticeship mechanism (recruit/ apprentice pending-state handshake, family mapping population, assign_apprentice() title generation) works correctly end-to-end — no bug found, clean pass.

Formatter note

Could not run the §9 LPC formatter (~/src/fluffos/tools/lpc-syntax/ bin/format-corpus.mjs) this pass — node is not installed/on PATH in this session's shell environment (checked which node, nvm, common install paths; none found). The feature/vendor_sale.lpc edit itself hand-matches the existing codebase's own indentation/brace style (copied directly from the sibling feature/dealer.lpc idiom, no ::, case, or CJK-string edits involved, so the formatter's three known blind spots don't apply here regardless). Flagging for whichever future pass has a working node available: re-run the formatter on this one file and re-verify.

Testing setup note

Two concurrent scripts/tmux_mud.sh sessions (admin fluffos, throwaway player qinmaiwu/秦买物) driven with only ASCII/pinyin commands past the initial Chinese-name registration step (done via a one-shot scripts/mudclient.py call first, per this project's standing tmux-mojibake caution) — no mojibake observed on the ASCII-only in-play commands, consistent with the false-alarm pattern being specific to multi-byte *input* during registration, not general gameplay. Test character's save file was never observed on disk under data/user/q//data/login/q/ despite score/reconnect showing correctly persisted state across the two tmux sessions — most likely a net-dead in-memory reattach rather than an actual disk restore (the first tmux session was never sent quit, just closed); nothing to clean up on disk either way since no qinmaiwu.o file exists. Driver killed by exact PID after testing; both tmux sessions stopped.

§7.19 enable_player() reentrancy fix (2026-09-01)

Corpus-wide mechanical fix (AGENTS.md §7.19, Batch F of 6). feature/command.lpc's enable_player() had no reentrancy guard: NPC init() (e.g. d/shushan/npc/zhangmen.lpc) -> setup()/reset_me() -> enable_player() -> enable_commands(), which can synchronously re-invoke the same object's init() (driver docs BUGS note; live-verified mhxy/wuhanzhan precedent), and revive()/wakeup()/wakeup2() re-invoke enable_player() while already living(), ruling out a bare living() guard. Fixed with the standard in_enable_player_now true reentrancy flag (mhxy's reference fix). Verified via single-file lpcc --batch PASS.