Dead Souls 3.8.6 (fluffos/dead-souls fork)

✅ 可玩

DeadSouls-FluffOS2019

deadsouls_fluffos

更新 1309d15 2026-10-22 源码 下载 ZIP 上游 fluffos/dead-souls

▶ 开始游玩 · Play Now

`fluffos` GitHub 组织自己维护的 Dead Souls 3.8.6 fork (<https://github.com/fluffos/dead-souls>),已经预先适配现代 FluffOS (`v2019` 驱动分支)——不是从老式压缩包提取的传统档案,是这批语料库里少数几个"英文原版、本身就来自 FluffOS 生态"的收录对象之一。

English

The fluffos GitHub org's own actively-maintained fork of the classic Dead Souls 3.8.6 mudlib, pre-adapted for the modern FluffOS v2019 driver branch (tracked as a git submodule) rather than a legacy archive dump -- a separate, independently-sourced snapshot from this collection's other two Dead Souls entries (ds386, dshakkard), already carrying its own AUTO_WIZ registration, a websocket listener, and a www/ web client. New characters begin in "The start room" near the Praxis campus, then `enter town` to reach the main city; playable races are dwarf, elf, half-elf, half-orc, halfling, human, orc, and poleepkwa. The town/campus/Praxis/amigara/learning/Ylsrim/cave/examples domains are Dead Souls' own official "how to build an area" teaching material, and AUTO_WIZ lets a new registrant answer "creator" instead of "player" to get builder access immediately, with `read chapter 1 in handbook` as the suggested first step.

README

本馆只保留说明和启动脚本。 work/ 是 fluffos/dead-souls submodule(mudlib 根目录 work/lib)。LPC 在那个仓库里是 .lpc + §9 格式化——修复请提交到那个仓库,不要在本馆再改一份。OLD_ED 驱动兼容 shim(ed_compat.lpc)和管理员试玩号在 overlay/。

和 ds386 的关系

本项目还收录了另一份 Dead Souls——libs/ds386,来自一份完全独立的、 老式压缩包归档,代表的是未经 FluffOS 生态改造的原始 Dead Souls 3.8.6 快照。两者按本项目"同一游戏不同来源快照各自保留独立 slug"的一贯做法 分别收录(参见 bxsj/bxsj1、xiyouji 系列等先例),并未合并或去 重。这一份的显著特征:仓库自带 driver/ git submodule 跟踪 fluffos/fluffos 的 v2019 分支、已经内置 AUTO_WIZ(注册时可直接 选择成为创造者)、websocket 监听支持、以及一套 www/ 网页客户端—— 是"已经被 FluffOS 生态本身认领和现代化"的一支。

内容说明

WASM / 驱动兼容说明

上游仓库使用的是"现代"编辑器 efun 组合 (ed_start()/ed_cmd()/query_ed_mode()),而本项目共享的驱动 构建是以 OLD_ED(服务语料库里占多数的老 MudOS 时代 mudlib)编译 的,这三个 efun 在这个驱动构建里并不存在——直接导致玩家类 (/lib/player.lpc)完全无法编译。已经用纯 LPC 从零实现了一套自包含 的兼容 shim(secure/sefun/ed_compat.lpc,覆盖经典 ed(1) 命令子 集),详见 NOTES.md 的完整说明。另外还发现并修复了一处影响面很广 的真实 bug:.c→.lpc 改名后残留的定宽切片错误,最严重的一处让 全局命令派发表(daemon/command.lpc)把每一条命令都注册错了 文件名(多出一个 .l 尾巴),导致 score 等所有走标准命令派发路径 的指令全部报错——已经系统性排查并修复了同一根因的全部 ~30 处实例, 详见 NOTES.md。

在线试玩

本地测试端口如下。

管理员 / creator 账号

overlay/ 带了一个试玩创造者号:shopdive / Mud@2026。Dead Souls 本身没有「第一管理员」引导,建造权限仍可通过两条路径获得:

1. AUTO_WIZ(推荐):注册新角色时,选定种族后会被问到 "作为玩家还是创造者加入",直接输入 creator 即可在登录后不久自动 被提升为创造者。 2. groups.cfg:secure/cfg/groups.cfg 里已经把账号名 sunyc 写入了 SECURE/ASSIST/ELDER/TEST/INTERMUD/ TELNET/EMOTES 等全部权限组——把这个名字改成你要用的账号名 (小写),或者新增一行,即可让该账号获得对应权限,无需走 AUTO_WIZ 流程。

若要正式对外开放主机,请根据实际需要调整 groups.cfg 的账号名单。

本地运行

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

游戏端口:40207。

NOTES · 移植与修复记录

deadsouls_fluffos — Dead Souls 3.8.6 (fluffos/dead-souls fork)

来源:fluffos/dead-souls submodule at 4e07e92(leftover 686: .lpc + §9 formatted on that repo;mudlib root work/lib; command-cache / last()/truncate() slice widened for .lpc)。 OLD_ED shim and the shopdive playtest seed are in overlay/。 端口 40207。状态:done(干净开机,注册流程全通, look/score/quit 均验证通过)。

leftover 686: work/ is the fluffos/dead-souls submodule

hosting is fluffos-upstream。work/ 现在是 fluffos/dead-souls gitlink(pin 4e07e921a7a9d841faeb81e3ccbc49612215d537),不再是 本馆第二份格式化树。config.fluffos mudlib directory 指向 work/lib。patches/ 保持为空——改名、§9 format、以及 NOTES item 1 的 .lpc 定宽切片(file[0..<3] → [0..<5]、 last(X,2) == ".lpc" → last(X,4))都在那个 fluffos-org 仓库上。 Catalog-only overlay:

Item 1 里「切片宽度是本馆改名副作用、不必上游」的旧判断已经过时: leftover 686 把 fluffos/dead-souls 本身改成了 .lpc,所以那边的 切片也必须是 4 / [0..<5]。

这是哪一份 Dead Souls

这是 fluffos GitHub 组织自己维护的 Dead Souls 3.8.6 fork (fluffos/dead-souls),已经预先适配现代 FluffOS(driver/ 作为 git submodule 跟踪 fluffos/fluffos 的 v2019 分支),不是从传统 压缩包提取的老档案。本项目同时收录了另一份独立来源的 Dead Souls—— libs/ds386,来自完全不同的、独立的老式压缩包归档,由另一位并发 agent 转换。两者按本项目"同一游戏的不同来源快照保留为独立 slug"的 惯例分别保留(参见 bxsj/bxsj1、xiyouji/xiyouji2003 等先例), 本次转换未尝试与 ds386 合并或去重。

已知的差异线索(仅基于本 lib 自身内容,未逐行比对 ds386):这份 deadsouls_fluffos 的仓库 README 本身就记录了"3.8.6 with FluffOS 2019"的适配历史,driver/ 子模块显式跟踪 fluffos/fluffos 的 v2019 分支,且仓库内已经内置 AUTO_WIZ、websocket 支持、以及一套 www/ 网页客户端——是一份"已经被 FluffOS 生态本身认领和现代化"的 分支,而不是从 MudOS 时代的原始压缩包直接抽取。是否与 ds386 内容 完全同源、只是适配层不同,留给未来统一协调的分支比较去判断。

转换流程说明(§2 的改编版)

源已经是 git 仓库、已经是 FluffOS 适配版,因此本次转换只做了 "验证 + 必要修复",而不是完整的§2/§3/§4 归档转换流水线:

发现并修复的真实 bug

1. 全局性:.c→.lpc 改名遗留的定宽切片 bug(§4.2 第 4 类),~30+ 处

convert_lib.sh 的引用修复只处理带引号的字符串字面量 (".c" → ".lpc"),不会、也不可能自动调整后续依赖"扩展名长度" 的定宽字符串切片/last()/truncate() 调用——原版对 2 字符的 .c 写的 str[<2..] == ".c" / last(str,2) == ".c" / truncate(str,2), 在把字符串字面量机械替换成 4 字符的 .lpc 之后,切片宽度仍然停留在 2,导致比较永远为假(2 字符切片不可能等于 4 字符字符串)、或者截断 只削掉了 2 个字符(留下 ".pc"残尾)。这不是这份 mudlib 自己的历史 bug,是这个项目自己的机械转换步骤的副作用,但只有在实际运行到对应 代码路径时才会暴露,编译期通常不报错(mixed/字符串比较不触发类型 检查)。

最严重的一处:/daemon/command.lpc 的 eventRehash()—— cmd = file[0..<3];——是全局命令派发表的构建函数,扫描 cmds/players/、cmds/creators/ 等目录下所有 *.lpc 文件建立 "命令名 → 文件路径"的映射。修好前,每一条注册进来的命令名都带着一 个残留的 .l 后缀(例如 /cmds/players/score.lpc 被注册成了命令名 "score.l"),导致所有走 command.lpc→CMD_D->GetCommand() 这 条标准派发路径的玩家/创造者/管理员命令全部无法被找到,运行时报 *call_other() couldn't find object '/cmds/players/score.l'.——玩家 看到的现象是"A runtime error occurred."。look/quit 之所以在修复 前仍然正常,是因为它们分别走 add_action()(动词系统)和 lib/interactive.lpc 自带的 action,不经过这条 CMD_D 派发路径, 因此在深度测试的 §8.4 检查项("score 才能真正跑到玩家类的完整代码 路径,look 未必够")里被专门捕获到——这正是这条经验教训本身描述的 情形。已修复:file[0..<5]。

同一根因,逐一确认并修复的其余 ~30 处(每一处都单独核实了变量来源 确实是 .lpc 文件名/路径,而不是无关的 2 字符字符串比较——例如 daemon/defines.lpc 里 last(str,2) != ".h" 是正确的,未改动):

修复方法:逐处人工核实调用上下文(确认变量确实来自 .lpc 文件名/ 路径,排除掉表面相似但语义无关的 2 字符比较,如 .h、"--"、 "%^"、"_D" 等),而不是无差别全局替换——grep -rn 'last([^,]*,\s*2)\s*[=!]=\s*"\.lpc"' 用于收尾核对,改完后确认为 0 命中。

2. ed_start()/ed_cmd()/query_ed_mode() 驱动兼容 shim

/lib/editor.lpc(被 /lib/interactive.lpc → /lib/player.lpc 间接 inherit,也就是每一个玩家角色对象都会 inherit 到)和 /secure/cmds/creators/lsed.lpc 调用的是 FluffOS core.spec 里 #ifdef OLD_ED 的 #else 分支——ed_start()/ed_cmd()/ query_ed_mode() 这一组"现代"编辑器 efun。本项目共享的驱动构建 (~/src/fluffos/build-debug)是以 OLD_ED 编译的(服务这批语料 库里占绝大多数的老 MudOS 时代 mudlib,那些用的是旧式回调型 ed()),因此这三个 efun 在这个项目自己的驱动构建里根本不存在 ——不是 mudlib 的 bug,是这个特定驱动构建选项和这份"更现代"的 Dead Souls fork 之间的兼容缺口。实测确认:ed_start(...) 在 lpcc 里报 Undefined function,导致 /lib/player.lpc 完全无法编译——这直接 挡住了每一个玩家角色的创建,是本次转换里编译层面最严重的一处阻断。

驱动侧的旧式 ed() efun 把原始连接输入直接交给驱动内部的编辑器状态 机接管(回调驱动,不是逐行可控的模型),从架构上无法用 LPC 包一层 兼容适配去模拟"modern ed_start/ed_cmd 那种逐行同步返回"的调用约定。 因此没有去 wrap 驱动的 ed(),而是在 secure/sefun/ed_compat.lpc 里用纯 LPC 从零实现了一个自包含的、不依赖任何驱动内部状态的行编 辑器,作为同名 simul_efun 提供:支持经典 ed(1) 的地址形式 (N/$/./+N/-N,N,N/N;N 范围)和 a/i/c/d/p/ l/w/q/Q/x/s///[g] 命令子集——覆盖了 Dead Souls 自己内部 两个调用者(editor.lpc 的 "$a"自动追加 + "."结束、 lsed.lpc 的脚本化编辑 + "x"/"Q")以及日常巫师交互式用法的 主要场景,但不是 src/packages/core/ed.cc 的逐字节复刻(没有正则 替换、没有 g 全局命令、没有撤销)。已在 secure/sefun/sefun.lpc 里 #include 接入,lpcc 单独验证 /lib/player、/lib/editor、 /lib/interactive、/lib/creator、/lib/nmsh、 /secure/cmds/creators/lsed 全部编译通过。

3. 缺失的 /open/ 目录导致两个巫师工具 daemon 的 create() 崩溃

/daemon/defines.lpc(defines 巫师指令,运行期动态生成一小段探测 代码来判断某个宏/符号是否已定义)和 /cmds/creators/types.lpc (同类思路的类型检查工具)在 create() 里都会无条件 write_file("/open/prog.lpc", ...),但整棵树里根本不存在 /open/ 目录(git 不追踪空目录,上游仓库这一个目录没有像其它好几十个空 目录那样配 .gitkeep)。目标目录不存在时 write_file() 的 fopen() 失败会触发未捕获的 error(),使这两个 daemon 的 create() 直接崩溃、对象整体无法加载(Wrong permissions for opening file /open/prog.lpc for overwrite. "No such file or directory")。修复:创建 work/open/.gitkeep(secure/daemon/ rooms.lpc、reaper.lpc、chat.lpc 等多处代码本来就把 /open/ 当作一个真实存在、受信任较低的巫师临时脚本目录在用,是 Dead Souls 自身的既有惯例,不是新引入的目录)。修复后两个 daemon 都能正常 create()、lpcc 单独验证通过。

Upstream note (2026-08-31): 上面 §1(.c→.lpc 定宽切片修复)和 §2(ed_start/ed_cmd driver-compat shim)均不上游——前者只是 本项目自己 .c→.lpc 改名步骤的副作用(上游仓库仍然全树用 .c, 不存在这个问题),后者是本项目共享驱动构建选用 OLD_ED 这个配置 选项造成的兼容缺口(本项目自己的驱动配置,不是上游 mudlib 的 bug)。§3(缺失 /open/ 目录)经审计确认是上游仓库自身的真实 bug (16 个其它必需空目录都配了 .gitkeep,唯独 /open/ 没有),已 filed upstream as PR #13: https://github.com/fluffos/dead-souls/pull/13。

4. domains/Praxis/supply2.lpc 的 set_pre_exit_functions 误用(比照姊妹文件的既有处理方式)

secure/include/compat.h 里 #define set_pre_exit_functions SetProperty 把这个旧式 API 名字映射到了签名完全不匹配的 SetProperty(string prop, mixed val)(单属性 setter),而 domains/Praxis/{hall,hall2,hall3,hall4,hall5}.lpc 全部 5 个姊妹 房间调用这个宏时传的是两个数组 (exits, funcs)——这 5 处调用在 上游仓库里全部已经被注释掉,只有 supply2.lpc 这一处还是活的、 未注释,编译报 Bad type for argument 1 of SetProperty ( string vs mixed * )。这是这份"更现代"的 upstream fork 自己遗留的一处内容层 不一致(不是本项目转换引入的),但既然 5/6 的姊妹房间已经用注释掉 达成了事实上的处理方案,为了让这个房间能编译,比照同样的处理方式把 这一行也注释掉,而不是去猜测重新实现这个从未真正工作过的旧 API。

Upstream note (2026-08-31): 确认这是上游仓库自身的真实 bug (supply2.c 在上游仍然是唯一一个未注释掉这行调用、因而编译失败的 文件),已比照姊妹文件的处理方式 filed upstream as PR #15: https://github.com/fluffos/dead-souls/pull/15。

已知但未修复的边角情况(不阻塞注册/核心玩法,记录备查)

以下 13 个文件在最终一轮 lpcc_check.sh --batch 里仍然报告 FAIL,均已逐一排查根因,均不在预注册流程(preload 列表)、新手房间 可达路径、或 look/score/quit 核心验证范围内,因此按 AGENTS.md "更深的内容层 bug 记录但不必修"的标准,记录而非强修:

跳过的步骤(因时间压力,记录以便后续补做)

验证记录

Sibling sweep of the dsIII §7.121 currency-float bug — confirmed present, fixed

AGENTS.md §7.121 documents a currency/economy bug found in dsIII (the Dead Souls 3.x lineage's shared secure/sefun/economy.lpc): query_base_rate(), query_player_money(), query_base_value(), and query_value() do real floating-point exchange-rate math internally but are declared to return int with no to_int() on the actual return, silently corrupting player currency into a float on every buy/sell/exchange (a declared int return type is compile-time only on this driver and never coerces a runtime float). deadsouls_fluffos shares this exact file byte-for-byte with ds386/pre-fix dsIII and had the identical gap, plus the same two sibling misses: lib/teller.lpc's eventExchange() (i = val / currency_rate(str2);) and lib/props/value.lpc's SetBaseCost() (Cost = i * rate;).

Fixed by wrapping all four secure/sefun/economy.lpc returns in to_int() (matching the convention already used by the file's own currency_mass()/currency_value()), plus the two call sites, exactly mirroring dsIII's fix. Verified live with a fresh AUTO_WIZ creator character (Qintestdsf), which let the check be done with eval as well as a real transaction rather than navigating the whole map: eval return query_value(23, "silver", "gold"); and a typeof() check both confirmed a clean int result (no residual float), and SetBaseCost() tested the same way. Then teleported (goto /domains/town/room/bank) to Zoe, opened a bank account, gave the character 100 silver via AddCurrency, and did ask zoe to exchange 23 silver for gold — printed a clean 2 gold with no decimal, and the raw save file after save showed Currency (["silver":186,"gold":2,]) — both integers, no float. log/debug.log stayed clean through the whole sequence.

Upstream note (2026-08-31): confirmed present byte-for-byte in fluffos/dead-souls upstream (secure/sefun/economy.c, lib/teller.c, lib/props/value.c), filed upstream as part of PR #12 (bundled with the two related float/int bugs documented below): https://github.com/fluffos/dead-souls/pull/12.

Sibling sweep of ds386's round-two eventRevive()/room.lpc/combat.lpc/beggar.lpc bugs (2026-08-27)

Per ds386/NOTES.md's "Deep functional test (round two, 2026-08-27)" section, checked the four flagged bug shapes here. All four present and fixed identically:

Committed only the four source-file fixes plus the new Qintestdfx test-creator account's realm/save files (following this lib's own precedent of keeping test-creator accounts, e.g. Qintestdsf above); RELEASE_NOTES_HTTP/daemon-state save churn from booting the driver was reverted first, not committed. Killed the test driver by exact PID when done.

Sibling sweep: become <class> SetClass -> ChangeClass fix (2026-09-01)

Ported the fix from riftsds (AGENTS.md §7.195/§7.196; commit 11216f003b1) -- see ds386/NOTES.md's identical write-up (same session, same batch) for the full bug mechanism. Summary: the stock Dead Souls "Praxis" demo guild-join rooms called SetClass() directly instead of ChangeClass(), so the multi-class privilege gate (high_mortalp(), which excludes creators too) always silently rejected a fresh explorer's first class despite a success message printing.

Confirmed present here, byte-identical to ds386/riftsds's pre-fix files: all six domains/Praxis/*_join.lpc files called SetClass(); secure/include/compat.h was missing the same three query_name/query_cap_name/query_gender mappings.

Fix applied: six SetClass() -> ChangeClass() edits, three compat.h accessor lines added -- identical to ds386.

Live-verified with a fresh AUTO_WIZ creator account (Qtestfixdfx, registered normally through the "creator" choice at the AUTO_WIZ prompt, following this lib's own precedent), Class starting clean at "explorer": become fighter/become cleric/become mage each printed the real success flavor text and score immediately afterward confirmed the genuine class change ("You are a level 1 Human Fighter"/"Cleric"/"Mage"). become monk/kataan/rogue were not re-tested here (the same pre-existing missing-class-data gap already confirmed on ds386/dsIII/riftsds's identical secure/cfg/classes/ listing -- explorer/fighter/mage/ cleric/thief only).

Committed the six join-file fixes, the three compat.h mappings, and the new Qtestfixdfx test-creator account's realm/save files (following this lib's own precedent above); daemon-state save churn from booting the driver (player list, mudinfo, snoop, preload class/economy/events/race/soul/stargate/unique/voting saves, files.o/functions.o) was left uncommitted. A stray CMD_EVAL_TMP_FILE.lpc scratch file left behind by the eval command in Qtestfixdfx's own realm was deleted before commit (matching the already-committed convention for Qintestdfx/Qintestdsf, whose own same-named scratch files were untouched). Killed the test driver by exact PID when done.

Confirmed present byte-identical in a fresh clone of github.com/fluffos/dead-souls (current master, commit 4ddbd3b, i.e. still un-fixed after PRs #12-16): all six lib/domains/Praxis/*_join.c files call SetClass(), and secure/include/compat.h is missing the same three query_name/query_cap_name/query_gender mappings (confirmed these resolve via global.h's COMPAT_MODE-gated auto-#include <compat.h> in every object, and that GetName/GetCapName/GetGender already exist as real accessors on player.c/race.c, so the mapping is a pure omission). Filed upstream as PR https://github.com/fluffos/dead-souls/pull/17.

Shop (2026-09-04 librarian slice)

Otik's general store (/domains/town/room/shop) is live. AUTO_WIZ creator shopdive / Mud@2026 (human, 68 starting silver from eventCompleteChar) goto'd the store, list showed the stock (wooden torch 60 silver, plastic flashlight 10, …). buy torch from otik matched the flashlight's extra "torch" id (rayovac) and completed a paid sale: Otik said "Here is a small plastic flashlight for 10 silver!", inventory two flashlights, money 48 silver (68−10−10). buy wooden torch from otik then correctly refused ("You don't have enough silver") — purse math is integer, no §7.121 float leftover on this path.

Bug found and fixed (heartbeat hobbled(this_player())): lib/body.lpc's heart_beat called hobbled(this_player()). During heartbeat this_player() is 0, so disable.lpc:7 did 0->GetMissingLimbs() every 2s (Bad argument 1 to EFUN call_other … Got: int(0)). hobbled() then returned 0, !stringp(0) was true, and eventCollapse() fired on every idle beat. First login that died mid-news left shopdive with every verb printing What? until this was changed to hobbled(this_object()) and hobbled() gained an !objectp(ob) guard. After reboot, look/goto/list/buy worked. Same this_player() line exists on ds386/dsIII/dsII/ dshakkard/riftsds/brassring — not swept this pass except brassring (next in queue). 拜师 analogue is become <class> in the Praxis guild rooms, already covered 2026-09-01.

Leftover 678 (2026-09-12) — upstream pin

fluffos/dead-souls PRs #12–#16 are the in-tree float/currency, /open/, beggar map, supply2, and replace_program() fixes. Pin advanced to 4ddbd3b964. No new rooms. Do not rebase the local FluffOS tree over those commits.

深度功能测试(§10.7,2026-10-04)— fluffos/dead-souls#17 merged, pin moved

The Praxis become <class> fix (SetClass() -> ChangeClass() in the six join rooms, three query_* mappings in compat.h) was filed as fluffos/dead-souls#17 on 2026-09-01 and sat open; the .lpc rename made it conflict. The branch was brought up to date with a merge commit (the six files re-edited under their new names), checked live on a tree built from it against a tree built from current master (the seeded shopdive creator, goto /domains/Praxis/fighter_join, become fighter, skills: master leaves a level 1 Explorer with Explorer skills after the initiation text, the branch a level 1 Fighter with the fighter list), merged as e7a25776, and the pin bumped to it.

深度功能测试(§10.7,2026-10-04)— compile warnings: 3114 -> 2 diagnostics (fluffos/dead-souls#18)

Baseline (scan with scripts/lpc_warnings.py, KB 04 §6.10): 2386 files compile, 23 do not, 3114 distinct diagnostics — 1922 Redeclaration of global variable in 1011 files (Search 595, counter 590, Unique 229, my_save 165), 305 unused locals, 83 nosave functions, 54 argument-count disagreements, 55 unknown escapes, 15 (: "name" :) pointers, 13 bare returns, and about 110 x() inherited from both rows from 25 leaf files. After the PR: 2404 files compile and the scan lists 2 diagnostics, obj/area_room.lpc (includes a ../customdefs.h that does not exist) and obj/stargate.lpc (refuses to compile on purpose); open/prog.lpc, the third dead archive file the other Dead Souls libs keep, is not visited by the scan. The submodule is fluffos-upstream, so the change went in as one PR with two commits (mechanical, then structural), merged as 31d342a + 161b8dcc and pinned.

Method. The scripts want a repo whose libs/SLUG/work is the mudlib directory, so the work happened in a scratch repo (scripts/submodule_patch_scratch.py build deadsouls_fluffos: upstream's lib/ as work/), with the catalog overlay committed as a baseline (ed_compat.lpc, the shopdive creator save and the #include line in sefun.lpc) so that scans, the oracle and the live sessions boot the way the site does. Mechanical pass: lpc_warnings.py --fix. Structural pass: the ds386/dsIII recipe (KB 06 §7.213-§7.215) applied by hand to the 50 source files, since the lib was reformatted upstream and a line merge from ds386 conflicted in 1376 places (lpc_diamonds.py, lpc_rename_ident.py, lpc_name_winner.py, lpc_fix_strptr.py --comma found and did most of it). The PR is git apply --directory=lib -p4 of two scratch diffs; the merged tree is byte-identical to the tested one.

What the pass found beyond the warnings:

Verification. Rescanned with ~/src/fluffos's stale lpcc and with a build of fluffos at 01026e1f (same 2 diagnostics). Golden-master oracle (scripts/ds_oracle.py through the scratch repo, admin shopdive/Mud@2026, master against branch, same driver; 263 programs = lib/ plus the 38 leaf paths of ds386): 50 definer changes, every one an explicit choice above; 110 visibility rows (the nosave -> protected flips, whose cross-object callers the fixer greps for); 566 variable-count rows (the renames); 88 radiance rows; 180 removed functions = the enter/knock/scratch families of the five scenery containers; no GetSaveString() row changes; the remaining get rows are per-creation randoms (HP, carry). Live, paired, 167 + 10 steps as shopdive: fixtures (trashcan, river, sea, coffin, hoverpod), trainer, search, zpem, horse, virtual rooms, death and corpse, the Praxis callbacks, spells/history/clean. The branch printed no compiler warning in the whole session, master about 1000 lines of them; no insufficient permission on the console. The only visible difference besides the fixes: You can't enter that! -> You can't enter that. on the river (the refusal now comes from the parser, as on ds386).

Overlay. overlay/lib/secure/sefun/sefun.lpc is a full copy of upstream's plus one #include, so a pin bump that touches sefun.lpc is undone on the site unless the overlay is rebuilt: insert #include "/secure/sefun/ed_compat.lpc" after the astar.lpc include of the new upstream file (done for this pin; the rets local is gone from both).

Left alone: update.patch/update.blank still name .c files in a few cp/rm calls (the live-upgrade tool targets the pre-rename tree); the config has three obsolete lines the driver reports (address server ip, address server port, binary directory).

深度游玩测试(2026-10-21):telnet、setreboot 的无参数调用(上游待改,KB 06 §7.336)

裸动词扫描发现 telnet 不带参数时 sscanf(args, "%s %s", ...) 因 args 为 0 抛 Bad argument 1 to sscanf() Expected: string Got: 0,屏幕上是 A runtime error occurred;它本想打印用法的 this_object()->help() 是没有任何对象定义的函数(命令用 GetHelp() 作答),所以即使不出错也什么都不打印。管理员命令 setreboot 调用同一个不存在的 help(),并且缺 return(继续执行 SetRebootInterval(0),该函数对 x <= 1 保持旧值,所以无害)。本仓库里 dsIII、ds386、dshakkard、riftsds、brassring 的同一文件已改(if (!args || sscanf(...) != 2)、write(this_object()->GetHelp() + "\n")、setreboot 补上 return 1;)。fluffos/dead-souls 是 gitlink 库,改动需要上游 PR(lib/secure/cmds/players/telnet.lpc、lib/secure/cmds/admins/setreboot.lpc),本会话的 GitHub 范围只有 fluffos/mudlibs,未提交。上游的 help 守护进程不受影响:verbs/spells、verbs/undead 在子模块里作为空目录存在,strip_colours() 也已经带着 \b 的做法。

深度游玩测试(2026-10-21):创造者/管理员命令不带参数、A* 的填充字符(上游待改,KB 06 §7.336、§7.338)

以种子管理员身份扫描同一份 Dead Souls 3.x 代码(play_sweep_lib.py ds386 --wiz --account fluffos:Mud@2026 --shapes one;本库没有种子管理员账号):裸命令即抛错的有 areaclone、cryptogram(sscanf(0, ...))、pause(lower_case(0))、html(read_file(0))、encre(player_save_file(args) 在检查参数之前就执行,对非字符串故意 error("Bad argument 1 to save_file()."))、resolve、wget(守护进程里 sscanf(0, ...) / strsrch(0, ...))。A* 的 find_path()(创造者命令 astar)用 sprintf("%N'\27's", "") 给地图补位,FluffOS 要求填充串有显示宽度,控制字符宽度为 0,报 (s)printf(): Null pad string specified。本仓库 dsIII、ds386、dshakkard、riftsds、brassring 的同名文件已改:参数检查后打印 GetHelp(),astar 改为先用空格补位再 replace_string(..., " ", "\27")。在本库用创造者身份(PLAY_DS_ROLE=creator,AUTO_WIZ 注册时答 creator)实测复现了 areaclone、astar、cryptogram、html(其余几个需要管理员权限)。上游 fluffos/dead-souls 的对应文件(lib/secure/cmds/builders/areaclone.lpc、lib/cmds/creators/cryptogram.lpc、lib/secure/cmds/admins/pause.lpc、lib/cmds/creators/html.lpc、lib/secure/cmds/admins/encre.lpc、lib/cmds/creators/resolve.lpc、lib/secure/cmds/creators/wget.lpc、lib/secure/sefun/astar.lpc)未改,需要上游 PR(本会话的 GitHub 范围只有 fluffos/mudlibs,未提交)。

log/ 下的日志子目录没有人建:写日志的 write_file() / log_file() 报错(2026-10-22,KB 06 §7.358)

这个库的代码往 log/ 的 7 个子目录写日志:log/adm(lib/lib/command.lpc)、log/harass(lib/lib/interface.lpc)、log/imc2_server(lib/secure/daemon/imc2server/server_log.h)等。压缩包、git clone 和站点包里都没有这些目录(本地启动没有写过它们,gen_keep_dirs.py 没记),write_file() 对不存在的目录抛 *Wrong permissions for opening file /log/dlog/money for append. "No such file or directory",调用它的函数在写日志那一行中断,后面的奖励、标志、步骤都没有执行。本库的 log_file() 是 write_file(LOG_DIR + file, text),目录不存在就抛同样的错误(实测)。把 log/adm、log/harass、log/imc2_server、log/reports、log/router、log/secure/log、log/watch 记进 scripts/wasm_keep_dirs.txt(scripts/lpc_keep_log_dirs.py;站点和 pristine 树按它建空目录)。证明(scripts/lpcc_eval.py,在 /adm/daemons 这类目录下的求值对象里写 /log/zzdirprobe/...,新进程)见 KB 06 §7.358 的实测表。