Rifts DS

✅ 可玩

riftsds

更新 3e5694f 2026-10-27 源码 下载 ZIP 上游 grav1tyzero/rifts-ds

▶ 开始游玩 · Play Now

A Dead Souls 3.9 lineage mudlib (same ds386/dsI/dsII/dsIII/dshakkard/deadsouls_fluffos engine family) reskinned onto Palladium's post-apocalyptic Rifts tabletop RPG setting -- not a duplicate of the already-onboarded rifts2, an unrelated DarkeLIB/Nightmare-lineage codebase that happens to use the same tabletop setting. What's actually playable is the stock Dead Souls demo world (campus/town/Praxis/Ylsrim/default/amigara) plus thin continent-domain stubs (Africa, Americas, Atlantis, Australia, China, Europe, Japan, Mexico, Russia, SouthAmericas and more), with real Rifts-flavored touches layered onto that working base: a P.P.E. (Potential Psychic Energy) resource on the HUD and score sheet alongside HP/SP, a custom greathorneddragon race alongside the stock dwarf/elf/human set, and 'universal credits' as the town's currency. The town hub has Horace's General Supply Shop, the First Village Bank (with Zoe the bank teller), a Healer's Guild selling remedies, and a talking beggar on Saquivor Road who hands new arrivals a real map item. The repo itself is a genuine slice of someone's personal, in-progress mud rather than a finished release or a pristine archive -- it arrived with three live wizard accounts (gravity, thurtea, shrub) already in play and only ten total upstream commits, the last few just balancing tweaks ('multiple fixes credits added mana now ppe'). Its most striking piece of content, though, doesn't work: domains/omega is 150 files describing a detailed multi-floor sci-fi space station ('Omega Station') with NPCs, a shop, and an elevator, backed by a further 33-file domains/common item/ship library -- but every file in both trees inherits base classes (LIVING, M_VENDOR, ARMOUR, ELEVATOR, and more) that are never defined anywhere in the repository, so none of it compiles or loads; the author's own half-built compat.h shim shows they were actively working toward supporting it before development stopped. The bundled domains/std Lima-style builder-school example area (315 files, stock to Dead Souls 3.9 itself) hits the identical missing-framework problem.

README

A Dead Souls mudlib (the same well-known generic LPC starter codebase as ds386/dsI/dsII/dsIII elsewhere in this collection, here on version 3.9) reskinned onto Palladium's post-apocalyptic Rifts tabletop RPG setting: characters track a P.P.E. (Potential Psychic Energy) resource alongside the usual HP/SP, and the race list includes a custom greathorneddragon option alongside the stock Dead Souls choices (dwarf, elf, human, half-elf, half-orc, halfling, orc, poleepkwa).

Not to be confused with rifts2 (RiftsMUD2) elsewhere in this collection -- that's a completely different, unrelated codebase (DarkeLIB/Nightmare lineage) that happens to use the same Rifts setting.

Important: the "Omega Station" content does not currently work

This lib ships a large, detailed domains/omega area (a multi-floor sci-fi space station, "Omega Station," with named NPCs, a shop, and an elevator connecting nine levels) plus supporting domains/common sci-fi item/ship content. Neither compiles or loads. Both depend on a whole second set of base object classes (a LIVING/CONTAINER/ ARMOUR/ELEVATOR/vendor-and-container "mixin" framework) that the original repository never actually included -- not a conversion mistake on this project's part, and not something a quick patch can fix (see NOTES.md and AGENTS.md §7.164 for the full technical writeup). The same problem affects a bundled stock-Dead-Souls-3.9 "builder school" tutorial area (domains/std) that ships with this version of the engine. All of this content is left in place, exactly as it arrived, in case a future pass wants to build out the missing framework -- but as of this onboarding, none of it is reachable or playable.

What actually works

Play online

WASM status: playable. Shared WASM driver. socket_address() in secure/sefun/sefun.lpc is #ifdef __PACKAGE_SOCKETS__ so the eager simul_efun compiles without the sockets package (same treatment as dsIII/ds386). Admin login (fluffos / Mud@2026) into First Village Bank, plus score ("Fluffos the unaccomplished") / quit, verified with scripts/wasm_client.js. Play: https://mudlibs.fluffos.info/riftsds/

Admin account

Warning: Mud@2026 is a published default for local play. Change it
before exposing this to a public network.

Run locally

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

Game port: 40257.

NOTES · 移植与修复记录

grav1tyzero/rifts-ds → riftsds

