info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
A stripped-down classic descendant of the Nightmare mudlib lineage from the mid-1990s, positioned by its own boot banner as an engine for hardcore LPC programmers, not a beginner-friendly game world -- it ships no combat, limbs, or stats system at all. What shipped and works: a real first-boot admin-setup installation wizard, full login/registration, security/privs access control, command dispatch, help, mail, message boards, and Intermud-3 networking, plus one small starting domain. Two optional example/teaching content trees included in the archive reference an incompatible, differently-named room/item API from a different snapshot of this engine and don't compile, so they're not playable content.
README
A stripped-down, classic descendant of the Nightmare mudlib lineage, originally released by George Reese ("Descartes of Borg") in the mid- 1990s. Per the mud's own boot banner: "This mud is NOT for newcomers to LPC. You must be hardcore." Foundation II ships no combat, limbs, or stats system at all -- it's a minimal, complete engine (login/ registration, security, command dispatch, help, mail, message boards, Intermud-3) meant as a clean base for LPC programmers to build their own game world on top of, not a playable-out-of-the-box game.
Source: a maintained, already-FluffOS-adapted archive
(foundation2_fluffos_v1.zip) from lpmuds.net, a now-defunct LPMud
archive site. Recovered via the Wayback Machine
(<https://web.archive.org/web/20160306101019if_/http://lpmuds.net/files/foundation/foundation2_fluffos_v1.zip>)
since the original site is down. Slug foundation2, number 174, port
40221.
Highlights
- A genuine, working first-boot admin-setup wizard: connecting to a fresh install walks through a short "installation process" (superuser name, password, capitalized display form, gender, real name, email) before the mud's real login/registration flow ever activates. See
NOTES.mdfor how this project's admin account was created through this exact flow. - One small shipped starting area (
domains/Standard, a softly-lit cavern with glowing lichen and magic pools) plus two optional teaching/ example content trees (domains/Examples,domains/School) that, perNOTES.md, were evidently carried over from a different, incompatible version of this engine's room/item API and don't compile as shipped -- documented as a pre-existing content mismatch in the archive, not something this port introduced or fixed. - The highest-impact bugs found while porting this to FluffOS: a missing
creator_file()master apply broke loading nearly every single object in the mudlib (this driver'sPACKAGE_UIDSsupport calls it on every object creation), and a.c-to-.lpcrename side effect in the command-dispatch table's filename parsing silently broke every ordinary player/wizard command (inventory,help,update, ...) while soul/emote commands kept working through a separate path -- both fixed, full details inNOTES.md. - Foundation II makes a real outbound network connection attempt to the historic Intermud-3 router a couple of seconds after every boot (
daemon/intermud.c, preloaded by default) -- genuine upstream functionality, not a bug, but flagged inNOTES.mdsince it's unusual for this project's normally fully-sandboxed corpus.
Registration flow
new name -> "Do you really wish <name> to be your name? (y/n)" ->
password (5+ letters) -> confirm password -> gender (male/female) ->
display name (blank = default) -> email (required, must look like
user@host) -> real name (optional, blank OK) -> two news screens
(<return> to continue) -> lands in the Standard domain's starting cavern.
Every account (mortal or wizard) saves under the same secure/save/
creators/ tree -- this engine has no separate mortal/wizard save-file
split by default.
Admin account
- id:
fluffos - password:
Mud@2026 - privilege:
ASSIST+SECUREgroup membership (secure/cfg/groups.cfg)
Created through the mud's own first-boot installation wizard (see
NOTES.md), not a save-file hand-edit. Verified live: update <path>
(this project's canonical ACL/compile-permission check) against a real
cmds/player file succeeds.
Status
Boots clean: zero compile errors across repeated fresh-driver boots.
Verified live with a real driver session and a raw socket client: the
first-boot admin installation wizard, a brand-new ordinary character's
full registration, and look/inventory/who/save/a soul command
(smile)/the wizard update command all producing correct output, and a
clean quit. This engine has no combat/stats system, so there is no
score command to verify (see NOTES.md for what substitutes for it in
this port's verification pass).
223 of 246 .lpc files pass a full lpcc_check.sh batch compile sweep;
the 23 that don't are all non-preloaded, non-boot-critical example/
teaching content with a pre-existing content/API mismatch predating this
port (see NOTES.md \S3) -- expected for a deliberately minimal engine
like this one, not a sign of missing conversion work.
WASM status: playable. Login as fluffos into the starting cavern
and inventory are verified under the shared WASM driver (NOTES.md
WASM status update). Play: https://mudlibs.fluffos.info/foundation2/
Local run
cd libs/foundation2
~/src/fluffos/build-debug/src/driver config.fluffosGame port: 40221.
NOTES · 移植与修复记录
Foundation II -- porting notes
Source: foundation2_fluffos_v1.zip, a maintained, already-FluffOS-adapted
archive from lpmuds.net (a now-defunct LPMud archive site). The live site is
down; this exact archive was recovered from the Wayback Machine
(<https://web.archive.org/web/20160306101019if_/http://lpmuds.net/files/foundation/foundation2_fluffos_v1.zip>)
and content-verified (valid zip, non-corrupted) before this session started.
Slug foundation2, number 174, port 40221. The zip bundles a FluffOS
2.9-ds2.13 driver source snapshot (fluffos-2.9-ds2.13/), plus bin/,
win32/, old/ build/install scaffolding -- all ignored, this project uses
its own driver. The real mudlib root is the zip's lib/ directory.
Foundation II is a stripped-down classic descendant of the Nightmare mudlib
lineage: no combat/limbs system at all, positioned (per its own boot banner)
as a minimal base "NOT for newcomers to LPC. You must be hardcore" --
aimed at LPC programmers building their own game on top of a bare engine,
not a playable-out-of-the-box game world. This matches what shipped: a
working login/registration/security/command-dispatch/help/mail/board/
Intermud-3 engine, one small "Standard" starting-cavern domain, and two
optional teaching-only content trees (domains/Examples/,
domains/School/) that reference a different, incompatible content API
(see \S3) -- there is no score/combat/stats system anywhere to test.
1. Conversion
English-language archive (not GBK-encoded) -- scripts/convert_lib.sh
against raw/foundation2_fluffos_v1/lib confirmed this: 1126/1160 files
already valid UTF-8, only 1 lossy conversion (doc/faq/lpmud, a handful of
stray invalid bytes in a plain-text FAQ file, not code), 244 files renamed
.c->.lpc, 67 literal .c" references fixed, 13 local angle-bracket
#includes converted to quotes, 118 files touched by the static->nosave
sweep.
One file needed the same static->nosave treatment by hand:
secure/lib/login.real is not .c/.lpc (so the mechanical sweep skipped
it) but is real, live LPC source -- the mudlib's own first-boot admin-setup
wizard (secure/lib/login.lpc, see \S5) cp()s it over login.lpc at the
end of setup and it becomes the mud's actual ongoing login/registration
object from then on. Its 45 static declarations were converted the same
way by hand, then verified by compiling it standalone under its real target
path before ever letting the install flow copy it into place live.
2. Compile-time driver-compat fixes
refis a reserved keyword on this driver (call-by-reference parameter modifier) --secure/daemon/master.lpc'sload_access()used it as an ordinary mapping parameter/variable name (mapping ref,ref[fl] = ...), in both the function and itsmaster.hprototype. Only 3 sites in the whole tree (confirmed via a repo-wide\bref\bgrep before and after); renamed torefmap. Same bug class documented inlibs/nightmare3/NOTES.md.PACKAGE_UIDSis enabled in this project's driver build, and this master object never defined the two applies it requires -- confirmed empirically (this driver build has neither anf_ed_start/f_ed_cmd/f_query_ed_modeefun, i.e.OLD_ED, nor wereget_root_uid()/get_bb_uid()ever satisfied here) and by readingvm/internal/master.cc:set_master()callsexit(-1)outright ifget_root_uid()is missing, so the driver couldn't boot at all --lpcc's own batch harness surfaced this identically ("No function get_root_uid() in master object"). Ported the exact two-line pair from the sibling Nightmare-lineage master (libs/nightmare3/work/secure/ daemon/master.lpc, which literally shares this file's "from Nightmare 3.3" header comment):get_root_uid() { return "Root"; },get_bb_uid() { return "Backbone"; }.creator_file()-- also required byPACKAGE_UIDS, and missing -- was the single highest-impact fix in this port. Unlikeget_root_uid()/get_bb_uid()(checked once, at master-object-load time),creator_file()is called by the driver on every single object load to assign its uid (vm/internal/simulate.cc'sapply_master_ob(APPLY_CREATOR_FILE, ...), harderror()s uncaught if missing). With it undefined, essentially every object in the mudlib failed to load -- confirmed vialpcc_check.sh: only 18/245 files passed before this fix (log/catchwas flooded with identical*master object: No function creator_file() defined!traces from every preloaded daemon's own creation), 218/245 immediately after. Nightmare's own lineage (libs/nightmare3/work/secure/SimulEfun/creator_file.lpc) solves this with a *different*, more elaborate per-directory uid-name scheme (UID_ROOT/UID_MUDLIB/UID_BACKBONE/etc.) that doesn't exist in this codebase at all -- rather than inventing a parallel naming scheme,secure/daemon/master.lpc's newcreator_file()reuses the privs-category classification this codebase's ownsecure/sefun/security.lpc:file_privs()already provides for its privs/access-list security model (ASSIST/CMDS/GENERAL/MUDLIBPRIV/SECURE/WWW/domain-name), falling back to the backbone uid for anything uncategorized. There's no uid-based permission check anywhere in this codebase (its whole security model ischeck_access()'s privs scheme) for the exact uid string to matter -- only that every object gets *some* non-zero string.
- Follow-on bug in the fix's own sscanf() use: creator_file(str)
strips a clone-instance suffix via sscanf(str, "%s#%d", base, ext),
same idiom as nightmare3's version -- but nightmare3's version never
hits the case where str has no # at all in a context that matters,
while every one of this driver's *own* boot-time preload calls does.
An unmatched %s target keeps its int-0 default (not ""), so
base stayed the integer 0 for any non-cloned path, and passing
that into file_privs()'s file[0..1] string-indexing crashed with
"Cannot index type 'int' using [ .. ] operator" -- caught by
preload()'s own catch(), so non-fatal, but every single preloaded
daemon hit it (confirmed via log/catch). Fixed by falling back to
base = str whenever sscanf() doesn't report a full 2-field match.
daemon/soul.lpc: "Illegal to inherit after defining global variables." Its#include <soul.h>(before its owninherit SOUL_ADVERBS;line --SOUL_ADVERBSis itself a macro defined a few lines up in that same header) pulled in a real global-variable initializer,nosave string HelpPage = (...), ahead of the inherit in the expanded token stream. The CD/MudOS driver this shipped for evidently tolerated the ordering; this driver doesn't. MovedHelpPage(used nowhere outsidedaemon/soul.lpc) intodaemon/soul.lpcitself, after itsinherit, rather than touching the header's macro-only contents any other includer might also pull in.- The command-dispatch directory scanner's fixed-offset string slice -- the second highest-impact fix, found only by live playtesting (
lpccgave zero indication of it, since every affected file compiles and loads fine standalone).daemon/command.lpc'seventRehash()builds the whole verb->file lookup table withcmd = file[0..<3], which is exactly right for stripping a trailing 2-character.cextension but, after this project's standard.c->.lpcrename (2 characters longer), turns"look.lpc"into"look.l"instead of"look"-- every real command incmds/player/,cmds/creator/, andsecure/cmds/creator/silently stopped matching typed input. Confirmed live: before the fix,inventory/help/every othercmds/playercommand returned the driver's bare "What?", whilelook(not implemented as acmds/file at all -- handled elsewhere) and soul/emote commands (dispatched through a separate path,SOUL_D, not this table) kept working, which is exactly the fingerprint that made this a dispatch-table bug rather than a per-command one. Same bug class, and nearly the identical fix, aslibs/nightmare3/NOTES.mditem 13 (daemon/command.lpc'srehash()there). Widened the slice tofile[0..<5](drop the trailing 4 characters). - Two more instances of the identical rename-arithmetic bug, this time checking/appending rather than stripping:
lib/remote.lpc's remote- creator-protocolupdatehandler andsecure/cmds/creator/update.lpc(the wizardupdate <file>command -- this project's own canonical ACL/recompile smoke test) both hadarg[<2..] == ".lpc"/x[<2..] != ".lpc"-- a 2-character end-slice compared against the (already mechanically rename-fixed) 4-character literal".lpc", always false either way. Inupdate.lpc'scmd()this meant.lpcgot appended even when the user already typed it (update foo.lpcproducedfoo.lpc.lpc, "File not found" -- confirmed live before the fix); in both files' extension-stripping checks it meant an explicit.lpcsuffix was never stripped beforefind_object()/eventDestruct(). Fixed both to<4../[0..<5], matchingdaemon/command.lpc's fix. Verified live afterward:update /cmds/player/inventoryandupdate /cmds/player/inventory.lpc(with and without the explicit extension) both now reportOk, and the command keeps working correctly post-recompile. - Small mechanical typos/gaps, one file each:
- secure/cmds/creator/grant.lpc: file[strlen(file)-1] != "/" compares
a single character (an int) against a string literal -- this
driver's strict-types pass makes that a hard "!= always true"
compile error (not just a warning), unlike whatever drove the
original archive. Fixed to the character literal != '/'.
- secure/cmds/creator/banish.lpc: used UID_BANISH (a real constant,
defined in secure/include/security.h) without including that
header -- had a literal duplicate #include <daemons.h> instead (one
of the two copies replaced with the missing include).
- secure/cmds/creator/memcheck.lpc: inherit DAEMON; -- DAEMON is
not a macro anywhere in this codebase (the real one, used everywhere
else, is LIB_DAEMON); a lone pre-existing typo, not something this
port introduced. Fixed to LIB_DAEMON.
3. Bundled example/teaching content targets a different, incompatible API (not fixed -- pre-existing content mismatch)
23 of 246 .lpc files still fail lpcc_check.sh's batch sweep (down from
227 before the fixes in \S2); every one of them is non-preloaded, non-boot-
critical teaching/example content, and every failure traces to the same
root cause: this content was written against a different room/item API
than the one this engine actually ships.
domains/School/(7 files) callset_properties()/set_short()/set_long()/set_exits()/set_items()on a room inheritingROOM(/lib/room.lpc) -- but this engine's real, actual room/object API (confirmed by reading/lib/object.lpc//lib/exits.lpc//lib/items.lpc, the base classes/lib/room.lpcitself inherits) is CamelCase and singular:SetShort(),SetLong(),SetProperty()(one property at a time), etc. None of theset_properties/set_short/set_long/set_exits/set_itemsnames exist *anywhere* in this entire archive -- this School domain was evidently authored against an older or sibling Foundation/Nightmare-lineage codebase and never ported when it was bundled alongside this particular engine snapshot. Not a driver-compat issue; a pre-existing content/engine-version mismatch in the archive itself, same category asgenesis's missing-domain gaps.- **
domains/Examples/{weapon,armour,npc}/*,etc/{match,torch},virtual/*(15 files)** reference base classes and macros that were simply never shipped in this "no combat/limbs" engine at all:LIB_NPC,LIB_ARMOUR,LIB_WEAPON-style macros don't exist inlib.h, and no corresponding/lib/npc.lpc//lib/armour.lpc//lib/weapon.lpcbase files exist anywhere in the tree either (unlikedomains/School's case, this isn't a missing macro pointing at real content -- the content itself was stripped along with the rest of this engine's combat system).<armour_types.h>/<damage_types.h>/<vendor_types.h>are likewise never-shipped headers. This is fully consistent with the archive's own framing (a deliberately minimal engine) -- these example files were evidently carried over wholesale from a fuller ancestor codebase'sdomains/Examples/without pruning the parts that depend on subsystems this stripped-down engine doesn't have. Not chased further (would mean inventing a combat/item-type system wholesale, a content addition far outside this project's driver-compat scope). secure/cmds/creator/move.lpc(a "move any object anywhere" wizard debug utility, credited "from the Nightmare Mudlib")inherits aREFS_Dmacro that's undefined anywhere in this codebase, and no reference-tracking daemon (/daemon/refs.lpcor similar) exists in this archive at all -- genuinely missing/never-ported infrastructure from another Nightmare-lineage codebase, not something this port broke. Left unfixed (a single non-boot-critical wizard debug command); it also separately has a#include <move.h>that can't resolve via this driver's configured include path (/secure/include:/include-- the realmove.hlives in/lib, a different directory, so angle brackets never find it even though a same-directory local include would), but fixing that alone wouldn't make the file compile given the missingREFS_Ddaemon, so left as one combined, documented gap.
None of these 23 files are in secure/cfg/preload.cfg, referenced from
the Standard starting domain, or reachable from any command a normal
player or the seeded admin would run -- confirmed via a full live boot and
play session with zero related errors.
4. First-boot admin-setup wizard (not a bug -- this engine's own onboarding flow)
Unlike most archives in this project (which need a wizard account
hand-seeded via a save-file edit or a normal-registration + config-edit
combo), Foundation II ships its own genuine one-time superuser setup flow:
secure/lib/login.lpc starts as a small self-contained "installation
process" wizard (name -> password -> confirm -> capitalized-name ->
gender -> real name -> email), and on completion it clones the real,
full-featured login/registration object over itself
(cp("/secure/lib/login.real", "/secure/lib/login.lpc"), see \S1),
destructs the temporary admin object and the master object (forcing a
clean reload of both), and disconnects with "Login as your admin
character." From then on the mud behaves like any other -- new
characters register through the normal flow (name -> confirm (y/n) ->
password -> confirm -> gender -> display name (blank OK, defaults to the
account name) -> email, which must match user@host and is NOT
optional/skippable -> real name (optional, blank OK) -> two "Press
<return> to continue" news screens -> world entry).
The project's admin account was created through this exact first-boot
flow (not a save-file hand-edit): id fluffos, password Mud@2026,
granted ASSIST+SECURE group membership in secure/cfg/groups.cfg
automatically by the setup flow itself (it renames the archive's
placeholder admin group entries to the chosen admin's name), and given
a real per-account "realm" directory (realms/fluffos/workroom.lpc,
copied from domains/Standard/xtra/workroom.lpc by the same flow).
Verified live with update <path> (this project's canonical ACL/compile
check) against a real cmds/player file.
Note for anyone re-running this install flow: every account in this
engine saves under secure/save/creators/, not a separate mortal-vs-
wizard split -- #ifdef PLAYERS branches throughout the codebase (e.g.
creator_file()'s own base-name check) are dead in this build (PLAYERS
is never defined), consistent with this being a bare "for LPC experts"
engine with no mortal/wizard distinction baked in by default.
5. Live outbound network side effect at boot (by design, not a bug -- same class as imud, see AGENTS.md \S2.3)
daemon/intermud.c (Intermud-3 protocol support, preloaded unconditionally
via secure/cfg/preload.cfg) is guarded by #ifndef __PACKAGE_SOCKETS__
#error ...#endif -- and this project's driver build has the sockets
package enabled (confirmed via nm: f_socket_create is present), so
the guard doesn't fire. Its create() unconditionally schedules
call_out((: Setup :), 2), and Setup() calls eventCreateSocket()
against a hardcoded real address (204.209.44.3:8080, the historic *i4
Intermud-3 router) roughly 2 seconds after every boot. This is genuine,
intentional upstream functionality (not something to patch away), but --
exactly like imud (AGENTS.md \S2.3) -- it means every boot of this lib
makes a real outbound connection attempt to a real third party. That
router is a 1990s-era service and near-certainly long defunct by now (no
connection-success evidence was seen in any boot log this session), but
this should not be swept into high-frequency automated re-boot/re-test
loops the way this project's other (fully sandboxed) libs safely can be.
6. Boot and play test
Booted ~/src/fluffos/build-debug/src/driver config.fluffos repeatedly;
zero compile errors, Initializations complete., accepting telnet
connections on 40221 every time. Verified via a raw Python socket client
(scripts/mudclient.py), end to end:
- First-boot admin setup (\S4): completed the installation wizard as
fluffos/Mud@2026, then logged in as that account into the real game world (/domains/Standard/center, the starting cavern). - Brand-new character registration (a second, ordinary account,
gorlak): the full name -> confirm -> password -> confirm -> gender -> display name -> email -> real name -> news flow, landing in the same starting room. - Commands:
look,inventory(You are carrying nothing.),who(correct member listing),save(Fluffos is now saved.), a soul/emote command (smile->You smile happily., confirmingdaemon/soul.lpc's fix and this codebase's(: function :)-closure-basedadd_action()calls -- already the correct FluffOS idiom throughout this archive, no bare-function-value bug found here unlikegenesis/finalrealms), and the wizardupdate <path>command (both with and without an explicit.lpcsuffix) all produce correct output.quitdisconnects cleanly ("Please come back another time!"). - Since this engine has no combat/stats/limbs system at all, there is no
scorecommand to test (confirmed: no file namedscoreor string literal"score"exists anywhere incmds/) --inventory+who+updatesubstitute for this project's usuallook/score/quitverification bar, per the precedent set for other minimal/non-standard engines in AGENTS.md \S2.3.
Zero uncaught runtime errors in log/runtime/log/catch across this
session's boots and play-testing (the log/catch entries seen mid-session
were creator_file() traces from the lpcc_check.sh batch runs that share
this lib's log/ directory, not from the live driver -- confirmed by their
file-modification time not advancing while the real driver was up).
WASM status update (2026-08-26, another session)
Promoted wasm_status from "" to playable. Same root-cause class
as ds386/discworld/deadsouls_fluffos/tmi2: secure/sefun/
sefun.lpc's own dump_socket_status() (part of the eagerly-loaded
simul_efun object) called socket_status() unconditionally, undefined
on this driver build (no sockets package). Gutted to a safe stub
(diagnostic admin tool only, not on the boot/login path). No other
socket-related eager-load failures found. Verified with a scripted
WASM session: login (fluffos/Mud@2026), arrival in the correct
starting cavern room, and inventory producing correct output.
quit wasn't recaptured distinctly in this transcript but is already
verified clean under native testing above and untouched by this fix.
深度功能测试(round two, 2026-08-27)
Full §10.7 continuous playthrough against the native debug driver
(~/src/fluffos/build-debug/src/driver config.fluffos), never done
for this lib before (only the original onboarding-tier boot/play pass
in \S6/\S "WASM status update" above). This is a genuinely minimal
engine (no combat/limbs/stats/score, one small starting room with no
working exits -- see \S3), so the test surface is: login/registration,
look/inventory, save, mail (IIPS), who, quit+reconnect, and a
systematic grep sweep for every standing cross-cutting bug class
tracked in AGENTS.md (\S7.121-\S7.135, \S8.3a).
Playthrough (admin fluffos/Mud@2026, plus a throwaway English
test character Aldric, cleaned up before commit): full first-boot
admin login and a brand-new registration (name -> confirm -> password
-> confirm -> gender -> display name -> email -> real name -> news
screen -> world) both landed cleanly in /domains/Standard/center
with correct look/inventory output. save, the mail command
(IIPS letter reader, empty folder, clean q-driven exit), and who
all worked correctly. quit printed the normal "Please come back
another time!" with zero log/runtime growth; reconnected after a
real multi-minute wall-clock gap and got a genuinely fresh Setup()
run (not a "Reconnected." link-restore), landing back in the same
room with the same empty inventory -- no state loss, no duplication.
Confirmed (per \S3) that the starting room's own pool-spawning code is
commented out in domains/Standard/center.lpc's create(), and even
if it were re-enabled both pool destinations are dead ends (one points
at a non-existent /domains/Discussion/MudOS, the other at
domains/School/room/entrance -- one of the already-documented
incompatible-API School files that doesn't compile) -- so the starting
cavern legitimately has zero working exits; this is pre-existing
content/archive state, not something introduced by this port, and not
a programming bug to invent a fix for.
Cross-cutting pattern sweep -- grepped systematically for every
class in AGENTS.md \S7.121-\S7.135 and \S8.3a. Confirmed genuinely not
applicable/structurally clean on this lib for: \S7.121 (only float
usage in the whole tree is secure/sefun/percent.lpc's own correctly-
float-declared percent()), \S8.3a (zero private command-dispatch/
call_out-target functions anywhere -- every add_action/call_out
target checked is a plain public/nomask function), \S7.112 (no NPCs
at all outside the non-compiling domains/Examples/npc), \S7.122
(lib/autosave.lpc's AutoLoad item-reload mechanism is correctly
gated by lib/interactive.lpc's own Setup re-entry guard --
net_dead()/reconnect via boot_copy() never re-invokes Setup() at
all, so the autoload-on-Setup path only ever runs once per genuinely
fresh login), \S7.123 (the one raw grep hit,
secure/cmds/player/tell.lpc:109's morse() local variable
assignment, is inside a function body, not file scope -- false
positive), \S7.124 (no percentage/threshold fields of this shape exist
at all -- no combat/wimpy system), \S7.126 (no coordinate-area door
save-data of this shape), \S7.129 (secure/sefun/communications.lpc's
tell_room()/say()/shout() wrappers all explicitly convert a
falsy/omitted exclude to ({}) before forwarding to message() --
already safe, unlike the buggy es1 shape this class describes),
\S7.130 (disconnect handling goes through the net_dead() apply
directly, no heart_beat()-based query_idle() polling anywhere),
\S7.131 (lib/interactive.lpc:63 calls set_living_name(GetKeyName())
inside Setup(), and find_player()/find_living() lookups were
directly exercised live via secure/lib/login.lpc's own reconnect-
detection and secure/cmds/creator/call.lpc/whisper/tell --
already confirmed working), \S7.132 (no map() over a mapping
anywhere in the tree -- every map() call site targets an array),
\S7.133 (net_dead() -- the correct apply name for this driver -- is
genuinely defined in lib/interactive.lpc/lib/creator.lpc/
lib/chat.lpc), \S7.134 (lib/room.lpc's own "extra descriptions
contributed by present items" accumulator, ExtraLongs, IS
initialized at its declaration site in create() -- structurally the
same feature class as the arkadia/genesis bug but already safe
here), \S7.135 (checked every lazily-guarded global family found via
grep -- lib/nmsh.lpc's Nicknames/Aliases/Xverbs,
lib/access.lpc's __ReadAccess/__WriteAccess,
secure/daemon/master.lpc's Groups/ReadAccess/WriteAccess,
daemon/banish.lpc's __Names/__TmpBanish/etc. -- every one is
unconditionally initialized in its own file's create(), not merely
guarded lazily per-accessor, so there's no "one accessor forgot the
guard" gap to find).
New bug found and fixed: secure/daemon/events.lpc (the reboot-
warning/scheduled-event daemon, EVENTS_D) defines GetEvents() and
GetRebootInterval() as its real query accessors, but two OTHER files
call a same-purpose function under a different name that is never
defined anywhere in the whole tree: cmds/creator/events.lpc's cmd()
(the player-facing events <wizard> command, reachable by any
arch/creator) called EVENTS_D->query_events() at its second branch
(the events command's first branch, no argument, already correctly
calls GetEvents() -- so this is an internal naming inconsistency
within the SAME file, not a guess), and secure/sefun/events.lpc's
(dead, uncalled) event_pending() made the identical mistake. This
predates this project's port entirely -- confirmed via raw/
foundation2_fluffos_v1/lib/{cmds/creator,secure/sefun}/events.c, the
pristine 1990s archive already has this exact mismatch, byte for byte.
On this driver, a call to an undefined function via -> returns a
bare int(0), and (mapping)0's cast is a no-op (this driver's
(type) casts are compile-time hints only, see the reference_lpc_
int_cast_is_compile_time_only note) -- so mp stayed int(0), and
the very next line's keys(mp) crashed uncaught: reproduced live,
events somebody as the seeded fluffos admin threw *Bad argument 1
to keys() Expected: mapping Got: 0 (log/runtime, /cmds/creator/
events line 36), aborting the command with no player-facing
indication beyond the raw driver error text. Fix: both call sites
changed from EVENTS_D->query_events() to EVENTS_D->GetEvents(),
matching the daemon's own real, only accessor and the working sibling
branch already right above it in the same file
(cmds/creator/events.lpc:35, secure/sefun/events.lpc:16). Verified
live post-fix: events somebody now correctly prints "somebody
doesn't have any events pending." with zero log/runtime growth; a
full re-boot-and-retest after running the \S9 LPC formatter on both
touched files (all three formatter blind-spots checked clean: no ::
mis-split, no case/comment merge -- neither file has a switch, no
CJK/escape re-spacing) reconfirmed the fix.
Related gap found, NOT fixed (documented per this section's own
"when unsure, don't guess" rule): cmds/player/nextreboot.lpc (a
real, reachable player command) calls EVENTS_D->query_next_reboot(),
which also does not exist anywhere in the tree -- and unlike
query_events(), there is no matching same-purpose sibling function
to redirect it to (GetRebootInterval() only returns the configured
*interval* in hours, not an absolute next-reboot timestamp). This
doesn't crash (the undefined-function call_other returns 0 and the
command just prints a bogus "Wed Dec 31 16:00:00 1969 PST" instead --
confirmed live), but fixing it correctly would mean inventing new
bookkeeping (e.g. tracking mud-start time and deriving boot_time +
RebootInterval*3600) rather than just correcting a mistaken function
name, and the entire auto-reboot subsystem it would report on is
already dormant by original-archive design: secure/daemon/
events.lpc's own eventPollEvents() self-rescheduling poll loop --
the only code path that ever checks uptime() > RebootInterval*3600
and triggers eventReboot() -- is never started at all, because its
sole call_out((: eventPollEvents :), 60) in create() is commented
out. Confirmed via raw/foundation2_fluffos_v1/lib/secure/daemon/
events.c that this call_out was ALREADY commented out in the
pristine 1990s archive, not something broken by this port. Left
untouched as a pre-existing, structurally dormant feature gap rather
than guessed at. (A third, related latent bug in the same dead
subsystem, also pre-existing and also left untouched: secure/sefun/
events.lpc's event() wrapper calls EVENTS_D->AddEvent(f, when,
reg) with 3 arguments of the wrong types against AddEvent()'s real
6-parameter (string c, string s, string f, mixed *a, int w, int r)
signature -- but event() itself is never called by anything anywhere
in the tree, so this is unreachable dead code, not a live crash.)
Evidence left in place: the seeded admin account
secure/save/creators/f/fluffos.o (age/login-time counters naturally
advanced by this session's repeated logins). The throwaway test
character Aldric (secure/save/creators/a/aldric.o,
secure/save/postal/a/aldric/) was deleted before committing, per
this project's standing cleanup convention.
Shop + 拜师 (2026-09-04 librarian slice)
Content-absent, same class as foundation1. Bare 1995 engine: no
combat, no stats, no currency, no vendor objects, no apprentice
verbs. Live on 40221 as seeded admin fluffos / Mud@2026 in
/domains/Standard/center: buy, list, shop, apprentice,
join, become, and 拜师 all printed What?. look/inventory
still work. Left as-is.
深度功能测试(§10.7,2026-10-03)— site-like tree, every compile warning
Same method as foundation1 (a tree built the way the site builds it:
tracked files, work/log, and the directories wasm_keep_dirs.txt records;
see that lib's 2026-10-03 section). Registration, bug -r (the ed-based
tracker), mail, the soul and chat commands, ~60 further commands, and
disconnects at every login prompt and inside the bug dialog, the mailer and
the news pager. Fixed:
1. tmp/ missing: bug -r ran ed on tmp/<name>, then Couldn't open
file for writing! -- no bug could ever be filed. Added tmp/.gitkeep.
2. secure/save/letters/ missing: mail composed, then *Could not open
/secure/save/letters/<n>/<id>.o.tmp for a save (the daemon makes the digit
directory but not its parent). Added secure/save/letters/.gitkeep.
3. log/ subdirectories: the keep list had no entry for the lib, so the
site's new characters died at the y prompt exactly like foundation1
(gen_keep_dirs.py --merge), and the recorded set was still not complete:
eval logs to /log/adm/eval, which no local boot had ever created
(Wrong permissions ... "No such file or directory" after the command had
run). master_log_file() now creates the missing parent directories of
the log file it writes, so the list no longer has to be complete.
4. drop <thing you do not hold> fell through the unfinished "do money
here later" branch and called CanDrop on 0: Bad argument 1 to EFUN
call_other(). It now says "You do not have that."
5. magic called new("/realms/b/beek/magic")->eventMove(...); the object
(a wizard's private game, not in the archive) does not exist, so new()
returned 0 and the command raised instead of printing its own "Error in
loading magic object." message. Null-checked.
6. Every compile warning fixed at its source (scripts/lpc_warnings.py
--fix, KB 04 section 6.10): ~500 nosave function declarations
(protected; private ones lose nosave; names another object calls stay
public), 94 unused locals, get_stack() printing the function name twice
(stack0 was the unused variable), interactive.lpc SetKeyName returning
void against object.lpc's string, SetPassword with a bare return
in a string function, the Examples start room's (: "search_brush" :).
lpcc --batch over 246 files reports no warning; the 23 Examples /
School / move objects of section 3 still fail with their missing-subsystem
errors, unchanged.
7. Not a bug: chfn, passwd and suicide -- this lib has no passwd or
suicide command at all (cmds/player, secure/cmds/player).
深度功能测试(§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-05 — 新玩家首次存档失败(KB 06 §7.227)
secure/daemon/master.lpc 的 compile_object() 用 else if 先建 players 目录、再建字母目录:父目录不存在时只建父目录,这位玩家第一次存档(设密码时)打开 players/<字母>/<名字>.o.tmp 失败。git 不跟踪这个目录下的文件,全新检出与网页版每位访客都会遇到。第二个 else if 改为 if(与 dsI 相同)。nightmare4 上用只含被跟踪文件的树实测注册→重登通过;本库主控编译通过。
深度功能测试(§10.7,2026-10-08)— .lpc 后缀的定宽切片
.c 改名 .lpc 时只改了字符串没改切片宽度:f[<2..] == ".lpc" 拿两个字符比四个字符,永远不成立(!= 永远成立),按后缀筛文件、去后缀的代码因此从不运行或截成 x.l(KB 06 §7.238)。scripts/lpc_lpc_suffix_slice.py --apply 改了 2 处比较、0 处去后缀(如 secure/cmds/creator/call.lpc);改动的文件 HEAD 与工作树分别加载(新进程),无回退。
Macros the global header lost (2026-10-19, KB 04 6.16)
/secure/include/global.h did not define VT_WEAPON; the objects that use them (1 on the list) stopped at Undefined variable/Undefined function, and since the compiler stops at the first undefined name the errors and warnings behind it stayed hidden. The sibling libs of the same lineage carry these lines in their headers: the copy of the sibling with the most files in common was added after the last #define of /secure/include/global.h; every path in a macro body exists in this lib. the listed objects have other errors as well and still do not load. (scripts/lpc_restore_defines.py)
call_out_info() 的每一项只有 3 个元素(2026-10-20,KB 06 §7.287)
驱动返回的是 ({ 物件, 函数, 延迟 }),不带 call_out 的参数,旧代码读第 4 项 info[i][3](callouts 指令、calloutd、cod 工具)一读就报 Array index out of bounds,指令从第一行起什么也打印不出来。改为 (sizeof(info[i]) > 3 ? info[i][3] : 0)(scripts/lpc_fix_callout_info.py)。改动:/cmds/creator/callouts.lpc(1 处)。
登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)
驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/secure/sefun/sefun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。
no_colours():FluffOS 的 terminal_colour() 会给空的 RESET 换上 ANSI 复位序列(2026-10-21,KB 06 §7.337)
/daemon/terminal 的 no_colours() 把 RESET 为空串的表交给 terminal_colour(),FluffOS 把空的 RESET 换成 ESC[49;49m ESC[0;10m 并写回映射,所以"去掉颜色"的文本里仍有转义序列(no_colours("%^CYAN%^[1]%^RESET%^ abc") 得到 [1]\e[49;49m\e[0;10m abc,用 scripts/lpcc_eval.py foundation2 --at /open/zzeval.lpc 验证),strip_colours() 少算宽度,arrange_string() 与 format_page() 会把含颜色码的格子截短。Foundation II 自带的菜单屏幕里格子大多不含颜色码,没有观察到可见症状;现在 RESET 先映射为退格符再删掉。修改:/daemon/terminal.lpc。
巫师命令不带参数时对 0 调用 efun(2026-10-21,KB 06 §7.336)
巫师(管理员)把命令敲成不带参数时,驱动把 0 传给字符串参数;下面的命令第一句就把这个 0 交给 efun,运行时报 Bad argument 1 to ...,这条指令从来没有用过。用 scripts/lpcc_bare.py 不登录、在一次 lpcc 求值里以 0 调用 main() 逐个确认后,由 scripts/lpc_fix_bare_arg.py 在第一处使用参数的语句前加守卫:没有 help() 的是 return notify_fail("…\n");。改动(1 个文件,括号里是接住 0 的 efun):/secure/cmds/creator/banish.lpc(lower_case)。加载检查 HEAD 与工作树对照,没有退步。
注册到不存在函数的 add_action,第二轮:宏路径父类和静态解析不了的对象(2026-10-21,KB 06 §7.349)
第一轮的静态解析读不了经宏路径继承父类的对象(#define ROOM STD_DIR "room/room" 这类定义的正文以另一个宏开头)和它解析不了的继承,这些对象当作 UNSURE 没有扫;第二轮修了解析器,重扫并对这些对象在运行时问 function_exists()。add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 1 条注册(1 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/lib/bboard.lpc(cmd_save)。最多的函数名:cmd_save 1 处。
战斗探针(2026-10-22,KB 08 §10.16):domains/Examples/npc/ 的两个示例 NPC 编译不过
scripts/play_combat_probe.py foundation2 --account ... --clone ... 克隆 /domains/Examples/npc/smiley 和 centaur,两个文件都是 inherit LIB_NPC;(9:9: syntax error):这个压缩包里没有 NPC 基类(lib/ 里没有 npc.lpc,lib.h 没有 LIB_NPC),示例 NPC 没法加载。没动(缺的是基类,不是笔误)。