Tianxia Beta (World Under Heaven)

✅ 可玩

天下 Beta

tianxia

🔑 fluffos / Mud@2026Admin 更新 85d6a35 2026-09-02 源码 下载 ZIP

▶ 开始游玩 · Play Now

自称 《天下》Beta,作者署名"发现号(Find@tx)",与本项目中的"泥潭"(nitan)系列师出同门(同一作者),但代码库本身是完全独立的一套,游戏自述仍处于"正处于发展时期,会有经常的更改和增添"的早期建设阶段的中文武侠 MUD。新角色会先自由分配六项属性天赋点数(可手动加减或交由系统随机分配),再被"师傅"安排到清幽的"松竹小院"开始故事,由 NPC 书童递上入住手册,带有一点角色扮演的仪式感;中文姓名不可以"唐"或"踏雪"开头——游戏内本身设有"唐门"势力,这两个开头的姓名会被系统保留拒绝;巫师账号另有更严格的独立密码强度规则(至少 10 位,需同时包含大小写字母和特殊符号)。

English

Self-titled "Tianxia (World Under Heaven) Beta," credited to an author known as "Discovery No. (Find@tx)" — from the same author as this archive's "Nitan" series, though built on a completely independent codebase, and by its own in-game text still an early, actively-changing build ("regularly modified and added to"). New characters freely distribute talent points across six attributes (hand-adjusted within limits, or rolled at random), and are then sent by their "master" to Bamboo Courtyard (松竹小院), a quiet retreat where an NPC page-boy formally hands over a welcome handbook — a small deliberate piece of roleplay ceremony rather than a bare stat dump. Chinese names may not begin with "Tang" (唐) or "Treading Snow" (踏雪), reserved because the game has its own in-world Tang Clan (唐门) faction; wizard accounts are additionally held to a stricter password policy (10+ characters, mixed case plus a symbol) than ordinary players.

README

在线试玩

https://mudlibs.fluffos.info/tianxia/

管理员账号 / Admin account

警告:对外公开架设前请务必修改此密码。

本地运行

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

游戏端口:40044。驱动自带一个 30 秒的启动保护期,远程连接需等待 启动完成后再试;本机(127.0.0.1)连线不受此限制。

NOTES · 移植与修复记录

