Twin Dragons of the Tang Dynasty (DaTangShuangLong)

✅ 可玩

大唐双龙 (DaTangShuangLong)

dtsl2

🔑 fluffos / Mud@2026 更新 abece81 2026-09-04 源码 下载 ZIP

▶ 开始游玩 · Play Now

取材自黄易的武侠小说《大唐双龙传》,以隋末唐初天下大乱的江湖为背景。与 dtsl(大唐双龙传)、dtslmud 共享同一套"大唐双龙"世界观内容——逐档比对发现 dtsl2 自身 5,095 个地图/剧情文件与 dtsl 达 99% 以上字节级一致,实际仅 4-5 个文件存在差异(主要是一名 NPC 与一处枢纽房间),三者的真正区别在于各自独立的服务器与安全层实现(本档案采用更简化的登录/安全模型)。新角色同样从"大唐学院"起步,可 `down` 直入江湖,也可先 `enter` 学院详细了解系统;"石龙武馆"新手枢纽区提供银行存取、`qiecuo` 安全切磋、NPC 陪练学艺(`learn`/`xue`),并可经由各门派入口 `bai` 拜师加入阴癸派、净念禅院、突厥、东溟派、慈航静斋等门派。中文取名会直接拒绝与小说主角(寇仲、徐子陵)或真实历史人物重名;新建账号需连续在线超过一小时才会自动存档,退出前会主动询问是否保留账号。

English

An independent server/security-layer fork sharing the SAME 大唐双龙 authored game world as siblings dtsl and dtslmud, based on Huang Yi's novel and set amid the chaos of the early Tang jianghu -- a file-level check found dtsl2's own domain tree (5,095 files) is 99%+ byte-identical to dtsl's (only 4-5 files actually differ, mostly one job NPC and one hub room), confirming these three archives are the same content wrapped in different engines, not independently-written storylines. What genuinely differs here: its own, simpler master.c/logind.c/security model (no deferred security daemon), Chinese names rejected outright if they match the novel's own protagonists (寇仲/Kou Zhong, 徐子陵/Xu Ziling) or real political figures, and new accounts needing over an hour of continuous online time before the first autosave (with an explicit "keep this account?" prompt on an early quit). Same 石龙武馆 (Stone Dragon Martial Hall) newbie hub as its siblings -- banking, `qiecuo` safe sparring, organic `xue`-taught skills, and `bai`/`apprentice` sect-joining through direct gate exits into 阴癸派/净念禅院/突厥/东溟派/慈航静斋.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/dtsl2/

管理员账号 / Admin account

警告:Mud@2026 是本地游玩用的公开默认密码。若要正式对外开放主机,
请先修改此密码。

本地运行

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

游戏端口:40043

NOTES · 移植与修复记录

