Twin Dragons of the Tang Dynasty

✅ 可玩

大唐双龙传 (Twin Dragons of the Tang Dynasty)

dtsl

🔑 fluffos / Mud@2026 更新 66a3bd8 2026-10-28 源码 下载 ZIP

▶ 开始游玩 · Play Now

取材自黄易同名武侠小说,以隋末唐初风云变幻的江湖为背景。本站收录的 dtsl、dtsl2、dtslmud 三份档案实为同一套"大唐双龙"世界观内容——逐档比对发现三者的地图与剧情文件(58 个分区、约 5,100 个文件,涵盖石龙武馆、净念禅院、阴癸派、突厥、东溟派、慈航静斋、海沙帮、高丽等)99%以上字节级一致,真正的差异在于各自独立的服务器与安全层实现(登录流程、密码规则等),而非各自独立创作的剧情。新角色从"大唐学院"起步,下山(`down`)即进入石龙武馆——新手枢纽区,提供银行存取、`qiecuo` 安全切磋、NPC 教学式学艺(`xue`),以及经由各门派入口 `bai` 拜师入门的完整新手引导。

English

Based on Huang Yi's wuxia novel of the same name, set in the jianghu as the Sui dynasty collapses and Tang rises. This collection holds three separate uploads of what turns out to be essentially the SAME authored game world: a file-level check found dtsl's own domain tree (58 zones, ~5,100 files -- including 石龙武馆/Stone Dragon Martial Hall, 净念禅院, 阴癸派, 突厥, 东溟派, 慈航静斋, 海沙帮, 高丽, and dozens more) is 99%+ byte-identical to sibling dtsl2's and ~99% identical to dtslmud's -- the three archives are different server/security-layer implementations (distinct master.c/logind.c, registration flows, password rules) wrapped around nearly the same content, not three independently-written storylines as earlier framed. New characters begin at the Great Tang Academy and descend (`down`) into 石龙武馆, the newbie hub offering banking, a room-scoped `qiecuo` safe-sparring verb, organic NPC-taught skills (`xue`), and `bai`/`apprentice` sect-joining through the hub's own sect-gate exits. This archive's own passwords are 5-8 characters with no complexity requirement (siblings enforce stricter rules).

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/dtsl/

管理员账号 / Admin account

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

本地运行

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

游戏端口:40008。

NOTES · 移植与修复记录

dtsl.rar → dtsl

Status: DONE — boots clean, playable over telnet

Booted with zero fixes needed on the first attempt (matching lineage to lib #1, and lib #1's proactive checks — no load_object in valid_read/valid_write, get_root_uid() present — all held here too).

What was fixed (all from the lpcc sweep, not the boot path)

1. Encoding: 6870 converted, 327 already UTF-8/ASCII, 377 skipped binary, 4 lossy. .c→.lpc: 6521 files, 1037 refs auto-fixed. static→ nosave: 87 files. 2. F_UNIQUE macro was referenced (inherit F_UNIQUE;) by ~91 clone item files but never #defined in include/globals.h, despite feature/unique.lpc (the file it should point to) existing — added #define F_UNIQUE "/feature/unique.lpc" alongside the other F_* macros. Fixed all ~91 files' "unexpected L_IDENTIFIER" errors in one shot (misleadingly, the error pointed at the NEXT line's inherit F_UNIQUE, since the undefined macro parses as a bare identifier where a string/path was expected). 3. 6 clone/weapon/*.lpc legendary-weapon files (jin-sjian, shizi, feihuangshi, jinhua, sjian, feidao) redundantly inherit COMBINED_ITEM; directly AND via inherit THROWING; (which already inherits COMBINED_ITEM itself) — a diamond that this driver's stricter nomask conflict detection rejects (Illegal to redefine 'nomask' function '_query'/'_delete'/'_set'/'weight'), where an older/ more permissive driver apparently just deduplicated it silently. Fixed by deleting the redundant top-level inherit COMBINED_ITEM; line (kept the one reached transitively via THROWING). Gotcha while fixing: these files use CRLF line endings — a sed '/^inherit X;$/d' silently no-ops on a line ending ;\r\n because $ doesn't absorb the \r; needed /^inherit X;\r?$/d. 4. clone/weapon/sm_ding.lpc (7th file in the same cluster) has a DIFFERENT, unrelated problem: #include "/inherit/item/embed.h", which doesn't exist anywhere in this archive — genuine missing content, not fixed (not fabricating a header).

Known remaining issues (documented, not fixed)

How to run

cd libs/dtsl
~/src/fluffos/build-debug/src/driver config.fluffos
python3 ../../scripts/mudclient.py 127.0.0.1 40008 --timeout 10 --send "" --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 config's scary default error message ("你发现事情不大对了,但是又说不上 来。") to the connected (non-wizard) player for every compile-time diagnostic funneled through APPLY_LOG_ERROR — including harmless "Unused local variable" warnings triggered by the first lazy compile of an ordinary room/NPC the player merely walked into (e.g. /d/job/...'s job_money.h-included files, /cmds/std/look.lpc, etc). In a real registration+play session this fired dozens of times per minute of walking around — same bug class already documented and fixed on wuhanzhan/ shenzhou (§15af). Fixed identically: only show the default-error message when message does NOT contain the substring "warning:" (still logs every diagnostic to the per-file log either way; only the in-band player-visible alarm is gated). Re-verified with a fresh registration (real name 秦北) followed by look/score/quit — zero spam, zero real error: lines in debug.log.

Post-hoc fix: UTF8-native is_chinese/registration (AGENTS.md §15h)

Applied in a later batch pass across the whole project: is_chinese/is_chinese2 in the shared chinese.lpc simul_efun fragment used GBK byte-range checks that silently never match real Chinese text once strings are UTF-8 (this driver's str[i] returns a Unicode codepoint, not a GBK byte). This broke character registration specifically -- any real Chinese name was rejected. Fixed the range check to test the CJK Unicode block instead, and halved the GBK-byte-calibrated length bounds in check_legal_name to match. See AGENTS.md §15h for the full writeup; confirmed via a real interactive registration test (Chinese surname + given name reaching the next prompt).

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

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

Same "大唐双龙" simple-lineage shape as sibling dtsl2 (byte-identical adm/daemons/band.lpc/sited.lpc) -- checked directly rather than assumed:

Admin account: id fluffos / Mud@2026 / 浮浮, registered through the normal flow (id -> confirm y -> Chinese name 浮浮 -> password x2 -> email skipped -> gender m -> stat allocation 20 20 20 20 -> confirm yes), granted (admin) via adm/etc/wizlist (fluffos (admin) line added, existing fengfei/kouzhong/ttj/zhujiepo entries kept) before registration, so wizard status was active immediately ("作为巫师,你可以迅速离线" shown on the very first quit). Verified on a SEPARATE re-login: password accepted, update /d/newbie/door succeeded ("重新编译 /d/newbie/door.lpc:成功!") -- unlike sibling dfgs2, this lineage's restore path has no permission bug, re-login just works. Save files (data/user/f/fluffos.o, data/login/f/fluffos.o) are plain untracked paths, not covered by any .gitignore pattern -- a normal git add libs/dtsl/ picks them up, no force-add needed.

Retest: fresh normal registration (id ceshijiu, name 秦鹏, female) reached 大唐学院, look correct, clean quit. debug.log clean across both driver runs this pass (only expected boot-time config dump and SIGTERM-on-kill lines -- the visible "编译时段错误" warning spam during fluffos's OWN session is expected/correct: this lib's log_error() fix (see above, 2026-07-23 pass) shows full diagnostics to wizards and only gates the generic scary message for non-wizards, and fluffos is a wizard). Two driver instances started/killed by exact PID; test character ceshijiu's saves removed afterward (data/user/c/ ceshijiu.o, data/login/c/ceshijiu.o), fluffos's kept. Note: this lib's data/user/+data/login/ trees already contained other untracked leftover test saves (e.g. q/ shard, from this lib's own earlier qinnuo/qinao verification passes documented above) predating this session -- left untouched as out of this pass's scope.

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

