Dead Souls 3.0

✅ 可玩

dsIII

更新 3e5694f 2026-10-27 源码 下载 ZIP

▶ 开始游玩 · Play Now

The real, self-identified 'Dead Souls' object library at its 3.0 point release (December 2009) -- an early snapshot of the same 3.x codebase branch that later became the 3.8.6 generation, several point releases and years earlier, with seven of that later version's nine domains: a default starting area, a town, the example town Ylsrim, an LPC-University-style campus, a cave zone, an examples area, and Praxis. Registration includes an age gate ('Are you 13 years of age or older?') followed by a race pick from eight options -- dwarf, elf, half-elf, half-orc, halfling, human, orc, or poleepkwa -- and, like dsII, the very first account ever created walks through a one-time admin-installation wizard before the mud restarts into its normal login flow. This is the most network-connected mud in the collection: it dials out to a live IMC2 hub and a separate Intermud-3 network on every boot, and even a bare, unattended boot triggers an automatic version-check that fetches the still-live dead-souls.net's release notes over a real HTTP connection.

README

The real, self-identified "Dead Souls" object library at its 3.0 point release -- released 2009-12-30, signed Cratylus @ Dead Souls (see doc/RELEASE_NOTES). A genuinely distinct release from the other four Dead Souls entries already in this collection (dsI, 1996-98 Nightmare-IV-era; dsII, 2006's "Dead Souls II"; ds386/ deadsouls_fluffos, the 2014-era 3.8.6 generation) -- but the CLOSEST of the four to ds386/deadsouls_fluffos by a wide margin: 90.8% of shared files are byte-identical, and secure/daemon/master.lpc differs from ds386's by only 8 trivial lines. Best understood as an early snapshot of the exact same "3.x" codebase branch that later became 3.8.6, captured a few point releases and several years earlier -- see NOTES.md \S1 for the full byte-level comparison against all four siblings.

Source: a ZIP archive from mudbytes.net, a now-dead site. Recovered via the Wayback Machine (<https://web.archive.org/web/20190802014424id_/http://mudbytes.net/files/download/2733/>) since the original site is down. Slug dsIII, number 182, port 40229.

Highlights

Registration flow

name -> "Do you really wish to be known as <Name>? (y/n)" -> "Are you 13 years of age or older? (y/n)" -> password (5+ letters) -> confirm password -> gender (male/female/neutral/none) -> email (required, validated as user@host) -> race selection (list to see options, pick <race> to choose) -> news screens (<return> to continue) -> lands in /domains/default/room/ (the default domain's start room).

The very first account ever created goes through a separate, one-time admin-installation wizard instead (see above) -- the mud shuts down after that wizard completes, and every account after the restart uses the normal flow described here.

Admin account

Created through the mud's own first-boot installation wizard; no manual group-membership fix needed (see Highlights above). Verified live: score shows the correct character sheet, and update (this project's canonical ACL/compile-permission smoke test) succeeds with Ok.

Status

Boots clean, zero fatal errors. Verified live with a real driver session and a raw socket client: the first-boot admin installation wizard (including the mud's self-shutdown-after-install), a restart, admin login, a brand-new ordinary character's full registration (including the age gate and race selection), a distinct reconnect-login code path, and look/score/update/a clean quit all producing correct output for both accounts.

2282 of 2291 .lpc files pass a full lpcc_check.sh batch compile sweep (99.6%); the 9 that don't are all pre-existing non-bugs or compile-sweep-only eval-cost artifacts (a deliberate author anti-load trap, two builder-tool scaffolding templates, two confirmed-harmless isolated-compile warning failures, two confirmed batch-eval-cost artifacts that pass standalone/live, one genuine but pre-existing and non-blocking content-side performance bug in the cave domain's orc NPC race setup, a missing LIB_CAPTURE library component, and a missing opcprof() efun) -- see NOTES.md \S6 for the full breakdown.

WASM status: playable. Shared WASM driver (secure/sefun/sockets.lpc already stubbed). Admin login (fluffos / Mud@2026) into Fluffos' workroom, plus score / quit, verified with scripts/wasm_client.js. Play: https://mudlibs.fluffos.info/dsIII/

Local run

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

Game port: 40229. Note the THREE outbound-network-connection caveats above (two at every boot, one also from a bare compile sweep) before repeated/automated boots or lpcc_check.sh runs.

NOTES · 移植与修复记录

Dead Souls 3.0 -- porting notes

Source: a ZIP archive (ds3.0/, 5553 files, WINDOWS_INSTRUCTIONS.txt at top level) from mudbytes.net, a now-dead site. Recovered via the Wayback Machine (<https://web.archive.org/web/20190802014424id_/http://mudbytes.net/files/download/2733/>), re-verified live (HTTP 200, real ZIP magic bytes, 5553-file/ds3.0/ top-level-dir match) at the start of this session before extracting. The zip bundles fluffos-2.18-ds07/ (a driver source snapshot), bin/, win32/, extra/ build/install scaffolding -- all ignored, this project uses its own driver. The real mudlib root is the zip's lib/ directory (bin/mudos.cfg's own master file : /secure/daemon/master confirms it). Slug dsIII, number 182, port 40229.

1. Duplicate check -- distinct release, but the closest sibling by far

This collection already has FOUR Dead Souls entries: dsI (~1996-98, Nightmare-IV-era, 2 domains), dsII (2006, "Dead Souls II", 4 domains), and ds386/deadsouls_fluffos (3.8.6, 2014-era, 9 domains, confirmed identical codebase generation to each other). Before building out a full pipeline, checked whether "Dead Souls 3.0" repackages any of the four:

This is a genuinely distinct release -- not a byte-identical duplicate, since 419 of the 4555 shared files differ and 43 are unique here -- but it sits MUCH closer to ds386/deadsouls_fluffos in the lineage than dsII ever did: this is best understood as an early-2010s snapshot of the exact same "3.x" branch that later became 3.8.6, captured about 4-5 years and several point releases earlier. duplicate_of is null in meta.json, but the whole fix catalog below was ported from ds386's own NOTES.md wholesale (verified per-file via diff against ds386's raw archive before applying each fix) rather than rediscovered from scratch, since nearly every driver-compat bug turned out to be byte-identical.

2. Conversion

English-language archive (public domain, from mudbytes.net) -- scripts/convert_lib.sh against raw/ds3.0/lib confirmed this: 4562/4598 files already valid UTF-8 (pure ASCII), only 5 lossy conversions, 28 binary files skipped. 2290 .c files renamed to .lpc, 603 literal .c" references fixed, 31 local angle-bracket #includes converted to quotes, 1429 files touched by the static->nosave sweep.

2.1 One source file was genuinely MIXED-encoding, not GB18030 -- and the lossy conversion broke its string-literal syntax

secure/sefun/translate.lpc's FLUBS array (the same multilingual "drunk talk" curse-word list ds386/dsII both have) contains:

A single GB18030 pass mangled BOTH differently: the Cyrillic bytes (already-correct UTF-8) got mis-decoded as GB18030 multi-byte sequences into garbage CJK characters, and the Latin-1 ä bytes got silently dropped as invalid, which ate the closing quote immediately after kyrp too ("kyrp\xe4" -> "kyrp" was NOT what happened -- the actual result was "kyrp,"r鋕,"shawtay", a real syntax break: an unterminated string swallowing the next comma, then an invalid bare identifier r鋕). This is a genuine, driver-compat-relevant compile failure, not just cosmetic mojibake -- confirmed by a first driver boot failing simul_efun's compile entirely (*No program in object '/secure/sefun/sefun'!). Fixed by hand: re-decoded the Finnish words as Latin-1 and re-typed the Cyrillic word as literal UTF-8, leaving everything else in the file (99% pure ASCII) untouched. Also individually re-decoded with iconv -f ISO-8859-1 -t UTF-8: doc/CREDITS, doc/help/players/credits (a Lars Pensjö name-with-diacritic), and domains/town/txt/mystery.txt (a ¯\(°_o)/¯ ASCII-art shrug). One more lossy file, secure/save/functions.o, turned out to be a stale function-signature cache (secure/daemon/function.lpc's RestoreObject()/file_exists() guard handles a missing file gracefully and rebuilds on demand) -- deleted rather than hand-repaired, same precedent as ds386's functions.o/books.o cache files.

3. Driver-compat fixes -- ported wholesale from ds386's catalog, verified per-file

Every fix below was checked against ds386's raw archive first (diff on the specific file) to confirm the bug is byte-identical before porting ds386's exact fix; all were.

4. TYPE array NAME dialect -- 166 files via an automated fixer, plus 7 hand-caught shapes the fixer's regexes didn't cover

Wrote a quote/comment-masking Python regex fixer (handles single-name declarators with/without default values, multi-name comma lists, bare array NAME/array *NAME, and (TYPE array) cast forms) and ran it across the whole work/ tree: 166 files changed on the first pass (secure/cmds/admins/admintool.lpc's array *classes;/array *races;, secure/sefun/sockets.lpc, secure/sefun/sefun.h, and 163 more). Then a full driver boot + a standalone re-grep surfaced 7 more shapes the regex set didn't anticipate, each fixed individually and verified there was exactly one instance:

All 7 fixes verified individually: a clean re-grep for each pattern class found zero remaining instances, and a full driver boot + full lpcc_check.sh batch sweep both came back clean of these specific error signatures afterward.

5. Live verification

Booted clean (Accepting telnet connections on 0.0.0.0:40229., Initializations complete., zero fatal errors, zero uncaught compile errors in log/debug.log).

Confirmed THREE independent real outbound network side effects at boot -- more than any prior Dead Souls-lineage lib in this collection, and the third one is NEW to this archive specifically:

1. IMC2 (secure/daemon/imc2.lpc, preloaded, DISABLE_IMC2/ DISABLE_INTERMUD both 0 in secure/include/config.h): makes a real socket_connect() a couple seconds after boot. Confirmed live (ss -tnp showed ESTABLISHED ... 204.209.44.3:8080). 2. Intermud-3 (daemon/intermud.lpc, preloaded): a second real connection attempt, confirmed live (ss -tnp showed SYN-SENT ... 74.207.247.83:5000). 3. NEW: an automatic release-notes HTTP fetch on every single boot, with zero player interaction required. secure/daemon/autoexec.lpc is ITSELF preloaded, and its create() schedules eventRun() via call_out(..., 1) -- one second after preload completes, with a hasrun guard so it only fires once per boot. eventRun() unconditionally does catch( load_object("/secure/cmds/creators/dsversion")->cmd());, and dsversion.lpc's cmd() immediately opens a real socket to dead-souls.net (97.107.133.86:80) and fetches /RELEASE_NOTES, overwriting doc/RELEASE_NOTES_HTTP with the live response. Confirmed live and reproducible on a bare idle boot with NO login at all: doc/RELEASE_NOTES_HTTP's mtime changed within ~14 seconds of driver start with zero connections made, and its content is a REAL, CURRENT response from a still-live server -- the fetched response includes release notes up through "3.9, Released 2020-04-08", meaning dead-souls.net itself is not dead (unlike mudbytes.net, this project's actual archive source). A SEPARATE creator command, secure/cmds/creators/rss.lpc, has the same eager-create()-time network behavior (registers RSS feeds from lpmuds.net/feedburner.com via call_out((: Setup :), 2)) but is NOT itself preloaded -- it only fires if something actually loads the file, which an lpcc --batch compile sweep does (merely compiling secure/cmds/creators/rss.lpc as one of thousands of files in the sweep triggers its create() and, 2 seconds later, its own real HTTP/RSS fetches) -- confirmed by doc/RELEASE_NOTES_HTTP and tmp/imc2.log both getting freshly rewritten after this session's lpcc_check.sh runs, with no driver boot in between. This means BOTH a live boot of this lib AND a bare lpcc --batch compile sweep of this lib can make real, uncontrolled outbound network connections to third-party servers -- extend the existing "don't sweep into high-frequency automated re-boot loops" caveat (already standard for dsI/dsII/imud/foundation2-class libs) to cover automated/batch COMPILE sweeps of this lib too, not just driver boots. doc/RELEASE_NOTES_HTTP and tmp/imc2.log were reset to their originally-shipped/pristine state before every commit in this session; a future re-test will regenerate fresh copies again on its own next boot/sweep, which is expected and not a bug.

Tested with a raw Python socket client (scripts/mudclient.py), all required flows in one continuous session:

1. First-boot admin installation wizard (secure/lib/connect.lpc): username fluffos -> password/confirm Mud@2026 -> default display name -> gender male -> blank real name -> [email protected] -- completed successfully; the mud self-shut-down as designed (connect.real's shutdown() + destruct(master()), matching dsII/ds386's installer behavior -- a driver restart is required before the new admin can log in). secure/cfg/groups.cfg and secure/include/config.h correctly ended up with fluffos in place of the ADMIN/DEBUG_NAME placeholder tokens -- unlike dsII, this archive's shipped groups.cfg/config.h already had the correct placeholder tokens (not a hardcoded original-author name), so no hand-fix was needed here. 2. Reboot + admin login: fluffos/Mud@2026 -> arrived in /realms/fluffos/workroom with wizard robe+hat, general/creator/ admin news tiers all paged correctly, who-equivalent (people, auto-sourced via .profile) showed the admin present, score produced a correct full character sheet ("You are First Admin Fluffos (neutral)... a level 1 Human Explorer..."), update (no argument, current-room form) reported Ok (warnings-only compile, no errors), quit disconnected cleanly with correct inventory-removal messages. 3. Ordinary new-player registration (tested with Qinbing, an ASCII name -- this archive enforces a strict A-Z a-z ' - name-charset check with an explicit rejection message, so a real Chinese name is out of scope here by design, same adapted bar as ds386's own testing): name -> confirm (y) -> age-gate (y, "Are you 13 years of age or older?") -> password -> confirm -> gender -> [display-name step skipped, CUSTOM_FORMAT is off by default] -> email (validated, a blank/malformed first attempt is correctly rejected and re-prompted) -> [real-name step skipped, REAL_NAME is off] -> race selection (list/help RACE/pick RACE -- 8 races: dwarf, elf, half-elf, half-orc, halfling, human, orc, poleepkwa) -> news pages -> landed in "The start room" (the default domain's entry point) with a correct ASCII-art room map and an HP/MP/SP status bar. Re-logged in as the same character on a second connection: Reconnected. (confirms the reconnect path is a distinct, working code path from fresh registration, not just registration succeeding once), score produced correct output, quit clean. 4. update <path> on the 4 lpcc-flagged "eval-cost" failures (section 6 below) confirmed 3 of the 4 are batch-sweep artifacts, not real bugs -- see that section for the one (domains/cave/room/ common1) that is a genuine, but pre-existing and non-blocking, content-side performance bug.

2282 of 2291 .lpc files pass a full lpcc_check.sh batch compile sweep (99.6%); the 9 that don't are broken down in section 6.

6. Known pre-existing gaps and sweep artifacts (not fixed -- content/archive design or tooling artifacts, not driver-compat)

7. WASM pass

Measured 2026-09-03 against the shared ~/src/fluffos/build-wasm. secure/sefun/sockets.lpc was already stubbed (the ds386 sibling treatment this section originally predicted). No further mudlib-side compile fix was needed. Verified with scripts/wasm_client.js: fluffos / Mud@2026 into Fluffos' workroom, score showed "First Admin Fluffos", clean quit. IMC2/I3/HTTP version-check still try outbound connects at boot — do not loop-reboot. Shop/combat/death were not exercised this pass.

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

First real *playthrough* pass on this lib (prior sessions only verified registration + look/score/quit + the admin install wizard, per section 5 above). Played a full, continuous session as an ordinary new player on the native driver (build-debug), using a second admin connection only for a controlled death test at the end. This is also the first lib in the Dead Souls lineage (dsI/dsII/ds386/ dshakkard/deadsouls_fluffos) to get a §10.7 pass -- none of the siblings have a "深度功能测试" heading in their own NOTES.md yet, so the bug found below (and its fix pattern) should be checked against all of them, not just this archive.

Newbie path: read chapter 1 in handbook (repeated through "chapter N" for the whole book) is this lib's own onboarding doc. Chapter 4 spells out the intended early game explicitly: level up on weak monsters in "the newbie mansion" (reached from the village, past a newbie-only gate guard and a locked mansion door -- the real way in for a level-1 character is external: grab the ladder from the gardener's shack west of the mansion, drop it under the mansion's open second-floor window, and climb ladder/enter window), sell loot to a shopkeeper (this archive's actual town vendors are James at the Healers' Guild and Lars at the pub -- the handbook's own "Otik" example is stock Dead Souls boilerplate text, not hand-tailored to this archive's actual NPC names, a content detail, not a bug), bank with Zoe, and eventually join a class (fighter/mage/cleric/thief) via a guild NPC once a gating quest (Orcslayer) is done.

Test character: id Qintestds, ASCII name (per this archive's own strict A-Z a-z ' - name-charset validation, same as ds386's and this archive's own admin-install testing in section 5 -- a Chinese name is out of scope here by design), password Abc12345, human Explorer -- kept (not cleaned up) as a representative playthrough character, currently a ghost-then-regenerated corpse sitting in "The start room" at half HP/MP/SP after the controlled death test below. Save file: work/secure/save/players/q/qintestds.o.

Bug found and fixed

Silver/gold/every other in-game currency silently turns into a float the first time it passes through ANY currency-exchange calculation, corrupting the player's Currency mapping permanently -- secure/sefun/economy.lpc's query_base_value()/query_value()/ query_base_rate()/query_player_money(), plus one sibling miss in lib/teller.lpc's eventExchange() and one in lib/props/value.lpc's SetBaseCost().

What was tested and confirmed working

Minor observation, not fixed (cosmetic, not a programming bug)

domains/town/room/mansion_uhall1.lpc (and likely other rooms using the same window-as-exit idiom) defines "window" as BOTH a raw SetExits() key and a SetEnters() entry. The room's own GenerateObviousExits()-generated text lists "window" as if it were a plain bare-word direction right alongside down/west/east/south ("Obvious exits: down, west, east, window, south, enter window"), but typing bare window actually fails ("There is no go window here.") -- only enter window (or, per verbs/rooms/go.lpc's own help text, presumably go window) actually works. Root cause: lib/nmsh.lpc's bare-word-to-"go "-command pre-aliasing only covers the fixed classic-cardinal-direction set (n/s/e/w/ne/nw/se/sw/u/d/out), never arbitrary custom exit names, while lib/std/room.lpc's GenerateObviousExits() blindly implode()s every raw exit key into the display text regardless of whether it's bare-word-usable. This looks like it would affect every room in the whole Dead Souls lineage that names a custom exit outside the cardinal set (not something introduced by this archive), and it's misleading-but-harmless UI text, not a crash or wrong efun call -- documented here per the project's own scope discipline (ERR_THERE_IS_NO is a normal, non-fatal parser rejection, not a bug signature) rather than "fixed" by guessing at what the room author actually intended.

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

ds386's own round-two deep-functional-test pass (this NOTES.md's own sibling file) found and fixed four bugs and flagged all of them for a sweep across the whole Dead Souls 3.x lineage: (1) SEVERE eventRevive() feeding float arithmetic into AddHealthPoints()/ AddMagicPoints()'s int parameters (AGENTS.md §7.121 class), (2) the MudOS-era replace_program() fold in lib/std/room.lpc's create() (AGENTS.md §7.141), (3) the Wimpy fraction-vs-percentage bug in lib/combat.lpc (AGENTS.md §7.124 class), and (4) a minor GiveMap() reentrancy gap in domains/town/npc/beggar.lpc. Checked all four here:

Verified live end-to-end: fresh boot compiled clean (zero new errors in log/debug.log, only the same pre-existing benign warning classes already catalogued), say hello world worked immediately with no crash, and wimpy showed the fixed 20%. Killed the test driver by exact PID when done.

Source pattern for all four checks: ds386/NOTES.md's "Deep functional test (round two, 2026-08-27)" section.

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 the seeded fluffos admin (Class reset to "explorer" via eval between attempts, to force the exact creator-excluded-from-high_mortalp() scenario the bug hits): become fighter/become cleric/become mage all now print the real success text and score confirms the genuine class change ("You are a level 1 Human Fighter"/"Cleric"/"Mage"). become monk/kataan/ rogue do not work, but this is the identical pre-existing, shared-lineage missing-class-data gap documented in ds386/NOTES.md (secure/cfg/classes/ here also only ships explorer/fighter/ mage/cleric/thief -- confirmed, same as riftsds and ds386), not a regression or a miss in this fix.

Committed only the six join-file fixes and the three compat.h mappings; incidental save-file churn from this session's boot (seeded fluffos admin's Class field, player list, mudinfo, snoop, preload saves, RELEASE_NOTES_HTTP refetch) was left uncommitted. Killed the test driver by exact PID when done.

Shop (2026-09-04 librarian slice)