dtsl2 — 大唐双龙 (archive #49, 大唐双龙.rar)

Archive: 大唐双龙.rar. Port: 40043. Status: done (boots clean, full registration flow verified end-to-end including a real Chinese name reaching the actual game world).

What this is / naming discrepancy

Mudlib root was raw/DTSL/ directly (no extra nesting). The archive's ASCII-art connection banner, cmds/wiz/mudinfo.lpc's system-info text, and the welcome messages all say "大唐双龙"/"DaTangShuangLong"/"Datang ShuangLong Mudlib 1.0.0" — that's the name actually shown to a connecting player, and what config.fluffos's name field was set to.

However, the raw config file (config.bxcy)'s own name directive literally says "碧血残阳 之 豪侠晚歌" ("Bloody Sunset - Ballad of the Bold Heroes") — which is the title of archive #71 (碧血残阳之豪侠晚歌.rar), not yet processed as of this writing. This is almost certainly a leftover/copy-paste artifact from whatever base this lib's config was forked from (the same class of thing seen on xkxz2(#41), whose config had a leftover "海洋II" MUD_NAME) — confirmed this is NOT a duplicate of #71: nothing else in this archive references "碧血残阳"/"豪侠晚歌" anywhere (checked adm/etc/welcome, mudinfo.lpc, banned-name lists, room content) — it's purely a stale config field, coincidentally matching a different not-yet-processed archive's title. Went with the live-banner name ("大唐双龙") for config.fluffos, per instructions, and left a comment in the config explaining the discrepancy for whoever processes #71 next.

Lineage check against dtsl(#8) / dtslmud(#18)

Both already-processed archives share the exact title "大唐双龙传"/ "LLMUD大唐双龙". Per AGENTS.md's repeated lesson that similar titles do NOT reliably imply shared code, checked directly (raw archive vs raw archive, not post-fix work copies):

Conclusion: this is the same underlying "大唐双龙" mudlib family/base (shared item content, shared chinese.c), but a distinct branch/fork from dtsl/dtslmud — different master.c/logind.c implementations, different (smaller/simpler) security model, its own config with the misleading stale name field. Not a duplicate archive; processed as its own lib per instructions.

Fixes applied

1. AGENTS.md §15h, standard shape (pre-confirmed from raw source before conversion): adm/simul_efun/chinese.lpc's is_chinese() was the two-specific-byte GBK variant (str[0]/str[1] range checks) — rewritten to the standard single-codepoint CJK range check (str[0] >= 0x4e00 && str[0] <= 0x9fff). adm/daemons/logind.lpc's check_legal_name() bound halved (< 4 || > 10 bytes → < 2 || > 5 characters) and the i%2==0 even-byte-offset gate removed so every character position is checked. 2. F_UNIQUE macro gap (same as dtsl(#8)/dtslmud(#18)): referenced via inherit F_UNIQUE; by ~91 clone/ item files but never #defined in include/globals.h, despite /feature/unique.lpc existing. Added #define F_UNIQUE "/feature/unique.lpc". Fixed ~91 files' "syntax error, unexpected L_IDENTIFIER" errors in one shot. 3. F_BACKUP macro gap (same shape, new instance, not previously seen in dtsl/llmud): obj/fabao.lpc and obj/jiasha.lpc inherit F_BACKUP; with no matching #define, despite /feature/backup.lpc existing. Added #define F_BACKUP "/feature/backup.lpc". 4. Same 6-file diamond-inherit bug as dtsl/llmud: clone/weapon/{jin- sjian,shizi,feihuangshi,jinhua,sjian,feidao}.lpc redundantly inherit COMBINED_ITEM; both directly and via inherit THROWING; (which itself already inherits COMBINED_ITEM) — this driver's stricter nomask conflict detection rejects the diamond. Removed the redundant direct inherit COMBINED_ITEM; line in all 6 (CRLF-safe sed, per the dtsl gotcha). sm_ding.lpc (7th file in the same cluster) has the same unrelated, unfixed problem as in dtsl/llmud: #include "/inherit/item/embed.h" doesn't exist anywhere in this archive either — genuine missing content, left as-is. 5. Confirmed NOT needed, via source reading of the raw master.c before conversion (per task instructions): no §4 lazy-security-daemon recursion guard needed — valid_write only does a bare find_object(SECURITY_D) check (no load_object call at all), and valid_read is an unconditional return 1;. No §15l destruct-in- create() crash pattern — create() is a one-line write(), no SIMUL_EFUN_OB/MASTER_OB destruct anywhere. get_root_uid()/ get_bb_uid() (§7) both present and correct. 6. adm/daemons/securityd.lpc has a real custom ACL (trusted_read/ exclude_read/etc, same general shape as the §15n family) but it is dead code here — nothing calls SECURITY_D->valid_read() anywhere in the lib (master.lpc's own valid_read never defers to it, unlike valid_write which does defer to SECURITY_D->valid_write()). So §15n's mid-connection lazy-compile lockup and §8c's this_player()- override footgun don't apply here — confirmed by reading, not by hitting a bug. 7. adm/etc/preload: /adm/daemons/network/dns_master was already commented out in the raw archive — verified no other network/DNS/ intermud daemon is active in preload (grepped case-insensitively). 8. No local <header.h>-next-to-its-user angle-bracket includes found (§8d) — checked proactively, none present.

Routine conversion (convert_lib.sh, 8249 raw files): encoding already_utf8=359, converted=7490, lossy=15 (mostly data/board/*.o and data/emoted*.o save files with invalid bytes — seed/example data, not source), skipped_binary=384; .c.lpc rename to 6874 files; 1167 literal-.c"-reference fixes; 4 angle-bracket #include <x.c> fixes; 76 local angle-bracket #include <x.lpc>"x.lpc" conversions (proactive §8d insurance); 103 files staticnosave. A handful of .lpc/.h files (bbsd.lpc, d/job/haishajob/killer{1-6}.lpc) were flagged by the post-conversion file-classifier straggler check but are confirmed-clean UTF-8 LPC source — false positives from file's heuristic on their specific content shape, not actual encoding misses.

Boot

Booted clean on the first attempt after the fixes above — zero error: lines in the debug log, all adm/etc/preload daemons loaded successfully, Accepting telnet connections on 0.0.0.0:40043 / Initializations complete. reached in well under 20 seconds. Only warnings (nosave-on-function, unused locals, no-effect expressions in natured.lpc's season-flavor-text switch — none fatal).

Interactive test result — full registration flow

Read logind.lpc's actual call chain before testing (not assumed from another lib). No hidden pre-id prompts (INFO_CMD/cmds/wiz/mudinfo.lpc is non-interactive, just prints system stats). Flow shape: banner → system info → English id (3-8 lowercase letters, no "new" keyword — any unused id works) → y/n create-confirmationChinese name (2-5 characters, NO separate y/n confirmation) → password (5-8 chars) → confirm password → email → gender (m/f) → stat-point allocation (4 numbers summing to 80, each 10-30) → yes/no confirmation → enters game.

banned_name includes the novel's own protagonists (寇仲/Kou Zhong, 徐子陵/Xu Ziling — literally the title characters of "大唐双龙传"), plus real political figures (江泽民, 邓小平) — avoided all of these.

Verified the complete registration path in one continuous mudclient.py connection: id qinfeng → confirm yreal Chinese name 秦风 → accepted, straight to password prompt → password test1234 → confirmed → email [email protected] → gender m → stats 20 20 20 20 → confirmed yesentered the actual game world (大唐学院/"Datang Academy" starting room) → issued look successfully → quit. This is the strongest possible verification (not just "reached a prompt" — a real Chinese name was accepted and the character was fully created and playable).

lpcc sweep

6874 files. First pass (before F_UNIQUE/F_BACKUP fixes): 6434 pass / 440 fail (93.6%). Second pass (after both macro fixes): 6506 pass / 368 fail (94.6%).

Remaining failure categories, triaged by error-text histogram (not fixed further, per AGENTS.md §6b/§13 — none of these are on the boot/ registration path, confirmed above):

Memory stayed healthy throughout both sweeps (free memory fluctuated 2-9GB available on this 23GB host, shared with another concurrent lpcc run from a different session at the time — never approached the OOM-risk threshold documented in AGENTS.md §6b).

How to run

cd libs/dtsl2
~/src/fluffos/build-debug/src/driver config.fluffos
python3 ../../scripts/mudclient.py 127.0.0.1 40043 --timeout 20 --idle 1.5 \
  --send "" --send "<newid>" --send "y" --send "<中文名字>" \
  --send "<password>" --send "<password>" --send "[email protected]" \
  --send "m" --send "20 20 20 20" --send "yes" --send "look" --send "quit"

Retroactive fix (QA re-verification pass, 2026-07-23): log_error() compile-warning spam (AGENTS.md §15af)

Found during a routine re-verification pass (full registration + post-login look/score/quit test): adm/obj/master.lpc's log_error() showed the raw compiler diagnostic text to ANY connected player — no wizard check at all — for every compile diagnostic funneled through APPLY_LOG_ERROR, including harmless "Unused local variable"/"Unknown #pragma" warnings triggered by the first lazy compile of an ordinary room/command file the player merely walked into or typed (/cmds/std/look.lpc, /adm/daemons/ examined.lpc, /adm/daemons/combatd.lpc, /cmds/std/score.lpc, etc). In the pre-fix test transcript this showed up as several screens of raw compiler internals interleaved with normal game text right after registration — exactly the same bug already found and fixed on dtsl(#8) (this lib's own chinese.c-sharing but otherwise-distinct sibling) and wuhanzhan/shenzhou/dfgs2 elsewhere in this project. Fixed identically: only show the full diagnostic to a wizard; only alarm an ordinary player with the generic default error message for a genuine compile error (gated on absence of "warning:" in the message). Also needed #include "/include/runtime_config.h" added to the top of master.lpc (for the __DEFAULT_ERROR_MESSAGE__ macro) since the file had no includes before. Re-verified with a fresh registration (real name 秦海) followed by look/score — zero spam, clean output, matching what a real player should see.

Also observed (not fixed, off the critical path): one background-daemon *Read access denied. runtime error appeared once during this test, rooted in a job-system NPC (/d/gaoli/npc/xiake, spawned by /adm/daemons/ jobmond.lpc's periodic job-posting logic) trying to new() a carried item that hits an ACL denial somewhere in its own chain. This fires from the job/quest daemon's own independent heartbeat, unrelated to the player's registration/look/score actions being tested, and matches this project's established "content gap in non-critical-path daemon content" pattern (AGENTS.md §13) rather than a registration-blocking defect — not chased further given time constraints.

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

WASM-enablement pass (2026-07 standard: loopback-allow, admin seed)

Gates patched (fail-closed: only an exact 127.0.0.1/localhost/ 127.-prefix match bypasses; a malformed/non-string address falls through to the original regexp-scan logic unchanged):

Admin account: id fluffos / Mud@2026 / 浮浮, registered through the normal flow (id -> confirm y -> Chinese name 浮浮 -> password -> confirm -> email skipped -> gender m -> stat allocation 20 20 20 20 -> confirm yes), then granted (admin) via adm/etc/wizlist (fluffos (admin) line added, hpdxxd's existing entry kept) and driver restarted. Verified: re-login shows "目前权限:(admin)", update /d/newbie/door succeeded ("重新编译 /d/newbie/door.lpc:成功!"). Save files (data/user/f/fluffos.o, data/login/f/fluffos.o) are plain untracked paths, NOT covered by any .gitignore pattern (checked repo-wide and per-lib) -- a normal git add libs/dtsl2/ picks them up, no force-add needed.

Retest: fresh normal registration (id ceshiqi, name 秦岭, female) reached 大唐学院, look correct, quit-retention prompt handled (kept the account then disconnected). fluffos admin login + update verified above. debug.log clean across all three driver runs this pass (only the expected boot-time config dump and SIGTERM-on-kill lines). Three driver instances started and killed by exact PID during this pass; test character ceshiqi's save files removed afterward (data/user/c/ ceshiqi.o, data/login/c/ceshiqi.o), fluffos's kept.

深度功能测试 / Deep functional test (round two, AGENTS.md §10.7, 2026-07-25)

Full hands-on playthrough per the §10.7 checklist. Read doc/help/newbie first (generic ES2-heritage newbie-guide text — establishes fight as the lib's point-and-stop safe-sparring command, bai/xue/skills as the sect-join/skill-learn verbs). Native driver (~/src/fluffos/build-debug/src/driver config.fluffos, cwd libs/dtsl2/), scripts/mudclient.py, one continuous session per leg, real Chinese names throughout (陈慕白/id chenmu, 秦学才/xuecai, 秦一飞/chenyi, 秦韬/qintao, 秦直/qinzhi, 王启/wangqi, 郝竹/haozhu, plus wizard fluffos/Mud@2026 — all throwaway test accounts' save data removed afterward except fluffos). Full flow verified repeatedly: id → confirm → Chinese name → password → confirm → email → gender → 4-stat allocation → confirm → lands in 大唐学院 (走廊) → look/score/i all correct. Explored the 石龙武馆 (Stone Dragon Martial Hall) hub reachable via down from the academy — walkway/正厅/北廊/账房(bank)/兵器铺(weapon shop)/练功房 rooms, plus the hub's direct sect-gate exits (阴癸派, 净念禅院, 花间, 慈航静斋, 突厥, 东溟派, 弈剑派, 宋家堡). Withdrew bank funds (withdraw <n> coin), attempted a weapon purchase (buy <item> from <vendor>, correctly rejected for insufficient funds — cheapest stock item is 80 coins, a fresh character's opening deposit is only 10), and exercised the organic NPC-teacher skill-learning path (bai/xue/cha against 石龙武馆's 武馆教头) extensively — this is where both bugs below were found. Sect-join (bai <family-NPC>) confirmed correct via code review (d/yinkui/npc/dizi2.lpc's create_family("阴癸派", 7, "弟子") gives a real target, matching feature/apprentice.lpc's flow) and one live negative-case check (bai jiao against the family-less 武馆教头 correctly refuses: "既不属於任何门派,也没有开山立派,不能拜师。") — the positive case was NOT completed live (repeated reconnect-position navigation errors ate the time budget; noted here rather than silently skipped). The lib's only reachable "safe spar" for a genuine newbie is fight itself (per newbie help); every NPC actually reachable from the starting hub (教头/shifu, 弟子/dizi, 弟子/shoumen_dizi, 教头/liangongfang's jiaotou) has accept_fight() hardcoded to return 0, and the lib's real stat-mirrored training-dummy mechanic (std/char/obj/mukuai.lpc's pinzhuangstd/char/fighter.lpc's set_status(), confirmed via code to genuinely scale down to the attacker's own stats) is not stocked anywhere reachable from this hub — newbies must first grind combat_exp up via the outside world (or the 一次性 li-guan gift, capped at 6000) before the hub's own mirrored qiecuo sparring (d/slwg/get_skill.lpc, gated 10000 <= combat_exp < 20000) opens up. Documented as an observation, not fixed — plausibly intentional newbie-zone pacing, not a defect.

Bug 1 (fixed): learn/xue charges tuition even when the requested skill is unteachable — new class, AGENTS.md §7.33-shaped

Found live: a fresh character with money in hand, asking a teacher to xue <teacher> <skill they don't teach> (e.g. shifu doesn't teach unarmed), got the expected "这项技能你恐怕必须找别人学了。" rejection — but the attempt still silently deducted 3 coins of tuition (recognize_apprentice()'s side effect, MONEY_D->player_pay()), which had already run and succeeded BEFORE the code ever checked whether the requested skill exists in the teacher's roster. Same root shape as AGENTS.md's own zhongjidiyu §7.33 entry ("persisting a state change before validating that the underlying action actually succeeded"), generalized from "writing a permanent field" to "spending real player currency" — every one of master_skill/prevent_learn/my_skill >= master_skill/skill-cap/valid_learn/gin/potential can independently reject the attempt for reasons that have nothing to do with payment, and ALL of them ran only AFTER the money was already gone.

File actually live for ordinary players is cmds/usr/learn.lpc, NOT cmds/std/learn.lpc — this itself is worth recording: adm/daemons/ cmd_d.lpc's find_command() walks a player's command path (PLR_PATH = ({"/cmds/std/", "/cmds/usr/"})) via while (i--) starting from sizeof(path), which checks the LAST-listed directory FIRST — so /cmds/usr/ shadows /cmds/std/ for any verb defined in both (this matches several other genuinely-diverged std/usr pairs in this lib — ansuan/cemote/maphere/suicide/top — so it's very likely an intentional "usr overrides std" customization layer, not itself a bug; just a fact worth knowing before patching ANY cmds/std/* file in this lib without first checking for a same-named cmds/usr/ shadow). My first attempt patched only cmds/std/learn.lpc and had zero live effect until this was discovered and the identical fix applied to the actually-live cmds/usr/learn.lpc. cmds/std/learn0.lpc and cmds/std/learn1.lpc are separate, genuinely-unreachable byte-identical/near-identical backup files (no verb named learn0/learn1 is ever looked up) carrying the same original ordering bug — left untouched as dead code, matching project convention.

Fix (applied identically to both cmds/std/learn.lpc and the actually-live cmds/usr/learn.lpc): moved the is_apprentice_of/ recognize_apprentice payment gate from immediately after the living(ob) check down to immediately before the final write("你向... 请教...") success line — i.e., after every free/no-side-effect validation (master_skill, prevent_learn, skill-level/cap checks, valid_learn, gin/potential). Verified live, before/after: a fresh character with 10 coins, asking for an unteachable skill, now keeps all 10 coins and gets the identical rejection message; the same character asking for a real-but-currently-fruitless skill (sword, which the teacher does teach but the "lack of combat experience" content gate still blocks progress on) is correctly charged exactly once (10 → 7).

Bug 2 (fixed): enable_player() double-registers command_hook, silently re-running every FAILED command's side effects — exact match, AGENTS.md §7.28

Found live while investigating Bug 1: the SAME xue jiao unarmed attempt (before this fix) deducted 6 coins, not 3 — i.e., recognize_apprentice's payment ran TWICE for one typed command. Root cause is byte-for-byte the scenario already described in AGENTS.md §7.28: adm/daemons/logind.lpc's login-success flow calls user->enable_player() directly (~line 570), then a few lines later calls user->setup(), whose own body (std/char.lpc) ALSO calls enable_player() internally — two calls to feature/command.lpc's enable_player() in the same login, each doing a bare add_action("command_hook", "", 1) with no idempotency guard, so two identical command_hook sentences get stacked on every login. Invisible for any command that succeeds (the driver stops at the first sentence that returns nonzero) but every FAILING command re-ran its entire body a second time via the second stacked sentence — confirmed live pre-fix with a plain look xyz (nonexistent item) printing "你要看什么?" TWICE, and with the xue-on-unteachable-skill case above silently double-charging. cmds/std/sleep.lpc legitimately calls me->enable_player() again while already living() (wake-up path), so the living()-gated guard §7.28 warns against is correctly avoided here too.

Fix: feature/command.lpc's enable_player() now does remove_action("command_hook", ""); add_action("command_hook", "", 1); — guarantees exactly one sentence regardless of call count/order. Verified live: look xyz now prints its failure message exactly once, and the xue-on-unteachable-skill case (combined with the Bug 1 fix) now costs nothing at all, as it should.

Bug 3 (found, code-reviewed, NOT fixed — same class as AGENTS.md §7.36, live timing impractical)

feature/clean_up.lpc (inherited by every room via std/room.lpc's inherit F_CLEAN_UP;) is the exact §7.36 shape: its occupancy check for whether a childless room may destruct() itself is interactive(inv[i]) alone, with no userp(inv[i]) fallback — a net-dead player (real save state, userp() true, interactive() false since the socket is gone) is invisible to this check, so a room holding nothing but a net-dead player looks "empty" and can be destructed out from under them by the driver's own idle-object sweep, exactly as documented for xiaoyuxiyou. Not live-reproduced: this lib's config.fluffos sets time to clean up : 50000 (~13.9 hours of the object going unreferenced), and the driver's own sweep only runs once per 5 real minutes even after that — reproducing it live is outside any practical session budget, unlike xiaoyuxiyou's apparently-shorter effective window. Left unfixed pending a decision on whether to patch proactively by code-shape match alone (this pass's time was spent confirming the two live-reproduced bugs above and did not budget for a speculative fix + full re-verification of a third). Grep interactive(inv\[i\]) in feature/clean_up.lpc for the exact spot; the §7.36 fix pattern (|| userp(inv[i])) applies unchanged. Also worth checking obj/user.lpc's user_dump()'s tell_room(environment(), ...) call (~line 182) for the same defense-in-depth objectp() guard §7.36 recommends, since this lib's net-dead handling shape (no void-park, stays in the current room, command("quit") after NET_DEAD_TIMEOUT=900s) is structurally similar enough that a room destructed out from under a net-dead player would hit the same corrupted-environment() crash path.

Investigated and resolved as NOT a bug: room re-displays a second time immediately after quit

Every quit (and every dropped item during the new-account "abandon this account" flow) is followed by a second, apparently-duplicate, full room re-render in a raw-telnet transcript. Traced this down via a byte-exact reproduction (fluffos/admin quitting produces it once; answering n to a fresh account's retention prompt, which drops each carried item one at a time, produces one extra room re-render PER dropped item, each one immediately preceded by matching lbadd/lbrem/ lbclear control tags). Those tags are a structured list-box protocol this lib maintains for a custom GUI client (the tomud-flagged path already visible in logind.lpc's reconnect()); a plain-text telnet client just sees the plain-text fallback of each protocol update, which happens to be a full room re-render. This is cosmetic noise for mudclient.py/any raw telnet client, not a functional defect — confirmed debug.log stayed byte-for-byte unchanged (zero new lines) across every occurrence. Recorded here so a future pass doesn't waste time rediscovering it.

Net-dead / reconnect testing

obj/user.lpc's net_dead() does NOT void-park the player (no §7.20 shape here) — it just stops the heartbeat, clears enemies, and leaves the body in its current room, scheduling user_dump(DUMP_NET_DEAD) at NET_DEAD_TIMEOUT (900s) to force a command("quit"). reconnect() is a plain re-link (set_heart_beat(1); remove_call_out("user_dump"); ...), called from logind.lpc's own reconnect(), which does NOT call enable_player() again (so Bug 2 above was never actually reachable via the reconnect path itself — only via the direct-call-plus-setup() double at initial login). Exercised repeatedly and live, across many accidental and deliberate unclean disconnects this pass (every non-quit session end is an unclean/net-dead disconnect from the server's point of view) plus one genuine ~5-real-minute wait (well inside the 900s window): every prompt AND delayed reconnect landed the character back in the exact room/state left behind, look/score both correct, zero debug.log signal. The full 900s user_dump force-quit firing itself was NOT waited out live (15 real minutes exceeds a comfortable single-pass time budget on top of everything else this session covered) — user_dump's code was read and looks correct (single tell_room + enable_player() + command("quit"), no §7.12-shaped 2-arg tell_room wrapper bug present), but that specific real-time boundary is unverified live; flagged explicitly rather than silently presented as tested.

Not reached this pass

Combat progression to death/respawn: not attempted live — every accept_fight()-enabled NPC reachable from the starting hub is either gated behind combat_exp thresholds the newbie gift doesn't clear (qiecuo, 10000-20000) or outright refuses fight (see the safe-spar discussion above), and reaching further content requires real travel time this pass's budget did not stretch to. Stated explicitly per §10.7 rather than silently skipped.

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

dtsl/llmud 血统的相关分支。

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

深度功能测试(round four/五, AGENTS.md §10.7, 2026-08-20):真实战斗→死亡→重生全链路验证

本轮专门补上第二轮结尾 "Not reached this pass" 里悬而未决的战斗/死亡/重生 链路。原文档担心新手礼包清不掉 combat_exp 门槛(10000-20000)而摸不到 qiecuo 陪练,且 fight 指令对每个可及 NPC 都 accept_fight() 硬编码拒绝 ——但那全是 fight(安全切磋指令)的限制。重新读 cmds/std/kill.lpc 才发现 真正的杀伤性战斗走 kill 指令,其 valid_kill()cmds/std/valid_kill.h) 只限制玩家互杀(PK 冷却),对 NPC/野兽完全不设防——accept_kill/ combat_exp 门槛根本不适用于 kill。用 fluffos(admin) 账号 goto /d/huashan/shanlu6 站到一只野鸡(yeji)边上,kill ji 验证。

关键发现(非 bug,纯游戏机制):无技能状态下双方 get_attack() 恒为 0 ——adm/daemons/combatd.lpcget_attack() 只有在学过名为 dodge 的技能 或 query("dodge") 轻功列表非空时才会用身法加成,否则 me_attack 死锁在 0,导致命中判定 random(ap+vp) > vp*2/3 || vp*3 < apap=vp=0 时恒假 ——连续 100+ 回合、2 分钟实时对打双方 0 命中,纯属"完全没学过技能的两个人 在互殴中谁都打不中谁"的真实力学结果,不是 bug(无编译错误/运行时错误/ driver 拒绝,符合 AGENTS.md 判定标准)。用 archsetskill ji dodge 300 给野鸡技能(对应"用 admin 工具加速进战斗位置,但不绕过战斗本身"的授权 范围)后,kill ji 立即在几回合内通过真实伤害/破防判定(receive_wound 使 eff_kee 转负)杀死了 fluffos——die()DEATH_ROOM->start_death()→ 移动到 /d/death/gate(鬼门关)→ 5 秒后房间自身 init()/run() 拉入 /d/death/gateway(阎罗大殿,d/death/npc/yanluo.lpc)。

顺带验证了 §7.112 缺口修复(d64ace0,2026-08-18 提交,此前未在本文档 记录)yanluo.lpc's init() 现在有 query_temp("in_death_stage") 守卫,防止 enable_commands()(每次 reconnect() 都会广播)叠加出第二条 独立 death_stage() call_out 链。本轮实测(含一次断线重连穿过鬼门关房间) 只观察到唯一一条完整的五段对话链("喂新来的"→"兵荒马乱"→"翻账册"→"阳寿未 尽"→"罢了罢了你走吧"),无重复/错乱叙事,reincarnate() 后正确按 combat_exp<20000 分支送到 /d/slwg/zoulang1(石龙武馆走廊,而非 REVIVE_ROOM)——修复在真实死亡流程下工作正常。

排查过一个疑似 bug、确认为无害死代码,未改动feature/damage.lpc die() 结尾 DEATH_ROOM->start_death(this_object());——全库搜索确认 start_death 从未被定义在任何文件(/d/death/gate.lpc 本身没有这个函数, 房间的死亡流程其实完全靠自己的 init()/run() call_out 完成,和 start_death 无关)。担心这是每次玩家死亡都会触发的未定义函数调用,但两次 实测死亡全程 debug.log 和玩家会话都没有任何"Undefined function"报错或 异常输出——这是 MudOS/FluffOS 系语言的标准行为:通过 -> 调用一个目标 物件上根本不存在(未声明)的函数会静默返回 0,不会抛出可捕获的运行时错误 (区别于"声明了原型但没有函数体"的情况)。没有编译错误/运行时错误/driver 拒绝信号,按 AGENTS.md 判定标准不算 bug,是纯粹的死代码/冗余调用,未修改。

结论:本轮是本库第一次真正打通"真实战斗指令 → 真实伤害判定 → 真实死亡 → 鬼魂对话 → 真实重生"全链路,全程用 goto/setskill 等合法 admin 工具 加速就位,但战斗判定、伤害结算、死亡分支、重生房间选择全部走真实游戏逻辑, 未绕过。debug.log 除已知的、与本次测试无关的 /d/gaoli/npc/xiake 背景 daemon "Read access denied"(此前几轮已记录,job 系统内容缺口,非本次死亡 链路触发)外无新增错误。测试角色为已有的 fluffos 管理员账号(复用而非新 建,因死亡本身不需要新角色;确认 env/immortal 未设置,纯合法弱身位承受 了真实死亡)。

标准清单核对(全部通过,本库属于"已修复完好"一类):

测试结束后按精确 PID kill 掉 driver;fluffos 存档(data/user/f/ fluffos.odata/login/f/fluffos.o)与本轮死亡测试连带触碰的 data/orgroom/baling.o(帮会/门派房间状态,战斗/重生流程的正常副作用) 一并 git add -u 提交,均为合法游戏状态变化,非测试垃圾。

§7.100 扫描修复(ROOM 基类多余 replace_program()

#define ROOM "/std/room":删除 687 处多余的、独立成行的 replace_program(ROOM);(保留 inherit ROOM;);686 处由脚本 自动删除,另 1 处在 obj/roommaker.lpc 第 137 行——本库房间建造 工具的"克隆我所在的房间"命令用字符串拼接 str += "\n\tsetup();\n\treplace_program(ROOM);\n}\n"; 把同一枚 多余调用烤进了每一个新克隆的房间,已同步手动修正为 str += "\n\tsetup();\n}\n";(该工具另一套"造一间空房间"heredoc 模板本来就是干净的)。修复后全库仅剩 82 处历史遗留的 //-注释掉实例,均确认无害、未改动。已用 build-debug 驱动干净 启动验证(0 个新增编译错误,端口 40043 正常监听,debug.log 无 新增 "cannot replace"/"cannot bind" 行);未做完整 §10.7 深度 游玩测试。

§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-09-04,round three,shop + 拜师)

新角度:石龙武馆兵器铺购物 + 净念禅院虚尘拜师。2026-07-25 第二轮只做了 钱不够的 buy 拒绝和教头负例(「既不属於任何门派…不能拜师」),正例 拜师没走完。2026-08-20 第四轮是野鸡战斗→死亡→阎罗重生,留下管理员 fluffos 存档 ghost 1last_damage_msg 被野鸡杀害)。本轮端口 40043。第一输入是英文 id(没有 GB 选单)。密码 Mud@2026

实测过程

管理员 fluffos / Mud@2026(权限 (admin))。上一轮死亡留下的鬼魂 状态下,full 只回 kee/sen/食物饮水,不清 ghost。鬼魂 living() 为假,所以 apprentice xu 会先打出虚尘源码里那句抢跑的「你成为了净念 禅院的俗家弟子!」,再被 cmds/std/recruit.lpc 以「没有办法行拜师之 礼」拒绝,score 仍是普通百姓 / 还未拜师学艺。call me->reincarnate() 后头衔不再是鬼魂,买东西和拜师才走得通。

goto /d/slwg/bingqipu(石龙武馆兵器铺,铁匠 d/slwg/npc/tiejiang.lpcF_VENDOR_SALE)。买法是 buy <物> from <人>,不能光 buy swordlist 长剑一两银子。buy sword from tiejiang 成功(「你向铁匠买下一把 长剑」),银子 99→98。长剑不进 autoload。

goto /d/chanyuan/wuchang,虚尘(d/chanyuan/npc/xuchen.lpc)。鬼魂那 次失败会把 pending/apprentice 留在身上,再 apprentice xu 只得到 「你想拜虚尘为师,但是对方还没有答应」,不会重跑 attempt_apprenticeapprentice cancel 之后再拜:磕头、「恭喜您成为净念禅院的第八代弟子」。 score 职称「净念禅院第八代弟子」、师承虚尘。cmds/usr/save.lpc 在 60 秒内再 save 会假报「档案储存完毕」却不写盘;本轮用 call me->save() 落盘。杀驱动冷启动再登录:称谓/师傅/一两黄金/九十八两银子都在,长剑不 在。虚尘只收男性、拒已婚、PKS≥5、杀气≥100。学院老师不收徒;阴癸派 dizi2create_family 但没有 F_MASTER/attempt_apprentice;朱玉 雁只收女弟子。

发现并修复的 PROGRAMMING bug

1. 鬼魂收徒失败后 pending/apprentice 不清理cmds/std/recruit.lpc): !living(ob) 分支已经告诉双方「没有办法行拜师之礼」,却既不收徒也 不 delete_temp("pending/apprentice")。玩家还阳后同一目标再 apprentice 会卡在「对方还没有答应」,必须先 apprentice cancel。 该分支补上 ob->delete_temp("pending/apprentice")。现场 update 后 用 cancel + 重拜验证通过。

虚尘 attempt_apprentice()command("recruit") 之前就 tell_object 「你成为了净念禅院的俗家弟子」——鬼魂路径下这是假成功提示。recruit 修好以后活人路径会立刻真收徒,这条抢跑提示不再误导,未改 NPC 台词。