First real *playthrough* pass on this lib (all prior passes verified only registration + look/score/quit + admin login, or watched boot output). Read doc/help/newbie in full first -- it named the fight-vs-kill distinction, the bai/skills(cha)/xue teaching loop, and dazuo/tuna cultivation up front, matching the methodology's prediction that it's the fastest way to learn the intended path. Played as an ordinary new player through registration, exploration of the d/slwg (石龙武馆) newbie hall, the lib's own room-scoped qiecuo safe- sparring command, the organic paid-teacher xue path (both a failing and a successful attempt), the bai/apprentice sect-join mechanism, a real sleep/wake cultivation cycle, a real quit, a real prompt net-dead reconnect, and a real 15-minute (NET_DEAD_TIMEOUT) unclean- disconnect wait to force the force-quit path, all on the native driver (build-debug). Found and fixed two bugs, both new classes for this project's catalog: a command-dispatch duplication bug (Bug 2, found first) and, from the mandatory real 15-minute wait, a critical tell_room() type-error bug (Bug 1) that disables the net-dead force- quit safety net entirely and was observed to crash the whole native driver process outright -- see Bug 1 below for the full story; it is by a wide margin the more important finding of this pass.

Test character (kept, not cleaned up, as representative playthrough evidence): id qintian, Chinese name 秦天 (male), password Qt12345. State: received the newbie 礼官 gift (combat_exp 6000, +1 to all four attributes), sparred a 弟子 via qiecuo at 石龙武馆's 练武场, withdrew and spent savings on a (gated-out, already-capped) xue jiao force lesson, survived a full quit→save→relogin round trip with all of the above state intact, and was the character used to reproduce and then re-verify the bug below across a prompt net-dead reconnect, a sleep/ wake cycle, and the full 15-minute net-dead force-quit path. Saves: work/data/user/q/qintian.o, work/data/login/q/qintian.o (state as of the pre-Bug-1-investigation clean quit, since the later crash meant no further save occurred for this id). Secondary throwaway characters created to isolate Bug 2 (ceyanzc/测验者, shuimian/沉睡) and to reproduce/verify Bug 1 (ceyanzc again, plus wangce/网测 and zongjian/总检, and goumai/购买者 for the shop-purchase check, all password Ab12345) were left in place rather than risk clobbering evidence mid-investigation -- their saves are harmless, ordinary fresh-registration state.

Bug 1 (CRITICAL, NEW bug class): a 2-argument tell_room() call inside user_dump()'s net-dead force-quit path crashes with an uncaught runtime error, permanently disabling the 15-minute net-dead force-quit safety net -- and, worse, appears to be the trigger for an actual DRIVER-LEVEL CRASH (full outage) observed live

File:line of the fix: adm/simul_efun/message.lpc's tell_room() (line 47-54). Directly reached from obj/user.lpc's user_dump() (line 182-183), the NET_DEAD_TIMEOUT-driven call_out handler that force-quits a player who never reconnects.

`` 执行时段错误:*Bad argument 4 to EFUN message() Expected: object, array, Got: int(0). 程序:/adm/obj/simul_efun.lpc 第 49 行 呼叫来自:/obj/user.lpc 的 user_dump() 第 182 行 ... 调用参数:tell_room("秦天断线超过 15 分钟,自动退出这个世界。\n", NULL) ` A few driver ticks later, reconnecting as either character crashed the entire native driver process with a fatal C-level abort (not an LPC-catchable error) -- md: debugmalloc: attempted to free non-malloc'd pointer ... inside dealloc_object()/free_svalue(), triggered while indexing a mapping whose value was a destructed object reference (assign_svalue()'s destructed-object auto-null path at interpret.cc:3792`), captured in the driver's own crash backtrace (printed to the driver's redirected stdout at the time -- not preserved as a repo artifact, this is a description of what was observed live, not a file left behind). This took the whole MUD down for every connected player, not just the two net-dead characters -- categorically the most severe finding of this pass, and of this project's round-two testing effort to date.

``lpc // BEFORE: message("tell_room", str, ob, exclude); // AFTER: if (exclude) message("tell_room", str, ob, exclude); else message("tell_room", str, ob); ` This is the single, root-cause fix point for the whole shared-root- cause bug -- it doesn't require touching any of the 80+ individual call sites, since the type violation only ever happens inside tell_room()`'s own forwarding call.

Bug 2 (NEW bug class): every enable_player() call beyond the first silently stacks a duplicate command-dispatch sentence, so any FAILING command runs its side effects more than once

File:line of the fix: feature/command.lpc's enable_player() (~line 79-105). Root cause is distributed across several call sites, none of which look buggy in isolation: adm/daemons/logind.lpc's enter_world() (~line 554, a direct user->enable_player() call, immediately followed a few lines later by user->setup(), which itself calls enable_player() again via std/char.lpc:50) and cmds/std/sleep.lpc's wakeup() (lines 43/61/67, three unconditional calls in a row on every single sleep-and-wake cycle).

`` 你现在的学费是每次三文铜板,请备好零钱。 你现在的学费是每次三文铜板,请备好零钱。 武馆教头说道:您太客气了,这怎么敢当? ` Nothing in debug.log — no error, no warning, zero log signal, the single hardest-to-notice shape in this catalog (matches the framing of §7.20's "zero signal in debug.log" but via a completely different mechanism). A player would have to be looking closely at the *exact* wording of a rejection message to ever notice; a resource-costing success path (a rejection with a side effect already applied before the failure return, exactly learn.lpc`'s shape here) would silently cost twice what it should, with no error to grep for afterward.