tianxia — 《天下》Beta (archive #50, 天下.tar.gz)

Port: 40044. Status: done (boots clean, full registration flow verified end-to-end including a real Chinese name, into an actual game room).

What this is

"《天下》Beta", credited throughout the source to author "Find" (also seen as "发现号(Find@tx)" in a doc comment shared with the nitan170911/ nitan6 family's docs — same author lineage, different codebase). adm/ obj/{master,simul_efun} layout. The archive bundles a full MudOS driver source tree at raw/mud/MudOS/, entirely ignored per this project's standing policy — mudlib root is nested at raw/mud/tx/. ~7,741 raw files, 5,786 .lpc/.h files after the .c.lpc rename.

2026-08-26 addendum: a separate onboarding pass considered git clone https://github.com/MudRen/txmud ("天下MUD") as a candidate new title and got as far as cloning it before a sanity check confirmed it is this exact same codebase (identical master.c ACL mappings, identical 47-entry d/ domain list, near-identical wiz/ wizard list — see AGENTS.md §11's duplicate-archive log for the full diff summary). Not re-onboarded; no libs/txmud/ created.

Fixes applied

1. AGENTS.md §15h, with a twist — TWO separate Chinese-detection functions in adm/simul_efun/chinese.lpc, not just the usual one: - is_chinese(str): standard GBK lead-byte check (strlen>=2 && str[0]>160 && str[0]<255) → standard CJK codepoint check (strlen>=1 && str[0]>=0x4e00 && str[0]<=0x9fff). - valid_chinese(str): a whole-string, byte-offset "commonly used character" GBK section-range check (section = tmp[i]-160, valid range 16-87, gated to every other index via i%2==0 to land on each character's GBK lead byte). This "commonly used character subset" refinement cannot be faithfully preserved under Unicode without a GBK→Unicode lookup table for exactly that subset — per this project's established practice for exactly this situation (see AGENTS.md §15h), simplified to a straight per-character CJK Unified Ideographs range check on every character, dropping the i%2==0 byte-offset gate (every index is already one full character under UTF-8) and the "commonly used" refinement itself. A new lib-specific comment in the file documents this simplification explicitly, matching the project's existing style for this pattern. 2. adm/daemons/logind.lpc's check_legal_name() (own inline copy of the same whole-string "commonly used character" logic, doesn't call is_chinese/valid_chinese at all): - Length bound strlen<2 || strlen>12 (byte count, "1 到 6 个中文字" in the message) → strlen<1 || strlen>6 (character count, matching what the message already promised). - The per-character loop (name[i]<128 + section=name[i]-160 range check + i%2==0 gate) → single CJK range check per character, same simplification as valid_chinese above. - Two byte-width surname slices found and fixed, both AFTER the check_legal_name loop, both checking for lore-reserved surnames that conflict with in-game NPC factions (this lib has a whole "唐门"/Tang Clan zone under wiz/qifan/tangmen/): - name[0..1] == "唐" (a 2-BYTE GBK slice meaning "first character") → name[0..0] (1-character slice). - name[0..3] == "踏雪" (a 4-BYTE GBK slice meaning "first TWO characters") → name[0..1] (2-character slice). - Confirmed no other byte-width slices in this function beyond these two — the only other slice in the whole file, id[1..]/ id[0..<2] in check_wiz_legal_password, operates on the ASCII-only English login id, unaffected by the byte/char distinction (verified by reading the surrounding function). 3. Same GBK "commonly used character" section-range shape recurs in 6 MORE files, all unrelated to registration (guild-affiliation naming and weapon-smithing naming — gameplay features, not reachable during boot or registration): std/banghui/banghui_auth.lpc, std/banghui/smith.lpc, std/char/smith.lpc, cmds/bangzhu/cdoor.lpc, cmds/bangzhu/mroom.lpc, cmds/bangzhu/cshort.lpc. Not fixed — per AGENTS.md §15f's precedent, these are long-tail gameplay-content occurrences outside the boot/registration critical path; noted here for whoever picks up deeper §15h-pattern cleanup work on this lib. 4. adm/etc/preload: removed /adm/daemons/network/dns_master per AGENTS.md §15p (proactive, before first boot). Reviewed every other preload entry (ftpd, smtp_d, ip_d, etc.) for synchronous remote-connection attempts at create() time — none found (ftpd's socket bind is local-listen-only and deferred via call_out, smtp_d's resolve() call is already commented out) — no further preload trimming needed. 5. NEW: a driver-version self-check (adm/obj/check_config.lpc) fatally errors during simul_efun's own construction on this driver. simul_efun.lpc does private inherit __DIR__ "check_config"; — its create() walks a checklist of MudOS-era #ifdef/#ifndef driver preprocessor flags and calls error() if any mismatch, intended to stop the mudlib from running on an incompatible driver build. Two checks fail on this FluffOS build: #ifdef __PRIVS__ (this driver DOES define __PRIVS__, alongside PACKAGE_UIDS — the old mutual-exclusivity assumption doesn't hold here) and #ifndef __AUTO_TRUST_BACKBONE__ (this driver doesn't define it, but master.lpc's own valid_override() already handles backbone-object trust explicitly). Since this check runs unconditionally inside simul_efun's construction with no catch() around it anywhere, the unhandled error() kills the ENTIRE boot before a single object exists. Fixed by disabling just those two specific checks (wrapped in a #ifdef DISABLED_LEGACY_..._CHECK that's never defined), leaving every other check intact. Worth checking proactively on any future lib from this same author/lineage — a check_config.lpc-shaped self-test inherited directly into simul_efun.lpc or master.lpc is a new pattern class (distinct from the missing-apply gaps in §7): a mudlib-authored driver-compatibility gate that can be simply WRONG about what "compatible" means for a modern FluffOS build using a different package combination than the original MudOS target. 6. NEW: three simul_efuns called pervasively but never defined anywhere in the archive (AGENTS.md §15b-style gap, but new functions not seen in that catalog before) — all added to a new file, adm/simul_efun/ansi_util.lpc, included right before chinese.lpc in simul_efun.lpc (ordering matters — §8b): - clr_ansi(str) — strip ANSI color codes. Called by valid_chinese() itself (blocking simul_efun's own compile) plus ~10 other files (weapon-naming, guild-naming, nick command). Restored using this lib's own include/ansi.h color-code table (same shape as the project's existing remove_ansi() precedent from the nitan family, adapted to this lib's own macro names — includes HIK which the nitan table didn't have, and omits macros this lib's ansi.h doesn't define). - chinese_number(int) — converts an integer to Chinese numeral text (e.g. 123一百二十三), called from ~90 files across the whole codebase (currency amounts, dates, uptime displays) — including from adm/simul_efun/time.lpc itself, another file composed directly into simul_efun.lpc, so this gap alone was fatal to boot. This project's own doc/efuns/chinese_number doc file (found in the nitan170911 archive, credited "by 发现号 (Find@tx)" — the SAME author as this lib) describes the exact contract; nitan170911/nitan6's adm/daemons/chinesed.lpc (also Find-lineage) has a byte-identical integer-conversion algorithm already verified working in this project — ported that algorithm directly (int-only path; the string/arbitrary-precision path in the source lib's version isn't needed since every call site here passes an int). 7. NEW: changed_match_path(mapping, string), called from feature/dbase.lpc (this lib's generic per-object property storage, inherited nearly everywhere) — never defined anywhere in the archive, but its signature is IDENTICAL to FluffOS's real match_path() efun (mixed match_path(mapping m, string str), confirmed present in this driver's core.spec, and this lib's own doc/driver/done-mudos changelog explicitly lists match_path() as an efun the original MudOS driver added). "changed_" strongly suggests a thin local wrapper around the real efun (possibly for a since-lost local tweak), not a from-scratch reimplementation — restored as a straight passthrough to match_path(). 8. db_affected(db) (AGENTS.md §15b's documented gap, recurred verbatim here in adm/daemons/databased.lpc, 14 call sites) — stubbed returning 1, same documented compromise as the catalog entry (every call site already checks db_exec()'s own return for the real success/failure signal). 9. NEW: query_bandwide(), called UNGUARDED from logind.lpc's begain_enter() ("/cmds/imm/bps"->main(), on the path of literally every new connection, no catch()) — a genuine MudOS-driver efun for live port bandwidth stats (cmds/imm/bps.lpc's own comment: "returns float*[0] incoming data, [1] outgoing data") with no FluffOS equivalent at all (checked core/sockets/contrib specs). This was the bug that silently killed every connection attempt before the fix — matches AGENTS.md §15d's silent-crash-in-logon()-chain shape exactly (new_conn_handler: logon() ... has failed, the user is disconnected, zero player-visible output). Stubbed returning ({ 0.0, 0.0 }) — purely cosmetic admin bandwidth readout on the login banner, not gameplay. 10. NEW: query_shadowed(), called bare from feature/self.lpc and std/equip.lpc to ask "is THIS weapon object currently shadowed by a special-effect object (e.g. a glowing/elemental weapon effect), and if so which one" — never defined anywhere, but this driver's own shadow(ob, flag) efun already provides exactly this reverse lookup (flag=0: "either 0 will be returned, or the object that is already shadowing 'ob'"). Restored as shadow(previous_object(), 0) — NOT shadow(this_object(), 0), since this resolves as a bare simul_efun call and this_object() inside it would be the SIMUL_EFUN object itself (AGENTS.md §15's this_object() footgun) — previous_object() correctly names the real caller during a simul_efun call. This was blocking /obj/user/user (the player body class) from compiling AT ALL, which silently broke character creation completion (make_body() returning 0) right after the Chinese name/password were accepted — the single most impactful fix in this lib's pass. 11. NEW: a real (not simul_efun-family) pre-existing efun-misuse bug: daemon/effect/weapon_effect/bleeding.lpc called the REAL efun query_shadowing() with zero arguments, but its signature requires one (object query_shadowing(object)). Fixed to query_shadowing(this_object()) (correct here — this IS a real, non-bare call inside the shadow object's own method, no simul_efun-context issue). 12. NEW variant of the "lossy iconv -c conversion eats a real newline" hazard — found on 3 files independently flagged as lossy by convert_lib.sh (d/shaolin/k_s24.c, d/tianshui/yidao1.c, d/tianshui/zahuopu.c): all three have a set("long", @LONG ... LONG) text block whose CLOSING LONG tag ended up merged onto the end of the preceding Chinese text line (...。LONG instead of ...。\nLONG), breaking the LPC text-block syntax (error: End of file in text block) since the closing tag must start its own line. Diffed against the raw pre-conversion bytes and confirmed: the ORIGINAL archive had a genuine \xa3\n (a GBK lead byte 0xA3 immediately followed by a literal newline 0x0A — not a valid GBK 2-byte pair, since 0x0A isn't a legal trail byte) right before the closing tag in all three files. iconv -c dropping this invalid sequence apparently consumed BOTH the bad lead byte AND the real newline that followed it (glibc's iconv skip-forward heuristic on an invalid multi-byte start advances by the presumed character width, not always just the one bad byte), merging the closing tag onto the text line. Worth watching for on future libs: any error: End of file in text block on a file that was ALSO flagged LOSSY conversion in convert_lib.sh's log is a strong signal to check for exactly this — a merged closing-tag line — rather than assume a from-scratch content bug. Fixed by re-inserting the dropped newline before each closing LONG tag (verified against the raw file's byte layout, not guessed). 13. AGENTS.md §8h recurrence: adm/daemons/convertd.lpc's Greek-table stray-trailing-backslash typo ("α\", should be "α",) — 45 occurrences. No CRLF present, plain sed pattern applied directly; re-grep confirmed 0 remaining. (One unrelated, separate, non-fatal warning: Unknown escape sequence '\额' remains elsewhere in the same file — a different typo shape, warning-only, left as-is.) 14. NEW: copy-paste duplicate inherit ROOM; in 12 near-identical room template files (wiz/qifan/tangbook/yaopu{2..9}.lpc, wiz/qifan/tangmen/yaopu{6,7,8,9}.lpc) — each file had inherit ROOM; written TWICE (lines 3 and 9 in the affected files), causing a cascade of "Redeclaration of global variable" warnings followed by fatal "Illegal to redefine 'nomask' function" errors for every nomask method /std/room defines. Fixed by removing the duplicate line in each file (kept the first occurrence). Found by grepping ^inherit ROOM;$ count per file across the whole tangbook/tangmen template set — every file with count=2 was in the lpcc failure list, every count=1 file passed, confirming the diagnosis before fixing. 15. Encoding straggler: data/emoted.o (the emote-command-list save file) is NOT valid GB18030 even in the RAW pre-conversion archive (iconv fails at byte offset 994) — file classifies it as data (binary), so convert_lib.sh's per-file guess for .o files (see AGENTS.md's "stragglers" check) skipped it entirely, leaving it as raw un-decodable bytes in work/. Manually converted with iconv -f GB18030 -t UTF-8 -c (drops the handful of invalid bytes) — this produced valid UTF-8, but restore_object() then threw a SEPARATE, genuine "Illegal mapping format" error (AGENTS.md §15m shape: the save data itself is structurally corrupted, not just an encoding artifact) — confirmed non-fatal to boot (caught by master.lpc's own preload() catch wrapper) but leaves emoted.lpc's emote mapping never initialized (partial create()). Per §15m's precedent, moved the corrupted file out of the way (data/emoted.o.corrupted-orig) so restore() finds nothing and returns cleanly instead of crashing — emoted's emote-command feature now starts with an empty list rather than a half-initialized daemon. A full straggler sweep of the rest of work/ (.lpc/.h via the documented file -b check, and all .o files via a UTF-8 decodability check) found no other affected files. 16. Minor content fixes found via the lpcc sweep (all pre-existing, none touch the registration path): - d/jinghai/shengji_quest.lpc: bare jinghai *shengji_quest=({...}) used an undefined identifier as a TYPE (should be mapping, given the array literally contains (["key":val,...]) entries) plus 3 missing + string-concatenation operators (HIG"凤凰弓"NOR should be HIG+"凤凰弓"+NOR) plus a missing #include <ansi.h> for the color macros. Fixed all three (this file is otherwise dead/orphaned — nothing else in the archive references it by path). - adm/daemons/banghui_d.lpc: inherit F_SAVE; was placed AFTER #include <banghui.h>, whose macro expansion declares real global variables — illegal ordering on this driver (inherit must precede any global variable declaration in the same compiled file). Reordered: inherit first, then the headers. Also added two missing local variable declarations (int n; mixed *groups;) in buildup_new_guild(). Not fully fixed — this file (guild creation, never preloaded, gameplay-only) has at least 2 more pre-existing bugs beyond this (a string value returned from an int-typed function, an undefined tmp variable) not chased further given zero registration-path relevance. - std/banghui/banghui_auth.lpc: removed one bare, nameless, initializer-less protected mapping declaration (a genuinely incomplete pre-existing edit — this whole file is dead/orphaned, nothing in the archive references it by path, so original intent is unrecoverable). Compiles further now but still has at least one more pre-existing bug (calls save() with no inherit providing it) — not fixed, dead code.

What was NOT fixed (documented, not chased)

Re-verification pass (2026-07-23) — found and fixed a §15w bug, added score to the test

The original pass tested look but not score — re-testing the fuller post-login flow surfaced adm/obj/master.lpc's log_error() broadcasting every compile *warning* (not just real errors) to the connected player as a scary 编译时段错误:...warning:... line (AGENTS.md §15w). Seen firing 10 times in a row for /std/char.lpc:4: warning: Unknown #pragma, ignored during a routine registration, plus once more for /std/room.lpc — right in the middle of an otherwise-correct session. Fixed by gating the broadcast on the message not containing "warning:" (still always logged to file). Re-verified with a fresh registration (id qftxac, real Chinese name 秦风霜, female) through look/score/quit: zero 编译时段错误 lines this time, all three commands produced correct real output (room description, full attribute/status sheet, clean save-and-quit message), and debug.log stayed clean of denied/cannot/undefined function/bad argument/error in error handler.

Preload

adm/etc/preload reviewed entry-by-entry; only network/dns_master excluded (§15p). No other daemon does a synchronous remote-connection attempt at create() time.

Interactive test result — full registration flow

logind.lpc's actual chain (read directly, not assumed): logon() → (no GB/BIG5 prompt shown — GB_AND_BIG5 is undefined in this build, so encoding("gb", ob) is called directly with no player input) → begain_enter() (banner + player counts) → English id prompt (get_id, lowercase-letters-only, 3-10 chars) → y/n confirmation (confirm_id) → Chinese name prompt (get_namecheck_legal_name()) → password (new_passwordconfirm_password, min 5 chars) → gender (get_gender, m/f) → attribute allocation (init_new_player — type str +5-style deltas, or 0 for random assignment, then y to confirm) → set_new_player()enter_world().

Also checked: driver enforces a 30-second startup grace period (if (uptime() < 30) { ...destruct(ob); } in logon()) — a fresh connection attempted before that window produces a single "还在启动中" line and disconnects; not a bug, just needs the test to wait.

Surname-conflict checks investigated: check_legal_name() rejects any name starting with "唐" or "踏雪" specifically because this game has a whole "唐门" (Tang Clan) zone/faction (wiz/qifan/tangmen/, tangbook/, tangmiti/) — this is a simple reserved-name collision guard, not a deeper surname-selection or clan-affiliation mechanic tied to character creation; no further clan-selection step exists in the registration flow itself.

Verified in one continuous connection (scripts/mudclient.py): qinfengx (English id) → y (confirm) → 秦风 (real Chinese name, ACCEPTED) → test1234 (password) → test1234 (confirm) → f (gender) → 0 (random attribute assignment) → y (confirm attributes) → successfully entered the actual game world at "松竹小院" (Bamboo Courtyard), greeted by name by the NPC "小书童" ("秦风你好!师傅说你这两天 就会来的..."), look command worked, quit saved cleanly ("你决定离开 《天下》Beta,档案保存中......"). This is a complete, real, working registration + login + basic gameplay loop, not just "reaches a prompt."

lpcc sweep

5,786 files, final: 5,735 pass / 51 fail (99.1%) (started at 5,717/5,786 = 98.8% before the fixes in items 12-14 above raised it). Memory stayed healthy throughout both sweep runs (18-20GB free on this 23GB host, well below any concerning threshold). Remaining 51 failures are the documented categories above (dead network daemons, individual wizards' personal-workspace content bugs, the 2-file missing FORK/FACE weapon/armor-type gap) — none on the boot or registration path.

Rebuilt-driver / formatter / WASM re-verification pass (2026-07-23)

1. LPC formatter applied across all 5,786 .lpc files in work/: {"total":5786,"written":5732,"wouldChange":0,"unchanged":36, "errors":18}. Found and fixed a genuine formatter bug, surfaced only by this lib's obj/user/user.lpc: the formatter mis-tokenizes copy(::query_skills()) — a ::-prefixed (explicit-efun-override) call immediately following an opening ( with no space — as the start of a (: ... :) closure literal. It rewrote return copy(::query_skills()); into the syntactically broken return copy (: : query_skills()\n);, which failed to compile (syntax error, unexpected L_FUNCTION_OPEN) the first time any connected player's client called query_skills() (e.g. the skills command). Fixed by hand-restoring the original copy(::query_skills()); call (confirmed via git diff against the pre-format version — no other change needed to this function). Re-verified via lpcc_check.sh (whole-lib batch compile): pass count unchanged at 5,735/51 fail, obj/user/user.lpc not in the failure list. The exact same ::fn()-immediately-after-( pattern was found and fixed in 3 other libs this same passd/changan/chengxf.lpc's if(::valid_leave(me,dir)) in this lib itself (a second, independent hit), plus shujian2008/ sjtx2's cmds/leitai/npc_leitai.lpc + d/tanggu/npc/npc_leitai.lpc (if(::move(dest,silently))) and syxjl's adm/object/bm.lpc (capitalize(::query("id"))) — see those libs' own NOTES.md. All confirmed via git diff to be the identical (: : corruption signature, all hand-fixed and re-verified via lpcc_check.sh with no pass-count regression. Worth flagging back to the formatter's own maintainers/AGENTS.md as a new bug class (not something to rediscover per-lib): X(::fn(...)) with zero whitespace between ( and :: is mis-lexed as a closure open. 2. Native re-test against the rebuilt build-debug/src/driver, after the fix above: booted clean (zero fatal errors). Full registration verified end-to-end via mudclient.py: id txfmtb → confirm → real Chinese name 秦风三十一 → password ×2 → gender f → attribute allocation (0/random) → confirm y → entered the game world at 松竹小院, greeted by 小书童 same as the original pass, look displayed the room, skills (exercises the just-fixed query_skills() — "你目前并没有学会任何技能", no crash) — score and quit also produced correct real output. debug.log: zero error in error handler/denied/undefined function/bad argument/syntax error lines. Also re-confirmed the driver's own 30-second startup grace period is still in effect (uptime() < 30 in logind.lpc) — not a bug, just needs the test to wait, same as the original pass. 3. WASM test: boots cleanly through Initializations complete (only the expected caught missing-sockets/db-package preload errors for ftpd/smtp_d/databased, each wrapped in master.lpc's own preload() CATCH()). Could not get past this lib's own 30-second startup grace period within scripts/wasm_client.js's harness model: the harness calls fluffos_connect() (which immediately invokes logon(), and thus the uptime() < 30 check) right after boot/preload completes, *before* any --idle-paced --send line is dispatched — so no amount of --idle/--timeout tuning delays the connect itself, only the pacing of sends after it (tried up to --idle 33 explicitly, no effect: the "《天下》Beta正在 启动过程中" gate message is emitted as part of the very first output burst, before any input reaches the server). This is not a wasm driver limitation or a mudlib bug — the grace period is intentional mudlib behavior that the native test above waits out fine between separate shell commands — it's a harness/mudlib timing interaction specific to this one-shot wasm smoke-test tool's immediate-connect design. Did not force it further (e.g. by padding preload with artificial delay) per the task's "honest assessment over forcing a full playthrough" guidance. Assessment: boots cleanly under wasm; this specific harness invocation cannot exercise the login flow for this lib due to a startup-grace-period/connect-timing collision, not because of a real wasm incompatibility (query_ip_number() was never even reached to test).

WASM-enablement pass (2026-07-24)

Standard four-change pass (AGENTS.md §1.3b/§1.3e/§1.5). Applied the CORRECTED (fail-closed) loopback pattern throughout — loopback is strictly query_ip_number(ob) == "127.0.0.1", == "::1", or a leading "127." prefix; a non-string/empty/malformed IP is NOT treated as loopback (the earlier WASM query_ip_number()/resolve() garbage-IP bug this defensive fallback existed for is now fixed upstream).

1. Loopback-allow: - adm/daemons/logind.lpc begain_enter() (~line 127) — the BAN_D->is_banned(query_ip_number(ob), 2) IP-ban gate now short-circuits to allowed for loopback. - adm/daemons/logind.lpc get_id() (~line 192) — the IP_D->identify_ip(arg, query_ip_number(ob)) wizard-address whitelist gate (self-registers a wizard's first-ever login IP as their allowed pattern, destructs on any later login from an unregistered address) now always passes for loopback. Without this, a wizard connecting from a second/different loopback-facing interface, or before their first-ever identify_ip self-registers, could be destructed — now moot for 127.0.0.1/::1. - adm/daemons/ban_d.lpc NOT touched directly — the call site gate above is sufficient (this daemon's is_banned() also handles id/name/word bans via the same function with a different n argument, so patching the call site keyed on the loopback IP is cleaner than threading a loopback exception through the shared multi-purpose function). 2. Uptime gate: adm/daemons/logind.lpc logon() (~line 62) — if (uptime() < 30) { ...destruct(ob); } (documented in AGENTS.md §1.3e as one of the known affected libs) now only applies to non-loopback connections; loopback connects immediately regardless of driver uptime. 3. Anti-flood throttle: none active — the only same-IP counter (Same_Ip/MAX_SAME_IP in begain_enter()) is already commented out in the original source (dead code), nothing to patch. 4. Admin account seeded: fluffos, registered through the real flow (qinfengx-style: id → confirm y → Chinese name 浮浮 → password → confirm → gender f → attribute alloc 0/random → confirm y → entered 松竹小院). Granted (admin) via /adm/etc/wizlist (append fluffos (admin); securityd.lpc only loads this file in create(), so the driver needed a restart for it to take effect — no hot-reload command found). Password deviation, found and handled: this lib enforces a SEPARATE, stricter password rule for any account whose status is not (player) (WIZ_PASSWD_CHKcheck_wiz_legal_password(): >=10 chars, must contain uppercase, lowercase, AND a symbol). The registration-time-only 8-char Mud@2026 (fine under the *player* rule of >=5 chars) fails this the moment the account becomes (admin) and is not caught until the FIRST subsequent login attempt, where get_passwd() forces an interactive password reset before completing login. Handled it live: entered Mud@2026Admin (13 chars, satisfies the rule, does not overlap with the id fluffos/its substrings per the similarity check) when prompted — this is now fluffos's real login password, documented in README.md. Save files: data/login/f/fluffos.o, data/user/f/fluffos.o. Neither path is gitignored in this lib (its data/ tree is already partly tracked, unlike several sibling libs) — a plain git add picks them up, no force-add needed. Verified: update /cmds/usr/score.lpc → 成功 (confirmed the self-update-destructs-silently quirk also applies here, same as sjpl2/others — not a bug introduced by this pass). 5. Retest: fresh registration (qftxwasm/秦风, deleted after test, including its data/news/y100m1d15s452.p.1 "new player" announcement file) end-to-end into 松竹小院 with look/score/quit all producing correct output, immediately after boot (no 30s wait needed from loopback — confirms the uptime patch). fluffos login (with the new password) + update wizard command verified in a separate session. debug.log: no new denied/undefined function/error in error handler/bad argument lines from either session.

Long-sit boot-watch pass (2026-07-24) — found and fixed a real

WASM-only login blocker in the SQL-backed user database

scripts/wasm_boot_watch.sh tianxia 200 (a real >3-minute sit, not the usual 20-30s smoke test) caught a bug the quick registration test never exercised: every single connection under WASM was destroyed at logon(), before the id prompt even appeared. Root cause: this lib's entire registered-user store is backed by a MySQL-ish daemon, adm/daemons/databased.lpc (db_connect/db_exec/db_close/...), not save files. Under WASM, the db package doesn't exist at all (AGENTS.md §1.3c), so databased.lpc fails to *compile* (not just "fails to connect") — every DATABASE_D->... call_other then throws *No program in object '/adm/daemons/databased'!. master.lpc's own preload() already catches this for the initial preload attempt (cosmetic, harmless — matches the already-documented "sockets/db absent, daemon just absent" class), but adm/simul_efun/user.lpc's count_reg_user() — called UNGUARDED from logind.lpc's begain_enter() (line 143, right after the banner, on literally every connection) — re-triggers the same compile failure with NO catch anywhere in the chain, and the resulting error escapes all the way up through logon(), causing new_conn_handler: logon() ... has failed, the user is disconnected. Zero player-visible output (same silent-kill shape as AGENTS.md's query_bandwide() fix, item 9 above, and the general §7.10/§1.3c pattern) — this is exactly the class of bug the long-sit methodology (§10.0) exists to catch and the quick smoke test structurally cannot.

Fix (adm/simul_efun/user.lpc, all 10 functions in the file — permit_add_cname, permit_reg_email, query_exceed_reg_time, del_user_data, change_cname, count_reg_user, newbie_buildup, newbie_reg, newbie_success_reg, query_register_station): wrapped every DATABASE_D->... call in catch(), degrading to a safe default (0 / permit / no-op) when the database daemon is broken or absent, per the standard §1.3c convention ("guard on find_object()/catch() truthiness, absent ⇒ skip the gate") rather than inventing anything new. Natively this is a no-op (the native driver's db package is present so databased.lpc compiles fine and these calls succeed normally, verified below) — the guard only changes behavior when the daemon is genuinely broken, which under WASM it always is.

Not fixed / flagged, not observed failing in this pass: three OTHER files also call DATABASE_D-> directly (bypassing user.lpc's simul_efuns) and share the identical latent risk if reached: adm/daemons/paiming_d.lpc (db_query_bang_top_ten() inside make_renyi_bang(), only reachable via the PAIMING_D->main() cron job that fires once daily at in-game 3am per adm/etc/crontab — didn't fire during this test's ~7:50-8:03am window), adm/daemons/pawn_d.lpc (query_user_all_pob/query_count_user_pob/pawn_one_object/ retrieve_one_object/query_all_exceed_pob, reached from logind.lpc's restore_players_pawnstamp() call gated on combat_exp >= 20000 — never true for a fresh character, so also didn't fire), and adm/daemons/renyi_d.lpc (the bounty-quest system, a whole family of DATABASE_D-> calls, gameplay-only, not on the boot/login path at all). None of these surfaced an actual error during this sit — noted here for whoever next touches the pawn-shop/bounty/ranking features so the same catch() treatment isn't rediscovered from scratch, not fixed preemptively since nothing observed actually broke.

Retest: re-ran wasm_boot_watch.sh tianxia 200 (full 200s) — transcript now shows the SAME databased.lpc compile-error spew (that part is unavoidable/cosmetic, matches the already-documented "sockets/db absent" class) but this time followed by 错误讯息被拦截: (caught) and a CATCH() frame in simul_efun.lpc's count_reg_user(), then the full login banner completes normally and the connection sits cleanly at the English-id prompt (您的英文名字:) for the rest of the 200s — no disconnect, no further errors. Native sanity check (fresh registration, real driver): id wasmck, Chinese name 测试客, through attribute allocation, into 松竹小院, look + score + quit all produced correct output exactly as before; debug.log stayed clean (no denied/undefined function/bad argument/error-in-error-handler lines). Test save files removed afterward (not committed).

深度功能测试 / Deep functional test (2026-07-24, round two)

First real *playthrough* pass on this lib per AGENTS.md §10.7 (every prior pass verified only registration + look/score/i/quit, never real movement beyond the auto-entered start room, never combat, never a sect/skill flow). Read work/doc/help/newbie/{guide,new} in full first — new names the exact intended early path (start at 长安 谪仙楼, east to 后厨 to work for money, mai/eat/drink at the inn, bai/upgrade to join a sect and learn) and cmds lists the full command set. Native driver (build-debug), one character across many continuous scripts/mudclient.py sessions (each session's own close — no quit sent — is itself an unclean/net-dead disconnect; NET_DEAD_TIMEOUT is only 60s here, include/user.h, so nearly every reconnect below is a REAL exercise of either the prompt-reconnect path or the full-timeout force-quit path, not a simulated one).

Test character (kept, not cleaned up, as playthrough evidence): id linhaoran, Chinese name 林浩然 (male), password TxTest2026#. Final state: at 谪仙楼 (长安, the valid_startroom-flagged start room), member of 华山派 (5th generation, teacher 令狐冲), skill dodge (纵跃闪躲之术) at level 1, inventory Shoes/Cloth/Mailbox, 298两20文 of the starting 信用点 credit balance (90-credit qu hsp coach fare to Huashan). Saves: work/data/user/l/linhaoran.o, work/data/login/l/linhaoran.o.

Bug found and fixed (NEW class): changed_match_path()'s earlier "restore" used the WRONG efun semantics, silently breaking every 2+-level query()/query_temp() call lib-wide — including every bare directional movement command

File:line of the fix: adm/simul_efun/ansi_util.lpc's changed_match_path(mapping m, string str) (originally added by a prior pass, see item 7 of "Fixes applied" above).

```lpc // BEFORE: mixed changed_match_path(mapping m, string str) { return match_path(m, str); } // AFTER: mixed changed_match_path(mapping m, string str) { string *parts; mixed cur; int i;

if (!mapp(m) || !stringp(str) || str == "") return 0;

parts = explode(str, "/") - ({ "" }); cur = m; for (i = 0; i < sizeof(parts); i++) { if (!mapp(cur)) return 0; cur = cur[parts[i]]; } return cur; } `` Confirmed match_path() (the real efun) has no OTHER call site in this archive (grep -rn '\bmatch_path\b'` outside this one function), so this change cannot regress any genuine ACL-style use elsewhere — there isn't one.

Command-hook private / command()-self-call path: re-confirmed healthy, extends further than §8.3a's baseline note suggested

AGENTS.md §8.3a already lists tianxia as an empirical exception where private nomask command_hook (feature/command.lpc:40, still private in the current source) does not break ordinary *typed* dispatch, and its addendum warns that exception does not necessarily extend to command()-efun self-calls. This pass specifically hunted for and exercised several such self-calls, live:

No new instance of the private command_hook/command()-self-call failure (the shape documented for shiji/xzyx) was found on this lib — every self-call path exercised here worked correctly, consistent with tianxia already being listed as an exception, now demonstrated on the command()-self-call axis specifically (not just typed dispatch) rather than merely asserted.

What was tested and confirmed working

- Prompt reconnect (reconnecting inside the 60s window): several times, always printed 重新连线完毕。 (obj/user/user.lpc's reconnect()) and resumed on the SAME live object at the exact room/state where the previous session left off (confirmed by look immediately after reconnecting showing mid-navigation rooms the character had no other way to be standing in). - Full-timeout force-quit (reconnecting after 60+ real seconds): happened repeatedly, simply as a byproduct of the real thinking/ investigation time between test commands in this pass — confirmed via work/log/USAGE's timestamps (a fresh loggined entry, not a silent reconnect, appears whenever the gap exceeded ~60s) that user_dump(DUMP_NET_DEAD)'s command("quit 68#@") path (itself ANOTHER command()-self-call, also confirmed working) correctly ran the real save-and-destruct flow and that the next login correctly restored the character at their last valid_startroom location with no state loss and no debug.log errors. This satisfies AGENTS.md §10.7 checklist item 8's "real full-duration net-dead timeout wait" cheaply, since 60s is trivially reachable within a normal investigation pass — no dedicated 15+-minute sleep was needed on this particular lib.

What was NOT verified live, and why

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

状态已从过时的 limited 修正——这份档案自己的 README 里从未记录过任何缺陷说明,本轮重新测试也没有发现:管理员登录干净正常,用的是这份档案自己记录的登录密码(Mud@2026Admin,不是标准的 Mud@2026——这份档案对巫师账号强制执行更严格的密码规则,首次管理员提升时会自动升级登录密码,这在它自己的 README 里有记录)——"目前权限:(admin)"。

深度功能测试第三轮 / Deep functional test round three (2026-08-15, post driver-upgrade re-test)

驱动于 2026-08-12 升级后的重测。标准检查清单发现并修复两处问题:

1. adm/simul_efun/file.lpclog_file() 没有 assure_file() 目录预建保护,补上调用及前向声明(log_file() 定义在 assure_file() 之前,本驱动不容忍未声明的前向调用);cat() 两处 write(read_file(file)) 补上 || "" 空值防护(此文件本身 已有 Find 加固过的 valid_read() 安全检查,未改动那部分逻辑)。 2. obj/user/user.lpc::reconnect()(AGENTS.md §7.108,第八条独立 确认的血统)adm/daemons/logind.lpc 有同款 exec(old_link, user); 踢掉重复登录写法,reconnect() 缺少 enable_commands()。按 §7.108 记录的写法预防性修复,现场用两个 真实连线复现"保持第一个连线不断开→第二个连线登录→答 y 踢掉旧连 线"验证:score 修复后立即正常显示完整角色档案。

cmds/wiz/update.lpc(§7.106)与 master.lpc::log_error()(§7.10 的 "arning:" 大小写无关写法)均已是正确写法,maximum evaluation cost 已经是 2000000,均无需改动;本档案无 adm/daemons/ closed.lpc,不受 §7.107 影响。

现场验证摘要

驱动干净启动,管理员 fluffos/Mud@2026Admin(此库自己文档记录 的巫师专用强密码,非标准 Mud@2026——首次尝试用标准密码触发 "密码错误!"并立即断线,重新连线用正确密码成功)登录确认 目前权限:(admin)update /adm/daemons/logind 成功验证真实写入 权限。踢掉重复登录重连路径现场验证通过(见上)。debug.log 全程 干净(227 行,无真实错误)。

本轮修改的文件

§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/yanluo.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.

深度功能测试第四轮 / Deep functional test round four (2026-08-20) — both previously-flagged gaps closed, both verified clean, no bug found

Native build-debug driver, tianxia's own config.fluffos, one continuous debug.log watch (log/debug.log, baseline 227 lines) across the whole session. Reused the existing test character linhaoran/林浩然 (id linhaoran, password TxTest2026#) via scripts raw-socket sessions (a purpose-built tx_client.py, not tmux_mud.sh), reconnecting between short scripted segments per this project's standing practice, validating each response before sending the next command.

Gap 1 — real shop list/mai purchase: VERIFIED, fully clean

std/char/dealer.lpc (the generic "mai"/"buy"+"list" mixin, init() registers both actions) gates do_list/do_buy on is_day() UNLESS the vendor object sets "sell_all_day"谪仙楼's 跑堂 (the only shop found in the prior round) doesn't set it, hence the earlier "打烊了" closure. Grepped all ~85 F_DEALER-inheriting NPCs corpus-wide for sell_all_day: 65 of them set it, including one in the immediate start zone — d/changan/npc/weaponboss.lpc (兵器坊老板, "本店全天营业,敬 请随时光临" — literally advertises 24-hour business in its own greeting text), housed in d/changan/weapony.lpc (长安兵器坊), reachable from 谪仙楼 in 10 moves (west, north, east×7, north) via the same 长安城 road network mapped in round two.

Live purchase, real character, real money: score before showed 298两 20文信用点credit; list printed the real price table (匕首/dagger at 五两白银); mai bishou → "你从兵器坊老板那里买下了一把匕首。"; i confirmed 匕首(Bishou) now in inventory; score after showed exactly 293两20文 (5两 deducted, matching the listed price to the tael). This is the actual do_buy() code path in std/char/dealer.lpc (player_pay()new(file)move(me)), not the coach-fare player_pay() call from round two — confirms the shop system itself, not just the currency plumbing underneath it. debug.log: zero new lines.

Gap 2 — real combat to death/respawn: VERIFIED, fully clean, one real death+respawn cycle completed

Confirmed via direct code read (cmds/std/kill.lpc) before attempting live: kill has no fight-vs-accept_fight-style safety net at all — its only gates are SAFE_ENV, PROTECT_AGE (only relevant vs. other *players*), NO_KILL/is_master, busy/netdead/edit-mode checks, and accept_kill() (which std/char/npc.lpc implements to always return 1 for an ordinary NPC, only adding flavor text/master-guard-pileup side-effects) — matching this session's established precedent that the fight auto-concede gate does not extend to kill. Went back to 长安 武馆's 练功场 and used kill mu ren (the SAME 木人 training dummy that safely auto-conceded under fight in round two — confirmed d/changan/npc/muren.lpc has no NO_KILL flag) against the still-fresh, still-unarmed, still only-dodge-level-1 test character (combat_exp 100 vs. 木人's 40000). Real combat ran automatically via heart_beat (no repeated kill spam needed) — a real damage sequence (bruises → welts → scratches → semi-conscious → "你的眼前一黑,接着什么也不知道了....") — and the character genuinely died: "你死了。" + a real death broadcast ("【谣言】某人:林浩然被木人杀死了。"), moved to DEATH_ROOM (/d/death/gate, 鬼门关).

Walked the real death-realm room chain north×3 (鬼门关→奈何桥→望乡亭→ 阎罗殿, d/death/{gate,gateway,road1,road2}.lpc) into d/death/npc/ yanluo.lpc's room, whose init() (guarded per the §7.112 fix documented immediately above — re-verified this fix is the version that actually ran, see below) started the real death_stage() call_out chain. All 4 stages fired in order, one real ~5s tick apart, with no duplication (confirming the reentry guard's set_temp/delete_temp pairing is intact and doesn't misfire): 阎罗王's cold stare → flipping through the『阴阳册』ledger → "你阳寿未尽,本王不能收留" → the final "你回去吧" + fog line, immediately followed by reincarnate() (real gin/kee/sen restored to half-max, food/water maxed) and a real move() to revive_loc[0] (/d/changan/badroom, 土地庙) — landed there correctly, set_status_xuruo(30) applied ("你感觉身体状况非常虚 弱。" / "你感觉身体非常的虚弱,一点力气也使不出来了。。。" both fired, confirming the post-respawn debuff also ran for real). Post-respawn score: no longer shows "鬼魂" (ghost) in the age line, "你共死亡一次" correctly incremented, stats/hp bars correctly at half. A final clean quit saved the post-death, post-respawn state.

debug.log monitored continuously through the ENTIRE sequence (shop purchase, real kill, death broadcast, the 4-stage death_stage() chain, reincarnate(), badroom landing, several reconnects, final quit) — stayed at the exact same 227-line boot baseline throughout, zero new lines. One incidental non-bug observation: feature/damage.lpc's die() calls DEATH_ROOM->start_death(this_object()) (DEATH_ROOM = /d/death/gate) but start_death() is never defined anywhere in the archive (grep -rn start_death finds only this one call site) — this call silently no-ops (FluffOS's default call_other-to-undefined- function behavior, confirmed by the zero debug.log growth through a real live death), so it has no observable effect and is not a bug by this project's own standing "no error signature ⇒ not a bug" rule; the real death-sequence trigger is entirely d/death/npc/yanluo.lpc's init(), reached by simply walking into its room, which is what actually matters and is what this pass exercised.

Test character linhaoran/林浩然 kept as further playthrough evidence (now: has died once, owns a 匕首/dagger bought at full price, resting at 土地庙 in a temporarily weakened state, 293两20文 credit remaining). Saves: work/data/user/l/linhaoran.o, work/data/login/l/linhaoran.o.

Fast standard-checklist confirmation pass (no changes needed, all already correct)

No bugs found this round — both explicitly-flagged gaps from round two/three are now fully closed by direct live verification (not just design reasoning), and the standard checklist is a clean confirm-only pass.

§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.