info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
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
- The real, functioning Dead Souls 3.9 base: a start room,
enter towninto a small village (a general store, a bank with a teller, guild halls), the classiced/qedline editor, mail, bulletin boards, and an in-game creation system for builders. - A set of thin (intentionally minimal, matching this project's usual treatment of stock-base Dead-Souls-lineage content) continent-themed stub domains: Africa, Americas, Atlantis, Australia, Australia Outback, China, Europe, Japan, Mexico, Russia, South America.
- The standard Dead Souls demo areas: a small campus,
Praxis, andYlsrim. - This lib's own Rifts flavor layered on top of all of the above: the P.P.E. stat, the extra
greathorneddragonrace, and Rifts-flavored currency/vendor dialogue in town.
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
- ID:
fluffos - Password:
Mud@2026 - This repository arrived as a live, previously-played snapshot (three real wizard accounts already existed:
gravity,thurtea,shrub) rather than a fresh install, so the usual first-boot admin wizard did not apply.fluffoswas added to every group insecure/cfg/ groups.cfgalongside the existing accounts, registered normally as a player, then promoted to creator status the same way the mudlib's ownencreadmin command does it (moving the save file from the players directory to the creators directory) -- seeNOTES.mdfor the exact steps if this ever needs re-seeding.
Warning: Mud@2026 is a published default for local play. Change itbefore exposing this to a public network.
Run locally
cd libs/riftsds
~/src/fluffos/build-debug/src/driver config.fluffosGame 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:
doc/CREDITS,doc/help/players/credits,domains/town/txt/mystery.txt-- Latin-1/MacRoman punctuation, byte- identical tods386's own known-bad set for these exact filenames (this lineage's stock Dead Souls doc tree). Fixed by directly copyingds386's already-corrected versions of these three files (confirmed byte-identical match before copying).secure/sefun/translate.lpc(the "speak a foreign language badly" word-scramble table) -- one Finnish entry ("kyrp\xe4","r\xe4k\xe4") is genuine Latin-1, and GB18030 decoding silently mangled it into"kyrp,"r鋕,"(the classiciconv -c-eats-an-adjacent-real-byte shape, AGENTS.md §4.1 -- here it also ate the string's own closing quote and the following comma). A second entry ("\xd0\xa5\xd0\xa3\xd0\x98\xd0\xa2\xd0\x90", Cyrillic "ХУИТА") was ALREADY valid UTF-8 but got re-mangled into other valid-but-wrong CJK codepoints by the same GB18030 pass. Both fixed by hand, matchingds386's own already-published fix for this exact byte-for-byte-shared file.save/intermud.o,secure/save/functions.o,realms/thurtea/tmp/ *.c(4 files): stale runtime caches (an I3-router mudlist cache, an empty function-signature cache, and 4 of the hundreds ofedscratch temp files) -- harmless, left as-is (all valid UTF-8 in their converted form, no fix needed).
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
- The
TYPE array NAMEidiom (AGENTS.md §4.3/§6.3), same driver build (ARRAY_RESERVED_WORDundefined, confirmed against this driver's ownoptions_internal.h): a quote/comment-aware Python sweep across the wholework/tree found and fixed 230 single- declarator instances across 62 files (object array x→object *x, etc.) plus 11 multi-declarator lines (string array a, b, c;→ all three get*, verified per-variable against actual usage --explode()/foreach/index-access confirms array use -- not blindly applied):daemon/classes.lpc,daemon/help.lpc,daemon/ seasons.lpc,daemon/races.lpc,domains/town/npc/brandy.lpc,secure/daemon/players.lpc(×3 lines),secure/cmds/admins/ mudconfig.lpc,lib/magic.lpc(send_to), andlib/chapel.lpc(Religion/Deities/DeityIds, initially missed in the first pass since it wasn't in the multi-declarator report handled at the same time -- caught by thelpcc_check.shsweep's "Bad assignment (string vs mixed*)" errors and fixed separately). New AGENTS.md §7.163: found one instance with aclasscompound type instead of a bare base type (lib/body.lpc'sclass MagicProtection array GetMagicProtection()), invisible to a plain-base-type regex. - The
.c→.lpcfilename-slice-length bug (AGENTS.md §7.118-class) -- several sites where the code checks a suffix against the 4-char.lpcstring but slices/truncates by the old 2-char.clength:daemon/help.lpc(3 sites inLoadIndices()'s helper closures + 1 inGetTopic()'s guard, this file wasn't part of the ported set since it had genuinely diverged fromds386's version),verbs/ builders/initfix.lpc,lib/std/boobytrap_object.lpc,secure/obj/ weirder.lpc(3 sites),secure/cmds/admins/mudconfig.lpc,secure/daemon/rooms.lpc,secure/sefun/sefun.lpc. All fixedlast(x,2)/[<2..]/[0..<3]→last(x,4)/[<4..]/[0..<5]paired with the matchingtruncate(x,2)→truncate(x,4). Verified via a corpus-wide re-grep that zero unrelated 2-char-suffix uses (.o,"/g",", ","--") were touched. lib/std/room.lpc'sSetLongwrapper was narrower than theSetInternalDescit forwards to (string SetLong(string str)vs the already-fixedmixed SetInternalDesc(mixed str)inlib/events/ look_in.lpc) -- widened tomixed SetLong(mixed str). This broke every room usingSetLong( (: SomeClosureFn :) )for a dynamic description (confirmed live:secure/room/router.lpcthrowsBad type for argument 1 of SetLong ( string vs function )without this fix). Two OTHERSetLongoverrides (lib/die.lpc,lib/ interactive.lpc) are also narrowlystring-typed but are only ever called with literal strings in this codebase -- left alone per scope discipline (no error, no fix).lib/player.lpc'sSetup()declaredstring oldparties = PARTY_D->GetOldParties();butGetOldParties()is declared (and actually returns)string *-- a plain wrong-type local variable, not the array-idiom bug. This crashedRemoveExtraChannels()(Bad type for argument 1 ( string * vs string )) on literally every successful login (the function runs unconditionally inSetup()). Fixed the declaration tostring *oldparties.domains/town/npc/zoe.lpc:SetCurrencies( ({ "universal credits"" }) );-- a stray doubled closing quote, present in the raw archive (confirmed viaraw/, not conversion fallout). Broke this NPC's compile with a cascadingEnd of file in string, which tookdomains/town/room/bank.lpcdown with it (Zoe stands in the bank). Fixed by removing the extra quote. Verified live: the bank room now loads correctly with "Zoe the bank teller" present.domains/Praxis/supply2.lpc:set_pre_exit_functions( ({ "north" }), ({ "go_north" }) );-- the exact same broken macro-aliased call (set_pre_exit_functionsis#defined straight toSetProperty(string, mixed)insecure/include/compat.h) already documented and fixed onds386's own copy of this identical file; every sibling room in the same directory (hall.lpc,hall2-5.lpc) already has the call commented out, confirming this never worked under any driver. Commented out identically, matchingds386's own fix verbatim (same file, same lineage).lib/interactive.lpc'squit()-path andlib/nmsh.lpc'sGetPrompt()both still had the pre-fixed_cmd()/query_ed_mode()calls fromAGENTS.md§6.2'sOLD_EDclass (their content had diverged fromds386's baseline enough that the bulk port skipped them) -- applied the identical remedyds386already proved: dropped the belt-and-suspenders manualed_cmd()save/close (the driver's owndestruct_object()already flushes a liveed_bufferon destruct underOLD_ED) and dropped the now-unreachablequery_ed_mode()-gated custom prompt branch (the driver prints its own ed-mode prompt directly from C while an editor session is open, bypassing this apply entirely).- 24
domains/<Continent>/virtual/{server,void}.lpcfiles (one pair per bundled continent-stub domain: Africa, Americas, Atlantis, Australia, AustraliaOutback, China, Europe, Japan, Mexico, Russia, SouthAmericas) all had#define __DIR__ "/domains/<Continent>/ virtual/"--__DIR__is a live compiler predefine on this driver (same class as AGENTS.md §7.39), so this iserror: Illegal to redefine a predefined value.on every one. This is the exact same virtual-server/void templateds386hit on its ownamigara/learningdomains, just replicated 12× here (once per continent stub) instead ofds386's 1×. Deleted all 24#definelines (the driver's live__DIR__produces the identical value, trailing slash included). The two matching top-level scaffold templates (std/virtual_void.txt,std/server.txt, with a literalCHANGEMEplaceholder) were correctly left untouched -- not meant to compile standalone, matching AGENTS.md'sarea_room.lpc/open/ prog.lpctemplate-scaffold precedent. - Admin account seeding -- see the new AGENTS.md §1.5 Dead-Souls- lineage sub-entry for the full writeup. Summary: this repo arrived PAST its own first-boot install wizard (three real wizard accounts already exist), so the usual "register through the bootstrap wizard" path doesn't apply. Added
fluffosto every line ofsecure/cfg/ groups.cfg(alongside the pre-existinggravity:thurtea:shrub, not replacing them), registeredfluffosthrough the ordinary NEW-player flow (passwordMud@2026), then with the driver stopped movedsecure/save/players/f/fluffos.otosecure/save/creators/f/ fluffos.ounchanged -- exactly what the stockencreadmin command does under the hood (creatorp()checks the live player object'sfile_name()againstDIR_CRES, not any ACL/rank table). Verified live:fluffosimmediately shows the admin room-path annotation and banner, andgoto/wizard commands work with no further seeding.
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:
- 496 -- the missing-framework finding above (§4): 148
domains/omega, 315domains/std, 33domains/common. - 9 -- personal wizard-realm scratch/test content (
realms/thurtea/tmp/*.lpc×5,realms/thurtea/legwand.lpc-- aSetPosition(int vs string)type error in a private test item --,realms/gravity/area/room/{startroom,wayne_manor_backroad}.lpc-- missingRACE_D/ROOM_NEWBIEmacros and genuine syntax errors --,realms/gravity/tmp/*.lpc×1). These are individual wizards' own private home-directory scratch/test content, not part of the shipped game world any ordinary player reaches -- left undocumented-further and unfixed, matching this project's treatment of admin-only/ out-of-scope tooling elsewhere. - 14 -- exactly matches
ds386's own already-documented "known non-functional" class (same files, several literally shared byte- for-byte withds386):obj/stargate.lpc(deliberate author anti-load trap),obj/area_room.lpc+open/prog.lpc(builder-tool template scaffolding,$S-placeholder/relative-include patterns never meant to compile standalone),domains/Ylsrim/broken/jar.lpc(pre-existing incomplete content in the archive's ownbroken/bin),secure/cmds/admins/opcprof.lpc(calls a MudOS efun never ported to FluffOS),secure/tmp/thurtea_CMD_EVAL_TMP_FILE.lpc(an auto-generatedcmd evalscratch file),secure/daemon/i3router/ server.lpc(optional admin-only Intermud-3 tooling, undefinedROUTER_NAME-- not on the boot/login/play path, matchesds386's own explicitly-left-alone i3router/imc2/ftp admin-tool list), anddaemon/help.lpc+secure/daemon/mudinfo.lpc+domains/campus/{chamber/elevator,room/monty,room/plab2}.lpc+domains/campus/npc/{charles,charly}.lpc(zero actual "error:" text in the failure log -- the AGENTS.md §10.4 "isolated single-file lpcc-batch-mode artifact" class;daemon/help.lpcin particular is proven fine live -- the driver's real boot compiled it clean andhelp-adjacent features worked throughout the playthrough).
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
- Movement/navigation: start room -> town intersection -> Saquivor Road (a talking beggar NPC gave a real map item) -> the Healer's Guild building, all with correct descriptions/exits/NPCs every time.
- Shop/economy: the Healer's Guild's real buy-a-service shop (
buy claritin from james) correctly refused an empty-walleted character with "You don't have enough universal credits to buy that." -- a genuine, live-verified insufficient-funds economy check, not just alistread. - Real combat, extensively: cloned a real stock-content NPC (
/domains/town/npc/orc, the ordinary Dead Souls API --inherit LIB_NPC) as the admin and fought it to the admin's own death. Limb-targeted damage was exchanged realistically in both directions for well over 30 rounds (hits, misses, "flourish of attacks," stunning blows, per-limb messages) with zero crash and zerodebug.loggrowth throughout. - Death and full resurrection cycle, live end-to-end -- normally one of the two hardest-to-reach §10.7 checklist items, verified for real here because the admin-vs-orc fight above went all the way:
SYSTEM <death> Orc has slain Fluffos.-> landed in/domains/ default/room/death(YOU ARE DEAD!, "You are in formless void without substance and outside time.") -> the room's ownregeneratecommand worked correctly, rematerializing the character back at/domains/default/room/startwith restored (if reduced) HP/PPE/SP. Not gated behindwizardp()the way some other libs' ghost-recovery flows are (AGENTS.md §7.68/§10.7 item 6a) -- this lib's own death system handles a wizard character exactly like an ordinary one. quit/ reconnect: clean disconnect (Please come back another time!),log/debug.lognever created at any point in the whole session (before, during, or after the death/regen sequence). A fresh reconnect asfluffoscorrectly restored the post-respawn location (/domains/default/room/start) and HP/PPE/SP state from the save file.- Negative control, confirming §4's finding is still accurate and scoped correctly: deliberately tried
clone /domains/std/monster/ troll(part of the already-documented brokendomains/stdtree) -- got the exact sameinherit ADVERSARY;compile error §4 already catalogued, then successfully cloned the WORKING/domains/town/npc/ orcinstead for the real combat test above. Confirms the broken/ working boundary from onboarding is still exactly where it was documented, not encroaching on stock content. - ~220s (3+ hour, see below) idle long-sit boot watch:
debug.lognever created; stdout showed zero new errors beyond the one expecteddomains/std/monster/troll.lpccompile error from this session's own deliberate negative-control clone above.
Minor, unresolved cosmetic observations (not fixed -- neither blocks anything or has a confirmed severe effect; scope discipline against guessing)
- HP/PPE/SP occasionally render as a raw float (
hp: 132.000000/440instead ofhp: 132/440) after the death/regenerate sequence -- some stat-regen calculation along that path isn't truncated back to an int before being substituted into the prompt template. Purely cosmetic (regeneration and the death/respawn cycle both worked correctly regardless); not root-caused given time constraints. domains/Praxis/supply2.lpc'sadd_sky_event((: "shop_closing" :))triggers this driver's "Function pointer returning string constant is NOT a function call" warning at compile time -- the same MudOS single-string functional-literal shorthand class already catalogued in AGENTS.md §8.23 (confirmed there to silently never fire on this driver). Here it would only affect a shop's scheduled "closing" cosmetic flavor event, not blockinglist/buy/sellthemselves; not chased further given the narrow, cosmetic blast radius and time budget.
Not reached this session, flagged honestly
- Skill/guild-class progression (as distinct from the Healer's Guild's plain buy-a-service building, which is a shop not a class/skill guild): not exercised -- time was budgeted toward the admin combat/death/respawn sequence instead, which this project's own methodology explicitly treats as one of the two hardest-to-reach checklist items and therefore worth prioritizing when it becomes reachable.
- A full two-sided paid shop transaction (buy AND sell, with correct change): only the insufficient-funds refusal path was reached; a fresh mortal character never accumulated enough starting currency to complete a real purchase in the time available.
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:
domains/Praxis/fighter_join.lpc:78domains/Praxis/cleric_join.lpc:67domains/Praxis/mage_join.lpc:77domains/Praxis/monk_join.lpc:66domains/Praxis/kataan_join.lpc:66domains/Praxis/rogue_join.lpc:73
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 GetGenderGetName(), 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):
- Buy:
buy healing slip from jameswith 29000 universal credits on hand correctly charged 20000 ("James exclaims... Here is a healing slip for 20000 universal credits!"), left exactly 9000, and added "A healing slip" to inventory. - Sell:
sell slip to jamescorrectly paid back 10000 (half of the 20000 buy price, perlib/std/vendor.lpc:eventBuy()'s owncost = cost / 2markdown logic for player->vendor sales) for a final balance of 19000, and correctly removed "A healing slip" from inventory. - Negative control:
sell pill to james(aclaritinbought earlier) was correctly refused ("I do not buy things like a claritin pill.") with zero currency/inventory side effects --claritin.lpcnever overridesSetVendorType()so it stays thelib/meal.lpcdefaultVT_DRINK, which doesn't match James's ownSetVendorType(VT_HERB). This is correct, intentionalGetVendorType() & GetVendorType()bitmask economy logic (a healer buys herbs, not the medicine he dispenses), not a bug.
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
libs/riftsds/work/secure/include/compat.h-- addedquery_name/query_cap_name/query_gender->GetName/GetCapName/GetGendermappings (BUG 2 fix).libs/riftsds/work/domains/Praxis/fighter_join.lpc,cleric_join.lpc,mage_join.lpc,monk_join.lpc,kataan_join.lpc,rogue_join.lpc--SetClass()->ChangeClass()(BUG 1 fix).
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 前后对比,主控载入段只少了这两行。
- 2026-10-14: FluffOS 970d9f09 warns
Float value truncated to int(#1426). 16 sites in 10 files: assignments, initializers and returns now spell the truncationto_int(...)(same value);x op= <float>converted the float before the operation (zeroingxfor a factor below 1) and is nowx = to_int(x op ...):/lib/teller.lpc(total +=),/secure/daemon/players.lpc(seed *=). Changed objects loaded before and after in fresh drivers, no regressions (scripts/lpc_fix_float_trunc.py, KB 04 §6.10). - 2026-10-14 (same rescan, by hand):
lib/combat.lpcset the defaultWimpy = 0.20into anint, so it was 0 and auto-flee never fired for anyone who had not set a wimpy value.Wimpyis a percentage of max health (wimpycommand,SetWimpy(95)on NPCs, compared withpercent(hp, max)), so the default is now 20 andSetWimpy/GetWimpytake and return an int. This is the same fix as brassring, and the default the later Dead Souls releases use (ds386, dsIII, fluffos/dead-souls:Wimpy = 20). Gameplay-visible: characters flee at 20% health by default. The 11 changed objects load, with no regressions.
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.