- qintian, immediately after a fresh login: a single failing xue jiao force now prints the tuition message exactly once. - ceyanzc/测验者, fresh registration → xue jiao force (fails, single message) → withdrew savings → xue jiao force again (succeeds, tuition charged exactly once, correctly then gated by the *separate*, correct "已经无法再进步" skill-cap check, not a regression) → full sleep/25s-real-wait/wake cycle → look/ score/xue jiao force all immediately functional afterward (the regression's exact repro scenario), single dispatch confirmed throughout. - shuimian/沉睡, an independent control character, used specifically to catch the living()-guard regression (which it did) and then to confirm the remove_action() fix resolved it (which it did). debug.log clean (no error:/Too deep recursion/Undefined function) across every iteration of this investigation.

What was tested and confirmed working

Not verified live (explicitly flagged, not silently skipped)

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

此前被错误标记为某个不存在于本项目任何档案的原始压缩包文件名(dtsl.rar)的 duplicate_of——那份"兄弟"上传显然从未被真正转档过,所以这份 7z 重新打包版是这款游戏唯一存活、完全独立的副本。duplicate_of 已清除。

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

Deep functional test round two (2026-08-14)

Independently re-verified against current code rather than trusting the round-one writeup above. This is by a wide margin the highest-stakes lib on this session's re-test list — round one's Bug 1 was a driver-crash- class finding — so the priority this pass was re-confirming it live, at real (accelerated) accuracy, not just checking the diff.

Re-verified live at accelerated timing: Bug 1's tell_room() fix (the driver-crash-class §7.12 finding) still holds

Confirmed the if (exclude) message(...); else message(...); fix is still present in adm/simul_efun/message.lpc, then re-ran the same accelerated methodology round one used: temporarily changed include/user.h's NET_DEAD_TIMEOUT from 900 to 20 (restored to 900 immediately after, confirmed via git diff returning empty), rebooted, logged in as fluffos, raw-killed the tmux session running its telnet connection (genuine socket drop, not quit) to trigger a real net-dead, waited a real 26 seconds past the shortened timeout. Result: work/log/debug.log's line count was unchanged before/after (no Bad argument 4 to EFUN message(), no anything), the driver process stayed alive (ps-confirmed by exact PID), and reconnecting as fluffos landed in a genuinely fresh enter_world() — confirmed by the "上次连线" (last-connect) banner showing the *previous* login's timestamp rather than a stale in-memory resume, proving user_dump()'s save()+destruct() path actually ran to completion this time. Did not re-attempt the two-simultaneous-character double-free reproduction from round one (a one-time, already-rigorously- documented confirmation of a downstream C-level consequence of the same root cause) — re-triggering the LPC-level root cause and confirming it no longer throws is sufficient to confirm this specific fix still holds.

Re-verified: Bug 2's enable_player() duplicate-dispatch fix still holds

Code-confirmed the remove_action("command_hook", "")-before-add_action() guard is still present in feature/command.lpc, with its explanatory comment intact.

New fix, and a near-miss: adm/simul_efun/file.lpc's log_file()/cat() hardening

Standard §7.11-class gap: cat() had no null-guard, log_file() had no assure_file() guard. First attempt at this fix defined log_file() (which calls assure_file()) BEFORE assure_file()'s own definition with no forward declaration — this driver does NOT tolerate forward references within a single file the way some LPC drivers do, and it broke the ENTIRE boot (Error: Undefined function assure_file inside /adm/simul_efun/file.lpc, cascading to No program in object '/adm/obj/simul_efun'! — nothing loads, not just this one function). Caught immediately by checking the boot log rather than assuming success; fixed by adding a void assure_file(string file); forward declaration before log_file(), matching the pattern already used correctly on this session's other libs (bxsj/dfgs2/dfgsiiv13b) — this lib's file.lpc just happened to have log_file() textually before assure_file() in the source, which those others didn't.

Checked and confirmed already correct, no action needed: log_error()'s severity gate already uses the case-agnostic strsrch(message, "arning:") check; no §8.9 food/water wrong-object read; the one printf("%O\n", ob) in logind.lpc is already commented out (dead code, harmless).

Observed, not investigated: an unrelated wizard-channel message during idle time

【系统】系统核心:/std/char/npc.lpc 第 11 行,物件:/d/gaoli/npc/xiake *Read access denied. appeared once, unprompted, during an idle stretch between commands (some background/heartbeat-driven NPC action hitting an ACL check). Not connected to either fix above, not a crash, no player- facing impact observed — flagged for whoever does a full playthrough pass on this lib next, not chased further this pass since it's outside this pass's scope (re-verifying round one's fixes plus the standard checklist).

Verification method

Booted native build-debug driver, admin login (fluffos/Mud@2026), update /adm/daemons/logind as the real privileged-action check (succeeded). Two rapid consecutive admin reconnects, both clean. Driver killed by exact PID after each reboot (three total this pass: initial boot, accelerated-timeout boot, final clean-config boot); incidental fluffos.o save-timestamp churn reverted before commit.

Files modified this pass

Round three deep functional test (2026-08-18)

Went past round one/two's scope on purpose: this pass targeted the items round one/two explicitly left "not verified live" (a full sect-join round trip, a successful funded shop purchase, real combat death/respawn) plus systems neither prior round touched at all (bulletin boards). Also confirmed the adm/obj/master.lpc §7.111 standard_trace() fix from today's corpus sweep wasn't disturbed (not independently re-exercised, since nothing in this pass triggered a driver-level error until the bug investigation below, whose repro's own driver-level-adjacent trace stayed clean throughout — see Bug 3).

Method: booted native build-debug driver; ran two-connection live sessions (admin fluffos + fresh test characters) via a raw Python socket harness (hard wall-clock recv budgets per AGENTS.md §10.7, no reliance on quiet-detection). Used goto/summon/force/clone/ give (all real cmds/imm|wiz|adm commands) to reach content round one/two's time budget didn't stretch to.

Confirmed working, no bug: full sect-join round trip

Test character shenceng/深层 (male, fresh registration) was summoned to 净念禅院's d/chanyuan/wuchang (虚尘's room) and forced to run apprentice xu chen. Full chain fired correctly end-to-end: cmds/std/apprentice.lpcmain() → xuchen.lpc's attempt_apprentice() (gender/family/couple/PKS/bellicosity gates, all satisfiable by a fresh male character) → command("recruit " + id) → cmds/std/recruit.lpc → feature/apprentice.lpc's recruit_apprentice()/assign_apprentice(). score afterward correctly showed title 【俗家弟子】, 称谓 "净念禅院第 八代弟子", 师傅 "净念禅院 虚尘". This closes round one's "not verified live" item — no bug in this chain.

Confirmed working, no bug: economy (funded purchase)

Used a genuine two-connection live session (admin + shenceng, both connected simultaneously) rather than force for this one, since cmds/std/give.lpc's do_give() gates on interactive(who) (an offline/netdead recipient falls through to an NPC-only accept_object() call_other, which silently no-ops for a player target — confirmed this is why an earlier force-based give gold to shenceng attempt failed with "对方不要你的东西" while shenceng was disconnected; not a bug, give to an offline character is a reasonable design gate, not chased further). With shenceng actually connected: admin cloned /obj/money/gold and give-gave it, then shenceng ran buy sword from tiejiang (d/slwg/bingqipu) — correct purchase, correct automatic denomination change (2两黄金 → 长剑 + 99两银子), 讨价还价 skill ticked up. Closes round one's "not verified live" item — no bug.

Confirmed pre-existing, not new: the round-two-flagged xiake "Read access denied"

Round two observed, but didn't investigate, an unprompted *Read access denied. on /std/char/npc.lpc:11 for object /d/gaoli/npc/xiake. Traced this pass: adm/daemons/jobmond.lpc's do_start_job() (its own periodic job-posting/NPC-restocking heartbeat, independent of any player action) tries to move() a job NPC into one of a hardcoded room list including /d/gaoli/qinglong-1, and xiake.lpc's create()carry_object()s a weapon/armor via new() under an ACL context that gets denied. Confirmed this is the exact same jobmond.lpc room-list content gap already documented for sibling dtsl2 (not this lib's own new finding) — fires from jobmond's own independent heartbeat during ordinary boot/idle time, unrelated to any of this pass's live test paths. Left unfixed, as already established for the sibling.

Bug 3 (NEW bug class, FIXED): reconnecting while a character sits in the death/ghost sequence stacks a duplicate death_stage() call_out chain, misdirecting the revival room and risking a double-applied death penalty

File:line of the fix: d/death/npc/yanluo.lpc's init() (~line 51) and death_stage() (~lines 61, 78).

- Interleaved/duplicate narration is the visible-but-harmless symptom. - The real damage: EACH chain independently runs the terminal branch (item-drop loop, lose() PK/combat_exp penalty, and the zuolao/combat_exp/zoulang1 room-move decision) when it reaches its own final stage. Whichever chain finishes LAST calls ob->reincarnate() a second time, which the earlier-finishing chain(s) already did — meaning by the time a stale second chain checks !ob->is_ghost() at the top of death_stage() (a check meant to catch a non-ghost object wandering in by mistake), ghost has *already* been reset to 0 by the first chain's completion, so the stale chain takes the "you're not actually a ghost, teleporting you to REVIVE_ROOM" shortcut (lines 61-70) unconditionally, silently overriding whatever room the real chain's combat_exp/zuolao branch had already (correctly) chosen. A character with nonzero combat_exp reconnecting mid-sequence could additionally have lose()'s PK/exp penalty applied more than once.

Confirmed working, no bug: bulletin board post/read/list

Tested at 净念禅院's obj/board/party_cy_b.lpc (placed in d/chanyuan/miaodoor via its own setup()'s move(query("location")) — the room's "...party_cy_b.lpc"->foo() call is a harmless no-op, foo() is undefined anywhere in the inheritance chain, same silent-0 call_other idiom as attempt_apprentice() on a non-recruiter NPC, already documented in round one; it's there only to force the singleton board to compile-load once at room-create() time). list correctly showed a pre-existing real player note; post <标题> (4 Chinese chars, even length, per do_post()'s gate) correctly invoked the line editor (me->edit(...), . to end); the new post was correctly appended and read 2 rendered it back with correct author/time/title/body formatting. No bug.

Verification method / cleanup

Two native driver boots this pass (initial, then post-fix). Both killed by exact PID after readlink /proc/<pid>/cwd confirmed this lib's work directory. Incidental data/user/f/fluffos.o + data/login/f/fluffos.o (admin account) and data/orgroom/baling.o (unrelated background-daemon resave, pure key-reorder no value change) save churn reverted before commit via git checkout -- on those specific files only. Kept as evidence: shenceng/深层 and chongshi/重试 test character saves (both data/user/ and data/login/), and the real board-post content change in data/board/party_cy_b.o + data/board/all_post_b.o (the aggregate board mirror, which legitimately picked up the same new post). work/log/debug.log never existed at any point this pass (no runtime errors triggered outside the Bug 3 investigation itself, which is LPC-catchable and doesn't write to debug.log when caught cleanly by the top-level dispatcher). Two harmless "Too deep recursion" lines appeared once in boot.log during the very first cold boot's daemon- preload sequence (adm/simul_efun/file.lpc:32 / adm/obj/master.lpc:396 — log_file()→assure_file()→mkdir()→valid_write()→SECURITY_D mutual bootstrap ordering during the earliest part of preload, before SECURITY_D itself is fully loaded), self-resolved (Initializations complete. still printed, never recurred during ~40 minutes of live testing afterward) — flagged here, not investigated further, since it's boot-time-only and unrelated to every live path this pass exercised.