Otik's general store (/domains/town/room/shop) is live on this 3.0 snapshot — the handbook's "Otik" example is not boilerplate here. Seeded admin fluffos / Mud@2026 (88 silver on the creator save) goto'd the store. buy wooden torch from otik completed a paid sale: Otik said "Here is an old wooden torch for 60 silver!", inventory showed the torch, money 88→28 silver. Purse math is integer. Do not use bare buy torch — /domains/town/obj/rayovac.lpc still carries "torch" in its id list. 拜师 analogue ask herkimer to join at /domains/town/room/magic_guild is still Orcslayer-gated ("First you must prove yourself worthy..."), same as the earlier §10.7 pass; re-confirmed live this slice.

Bug found and fixed (heartbeat hobbled(this_player())): same shape as deadsouls_fluffos/brassring/dsII. lib/body.lpc heart_beat called hobbled(this_player()); during heartbeat this_player() is 0, so disable.lpc did 0->GetMissingLimbs() every 2s. Changed to hobbled(this_object()) and added if(!objectp(ob)) return "No missing limbs."; after the decls in secure/sefun/disable.lpc. After this boot, look/goto/buy worked with no idle collapse. Verb-file hobbled(this_player()) checks in go/jump/enter/climb were left alone (those run in command context). Same one-line fix applied this session to dshakkard/riftsds/ds386 (dsI has no hobbled()).