Source: git clone https://github.com/grav1tyzero/rifts-ds (cloned 2026-08-30). Mudlib root is the repo's lib/ directory; bin/, extra/ (a wolfpaw/ subdir containing classic MudOS driver-build headers macros.h/configure.h/system_libs -- driver source, not mudlib code), fluffos-2.23-ds03/ (a full bundled driver source tree), win32/, and the top-level license/readme/batch files were not converted. Slug riftsds, number 955, port 40257.

Dead Souls 3.9 lineage -- same base engine family as the already- onboarded ds386/dsI/dsII/dsIII/dshakkard/deadsouls_fluffos (§2.1 lineage confirmation: secure/daemon/master.lpc is near-byte-identical to ds386's own pre-fix raw baseline, differing only in the same static→nosave/.c→.lpc conversion artifacts). The ASCII boot banner reports "Mudlib: Dead Souls 3.9" (a later point release than ds386's 3.8.6), which explains the bundled domains/std Lima-style builder-school example area that ds386 doesn't have. Not a duplicate of the already-onboarded rifts2 (tsathoqqua/RiftsMUD2) -- that lib is DarkeLIB/Nightmare lineage, a completely different codebase that happens to use the same tabletop setting.

This repo is a live/played development snapshot, not a pristine archive. It arrived with three real pre-existing wizard accounts (gravity, thurtea, shrub -- all full admins per secure/cfg/groups.cfg), a huge number of realms/thurtea/tmp/*.lpc scratch files (leftover ed/eval-session temp files from live development, hundreds of randomly-named files), and only 10 total git commits upstream ("money pops up a lot more than I thought", "multiple fixes credits added mana now ppe", etc.) -- this reads as one developer's personal in-progress mud, not a finished release.

1. Conversion

convert_lib.sh ran normally (GB18030 default encoding -- this lib is pure English/ASCII, so the encoding pass was a no-op except for a handful of genuinely non-Chinese legacy-8-bit files, see below): 5605 already-UTF-8, 7 converted, 9 lossy, 33 skipped-binary. 3219 .lpc files after rename, 747 literal .c" references fixed, 32 local angle-bracket includes converted, 1605 files touched by the static→nosave sweep.

Lossy-conversion files, all pre-existing (not conversion fallout), all fixed:

2. Ported ~160 already-proven ds386 driver-compat fixes wholesale

Confirmed via a fresh from-scratch reconversion of ds386/raw (into a scratch dir, matching this session's own convert_lib.sh output byte- for-byte) that large parts of this lib's shared "engine" tree (outside domains/) are byte-identical to ds386's own PRE-FIX baseline. A Python script walked every file under secure/, lib/, cmds/, daemon/, std/, obj/, verbs/, powers/, cfg/, include/, ftp/, open/, shadows/, doc/, and domains/; wherever this lib's file was byte-identical to ds386's freshly-reconverted (unfixed) version of the same path AND ds386's own committed (fixed) version of that path differs from the unfixed baseline, this lib's file was directly overwritten with ds386's fixed version. 160 files ported this way, including the highest-impact fixes from ds386's own onboarding: secure/daemon/master.lpc (uid stubs, runtime_config.h already swapped for the driver's canonical copy), secure/sefun/ economy.lpc (the to_int()-missing float-corruption bug, AGENTS.md §7.121), lib/editor.lpc/lib/interactive.lpc/lib/nmsh.lpc (the OLD_ED single-entry-ed() rewrite, AGENTS.md §6.2 -- see below for 2 files that needed the SAME fix applied by hand since their content had locally diverged from ds386's baseline), secure/lib/connect.real (the static/array-idiom fixes for Dead Souls' self-rewriting login object), secure/include/lib.h (LIB_REMOTE stale path), lib/events/ look.lpc+look_in.lpc (the SetLong/SetInternalDesc narrow-string- type bug, AGENTS.md §7.81-class), daemon/command.lpc (the .c→.lpc filename-slice bug that broke the stock score command), and secure/sefun/sockets.lpc/instances.lpc (WASM sockets-package- absent gutting -- not yet verified under WASM here, see below).

~724 other shared-tree files diverged from ds386's baseline (this lib is a materially newer/extended Dead Souls snapshot -- new systems not present in ds386 at all: a magic-point/P.P.E. resource, parties, voting, teacher/apprentice skill training) and were left untouched by the port; each one that actually failed to compile was triaged individually (§4 below).

3. Manual fixes beyond the ported set

4. SEVERE FINDING: domains/omega, domains/common, and the bundled domains/std example area are ALL non-functional -- a whole missing base-object framework, never committed to this repo

This is the single most important finding of this onboarding. See AGENTS.md §7.164 for the full catalogued writeup; summary here.

The task brief for this onboarding (and the repo's own file layout) strongly suggested domains/omega -- 150 files describing "Omega Station," a detailed multi-floor sci-fi space station with NPCs, a shop, and a working elevator -- was this lib's standout content. It is not functional. Every one of the 150 files (148 with real code, 2 are empty 0-byte stubs) fails to compile, because they inherit classes that are #defined nowhere in the entire repository: LIVING, M_VENDOR, CONTAINER, M_OPENABLE, M_WEARABLE, M_GETTABLE, ARMOUR, INDOOR_ROOM, ELEVATOR. Checked exhaustively: the whole mudlib tree, the bundled fluffos-2.23-ds03/ driver source, and even the non-mudlib top-level extra/wolfpaw/ directory (which turned out to be classic MudOS driver-BUILD headers -- macros.h/configure.h -- not mudlib code at all, a red herring this session chased down before ruling it out).

domains/common (33 files, shared sci-fi item/ship infrastructure -- WEAPON, DRINK, ITEM_DECAY_FOOD, SPACESHIP) hits the identical problem, evidently meant to back domains/omega. And bundled stock Dead Souls 3.9 itself ships a large Lima-style "builder school" tutorial/example area at domains/std (315 files -- a completely different missing-macro set: KEY, OBJ, PARAM_OBJ, BOOK, VEHICLE, ADVERSARY, STOCK_MASTER, RANGED_WEAPON, LIVING, INDOOR_ROOM) -- same failure shape, different intended framework (this one looks like it's meant to be a well-known classic LPMud "Lima"/LPUniversity teaching area, judging by the school/A/F/M/O/R/ W/X letter-subdirectory convention and a lima_guide.lpc/domains/ std/lima/ presence).

Together these three trees account for 496 of the lib's 519 total lpcc_check.sh compile failures (148 + 33 + 315) -- one root cause, not 496 separate bugs.

This is not a gap this project's conversion introduced. secure/ include/compat.h (gated live: COMPAT_MODE 1 in global.h, genuinely wired into every object's global include) proves the repo's own author was ACTIVELY building a compatibility shim for exactly this purpose -- it correctly maps ROOM/OBJECT/DAEMON/MONSTER/VAULT and ~50 lowercase set_*/query_* verb names (set_short→SetShort, etc.) onto this lib's own Dead Souls CamelCase API -- but it simply never got the additional ~18 class macros these three content trees actually need. The upstream repo's own git log (only 10 commits total: "Initial commit" bulk-imports these large trees, then all follow-up commits are small numeric tweaks -- "money pops up a lot more than I thought", "multiple fixes credits added mana now ppe") is consistent with one developer's personal, mid-construction mud: the big content trees were bulk-imported once and the compat layer was never finished before development moved on to other systems.