Round four re-test (2026-08-19): full catalog checklist, clean — no new bugs

Re-checked this lib against the current AGENTS.md catalog items that didn't exist (or weren't corpus-swept) when round one/two/three ran, plus a fresh full §10.7 playthrough with a brand-new test character. All items came back clean; no code changes this pass.

Fresh §10.7 playthrough (test character huice/秦回, male, password

Hc123456)

work/log/debug.log never existed at any point this pass (no runtime errors triggered by anything tested) — the only anomaly in the boot log was the same pre-existing, already-documented jobmond.lpc/xiake *Read access denied. content gap from round two/three's own independent heartbeat, confirmed byte-identical, not a regression.

Cleanup

One native driver boot this pass, killed by exact PID after confirming readlink /proc/<pid>/cwd matched this lib's work directory. Incidental data/login/f/fluffos.o + data/user/f/fluffos.o (admin save-timestamp churn) reverted via git checkout -- before commit. Kept as evidence: huice's saves (data/user/h/huice.o, data/login/h/huice.o) and the real board-post content change (data/board/party_cy_b.o + data/board/all_post_b.o).

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

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

深度功能测试(round three, 2026-09-01)— clean, no new bug found

A genuinely different-angle pass, per the standing rule that "already tested" libs keep turning up new bugs when probed differently. Booted native build-debug (port 40008), used a raw Python socket harness (recv_for(seconds) fixed-wall-clock reads, no quiet-detection) rather than tmux_mud.sh. Five angles targeted, all came back clean:

