Dead Souls 3.0

✅ 可玩

dsIII

更新 d911539 2026-09-11 源码 下载 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()).