深度功能测试(§10.7,2026-10-03)— compile warnings

Method: scripts/lpc_warnings.py dsIII --fix (KB 04 §6.10) over all 2298 .lpc files, then a pristine-tree (scripts/pristine_tree.sh) live boot: admin login (fluffos/Mud@2026), movement between the workroom and the sample room, NPC load, and a direct eval MASTER_D->log_error(...) probe. Clean boot, no crash, no "insufficient permission" apply-rejection anywhere in the console.

Fixed, mechanically, across ~1390 files: every nosave-function (the historic static→nosave conversion hitting functions, not just variables)→protected/dropped-if-called-cross-object, every unused local variable, every unknown escape sequence. See the commit log for the itemized breakdown; this is this lib's share of the corpus-wide warning-is-a-bug policy (AGENTS.md Conventions, 2026-10-03).

Fixed by hand, five distinct arg-count/return-type families (KB 04 §6.10's varargs-on-override pattern; none change call-site behavior since LPC already tolerates a call with a different arg count than declared):

Two real bugs found and fixed:

One self-caught mistake, documented so the class is on record: secure/sefun/fuzzymatch.lpc had been "fixed" in an earlier slice by moving alen/blen/dist inside each function's #ifdef __FLUFFOS__ guard (this driver never defines __FLUFFOS__, so the guarded block, and everything only used inside it, is dead on this build — KB 04 §6.10's documented ifdef-branch caveat). That earlier pass missed that mixed foo, baz = alen+1; on the next line is *also* only used inside that same guarded block, and had deleted it outright instead of moving it in too, leaving a dangling while(baz) reference in the (currently dead) branch. Caught on a pre-commit re-read of the diff, not by the compiler (the branch doesn't compile here, so lpcc never saw it either) — restored the declaration inside the guard in both affected functions. Grepped the rest of this session's unused-locals diff for the same shape (a deleted declaration whose *use* is also inside an #ifdef a few lines below the deletion point) and found no other instance.

Explicitly deferred, not rushed — a fresh full scan after all of the above shows 2193 distinct diagnostics remain (2298 files compile, 10 pre-existing/deliberate failures unrelated to this session: obj/area_room.lpc, obj/stargate.lpc, open/prog.lpc, secure/cmds/admins/opcprof.lpc — a missing customdefs.h, a file whose own header says "here on purpose to prevent this from loading", and an opcprof efun this driver build lacks):

深度功能测试(§10.7,2026-10-03, addendum)— corrections and follow-up

Correction to the 2026-10-03 section above: its sentence "Grepped the rest of this session's unused-locals diff for the same shape … found no other instance" was wrong. The grep was by name, not by scope. A scope-aware check (a deleted declaration whose name still occurs, inside the same function body of the original file, on a line under an #if/#ifdef) found 23 more: the declaration had been deleted because the driver does not compile the branch that uses it, which silently breaks the branch for the day it is compiled. All 23 were restored *inside* their inactive branch (daemon/map.lpc, domains/Praxis/roots.lpc, lib/body.lpc, lib/cedit.lpc ×2, lib/chamber.lpc, lib/lvs/position.lpc, secure/cmds/creators/gauge.lpc, secure/daemon/autoexec.lpc, secure/daemon/imc2.lpc, secure/daemon/master.lpc, secure/lib/net/http.lpc), the same fix fuzzymatch.lpc needed, and scripts/lpc_warnings.py now refuses to delete such a local (KB 04 §6.10).

Follow-up fixes (same day): 15 (: "name" :) function pointers in domains/Praxis/* (a string constant, not a call, in this driver) → (: name :) with a forward prototype; domains/town/obj/ebutton1/ebutton2 car->SetDoor(1) → SetDoorClosed(1) (KB 06 §7.211); lib/virtual/virt_land.lpc SetCoordinates(int x, int y, int z) renamed SetVirtualCoordinates (it clobbered LIB_ROOM::SetCoordinates(mixed) in every virtual room; the only callers of the 3-int form are in the file itself); return-type/prototype families (limb.lpc GetSaveString, room.lpc RemoveSearch, eventDeteriorate in obj/light/obj/whip, oob.lpc eventRead, corpse.lpc SetClass/GetClass); 29 more varargs and 14 bare return; by the tool; secure/cmds/admins/opcprof.lpc now reports that the opcprof() efun is gone from FluffOS. Oracle against the pre-session commit (8ef71a2827e): 1385 nosave→protected flips, no other change except the intended SetCoordinates rename.

Remaining diagnostics: 1671 Redeclaration of global variable (929 files) and 418 inherited from both, from 46 source files (17 redundant leaf inherits, 8 function overlaps, 4 partial overlaps, 17 name collisions; queued), and the four files that never compiled (obj/area_room, obj/stargate, open/prog, secure/cmds/admins/opcprof before this change).

深度功能测试(§10.7,2026-10-03, structural pass)— one file reached through two inherit paths

The last open cluster of the compile-warning pass (KB 06 §7.213 has the mechanism). 1671 Redeclaration of global variable + 418 inherited from both X and Y warnings came from 46 source files; the other ~900 files repeated them. dsIII now prints 5 diagnostics, all in the three files that never compiled (obj/area_room, obj/stargate, open/prog; type.h's OBJECT redefinition is open/prog.lpc including it next to compat.h).

Fixes, by kind:

Radiance (a bug the oracle exposed, not a warning): container::GetRadiantLight() already adds GetBaseRadiance(), the object's own glow, but npc.lpc and storage.lpc still added object::/item::GetRadiantLight() on top, so SetRadiantLight(7) gave 14 for an NPC, chest, bed or chair and 7 for an item or a player. Both return the container's value now (7); worn_storage did not count its contents at all (armor's getter won) and returns it too. No shipped object sets a radiance on those classes. The container formula itself (own glow attenuated by ambient: 0 at ambient 20 for a 7) is untouched.

Verification. Golden-master oracle (scripts/ds_oracle.py, KB 08 §10.12), 266 + 18 programs (every lib/ program and 61 leaf files) probed on HEAD and on the working tree: normalised GetSaveString() unchanged everywhere, no getter changed besides random per-creation stats; function definers change only where the resolution is now explicit (list above), duplicate variables disappear, the radiance rows above, healer's lamp-in-room 30→35, and the three hybrids lose the enter/knock/scratch functions. Live on a pristine tree, HEAD and edited side by side: trashcan (look at/in, get/put, "firmly attached", knock, open), river and sea (look, fill, drink, "stays in place", jump into sea swims), coffin and hoverpod (enter/out), pole GetSave(), sign and note read, plasma cannon wear/remove, the trainer scenario above, the OOB service port (oob-test, oob-begin, oob-file replies are byte-identical to HEAD) and the OOB client (clone to a local listener: same oob-begin packet, the socket closes after oob-end), a fresh mortal (13+ prompt, race, start room, score, inventory) fighting the dodo with damage both ways, and the admin command battery with no runtime error on the console. The formatter (KB 08 §9) was not run: this tree is not formatter-clean, it rewrites nearly every line of the 58 touched files.

深度功能测试(§10.7,2026-10-03, addendum 2)— a regression of mine in Praxis stone.lpc, and a corrected claim

Found while verifying the same Praxis files in ds386 (eval new("/domains/Praxis/obj/misc/stone")->GetLong()).

深度功能测试(§10.7,2026-10-04)— Praxis comma-form function pointers

The 30 comma-form pointers in the 21 Praxis files and the target fixes made in ds386 (see its NOTES section of the same date: SetSearch handlers return their text, the helm asks GetMorality(), test_invis takes the viewer, look_at_vault/look_at_manhole restored as text) carry over here: scripts/lpc_twin_port.py adopt ds386 dsIII --pre HEAD --apply (18 files are token twins: 12 byte-identical, 6 spliced into dsIII's own spacing; farm, west_road2 and yard carry dsIII's own code and were edited by hand the same way). All 21 files compile without a diagnostic, and a live spot check (search field, search mounds, look at mound/manhole/vault/treats, the planning room, a voting hall) answers as in ds386.

深度功能测试(§10.7,2026-10-05)— 主控 author_file()/domain_file()

驱动的 mudlib_stats 包在主控载入时各调用一次 author_file(<主控文件>) 和 domain_file("/"),要求返回字符串。本库这两个 apply 只为 domains / realms 目录下的文件返回名字,其余返回 0,所以每次启动都打印 author_file() in the master file does not work, using root_uid as fallback 和 domain_file() in the master file does not work, using bb_ui as fallback 两行,网页版每位访客的终端里都看得到。secure/daemon/master.lpc 改成与 fluffos 组织维护的 Dead Souls 上游(deadsouls_fluffos)相同的写法:其余文件的域返回 "BACKBONE",作者返回 "NONAME"。域等于 backbone 域时驱动让对象沿用创建者的域,与返回 0 时相同;作者统计(author_stats())多出一行 NONAME,汇总 realms 目录以外的对象(KB 06 §7.222)。用 lpcc config.fluffos /nonexistent.lpc 前后对比,主控载入段只少了这两行。

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). What it was: prompt / editor messages were never marked MSG_NOWRAP (msg_class == "prompt" && msg_class == "editor"). Changed: /lib/interface.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, 10 terms). Changed: /cmds/creators/cryptogram.lpc (1), /cmds/creators/rot.lpc (1), /domains/campus/npc/jennybot.lpc (1), /domains/default/weap/grenade.lpc (1), /domains/default/weap/plasma.lpc (1), /domains/default/weap/rpg.lpc (1), /domains/default/weap/torpedo.lpc (1), /lib/lead.lpc (1), /secure/sefun/mappings.lpc (1), /verbs/builders/reload.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. /secure/sefun/sefun.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).

The suffix strip after a directory read (2026-10-20, KB 06 7.296)

After the .c -> .lpc rename the names read with get_dir("*.lpc") were still cut by two characters ($1[0..<3]): baguadao.lpc became baguadao.l, and every path built from it failed to load (stories, battlefields, events, help indexes, player-made item data, summon lists). Now [0..<5] (scripts/lpc_fix_getdir_strip.py). Changed: /daemon/help.lpc (2).

help <topic> on a plain clone, telnet and setreboot without an argument (2026-10-21, KB 06 7.212, 7.336)

help <topic> loads /daemon/help, whose LoadIndices() adds get_dir() results with +: secure/cmds/common, verbs/spells and verbs/undead are directories the archive ships empty and a git clone does not have (git keeps no empty directory; the site recreates them from scripts/wasm_keep_dirs.txt). Booted without them, the first help <topic> printed A runtime error occurred (Bad type argument to +. Had array and int) and the half-built daemon answered every later topic with "no results". Every directory is now read through DirList() (get_dir(pattern) || ({})); help look, help telnet, help races and help commands answer with the four directories removed (PLAY_RMDIRS). The mortal telnet with no argument raised Bad argument 1 to sscanf() Expected: string Got: 0 (the bare-verb sweep: A runtime error occurred), and the usage line it meant to print came from this_object()->help(), a function no object defines: it tests !args || now and prints GetHelp(); the administrators' setreboot called the same missing help() and went on without a return, it prints its syntax and returns now. Changed: /daemon/help.lpc, /secure/cmds/players/telnet.lpc, /secure/cmds/admins/setreboot.lpc.

Creator and administrator commands with no argument, the A* padding (2026-10-21, KB 06 7.336, 7.338)

The creator / administrator verbs had never been run, only compiled. Swept as the seeded administrator (play_sweep_lib.py SLUG --wiz --account fluffos:Mud@2026 --shapes one), seven of them raised on the bare command: areaclone and cryptogram (sscanf(0, ...)), pause (lower_case(0)), html (read_file(0)), encre (player_save_file() runs before the argument is looked at and raises Bad argument 1 to save_file()), resolve and wget (the daemons RESOLV_D->eventResolve(0) and WGET_D->eventGet(0) run sscanf(0, ...) / strsrch(0, ...)). Each tests the argument first and prints its GetHelp(). The A* sefun's find_path() (the creator command astar) died with (s)printf(): Null pad string specified because it pads the map with sprintf("%N'\27's", ""), a pad string that has no display width in FluffOS; it pads with spaces and replaces them, and astar prints its demo route again. The sweep must skip removeraces (it empties the race table: every NPC created afterwards fails in its heart beat). Changed: /secure/cmds/builders/areaclone.lpc, /cmds/creators/cryptogram.lpc, /secure/cmds/admins/pause.lpc, /cmds/creators/html.lpc, /secure/cmds/admins/encre.lpc, /cmds/creators/resolve.lpc, /secure/cmds/creators/wget.lpc, /secure/sefun/astar.lpc.

add_action() registrations of functions nobody defines, second round: objects behind a macro path or that the static scan cannot settle (2026-10-21, KB 06 §7.349)

The first round's static scan could not read objects that inherit through a macro path (a #define ROOM STD_DIR "room/room" whose body starts with another macro) or an inherit it could not resolve, and skipped them as UNSURE; the second round fixed the resolver, rescanned, and asked function_exists() of those objects in a running driver. 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 3 registrations in 2 files (scripts/lpc_fix_dead_add_action.py), the verbs are left to their other handlers: /secure/obj/machine.lpc (check_it), /secure/obj/memo.lpc (clean_memo, erase_memo). Most frequent: check_it x1, clean_memo x1, erase_memo x1.

Combat probe with the seeded administrator (2026-10-22): clean

scripts/play_combat_probe.py dsIII --account ... --clone ...: the administrator cloned /domains/default/npc/tree, /domains/Praxis/cemetery/mon/ghosta, /domains/Praxis/obj/mon/balrog, /domains/campus/npc/charly, /domains/town/npc/thief, /domains/Ylsrim/npc/balrog into its room and attacked each for 20 s (6 fights read): no driver line and no error text on the screen. (KB 08 §10.16)

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. 2 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: 2 load at HEAD, 2 load now (0 newly loading, 0 regress).

Pointers (: name :) to functions the object never defines (2026-10-22, KB 06 §7.355)

A (: name :) in an item_desc / inquiry / chat_msg_combat names a function that neither the object, its headers nor its parents have a body for: it compiles, and the use raises *Undefined lfun pointer called: name (look THING / ask NPC about TOPIC fail on the spot, a combat or idle chat pointer fails inside the NPC's chat() and the rest of that heart beat is skipped). scripts/lpc_scan_callout_targets.py --kinds=lfun. 2 more function(s) stay missing (other 2): the prototype is there and the body is lost, or it was never written, and no sibling lib's copy of the file can be copied as it is (it lacks the function, needs helpers this lib does not have, or has another signature); nothing invented: /secure/lib/net/ftp.lpc:eventCmdPass, /secure/lib/net/h_ftpd.lpc:eventCmdPass.

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 3 subdirectories of log/: log/harass (lib/interface.lpc), log/imc2_server (secure/daemon/imc2server/server_log.h), log/secure/log (secure/lib/connect.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() is write_file(LOG_DIR + file, text) and raises the same error for a missing directory (measured). log/harass, log/imc2_server, log/secure/log 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.

Bare wizard command with a 0 argument (2026-10-27, KB 06 §7.336 Also)

After the permission gate, the command still handed 0 to sscanf / read_file / find_living. lpc_fix_bare_arg.py adds if (!arg) (or help(me) / GetHelp()) before the first use. See this commit for the files touched in this lib.