1. Mail system: does not exist in this lib. The only mail-named files (adm/daemons/sendmaild.lpc, adm/daemons/network/mail_serv.lpc, netmail.lpc, include/net/sendmail.h) are real internet-SMTP daemon plumbing (the sockets-package-gated networking subsystem, already documented elsewhere as WASM-incompatible but otherwise inert), not a player-to-player in-game mail command. Confirmed via grep across cmds/ for any add_action on "mail" — none. N/A, not a gap in testing coverage. 2. Board post/reply: this lineage's boards (std/bboard.lpc / obj/board/*.lpc) have no reply verb at all — only post/read/list/discard (confirmed via long()'s own usage text). post/read/list were already live-tested in round three; this pass added adversarial discard/read/list arguments (discard 999/-1/0/abc, read -5/abc, list -1/a 20-digit-overflow number) against 洛阳d/luoyang/zhulou's 花间弟子留言板 (/obj/board/party_hj_b) — every out-of-range or non-numeric case returned a clean notify_fail()/format-hint message, zero debug.log output. Also re-confirmed the §7.86 BULLETIN_BOARD-replace_program() corpus-sweep fix has no remaining instances (grep -rn "replace_program" | grep -i board returns nothing). One incidental finding, NOT a bug: a wizard-only post (bypassing the non-wizard 2-24-char/Chinese-only title gate, per do_post()'s own if (!wizardp(me)) {...} else if (i > 24) branch) left mid-edit() when the test connection net-deaded before the closing . — the pending post was correctly discarded on reconnect (no partial/corrupted post appeared), consistent with the already-documented §10.10 "an in-progress input_to()/edit chain doesn't survive reconnect" driver behavior, not a new defect. 3. Currency persistence across quit/reconnect (the exact angle that found fysjmb's severe dead-query_autoload() bug this session): checked std/money.lpc directly first — string query_autoload() { return query_amount() + ""; } (line 7) is already live, not commented out, unlike the Fengyun-lineage sibling family. This lineage is unaffected by that bug class. Live-verified anyway: test character ceshiq/测钱 withdrew 5 coins from 石龙武馆's 账房 (withdraw 5 coin), confirmed carrying 五文钱(Coin) via i, did a real quit (clean, no debug.log output), waited a real 12s, and reconnected — i and score's 存款/inventory both showed the exact same 5-coin cash and 10-coin deposit intact. No bug. 4. Adversarial/malformed input: a 10,000-character English "id" during registration, control-byte/ANSI-escape-embedded strings ("abc\x00\x01\x1b[31mdef\x07"), raw invalid-UTF-8 byte sequences (\xff\xfe\x80\x81, an encoded UTF-16-surrogate pair) and a 3,000-character valid-Chinese-codepoint string all fed into the id and Chinese-name registration prompts — every case was cleanly rejected by check_legal_id()/check_legal_name()'s existing length/charset gates (the NUL-containing string was silently truncated to its prefix, "abc", by the driver's own C-string handling before LPC ever saw it — a known telnet/socket-layer behavior, not an LPC bug) with zero debug.log output in any case. Mid-game: bai <nonexistent-npc-name>, xue <teacher> <nonexistent- skill-name>, buy with no args, a 5000-character item name, buy sword from <nonexistent-vendor>, and sell sword (no vendor context) all produced normal in-character "什么?"/"你要跟谁买东 西?"-style rejections, never a crash or debug.log entry. 5. §7.19-class reentrancy via enable_commands()/enable_player() reachable from an NPC's init(): grepped every enable_commands() call site in the lib. Only feature/command.lpc (the dispatcher itself), obj/user.lpc, d/death/npc/yanluo.lpc (round three's already-fixed §7.112 death-stage bug — re-confirmed the in_death_stage guard is still present at lines 73/74/83/101), and u/fengfei/object.lpc (a personal player-directory WIP file, already documented elsewhere as non-playable/out-of-scope content, not part of any real game path) call it. Every enable_player() call site funnels back through feature/command.lpc's own enable_player() (confirmed the round-two §7.16(Bug 2) remove_action("command_hook", "")-before-add_action() guard is still present, lines 108-109) — this defends against duplicate-dispatch regardless of how many unrelated call sites (feature/move.lpc's living-toggle logic included) redundantly invoke it. Live-reconfirmed with a real failing xue jiao force against ceshiq (single "缺乏实战经验" message, not doubled) that this fix has not regressed since round two.

Net result: no new bug found. This is an honest clean pass, not a forced one — work/log/debug.log never came into existence at any point across roughly a dozen driver interactions (registration, combat, economy, board edge cases, adversarial input, quit/reconnect), and the driver's own captured stdout showed only expected preload compile-warning spam. No code changes this pass; nothing to port to dtsl2/dtslmud.

Test character (kept, saves are ordinary fresh-registration state): id ceshiq, Chinese name 测钱 (male), password Cq123456. State: newbie gift received (combat_exp 6015, +1 str/con/dex), 5 coin cash + 10 coin deposit, one sparring session at 石龙武馆's 练武场. Saves: work/data/user/c/ceshiq.o, work/data/login/c/ceshiq.o.

Cleanup

One native driver boot this pass (PID confirmed via readlink /proc/<pid>/cwd matching this lib's work directory before kill). Incidental data/login/f/fluffos.o + data/user/f/fluffos.o (admin save, pure key-reorder, no value change) reverted via git checkout -- before commit.

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

深度功能测试(§10.7,2026-10-04)— paths named in another case

The archive comes from a case-insensitive host: files are stored as BAGUA0.lpc, longjing.C, d/SHAOLIN/, the code names them in lower case (__DIR__ "bagua0", "tea/longjing", "/d/shaolin/..."), so the rooms, NPCs and items behind them could not be loaded (a .C file is not an .lpc file; KB 03 §4.5). scripts/lpc_case_paths.py --convert --apply dtsl: 4 file(s) renamed to lower case (.C -> .lpc, .H -> .h) and 0 path reference(s) in 0 file(s) rewritten to the new spelling; 0 skipped, 0 ambiguous. Load check of the 7 objects that were renamed, edited or name a renamed path (HEAD against the working tree, an object is the same under any spelling of its path): 0 PASS -> 5 PASS, 0 that loaded before fail now, 5 more load.

4 files moved (4 .C); mostly under d/emeishan/npc (2), d/hangzhou/npc (1), d/luoyang/npc (1). A rescan with scripts/lpc_warnings.py dtsl is still due: the renamed files are compiled for the first time.

深度功能测试(§10.7,2026-10-05)— room base survives a raising NPC

scripts/lpc_resilient_room.py --apply: make_inventory() of std/room.lpc loads each inventory object as if (catch(ob = new(file)) || !objectp(ob)) return 0;. new() of a file that is not there returns 0 (the room just lacks that NPC), but new() of a file whose create() raises (a skill file for set_skill() this archive never had, a carry_object() item that is not there, a header that never shipped) raised inside the room and the room did not load at all. The wrong-case renames of this lib (the section above) make the NPCs behind them reachable for the first time, so a room that holds one with such a content gap would have gone from "NPC missing" to "room missing"; now it loads without that NPC, as it did before. The room base compiles and loads with the change (HEAD against the working tree). KB 03 §4.5.

深度功能测试(§10.7,2026-10-05)— tail() simul_efun

scripts/lpc_add_tail.py --apply: cmds/arch/tail.lpc calls tail(file), an efun of the MudOS this archive ran on that FluffOS does not have (Undefined function tail: the command did not compile). adm/simul_efun/file.lpc now defines void tail(string file): the last 10 lines of the file, written to the player. Load check (HEAD against the working tree): 1 of the 1 tail command file(s) failed to load at HEAD, all load now, and the simul_efun file loads. KB 04 §6.2.

深度功能测试(§10.7,2026-10-05)— 房间列出的物件缺档时房间照常加载

房间基类 std/room.lpc:reset() 的 case 1: 拿到 0 就跳过这一项(if (!objectp(ob[list[i]])) break;)。档案里没有的 NPC 或物品(或 create() 出错的物件)原先会让列出它的房间整个加载失败(Bad argument 1 to environment(),或对 0 的 call_other()),连带这个房间后面的区域;现在房间照常加载,只是少了那个物件,下次 reset() 再试(KB 05 §7.25;scripts/lpc_resilient_room.py、scripts/lpc_room_reset_guard.py)。 验证:探针(scripts/lpc_room_reset_probe.py:房间基类列出一个不存在的物件,再调用 reset())HEAD 单件 崩溃、多件 OK,工作树 单件 OK、多件 OK。

深度功能测试(§10.7,2026-10-07)— 正午的天气消息和刺杀任务的杀手

改动的文件在 HEAD 与工作树分别加载(新进程)通过。

深度功能测试(§10.7,2026-10-07)— 编译警告整理

scripts/lpc_warnings.py dtsl --fix 加 scripts/lpc_fix_no_effect.py --apply,并删去 adm/daemons/logind.lpc 没用到的 string ip = query_ip_number(ob);,共改动 286 个 .lpc 和若干 .h。HEAD 与工作树分别加载(新进程):265 -> 265 通过,无回退;改过的头文件的 245 个包含者 203 -> 203 通过。

启动时警告 23 -> 0;新进程登录(新人物确认)正常。

深度功能测试(§10.7,2026-10-08)— .lpc 后缀的定宽切片

.c 改名 .lpc 时只改了字符串没改切片宽度:f[<2..] == ".lpc" 拿两个字符比四个字符,永远不成立(!= 永远成立),按后缀筛文件、去后缀的代码因此从不运行或截成 x.l(KB 06 §7.238)。scripts/lpc_lpc_suffix_slice.py --apply 改了 1 处比较、0 处去后缀(如 cmds/wiz/clone.lpc);改动的文件 HEAD 与工作树分别加载(新进程),无回退。

负数伤害当作消耗(2026-10-15,KB 06 §7.241)

receive_damage("neili", -52)、receive_damage("qi", -12) 之类把 add() 的负数写进了 receive_damage():本库 feature/damage.lpc 对负数伤害报错(或归零),练功/运功时报 F_DAMAGE: 伤害值为负值 或消耗失效。已改为正数伤害,内力/精力类改为 add(type, -N)(scripts/lpc_fix_damage_calls.py):/d/clone/biwutai/biwutai、/d/clone/room/leitai/leitai。

内力/精力当作伤害种类(2026-10-15,KB 06 §7.241 第二类)

本库 feature/damage.lpc 的 receive_damage() 不接受 jingli/neili/force 等种类,me->receive_damage("jingli", 30) 之类在练功、绝招、吸取时报 F_DAMAGE: 伤害种类错误。6 处改为 add(type, -N)(scripts/lpc_fix_damage_calls.py 第二步)。

其他体系的属性名(2026-10-15,KB 06 §7.241 第三类,--any-file)

24 个文件(技能、绝招、兵器、书籍、区域 NPC 等)来自侠客行体系(qi/jing/neili),而本库人物是 gin/kee/sen/force:receive_damage("qi", ...) 报 伤害种类错误,query("neili") 永远为 0。按 qi→kee、jing→gin(本库学习/读书指令消耗的就是它)、neili→force、max_neili→max_force、jiali→force_factor 改名(scripts/lpc_port_es_props.py --any-file)。

内力/上限当作伤害种类(补)(2026-10-15)

victim->receive_wound("force", n, me)(兵器吸内力)、receive_damage("max_qi", random(100))(走火入魔扣上限)等本库不接受的种类,9 处改为 add(type, -N)。

治疗/疗伤的属性名(2026-10-15,KB 06 §7.241)

receive_heal()/receive_curing() 也只接受本库的种类。1 个文件(多为只调用 receive_heal/receive_curing 的药物、NPC)来自侠客行体系,按 qi→kee、jing→本库学习指令消耗的池、neili→force 改名(scripts/lpc_port_es_props.py --any-file)。

NPCs carrying items this lib never had (2026-10-15, KB 06 §7.73)

68 statements in 45 files did carry_object("<missing file>")->wear() (or wield(), set_amount()) in create(): carry_object() returned 0, the call raised Bad argument 1 to EFUN call_other() and the NPC never loaded (most common: /clone/misc/cloth, /u/smokes/junying/obj/blade, /d/city/obj/cloth). Each is now a guarded block (scripts/lpc_guard_carry_object.py): the NPC loads without the item, and carries it again if the file comes back. Load check: 0 of the changed files loaded before, 18 after (18 newly), no regressions.

Removed predefine SAVE_EXTENSION (2026-10-18, KB 04 6.11)

Older MudOS predefined SAVE_EXTENSION (".o") in every LPC object; the FluffOS ChangeLog (COMPAT_BUSTER) removed it in favour of __SAVE_EXTENSION__. This lib never #defines it, so each file that names it was Undefined variable 'SAVE_EXTENSION' and the object never loaded. Now __SAVE_EXTENSION__ (scripts/lpc_fix_predefined_macro.py): adm/daemons/network/netmail.lpc, d/zhiye/obj/old-zhujianlu.lpc, obj/token.lpc. Loaded one by one with lpcc after the change: 2 pass, 1 still stop on another error (adm/daemons/network/netmail: /adm/daemons/network/netmail.lpc:17: error: Cannot #include mailer.h).

set_skill() skips a skill the lib has no file for (2026-10-18, KB 06 7.256)

set_skill(), map_skill() and prepare_skill() of feature/skill.lpc raised error("F_SKILL: No such skill (...)") on a name without a skill file, which aborted the calling NPC's create(): the NPC never loaded and the room that spawns it lost it. 99 objects of this lib set a skill it has no file for (120 names, most often pojun-quan, sanhua-zhi, taizu-quan). The three now return: the skill is not granted or mapped and the object loads without it (the missing skill itself stays a content gap). Loaded a sample of these objects: 24 of 30 pass, the other 6 stop on another error (clone/npc/dzd_man: *No program in object '/clone/npc/dzd_man'!; clone/npc/tdh/fangdahong: *No program in object '/clone/npc/tdh/fangdahong'!, ...). (scripts/lpc_guard_set_skill.py)

Exits to rooms that do not exist, repaired from the room on the other side (2026-10-18, KB 06 7.257)

A room exit named a file the lib does not have, so the player was stuck (a typo, a renamed room, a numbered room the archive lacks). The room at the other end exists and its opposite exit points back, so that is where the exit was meant to go: /d/cahc/jinshuiqiao north: wangfu -> tongyuchi. Every repaired exit now resolves to a file of the lib (scripts/lpc_fix_dead_exits.py; libs whose virtual rooms load anyway are left alone).

Base classes the lib did not ship, restored from sibling libs (2026-10-18, KB 04 6.15)

These base classes are not in the lib, so every object that inherits them (rooms, NPCs, items) failed to load (inherit XXX; with no macro, or inherit "/std/..."; with no file). A sibling lib carries the file and shares many files byte for byte with this one, so the file is copied as it stands, and the missing macro is added to include/globals.h: /inherit/weapon/club (macro CLUB) <- bxsj, 9 object(s) inherit it, sample 9/9 load; /inherit/weapon/hook (macro HOOK) <- bxsj, 3 object(s) inherit it, sample 3/3 load; /inherit/weapon/brush (macro BRUSH) <- bxsj, 2 object(s) inherit it, sample 2/2 load; /inherit/weapon/weapon_combo (macro WEAPON_COMBO) <- xkxc98sj, 2 object(s) inherit it, sample 2/2 load. Each base was loaded alone and a sample of its users after it; a base that did not load, or none of whose users did, was taken back (scripts/lpc_restore_bases.py).

Objects that call an inherited function no parent has (2026-10-19, KB 06 7.259)

::create();, ::init(); and return ::buy_object(who, item); were written against a parent that has the function; this lib's parents do not, the old driver took the call for a no-op returning 0, and the current driver stops at compile time with Unable to find the inherited function, so the object never loads. A call written as a statement is deleted, return ::f(...) becomes return 0; (return; in a void function), which is what the old driver did. 2 calls in 2 files: ::ob_hit() (2). Only files whose every error was this one were edited; a file that also has another error (usually a parent that is itself missing) is left until the parent is restored. (scripts/lpc_fix_dangling_super.py)

simul_efun 丢了 unew()(2026-10-19,KB 06 §7.262)

adm/simul_efun/object.lpc 没有 unew()(带“独一无二”次数限制的 new():物件的 unique 值是允许同时存在的份数),而官府 NPC 和几件武器按 xkx 一系的写法调用 unew(...),于是 Undefined function unew,连同它们赋值给 object 变量的那一行都报错,整个物件载入不了。从同系的 xkxz2 把这个函数原样补回 object.lpc 末尾(scripts/lpc_transplant_function.py),受益物件 4 个:/clone/npc/guanfu/shi-qingshan、/clone/weapon/bestfalun、/clone/weapon/gold-falun、/clone/weapon/sword/jwqin。

Macros the global header lost (2026-10-19, KB 04 6.16)

/include/globals.h did not define NAME_D, IP_D; the objects that use them (2 on the list) stopped at Undefined variable/Undefined function, and since the compiler stops at the first undefined name the errors and warnings behind it stayed hidden. The sibling libs of the same lineage carry these lines in their headers: the copy of the sibling with the most files in common was added after the last #define of /include/globals.h; every path in a macro body exists in this lib. 2 of the listed objects load now, the rest need something else. (scripts/lpc_restore_defines.py)

Headers next to the including file, quoted (2026-10-19, KB 04 6.17)

2 files wrote #include <area.h> for a header that sits in their own directory (map.h, qipu.h ...). The old driver searched the includer's directory for an angle-bracket name; this one searches only the include path, so each stopped at Cannot #include .... Rewritten to #include "area.h" (resolved against the includer everywhere: preload, lpcc, run-time compiles). (scripts/lpc_fix_local_include.py)

RANK_D asked about nobody (2026-10-19, KB 06 §7.273)

4 function(s) of adm/daemons/rankd.lpc (query_respect(), query_rude(), query_self(), query_self_rude()) now start with if (!objectp(ob)) ob = this_object();. NPCs build "…" + RANK_D->query_respect(this_player()) + "…" in create() (an inquiry mapping, a chat_msg array) and the driver's own reset() runs with no command giver, so this_player() is 0. The old driver answered 0->query("age") with 0 and the title fell to its default branch; this driver raises Bad argument 1 to EFUN call_other(), the room's make_inventory() swallows it and a killed NPC never comes back. Found by scripts/lpc_reach_census.py --walk; fixed in the daemon, not in the 1,800 NPC files.

Object paths with two leading slashes (2026-10-19, KB 06 §7.276)

2 file(s), 2 site(s) wrote an object path as //… (like carry_object("//clone/cloth/hic_cloth")): /d/gaoli/npc/weishi.lpc, /d/gaoli/npc/xiake.lpc. The driver these games ran on did not mind (the path was normalised first); this one hands the string to the master's valid_read() as written, securityd looks up //clone, the NPC's euid has no entry and the answer is Read access denied.: the NPC does not load. One slash now.

x != "a" || x != "b" and x == "a" && x == "b": comparison chains of one value that can never differ (2026-10-20, KB 06 7.282)

if (arg != "wan" || arg != "pill") return notify_fail(...) is true whatever arg is, dir == "south" && dir == "north" is never true: the guard under the test never opens, or never closes. The author meant "neither of these" (&&) or "either of these" (||). != chains joined by || became && (only where the statement under the test is a return, a refusal or a guard clause), == chains joined by && became || (scripts/lpc_fix_cmp_chain.py). Changed: /cmds/adm/log.lpc (1).

Every call_out_info() entry has three elements (2026-10-20, KB 06 7.287)

The driver answers ({ object, function, delay }) without the call_out's arguments, so code that reads the fourth element info[i][3] (the callouts command, calloutd, the cod tool) died with Array index out of bounds on the first line of the list. Now (sizeof(info[i]) > 3 ? info[i][3] : 0) (scripts/lpc_fix_callout_info.py). Changed: /cmds/arch/callouts.lpc (1).

if (x = CONST): a comparison written as an assignment (2026-10-20, KB 06 7.290)

if (arg = "button" || arg = "pill"), if (!arg || arg = "") return 0;, else if (check = 0 && tempB != ""): = was typed for ==, so the condition is the constant: the branch is always taken for a non-zero number or any string (even "") and never for 0, and the variable is overwritten; the driver does not warn. Every such = became == (scripts/lpc_fix_cond_assign.py, 15 terms). Changed: /adm/daemons/competed.lpc (1), /clone/npc/mj.lpc (3), /cmds/std/duanzao.lpc (8), /obj/mj.lpc (3).

callouts: the guard sizeof(info[i]) == 4 hid every row (2026-10-20, KB 06 7.287)

The driver's call_out_info() entries have three elements, so the guard in front of the output line was never true and the command printed only its header. Now >= 3. Changed: /cmds/arch/callouts.lpc (1).

A login password over 255 bytes: crypt() raises (2026-10-20, KB 06 7.294)

The driver's crypt() raises (Error in crypt(), check your salt) for a key over 255 bytes, where the old driver hashed any length. A password of 256 bytes or more typed at the login prompt threw an error in the password check and left the connection at a bare > prompt, so the login never completed (the old-password test of password the same); found by the sweep shape of 300 x. /adm/obj/simul_efun.lpc now defines a simulated efun crypt(key, salt) of the same name: only an over-long key is cut (to 255 characters, then character by character until sizeof(to_buffer(key)) <= 255: the limit is in bytes, a CJK character takes three) before it is handed to efun::crypt(); every ordinary password hashes as before and the saved hashes are untouched (scripts/lpc_add_sefun.py crypt).

valid_read() does not trust the simul_efun object's euid "NONAME" (2026-10-20, KB 06 7.295)

Too deep recursion. twice at boot (log_file() -> assure_file() -> file_size() -> the master's valid_read() -> SECURITY_D->valid_read() denies the sefun object, whose euid is "NONAME", and the denial is logged with log_file() again), and on every later log_file(): no log file was ever written. valid_write() already trusted "NONAME"; valid_read() now does too (securityd.lpc).

howmany_visitor() reads the visitor counter unguarded (2026-10-20, KB 06 7.301)

howmany_visitor() reads the visitor counter file (VISITORS / COUNTER / BBSVISITOR, written only by a running mud; .gitignore keeps it out of the repository), a fresh tree has none, read_file() answers 0 and sscanf(0, ...) raises. This copy may not reach it at login, but raises when something calls it. if (!stringp(content)) return 0; after the read (the form xkx100, xiakexing100 and zzfy already had; scripts/lpc_fix_visitor_count.py). Changed: /adm/daemons/logind.lpc, /adm/daemons/logind1.lpc, /feature/logind.lpc.

get_config(__MUD_PORT__) through the old runtime_config.h is a string (2026-10-20, KB 06 7.302)

This lib's include/runtime_config.h has the old MudOS numbering (integer settings from 15, this driver from 256), so get_config(__MUD_PORT__) reads another setting (a string) and (int) is only compile-time bookkeeping: socket_bind(fd, LOCAL_PORT()) raises Bad argument 2 to socket_bind(), the version daemon's port = get_config(__MUD_PORT__) + VERSION_PORT is a string sum. The header's own comment says get_config(__MUD_PORT__) == __PORT__, so the call (with the (int) / to_int() around it) is now the driver's predefine __PORT__ (scripts/lpc_fix_mud_port.py). Changed: /include/getconfig.h (1).

NPC 的 chat() 在 chat_msg 为空数组时下标越界(2026-10-21,KB 06 §7.334)

NPC 基类的 chat() 是 if (arrayp(msg = query(...))) { if (random(100) < chance) { rnd = random(sizeof(msg)); if (stringp(msg[rnd])) ...,不检查列表是否为空。本库现有的 NPC 文件里没有字面写成空 chat_msg 的(扫描只认 set("chat_msg", ({ })));守卫挡的是运行时才为空的列表和以后新增的 NPC。在 random(100) < chance 前面加 sizeof(msg) &&(scripts/lpc_fix_chat_empty.py;if (random(100) > chance) return 0; 的写法是 !sizeof(msg) ||)。改动:/std/char/fighter.lpc、/std/char/jobnpc.lpc、/std/char/npc.lpc、/std/char/npcbak.lpc(共 4 个文件)。

add_action() registrations of functions nobody defines (2026-10-21, KB 06 §7.349)

An add_action("fn", "verb") whose function is defined neither by the object, nor by its headers, nor by its parents, nor by any object derived from it (scripts/lpc_scan_callout_targets.py, descendants checked transitively, then function_exists() in a running driver, scripts/lpc_verify_handlers.py). The driver walks the actor's sentences from the newest one: when that sentence's function does not exist it raises *Function for verb 'drop' not found. and tries no other handler of the verb (an older dead sentence is passed over silently). Removed 32 registrations in 20 files (scripts/lpc_fix_dead_add_action.py), the verbs are left to their other handlers: /clone/npc/flowergirl.lpc (do_buy, do_list), /d/changan/npc/cha.lpc (do_teach), /d/chanyuan/npc/buchen.lpc (do_answer), /d/chanyuan/npc/buju.lpc (do_answer), /d/huashan/xiaqiting.lpc (do_pass, do_resign), /d/luoyang/npc/duguce.lpc (do_fenglu, do_shengzhi), /d/quest/poison/yangxu-yan.lpc (do_teach), /d/quest/toutianjianfa/mazei.lpc (do_ok), /d/xiangyang/npc/cha.lpc (do_teach), /d/xiangyang/npc/dutu.lpc (do_start) and 10 more files (20 files). Most frequent: do_buy x10, do_list x10, do_teach x3, do_answer x2, do_pass x1. Left alone (read): /clone/npc/huolang.lpc do_buy (the object does not compile; the registration waits until it loads), /clone/npc/huolang.lpc do_list (the object does not compile; the registration waits until it loads), /d/changan/npc/guanchai.lpc do_ok (the object does not compile; the registration waits until it loads), /d/chengdu/npc/guanchai.lpc do_ok (the object does not compile; the registration waits until it loads), /d/jiujiang/npc/guanchai.lpc do_ok (the object does not compile; the registration waits until it loads), /d/luoyang/npc/guanchai.lpc do_ok (the object does not compile; the registration waits until it loads), /d/xiangyang/npc/guanchai.lpc do_ok (the object does not compile; the registration waits until it loads).

add_action() registrations of a base whose descendants define the handler: the base itself raises when loaded on its own (2026-10-21, KB 06 §7.349)

A base class (a room, an NPC, a board) registers add_action("fn", "verb") in init() for a function only its descendants define; when the base object itself is loaded (a room one can walk into, an NPC that is cloned) its visitors get *Function for verb 'x' not found. (the first scan passed these over as alive because a descendant defines the handler; scripts/lpc_verify_handlers.py on the base object showed function_exists() MISSING). The registration now stands behind if (function_exists("fn", this_object(), 1)) (flag 1: function_exists() treats protected / private functions as not defined by default); descendants register as before. 1 statements in 1 files (scripts/lpc_fix_dead_add_action.py --guard): /obj/sboard/sboard.lpc (do_appoint).

.. in an object path: new(__DIR__ "../obj/x") raises Read access denied (2026-10-22, KB 06 §7.351)

__DIR__ is the directory of the file with a trailing slash, so __DIR__ "../obj/x" is /d/zhiye/npc/../obj/x; the driver's legal_path() refuses a .. component and new() / load_object() raise *Read access denied. (measured; find_object() answers 0). This lib's NPC / room bases do not normalize the path first: the NPC fails in create() and cannot be cloned (the room's objects table silently loses it). Rewritten as the normalized absolute path (scripts/lpc_fix_dotdot_dir.py): /d/zhiye/npc/caikuang-shi.lpc (__DIR__ "../obj/tieqiao" -> "/d/zhiye/obj/tieqiao"), /d/zhiye/npc/caiyao-ren.lpc (__DIR__ "../obj/yaochu" -> "/d/zhiye/obj/yaochu"), /d/zhiye/npc/fangzhi-nu.lpc (__DIR__ "../obj/fangsuo" -> "/d/zhiye/obj/fangsuo"), /d/zhiye/npc/yangcan-popo.lpc (__DIR__ "../obj/jiandao" -> "/d/zhiye/obj/jiandao") and 2 more (6 uses).

new(MISSING)->move() / carry_object(MACRO(..))->wear() chains guarded (2026-10-22, KB 06 §7.73 addendum)

A statement new(PATH)->move(...); / carry_object(ARMOR_D("x"))->wear(); whose path names no file of this archive: new() answers 0 and ->move() on it raises Bad argument 1 to EFUN call_other(). In create() the object never loads, in reset() every spawn below the first missing one is skipped on every reset. 3 statement(s) in 2 file(s) (new) are now { object new_ob = new(PATH); if (new_ob) new_ob->move(...); } (scripts/lpc_guard_carry_object.py --new: the path is resolved with the macros the file itself sees, an ambiguous macro or a virtual-object path is left). Load check of the 2 changed files, HEAD vs tree: 1 load at HEAD, 1 load now (0 newly loading, 0 regress).

Calls to functions this lib's own daemon lacks (2026-10-22, KB 06 §7.354)

MACRO->func() named a function that the lib's copy of the daemon does not define: call_other() of a missing function answers 0 without a word, so the feature (a clock display, the time-of-day events, a name check, a price text ...) never worked. The same daemon of a sibling lineage has the function; it is copied, text and all, to the end of this lib's daemon (scripts/lpc_port_function.py; every function / global / macro / string key the body uses exists here and the call sites pass as many arguments as the function takes, checked; the daemon loads at HEAD and in the tree, and the function called once with a canned argument answers 0 at HEAD and a real value now): /adm/daemons/chinesed.lpc <- bxsj: chinese_time() (9 call sites).

Object paths without a leading slash: guarded chains for a missing item (2026-10-22, KB 06 §7.73 addendum)

A path written without the leading slash (carry_object("d/city/obj/jiasha")->wear();) is root-relative in this driver (new("d/city/obj/changjian") loads /d/city/obj/changjian), so it behaves like the absolute spelling: when the file is not in the archive new() answers 0 and ->wear() on it raises Bad argument 1 to EFUN call_other(), and the whole object does not load. The earlier guard pass skipped these paths as "relative to the cwd at run time: cannot tell". 2 statement(s) in 1 file(s) (d/city/obj/flower_shoe, d/city/obj/pink_cloth) are now guarded blocks (scripts/lpc_guard_carry_object.py). Load check of the 1 changed files, HEAD vs tree: 0 load at HEAD, 1 load now (1 newly loading, 0 regress).

log/ subdirectories nobody created: the logging write_file() / log_file() raises (2026-10-22, KB 06 §7.358)

The code of this lib writes logs into 1 subdirectory of log/: log/guoku (d/luoyang/npc/zhao.lpc). Neither the archive, a git clone nor the site pack has them (no local boot wrote into them, so gen_keep_dirs.py never recorded them), and write_file() raises *Wrong permissions for opening file /log/dlog/money for append. "No such file or directory" for a missing directory: the function that logs stops on that line and what came after it (the reward, the flag, the next step) never runs. This lib's log_file() makes the directory itself (measured, or read from the source: the nitan family's assure_file()), so only the directories written by a direct write_file() / save_object() are listed. log/guoku are now in scripts/wasm_keep_dirs.txt (scripts/lpc_keep_log_dirs.py; the site and the pristine tree create the empty directories from it). Proof (scripts/lpcc_eval.py, an eval object under /adm/daemons or similar writes /log/zzdirprobe/..., fresh processes): the measured table in KB 06 §7.358.

GBK byte offsets in string ranges never became character offsets: 27 site(s), the sect rename "澄" + name[2..3] and the generation test name[0..1] == "澄" among them (2026-10-22, KB 06 §7.360)

The archive counts string ranges in BYTES (a Chinese character is 2 bytes): name[2..3] was the second character of a name, name[0..1] == "澄" asked whether the name starts with 澄. After the conversion to UTF-8 this driver's ranges and strlen() count CHARACTERS (strlen("澄观") is 2, "澄观"[0..1] is both characters), so the old arithmetic stays and means something else: a player who joins a Shaolin / Wudang / Emei ... master is renamed wrongly (the two-character name 张三 becomes the one-character 澄, a longer name keeps its third character); the masters' generation tests (name[0..1] == "澄") never hold; the rename registrar's length gates count bytes, so no valid name passed.

This lib: 27 site(s) in 7 file(s): name assembled from slices 6, comparison with a Chinese literal 13, rename registrar gates 7, two name slices compared 1. Examples: newname[2..3] -> newname[1..1] (/d/chanyuan/npc/bonze.lpc:80); newname[0..1] -> newname[0..0] (/d/chanyuan/npc/xuchen.lpc:196); name[2..3] -> name[1..1] (/d/clone/room/lingwu/chanyuan/liaokong.lpc:119). scripts/lpc_fix_gbk_slice.py recomputes the indices in characters (the community's UTF-8 port xkx100utf8 carries the same text: the oracle); left alone: truncations (name = name[0..7] now allows 8 characters, only wider), ASCII ranges (fname[0..3] == "room", ctime(time())[4..19]) and ranges whose context does not show Chinese text. Proof (scripts/lpcc_eval.py, bixiecanyang; the other libs carry the same text): a Shaolin master's attempt_apprentice() of a visitor named 张三 gives 慧 at HEAD and 慧三 after; a 28-lib sample of the HEAD / tree load comparison shows 0 regressions. The tool is not idempotent (halving [0..3] gives [0..1], aligned again): the rewritten files are in scripts/gbk_slice_converted.txt, do not re-run it on them.

Length gates written in bytes next to messages written in characters: 16 site(s) (2026-10-22, KB 06 §7.362)

The archive's strlen() counts bytes (a Chinese character is two), this driver's counts characters: a check whose message says 2 to 6 Chinese characters is still strlen(name) < 4 || strlen(name) > 12, so a two- or three-character house, gang, group, room or title name is refused by the message that says it is allowed and the upper bound is twice what the text says; a board's title check i < 4 || i > 24 || i % 2 also refuses every odd-length title. This lib: 16 site(s) in 6 file(s): if ((strlen(name) < 4) || (strlen(name) > 10)) { (/adm/daemons/logind1.lpc:618); if ((strlen(name) < 2) || (strlen(name) > 8)) { (/adm/daemons/logindd.lpc:183). scripts/lpc_fix_strlen_gate.py takes the message as the oracle: every strlen gate of the condition must be exactly twice the number the message states before it is halved; the gates of a length variable (i = strlen(arg);), the || i % 2 parity term and the i % 2 == 0 && of the loop that follows go with it; a message that names English letters or bytes is left. Proof: see the bxsj NOTES (a board post with an NPC that records edit(): HEAD refuses a two- and a three-character title and accepts a 23-character one, the tree the reverse).

按字节写的汉字循环、茶馆猜诗和同盟频道标题(2026-10-23,KB 06 §7.363)

老档按字节做「一个汉字」:while (i--) if (i % 2 == 0 && !is_chinese(name[i..<0])) 只检查偶数下标,奇数位上的拉丁字母(张a)会过;茶馆猜诗 mixup() 用 strlen(str)/2 再 j=j+j、str[k..k+1] 交换两字节;泥潭系 channeld 的 switch (strlen(fname)) case 4/6/8/10 和 strlen(family)==4/6 按字节长度给同盟 / 门派名加空格。本驱动的 strlen() 和区间都按字符算。scripts/lpc_fix_byte_char.py:去掉奇偶项、把区间改成 name[i..i],按字符重写 mixup() 并把 strlen(first)>=14 折成 7,频道标题的 case / 门派长度改成字符数(对照 aoxiangtianji)。本库 汉字循环 / is_chinese 区间 10 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。

从末尾按字节取汉字、书剑开帮长度(2026-10-23,KB 06 §7.364)

§7.360 只折了数字下标的 [a..b],从末尾取的 [<2..<1](GBK 下一个汉字)和 [<4..<1](两个字)还留着:开帮 gcreate 的帮助写「二至六个汉字 / 3到8个英文字母」,检查却是 (strlen%2)==1 || <4 || >12,末字对 ({ "帮","会",… }) 用 gname2[<2..<1] 永远对不上;少林 / 峨嵋法名 "明" + name[<2..<1]、死讯 killer[<2..<1] != "。"、兵器改名的 can_name 同样坏掉。scripts/lpc_fix_tail_slice.py 只改汉字语境(对照非 ASCII 字面量、can_name / valid_group_name、拼名字),ASCII 补零 / ctime / 复数词尾 / .lpc 不动;[<2..<1]→[<1..<1],[<4..<1]→[<2..<1],开帮门槛按帮助改写。本库 从末尾切片 2 个文件。证明(scripts/lpcc_eval.py bxsj):HEAD 上 斧头帮[<2..<1] 对不上「帮」;改后 [<1..<1] 对上;长度放行 斧头帮/fut;"明"+"张三"[<1..<1] 得「明三」。

血条 / 表情标题按字节算显示宽度(2026-10-23,KB 06 §7.365)

老档把汉字当两字节:bar_string[0..(val * N / max) * 2 - 1] 把「格数」换成字节下标,半格血条在本驱动(按字符算区间)上会整条画满;巫师表情 edemote 的 repeat_string("─", 13 - (strlen(emotion) / 2)) 也会少减汉字宽度。scripts/lpc_fix_display_pad.py:去掉 CJK 血条 / 空白条上的 * 2(jings/qis 赋值和 blank_string3[0..(23-a)] 的 23→11 一并改),表情标题去掉 / 2;ASCII 空格的 none_string / empty_string 仍保留 * 2(一格两个空格)。本库 4 处(2 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。

examine 缺 sefun process_bar/log_time/filter_color/all_interactive(2026-10-27,KB 04 §6.16 Also)

cmds/arch/examine.lpc 调用 process_bar / log_time / filter_color / all_interactive,本 lib 的 simul_efun 未纳入 util(同系 zjdy2008wzb 有)。对象无法编译。新增 adm/simul_efun/util.lpc(#include <ansi.h> + 上述四个函数,all_interactive 为 efun::users()),并在 adm/obj/simul_efun.lpc 中 #include。证明:HEAD 加载失败;改后加载成功。

深度功能测试(2026-10-27,§6.25 one-arg message_vision)

message_vision("…") 补第二参(me / TP / this_player())。

深度功能测试(2026-10-27,§6.10 未知 pragma save_binary/optimize)

删除本库中 FluffOS 不认的 #pragma save_binary / #pragma optimize… 行。