Confirmed live, not just via the batch compile sweep. Logged in as the seeded fluffos admin and ran goto /domains/omega/room/ elevator_center -- the driver echoed the exact compile error interactively (error: syntax error, unexpected L_IDENTIFIER ... inherit ELEVATOR;) followed by *No program in object '/domains/ omega/room/elevator_center'! on the subsequent reference, then Could not load that location. goto /domains/omega/npc/hugo failed the same way. As a control, goto /domains/Praxis/supply2 (stock Dead Souls content, fixed above) loaded correctly and printed its real room description -- confirming the failure is specific to the three missing-framework trees, not a general problem with goto/this driver/this lib's boot.

No attempt was made to author the missing framework. Guessing at correct implementations for LIVING/CONTAINER/ARMOUR/ELEVATOR/ M_VENDOR/etc. (and the completely separate KEY/OBJ/ADVERSARY/ STOCK_MASTER/etc. set for domains/std) from ~500 call sites' worth of usage would mean writing a second mudlib engine from scratch, not fixing a bug -- this project's established policy (ds386's own LIB_CAPTURE/jar.lpc precedent: "implementing an entire mechanic from a one-line hint would be inventing content") applies at much larger scale here. domains/omega and domains/common are left exactly as shipped (uncompilable, documented); nothing was deleted.

What DOES actually work: the stock Dead Souls 3.9 base itself, and every domain built on its real, existing API -- Africa/Americas/Atlantis/Australia/AustraliaOutback/China/ Europe/Japan/Mexico/Russia/SouthAmericas (thin stock-base stubs, as expected and documented for Dead-Souls-lineage variants elsewhere in this corpus), campus/town/Praxis/Ylsrim/default/ amigara (the standard Dead Souls demo areas, same as ds386), plus this lib's own Rifts-flavored additions layered onto the REAL API: Rifts races (greathorneddragon alongside the stock dwarf/elf/human/ etc. set, secure/cfg/races/greathorneddragon), a P.P.E. (Potential Psychic Energy) resource shown on the prompt and score sheet alongside HP/SP, and Rifts-flavored currency/vendor content in the town.

5. lpcc_check.sh compile sweep

Ran under (ulimit -v 8388608; ...) given the tree size (3219 files, larger than ds386's 2427). Final: 2700/3219 pass, 519 fail, all 519 fully triaged and explained:

Zero unexplained failures.

6. Live playthrough (native driver)

Full continuous session via scripts/mudclient.py against ~/src/fluffos/build-debug/src/driver config.fluffos.

1. Fresh boot: log/debug.log clean, zero uncaught errors (only the expected benign nosave-on-function warnings, AGENTS.md §4.3), Accepting telnet connections/Initializations complete. 2. Registered a fresh test character, Qintestomegab, through the full flow (name → confirm → age gate → screen-reader prompt → password/confirm → gender → email → race pick human → two news pagers) -- landed in the start room wearing a t-shirt and jeans, the HUD prompt showing hp/ppe/sp (P.P.E. being this lib's Rifts- specific addition to the stock HP/SP pair). 3. look: correct room description/exits every time, across multiple rooms (start room, Horace's General Supply Shop, First Village Bank with "Zoe the bank teller" present). 4. score: correct full character sheet (name, level, race, class, native town, faith, birth date, training points, feeling/food/ drink/alcohol status, XP-to-next-level, customization points) -- confirmed both as a fresh player and as the seeded admin. 5. i (inventory): correct (starting Player's Handbook + worn t-shirt and jeans). 6. Reconnect path verified distinct from fresh registration (a second connection while the first session was still open correctly showed Reconnected./[X has rejoined]). 7. Admin verification: seeded fluffos per §3 above; confirmed live via the admin-only room-path annotation on look, the admin-FAQ welcome banner, and successful goto into real (stock) content. 8. Omega domain: directly navigated via goto (see §4) -- confirmed non-functional live, exactly matching the compile-sweep finding. This was the task's own explicit ask ("navigate into the omega domain specifically to verify its content actually works") and the honest answer is that it does not, for reasons entirely external to anything this onboarding pass could fix. 9. quit: clean disconnect (Please come back another time!, [Fluffos quits Rifts DS], worn items correctly stripped on the way out).

7. WASM status

Measured 2026-09-03 against the shared ~/src/fluffos/build-wasm. secure/sefun/sockets.lpc was already stubbed, but the eager secure/sefun/sefun.lpc still called efun::socket_address() unconditionally -- "Unknown efun: socket_address", simul_efun not loadable, boot failed. Guarded with #ifdef __PACKAGE_SOCKETS__ (same as dsIII/ds386). After that, scripts/wasm_client.js logged in as fluffos / Mud@2026 into First Village Bank, score showed "Fluffos the unaccomplished", hp: 440/440, quit printed "Please come back another time!" secure/daemon/instances.lpc still fails to compile on socket_status() during quit's channel broadcast — a graceful skip, not on the login path. Shop/combat/death were not exercised this pass. Do not loop-reboot (I3).

§10.7 deep functional test (2026-08-31, round two)

Full continuous playthrough against the shared ~/src/fluffos/build- debug/src/driver config.fluffos (no dedicated worktree needed), deliberately exercising the WORKING stock-Dead-Souls content per this session's own brief -- the confirmed-broken domains/omega/domains/ common/domains/std gap (§4 above, AGENTS.md §7.164) was not re-litigated or re-poked at beyond a single deliberate negative-control clone (below). A fresh throwaway mortal registration (Vantharion, human) through the full chargen ritual (name validation, age gate, screen-reader prompt, password, gender, email, race pick) landed correctly in the start room; enter town reached the real, working village hub. Also re-verified the seeded fluffos admin account (§1.5/AGENTS.md's Dead-Souls admin-seeding note) still logs in correctly and shows the wizard room-path annotation.

What was tested and confirmed working -- zero programming bugs found this pass

Minor, unresolved cosmetic observations (not fixed -- neither blocks anything or has a confirmed severe effect; scope discipline against guessing)

Not reached this session, flagged honestly

An extended idle window (not a soak-test bug)

The driver was left running considerably longer than the standard ~200s idle target this pass (a background-task notification delay outside this session's control stretched the wait to several hours of real wall-clock time) -- reported here for the record since it's an unusually long uptime, not because anything was found: debug.log remained completely absent the entire time, and memory growth (~1.5GB -> ~5.6GB RSS) is consistent with this driver's own already-documented ambient NPC heart_beat()/lazy-compile behavior (AGENTS.md §10.8) over that much extra wall-clock time, not a new leak specific to this lib.

Files modified this pass

None -- this pass found zero programming bugs. Runtime state generated during testing (the fluffos admin's updated save, the throwaway Vantharion character's save/mailbox, and a few daemon .o save files) was reverted before committing, per this project's own runtime-state policy.

§10.7 deep functional test (2026-09-01, round three) -- guild/class progression was completely broken, fixed

This pass deliberately targeted the two angles round two's own "Not reached" section flagged: skill/guild-class progression, and a completed two-sided (buy+sell) shop transaction. It also tried tell (including a nonexistent recipient) and a battery of malformed/empty command inputs. Both flagged gaps turned into real findings.

BUG 1 (SEVERE): every Praxis guild-join point silently failed to

ever change the player's class -- SetClass() called where

ChangeClass() was required

Symptom: domains/Praxis/fighter_join.lpc's become fighter (and the identical pattern in cleric_join.lpc, mage_join.lpc, monk_join.lpc, kataan_join.lpc, rogue_join.lpc) always printed its success message ("The Great Warrior initiates you into the class of fighters.") but score/skills afterward still showed the character as Explorer with none of the class's real skills -- 100% reproducible, on a fresh level-1 character, every time, for every one of the six guild-join rooms.

Root cause: all six become()/join() handlers call this_player()->SetClass(class_name) directly. lib/classes.lpc's SetClass() is NOT the entry point for a fresh character's *first* class -- its own header comment says so ("pass by reference... please see the FAQ") and its logic proves it: if(Class){ if(!high_mortalp()) return Class; ... } -- if the player already has ANY non-zero Class value (and every character does, since lib/player.lpc:317 sets Class = "explorer" for everyone at creation), SetClass() treats the call as multi-classing (bolting a second class onto an existing one) and silently refuses unless the caller is high_mortalp() (a non-creator player above level 24 -- Rifts DS's own "veteran mortal" tier for genuine multi-classing, secure/sefun/ pointers.lpc:95). A fresh level-1 (or even a creator, since high_mortalp() explicitly excludes creatorp() accounts too -- confirmed live: fluffos itself could never pass this gate either) explorer can never satisfy that gate, so the assignment is dropped on the floor every time, with the success message printed regardless because it's written to the screen *before* the doomed SetClass() call.

The correct entry point for a fresh explorer's first class is ChangeClass() (same file, lib/classes.lpc): it explicitly does Class = 0 before delegating to SetClass(), which is exactly what bypasses the multi-class gate for a legitimate first-time assignment, then restores the player's level via ChangeLevel(lvl). Proof this is the established, working convention in this very lib: domains/town/npc/herkimer.lpc:111 (the real, working, already round-two-tested Mage's Guild join command, "ask herkimer to join") calls ob->ChangeClass("mage"), not SetClass(). The six Praxis files are the only call sites in the whole corpus using the wrong one (grep -rln ChangeClass turns up exactly lib/classes.lpc, herkimer.lpc, Ylsrim/npc/roshd.lpc, and secure/lib/connect.lpc -- none of the six broken join files).

Fix: this_player()->SetClass("X") -> this_player()-> ChangeClass("X") in:

Live-verified: fresh boot, admin fluffos (level 1, Explorer) -> goto /domains/Praxis/fighter_join -> become fighter -> score now correctly shows Class: [Fighter] -> skills now correctly shows the real fighter primary skill set (blade attack, blunt attack, melee attack, multi-hand, multi-weapon, etc., all at the class file's configured 4/8 starting level) in place of Explorer's skills. Before the fix, score stayed Explorer and skills stayed unchanged no matter how many times become fighter was run. debug.log stayed empty and stdout showed no new errors across the whole test (expected -- this was a silent no-op, not a crash, per AGENTS.md §10.9's "invisible failure" class).

Known residual content gap, NOT fixed (out of scope, same shape as the already-documented domains/omega/common/std framework gap but much smaller): secure/cfg/classes/ only ships cleric, explorer, fighter, mage, and thief class definitions -- monk_join.lpc, kataan_join.lpc, and rogue_join.lpc call ChangeClass("monk")/("kataan")/("rogue"), none of which have a matching config file. Even with the SetClass->ChangeClass fix applied, CLASSES_D->SetClass() (daemon/classes.lpc:123) returns immediately for an unconfigured class name, so become monk/become kataan/become rogue still silently leave the player as Explorer -- this is a missing-content-data gap (nobody ever authored a monk/ kataan/rogue class file), not a wrong-function-call bug, and authoring three balanced class skill-lists from scratch would be inventing content, not fixing one -- left alone per this project's established policy. fighter/cleric/mage (and presumably thief, not separately live-tested but config-file-complete and structurally identical) are now fully functional via their Praxis join rooms.

BUG 2 (moderate, fixed as part of the same investigation):

query_cap_name()/query_name()/query_gender() are undefined

on the player object -- three missing entries in the lib's own

compat shim

While root-causing BUG 1, call me->query_cap_name() (admin call command, a direct function_exists() probe) confirmed this function plain does not exist anywhere on the player object -- grep -rln "->query_cap_name(" domains/Praxis turns up 51 call sites across 30 files (plus one in realms/gravity/cmds/testooc.lpc), all from the same bulk-imported content lineage documented in §4 above. Two siblings, query_name() (40 call sites) and query_gender() (8 call sites), are equally undefined.

This is the exact same class of gap as the already-documented secure/include/compat.h shim (§4: "the repo's own author was ACTIVELY building a compatibility shim... but it simply never got the additional ~18 class macros" for the three fully-broken trees) -- except this time the shim already has 50 other query_*/set_* mappings covering exactly this kind of naming-convention mismatch (#define query_class GetClass, #define query_open GetOpen, etc.), it just never got these three specific ones added, even though the Praxis tree that needs them is otherwise fully working, non-broken content. Confirmed live: before the fix, preview()'s say(this_player ()->query_cap_name()+" seeks to learn about fighter.", ...) line never produced its "X seeks to learn..." room broadcast to bystanders (silently absorbed); query_class() -- which DOES have a compat.h mapping already -- correctly resolved and printed "Welcome, explorer!" the whole time, proving the macro-substitution mechanism itself works fine and is exactly what was missing for the other three names.

Fix: added three lines to secure/include/compat.h, following the file's own established pattern exactly:

#define query_name                      GetName
#define query_cap_name                  GetCapName
#define query_gender                    GetGender

GetName(), GetCapName(), and GetGender() are all real, extensively-used lfuns already defined on the player object (lib/ player.lpc:531,563, lib/race.lpc:216) -- confirmed via grep -rln GetCapName returning 60+ genuine call sites across the working parts of this same lib (verbs/players/bump.lpc, secure/cmds/players/ tell.lpc, secure/daemon/chat.lpc, etc.), so this is restoring the established convention, not inventing a new one. Two stray hits inside already-broken domains/std/domains/common files (which don't compile at all, per §4) are harmless dead code either way. Because global.h (which conditionally #includes compat.h under COMPAT_MODE) is this driver's configured global include file, every .lpc file in the mudlib picks up the three new mappings automatically on next compile -- no per-file edits needed beyond the one header.

Live-verified: post-fix, call me->query_cap_name() (raw function_exists() probe) still correctly reports "not in OBJ" (a runtime string handed to call_other is never macro-expanded -- expected, since #define is a compile-time textual substitution over literal source identifiers, not a runtime symbol lookup), but the *compiled* become()/preview() functions in fighter_join.lpc -- which contain the literal identifier query_cap_name in their own source, and get recompiled fresh against the updated compat.h on driver restart -- now correctly resolve to GetCapName() at compile time. No regressions: full driver reboot, zero new compile errors, zero debug.log/stdout errors across the whole remaining test session.

Skill training (organic teacher-NPC route), verified working

With fluffos reverted to Explorer (which carries magic attack/ magic defense as innate secondary skills) and 5 training points granted via call me->AddTrainingPoints(5) (the admin-command shortcut this project's own brief for this pass explicitly sanctions in place of a lengthy real XP grind), goto /domains/town/room/ training -> ask radagast to train magic attack correctly matched against lib/base_trainer.lpc's eventTrain() (found the skill in the player's own skill list, found available training points, started a real multi-stage ContinueTraining() call_out sequence) and printed "Radagast begins teaching you about the skill of magic attack." -- confirming the organic NPC-trainer skill-progression path (distinct from both the Healer's Guild buy-a-service shop and the Praxis guild-join mechanism above) is genuinely wired up and functional. Not chased to full completion (the training sequence runs across several TRAINING_WAIT-spaced call_out()s) since the mechanism's entry path was the object of the test, not the multi-minute wait.

Two-sided shop transaction, completed and verified end-to-end

Registered a fresh throwaway mortal (Testshopper, human), granted currency via call %testshopper->AddCurrency("universal credits", N) (admin shortcut, same sanctioned pattern as above -- note the lowercase key name: find_player()/to_object()'s %name token lookup is case-sensitive on the saved keyname, %Testshopper capitalized fails with "Cannot identify any object" while %testshopper succeeds). At the Healer's Guild (domains/town/room/ healer, reached via enter town -> north -> north -> enter guild):

Both the earlier round-two insufficient-funds refusal and this pass's fully-funded buy+sell round trip are now live-verified for the same shop, closing out this checklist item completely.

tell, briefly

tell testshopper hello there (creator -> mortal, both connected) worked correctly in both directions and correctly charged 15 PPE (a deliberate Rifts-flavored "telepathic tell costs psychic energy" design choice, not a bug). tell <nonexistent-name> hello fell through to a confusing but 30-year-old stock secure/cmds/players/ tell.lpc (Descartes of Borg 950523) code path that attempts an inter-mud-instance tell lookup and then prints the generic "Tell whom what?" instead of a clear "no such player" -- odd UX, but it's unmodified stock Dead Souls library logic (not this lib's own bug), causes no crash, and is exactly the kind of pre-existing-library "design as shipped" case this project's scope explicitly says to leave alone.

Adversarial/malformed input, briefly

become (no arg), become fighter fighter fighter, become ''''"";;--, buy (no args), buy 99999999999999999999 from james, sell (no args) all produced sane rejection messages ("Become what?", "You cannot become that here.", "Buy what from whom?", "There is no james here." -- correctly, since james wasn't in the room this was tried from -- "Sell what to whom?") with zero crashes and zero debug.log/stdout growth.

Files modified this pass

Runtime state generated during testing (fluffos's class reverted back to Explorer via call me->ChangeClass("explorer") before finishing, the throwaway Testshopper character's save/postal files, and the incidental daemon/RELEASE_NOTES_HTTP/mudlist.txt .o/txt churn from having the driver up) was reverted before committing, per this project's own runtime-state policy.

Heartbeat hobbled(this_player()) (2026-09-04)

Two-sided shop already verified 2026-09-01; not re-run. Same lib/body.lpc heart_beat hobbled(this_object()) plus disable.lpc objectp guard as dsIII. Verb-file checks left alone. Admin fluffos / Mud@2026 logged in after the edit (village bank / Zoe). Save-file churn from this boot left uncommitted.

2026-10-03 — elevator buttons

domains/town/obj/ebutton1.lpc and ebutton2.lpc called car->SetDoor(1), the name the elevator had before it renamed its own function SetDoorClosed (it shadowed LIB_EXITS::SetDoor(dir, file)); the old name resolves to the inherited exit function and the call is a silent no-op, so the call buttons never closed the car door. Changed to SetDoorClosed(1), as in dsIII and ds386 (KB 06 §7.211). The rest of this lib's compile warnings are still to do (queue: Dead Souls siblings).

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

Method: as in dshakkard (same twin of ds386, KB 06 §7.213-§7.216): ds386's commit applied with patch (1476 files, 42 hunks and 31 absent files left over), scripts/lpc_warnings.py riftsds --fix for the mechanical hunks that did not apply (183 nosave functions -> protected, 23 dropped, 27 escapes, 8 varargs, 2 bare returns, 19 unused locals), scripts/lpc_move_decl.py (master.lpc), scripts/lpc_fix_strptr.py (orc_valley/guard.lpc), scripts/lpc_fix_unused_init.py (cmds/players/equipment.lpc), scripts/lpc_diamonds.py --fix-redundant (domains/town/meals/worm.lpc: LIB_BAIT_WITH already inside LIB_MEAL, which this lib has and ds386 does not) and by hand the empty if in daemon/intermud.lpc, room.lpc's SetSky() and opcprof.lpc (the efun is gone from FluffOS). Oracle (263 programs, HEAD against working tree): 1763 public -> protected flips, 46 definer changes (the same explicit choices as dshakkard, including the horse's five mount applies whose bodies are identical in LIB_MOUNT and LIB_LIVING), 455 duplicate variables removed or renamed, 92 enter/knock/ scratch/door functions the three dummy hybrids (trashcan, riverwater, seawater) no longer inherit, 88 glow rows. Live, paired: the trainer (HEAD "I am already training you!" after a pupil left and came back within two seconds, now "You will have to start your studies anew"), gallows and stone (HEAD printed long_func), fixtures, search, zpem, horse, death and corpse.

Result: 6213 -> 534 distinct diagnostics, none of them a warning in a loadable file. 533 are the errors of the 518 files this lib has never been able to compile (the Lima-style domains/std, domains/omega, domains/common and creator-realm content, triaged in the 2026-08 sections above: missing ITEM_DECAY_FOOD/ GUILD_D/RACE_D/ROOM_NEWBIE and headers that do not exist); the last is an Expression has no side effects in a creator's eval scratch file the archive shipped (secure/tmp/thurtea_CMD_EVAL_TMP_FILE.lpc, which the eval command overwrites on every use). The comma pointers (: this_object(), "fn" :) of the Praxis domain (29 sites in 21 files) are still to do (KB 04 §6.10).

深度功能测试(§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 riftsds --pre HEAD --apply (all 21 are token twins: 12 byte-identical, 9 spliced into riftsds's own spacing). 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-04)— last #pragma no_warnings removed

domains/std/{Attic,Church,wiz_hall}.lpc still opened with #pragma no_warnings. They inherit /std/indoor_room, which this lib does not have, so they never compiled and the pragma hid nothing; it is gone, and no lib file carries it any more.

深度功能测试(§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 riftsds: 2 file(s) renamed to lower case (.C -> .lpc, .H -> .h) and 1 path reference(s) in 1 file(s) rewritten to the new spelling; 0 skipped, 0 ambiguous. Load check of the 4 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 -> 1 PASS, 0 that loaded before fail now, 1 more load.

2 files moved (2 .lpc); mostly under domains/std/rooms (1), domains/std/Wizroom.lpc (1). A rescan with scripts/lpc_warnings.py riftsds is still due: the renamed files are compiled for the first time.

深度功能测试(§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 前后对比,主控载入段只少了这两行。

Save files the onboarding transcode broke (2026-10-15, KB 03 §4.4)

/save/intermud.o: the archive's Windows-1252 bytes (a \x92 apostrophe, ¿, ¡) were run through iconv -f gb18030 -c, which dropped an undecodable byte together with the quote after it, so restore_object() stopped with Illegal mapping format and the object started empty. Re-decoded from raw/ as Windows-1252 (scripts/lpc_save_encoding.py --from-raw); the driver restores each one now (scripts/lpc_save_restore_check.py).

Save files restore_object() refused (2026-10-15, KB 03 §4.4)

domains/campus/save/charles.o、domains/campus/save/charly.o、secure/save/mudinfo.o: a raw newline inside a string (save_object() writes it as \r; restore reads line by line and the string never closes) or a blank line (Illegal file format), so the object started empty. Newlines written as \r, blank lines dropped (scripts/lpc_save_encoding.py --from-raw); the driver restores each one now (scripts/lpc_save_restore_check.py).

Restored base-class macros (2026-10-18, KB 04 6.13)

inherit IDENT; named a macro that no header of this lib defines (syntax error, unexpected L_IDENTIFIER, expecting L_STRING or '('), so the object never loaded; the base file is in the lib and other libs map the macro to it. Added to the global header secure/include/global.h: BOOK -> /obj/book (2 file(s)) (scripts/lpc_restore_macros.py). Loaded one by one with lpcc afterwards: 0 of 2 pass, the other 2 stop on another error (domains/common/item/quest_todo.lpc: Undefined function set_proper_name; domains/std/lima_guide.lpc: Undefined function add_id).

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, 9 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), /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).

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). Neither verbs/spells nor verbs/undead was in wasm_keep_dirs.txt for this lib, so the site was affected too. 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): the omega NPCs do not compile

scripts/play_combat_probe.py riftsds --account fluffos:... --clone ... clones each opponent into the administrator's room and attacks it at once. Two files of the omega domain fail to clone: /domains/omega/npc/hugo.lpc and /domains/omega/npc/floor8/doc_williams.lpc start with inherit LIVING; inherit M_VENDOR; (Lima macros), which a Dead Souls base does not define (3:9: syntax error, unexpected L_IDENTIFIER, expecting L_STRING or '('): the domain was written for another lib and its NPCs never loaded here. Not changed (a port, not a repair). The Dead Souls NPCs of the lib fight cleanly.

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.