info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
A stripped-down, classic Nightmare-3.3-derived LPC library released by George Reese ("Descartes of Borg") in April 1995 -- a bare engine (login/registration, security, command dispatch, help, mail, message boards, Intermud-3) with no combat, limbs, or stats system at all, meant as a clean base for LPC programmers to build their own game on top of, not a playable-out-of-the-box world. This is the direct, earlier ancestor of this collection's separate `foundation2` (a distinct, more developed later release by the same author) -- not a duplicate. The one shipped starting room is a softly-lit cavern with magic pools, with the real player command set (look/status/inventory/quit and more) fully implemented. An optional teaching content tree references an incompatible room/item API from a different snapshot of this engine and doesn't compile, a pre-existing content mismatch. Like foundation2, this lib makes a real outbound Intermud-3 socket connection at boot (to a near-certainly-defunct 1990s host) and should not be swept into high-frequency automated re-boot loops.
README
A stripped-down, classic Nightmare-3.3-derived LPC library, released by George Reese ("Descartes of Borg") in April 1995. Per its own shipped README: "This LPC library is designed very much for people who know what they are doing. Unlike Nightmare, it does not come with any configuration script to get you started, a combat system for a fantasy MUD, or a starter area." Foundation I is a bare 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 on top of -- not a playable-out-of-the-box game world.
This is the direct, earlier ancestor of this collection's existing
foundation2 (a separate, more developed later release by the same
author -- Foundation I's own README says explicitly that its
documentation was due to be "customized for Foundation II, due May 15"
[1995]). The two are distinct codebases with real, independently-drifted
differences (see NOTES.md), not duplicates of each other.
Source: recovered from the MUDBytes File Repository (mudbytes.net, file
#1043) via the Wayback Machine, since the live download endpoint is gone.
Slug foundation1, number 946, port 40248.
Highlights
- A tiny shipped starting area: one room,
domains/Standard/center(the "Pool Room" -- a softly-lit cavern with glowing lichen and magic pools) plus a linkdead-holding "freezer" room. An optionaldomains/Examplesteaching content tree references an incompatible, differently-named room/item API from a different snapshot of this engine and doesn't compile as shipped -- a pre-existing content mismatch (same shape asfoundation2's own two incompatible teaching trees), not something this port introduced or fixed. - The highest-impact bugs found while porting this to FluffOS: missing
get_root_uid()/get_bb_uid()/creator_file()master applies broke loading nearly every object in the mudlib (this driver'sPACKAGE_UIDSsupport requires them), a.c-to-.lpcrename side effect in the command-dispatch table's filename-slicing arithmetic silently corrupted every ordinary player command's lookup key, and the reserved-word collisionsref/class(this driver'sREF_RESERVED_WORDoption and its nativeclassstruct type) broke the player body and a dead scaffolding file respectively -- all fixed, full details inNOTES.md. - Foundation I makes a real outbound network connection attempt toward the historic Intermud-3 router a couple of seconds after every boot (
daemon/intermud.c, preloaded by default) -- genuine upstream functionality (almost certainly against a long-dead host today), not a bug, flagged inNOTES.md.
Registration flow
new name (lowercase letters only, 2-15 chars) -> "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) -> a news screen (<return> to continue) -> lands in the
Standard domain's starting cavern.
There is no score command anywhere in this minimalist archive (matching
its own README's "no ... stats system" description) -- status is the
closest equivalent, printing hp/sp/mp/experience.
Admin account
- id:
fluffos - password:
Mud@2026 - privilege:
(SECURE)group membership (secure/cfg/groups.cfg)
Created through the mud's own normal registration flow (not a save-file
hand-edit) and granted access by adding it to the existing (SECURE)
group entry. See NOTES.md for the exact edit and how it was verified.
Status
Boots clean: zero fatal errors across repeated fresh-driver boots.
Verified live with a real driver session and a raw socket client: a
brand-new character's full registration, world entry into the Pool Room,
and look/status/inventory/quit all producing correct output.
270 of 275 .lpc files pass a full lpcc_check.sh batch compile sweep;
the 5 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. Shared WASM driver. dump_socket_status() in
secure/SimulEfun/SimulEfun.lpc is #ifdef __PACKAGE_SOCKETS__ so the
eager simul_efun compiles without the sockets package. Admin login
(fluffos / Mud@2026) into the Pool Room, plus look / status /
quit, verified with scripts/wasm_client.js.
Play: https://mudlibs.fluffos.info/foundation1/
Local run
cd libs/foundation1
~/src/fluffos/build-debug/src/driver config.fluffosGame port: 40248.
NOTES · 移植与修复记录
Foundation I -- porting notes
Source: foundationI_fluffos_v1.zip, recovered from the MUDBytes File
Repository (mudbytes.net, file #1043) via the Wayback Machine, since the
live mudbytes.net file-download endpoint is gone. The CDX API
(https://web.archive.org/cdx/search/cdx?url=mudbytes.net/files/download/1043*&output=json)
showed one 200-status snapshot, 20190802021701 (2019-08-02); fetched as
https://web.archive.org/web/20190802021701id_/http://mudbytes.net/files/download/1043/
(a 301-redirect snapshot for the bare, non-timestamped URL also exists but
resolves to nothing useful directly -- the exact 200 snapshot above is the
one that works). Content-verified: 2,329,667 bytes, a valid zip (store
compression), 1542 entries, 897 real files under lib/ dated 1994-95.
Slug foundation1, number 946, port 40248. The zip bundles a
fluffos-2.9-ds2.12 driver source snapshot, 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 (its own
bin/mudos.cfg confirms: mudlib directory : /mud/foundation/lib,
master file : /secure/daemon/master). A full-tree grep for
driver_hook/set_driver_hook across lib/ returned zero hits,
confirming genuine MudOS/FluffOS lineage (not LDMud).
Relationship to foundation2 -- NOT a duplicate
This collection already has an unrelated foundation2 (174, port 40221).
Foundation I and Foundation II are both by the same author, George Reese
("Descartes of Borg"), but they are two distinct, independently-shipped
releases:
- Foundation I is the earlier, more primitive release: its own
README(dated 2 April 1995) is subtitled "A minimalist LPC Library" and explicitly says "The intention is to have all Nightmare references removed, solidify the design base, and customize the documentation for Foundation II, which is due May 15" -- i.e. Foundation I's own authors describe Foundation II as its direct successor/rewrite. - Foundation I's
master.creads "from Nightmare 3.3 ... created by Descartes of Borg 940910"; Foundation II's own master carries the exact same "from Nightmare 3.3" header. Both are Nightmare-3.3-derived, but they are separately-maintained snapshots, not the same codebase --md5sum/diffagainstlibs/foundation2/workon shared filenames (master.lpc,daemon/banish.lpc,secure/daemon/events.lpc) shows real, substantive differences (Foundation I'smaster.lpcdoesn't even haveget_root_uid()/get_bb_uid()/creator_file()-- Foundation II's already-fixed copy does, from an earlier port of the sibling lib). - Both libs independently reference the historic
lpmuds.netIntermud-3 ecosystem and share the same install-your-own-content design philosophy ("no combat, limbs, or stats system at all"), but Foundation I is strictly older/less developed: it has no first-boot admin-setup installation wizard (Foundation II's biggest structural feature), simplermaster.lpc(no privs-string caching, no domain/write-access wizard install flow), and ships example/teaching content (domains/Examples) with the SAME kind of incompatible-API content gap Foundation II's own two teaching trees have -- a strong signal these are sibling snapshots of the same evolving Nightmare-3.3-based engine lineage at two different points in time, not a re-upload of the same release.
meta.json's duplicate_of is null -- this is treated as its own
distinct onboarding target per the task, not folded into foundation2.
1. Conversion
English-language archive (not GBK-encoded) -- scripts/convert_lib.sh
against raw/foundationI_fluffos_v1/lib confirmed this: 880/897 files
already valid UTF-8, 1 lossy conversion, 16 skipped binaries (compiled
.o save files under secure/save//daemon/save/, correctly left
alone), 275 files renamed .c->.lpc, 81 literal .c" references
fixed, 9 local angle-bracket #includes converted to quotes, 76 files
touched by the static->nosave sweep. No stragglers (*.C, unrenamed
.c) after conversion.
2. Compile-time driver-compat fixes
refis a reserved keyword on this driver (call-by-reference parameter modifier,REF_RESERVED_WORD) -- exactly the bug class already catalogued fromshadowgate(\S6.7) andfoundation2/nightmare3. Four sites, all fixed by renaming the identifier:
- std/user/refs.lpc's set_ref(string ref, ...)/get_ref(string
ref) -- this file is inherited straight into the player body
class (std/user.lpc via the REFS macro), so this alone broke
compilation of the ENTIRE player body. Renamed ref -> refname.
- daemon/refs.lpc's parallel set_ref/get_ref/resolv_ref
(the daemon-side interface to the above) -- same rename.
- secure/daemon/master.lpc's load_access(string cfg, mapping ref)
(plus its master.h prototype) -- renamed to refmap.
classis a reserved keyword on this driver (theclassstruct type) --secure/SimulEfun/magic_class.lpcdeclaredstring class;. This particular file is dead/unreferenced scaffolding (neverinherited or#included by anything real in this stripped-down lib --query_class()/character classes don't exist here), so it never actually blocks a real boot, but it's the same reserved-word collision class and cheap to fix on sight: renamed toclassname.- A
.c->.lpcfilename-slice arithmetic bug in the command-dispatch table -- \S7.118's exact bug class, confirmed on a 6th lib in the same session.daemon/command.lpc'srehash()builds the verb->path lookup byget_dir(dir+"/_*.lpc")then slicingchoses[j][1.. strlen(choses[j])-3]to strip the leading_and the extension -- correct arithmetic for the ORIGINAL 2-character.cextension, but after this project's.lpcrename every single verb key comes out corrupted (e.g._look.lpc->"look.l"instead of"look"), which would have silently broken every ordinary player command with no compile error and no crash. Fixed:-3->-5(stripping the leading_and the 4-character.lpcextension). This is Foundation I's own independent command dispatcher (daemon/command.lpc, "from Nightmare IV, created by Descartes of Borg 940119") -- NOT the same file asfoundation2's dispatcher, which was already fixed for an unrelated bug and never had this slice-arithmetic issue. PACKAGE_UIDSis enabled in this project's driver build, and this master object never defined the two applies it requires -- the exact same fixfoundation2/nightmare3needed (same "from Nightmare 3.3" master.lpc lineage, but NOT byte-identical -- diffed independently).set_master()callsexit(-1)at boot ifget_root_uid()is missing (confirmed vialpcc's own "No function get_root_uid() in master object" message, and by readingvm/internal/master.cc). Added:get_root_uid() { return "Root"; },get_bb_uid() { return "Backbone"; }(ported verbatim from the sibling libs' fix).creator_file(string str)-- also required byPACKAGE_UIDS, called by the driver on EVERY object load -- was likewise never defined here. Reuses this codebase's own existing top-level-directory privs classification (secure/SimulEfun/security.lpc'sfile_privs(), already used bycheck_access()/privs_file()for this lib's real access-control model) rather than inventing a new uid scheme, with the samesscanf(str, "%s#%d", base, ext) != 2unmatched-%s-defaults- to-int-0 guardfoundation2's port needed for the same reason (a clone-suffix-free path is the overwhelming majority of calls, and an unmatched%starget keeps its int-0default, which would otherwise crashfile_privs()'s ownexplode()/string indexing).statusas a legacy LPMud integer-return-type keyword, unsupported by this driver --std/room/senses.lpcdeclaredstatus smell_things(string str)andstatus do_listen(string str), both hard syntax errors (unexpected L_IDENTIFIER, expecting L_ASSIGN or ';' or '(' or ',') on this driver, which doesn't recognizestatusas a type keyword at all. This one file's failure cascaded intostd/room.lpcand every room in the mudlib (std/roominheritsstd/room/senses), including the actual starting room. Fixed:status->intat both sites (only 2 occurrences in the whole tree, verified by grep).- Missing
#include <save.h>--std/access.lpc(an inheritable access-list daemon, "from Nightmare 3.2.2") referenced theSAVE_EXTENSIONmacro (defined insecure/include/save.h) but only#included<std.h>, which doesn't pull it in. The only file in the whole tree with this specific gap (grepped every otherSAVE_EXTENSIONuser first). Fixed by adding the missing include. - Genuinely dead code that still had to be made syntactically legal:
daemon/refs.lpc'sresolv_str()parses a"(:%s,%s:)"-shaped input string (an old-style anonymous-function literal typed by a wizard into aref/call-style debug command) and tried to build(: gtmp1, gtmp2 :)-- a functional/closure literal binding parameters togtmp1, amixedGLOBAL VARIABLE, not a function name. This driver hard-rejects that shape at compile time regardless of whether the branch ever runs (grammar_rules_exprs.cc'sFP_L_VAR/FP_G_VARcase: "Can't give parameters to functional."). The branch guarding this return was ALREADY unreachable dead code even in the original archive -- its condition checksstringp(tmp2), buttmp2is never assigned anywhere in the function (onlygtmp2is, a few lines above) -- sotmp2is always its default-unset value and the branch can never actually execute. Fixed by replacing the illegal literal withreturn gtmp2;, preserving the branch's existing (already-dead) unreachability while making the file compile. This broke compilation of 10 wizard-only debug commands thatinheritdaemon/refs.lpc(_ref,_refs,_eref,_dref,_tref,_call,_move,_unref,_cref, and the ambassador_ss/_sc"set/show class" pair) -- none of these are on the player registration/gameplay critical path.
lpcc_check.sh result after fixes: 270/275 files pass. The
remaining 5 are pre-existing content gaps (see \S3), none boot-critical.
3. Pre-existing content gaps (not fixed -- out of driver-compat scope)
domains/Examples/etc/{match,torch}.lpc(inherit LIGHT;) anddomains/Examples/etc/flu.lpc(inherit GERM;) referenceLIGHT/GERMmacros that are simply never defined anywhere in this stripped-down lib'ssecure/include/objects.h(which only definesOB_SIMUL_EFUN/OB_LOGIN/OB_USER/OB_SHUT/OB_WORKROOM/OB_POSTAL) -- a bare, undefined macro used as aninherittarget is a hard parse error (unexpected L_IDENTIFIER, expecting L_STRING or '('), not a resolvable "undefined identifier".domains/Examples/etc/ chest.lpcsimilarly references an undefinedOB_COINS. This is the exact same class of gapfoundation2's own two "Examples"/"School" teaching trees have (content written against a different, fuller snapshot of this evolving engine lineage that this particular archive never carried the matching macros for) -- not something this port introduced, and not fixable without inventing a currency/lighting subsystem this lib was never shipped with./domains/Examples/room/*and/domains/Examples/virtual/*also fail, but only as a side effect of inheritingstd/room-- these compile cleanly now that \S2'sstatus->intfix landed; only the 4 files above (pluscowtown, next) are genuinely-broken content.secure/etc/cowtown.lpc(a standalone quest object, "Nightmare, March 1993") doesinherit "std/quest_ob";-- there is no quest subsystem anywhere in this archive at all (matches the lib's ownREADME: "does not come with ... a combat system for a fantasy MUD, or a starter area"). Not preloaded, not reachable from the real starting room; a leftover example file from a fuller codebase this minimalist release never included.domains/Standard/xtra/freezer.lpc(the linkdead-holding room a disconnected player's body is moved to, see \S4) has its own"square"exit pointing at/domains/standard/square-- lowercase, vs. this archive's real/domains/Standard/(capitalized). Moot either way:/domains/Standard/square(any case) simply doesn't exist anywhere in this archive -- a genuinely pre-existing dead-end exit in the original 1994-95 content, not something this port's file renaming touched (renaming.c->.lpcnever changes directory-name casing). Only reachable from the freezer holding-room, not the main gameplay loop.- A dormant instance of the
nightmare3-lineage__AutoLoadsave-before-strip double-clone bug (see thenightmare3/dsI/finalrealmscatalog entries) exists byte-for-byte in shape instd/user/autosave.lpc-- identical function/variable names (__AutoLoad,pre_save(),setup()), identical mechanism (setup()unconditionallynew()s every__AutoLoadentry with no presence check,pre_save()never strips carried auto-load items from inventory beforesave_object()serializes them). Unlikenightmare3, this is currently inert: grepping the whole tree forquery_auto_loadfound only 2 generic consumers (cmds/creator/ _clean.lpc,std/clean_up.lpc) and zero real content objects that ever overridequery_auto_load()to return a non-zero value -- no guild markers, no wedding rings, no disease objects, nothing. Calling an undefined function via->returns 0 on this driver, so the whole mechanism is a no-op today. Left as-is (matches this project's "genuinely not applicable" precedent, \S2.3) rather than speculatively hardening dead code with no observable behavior to verify against; flagged here in case future content ever adds a real auto-load item. - A real, intentional outbound network side effect at boot: like
imud/foundation2,daemon/intermud.lpc(preloaded bysecure/cfg/preload.cfg) makes a genuinesocket_create()/UDP handshake attempt toward a historic Intermud-3 nameserver hardcoded as"204.209.44.3 8080"a couple of seconds after boot. This project's driver build does have__PACKAGE_SOCKETS__compiled in (confirmed viastrings/the driver's own LPC-predefines dump), so the file loads and the attempt happens -- almost certainly against a long-dead 1990s host, but a real outbound connection nonetheless. Per \S2.3's precedent, this lib should NOT be swept into high-frequency automated reboot loops (round-two/round-three-style re-test cron, \S10.0 long-sit scans). - Pervasive, harmless
nosave-on-function warnings: this codebase (like itsfoundation2/nightmare3siblings) applies thenosavemodifier to ordinary function declarations throughout (an old MudOS- era idiom this driver just warns about -- "Illegal to declare nosave function" -- rather than erroring on). Hundreds of instances, cosmetic only, left as-is; several other harmless warnings turned up too during live testing (std/room/exits.lpc'squery_enters()prototype/definition return-type mismatch,secure/SimulEfun/ time.lpc'sdate()likewise,std/user/editor.lpc's(: "return_to_ edit" :)functional-literal-wrapping-a-string-constant warning) -- none of them affected any tested behavior.
4. WASM-era standard patches
- Loopback-allow (\S1.3b): added a fail-closed short-circuit to
daemon/banish.lpc'sallow_logon()(ip == "127.0.0.1" || ip == "::1" || strsrch(ip, "127.") == 0->return 1, before any real banish-list parsing). Note: on a fresh install this gate has nothing to actually block --__Sites/__WatchSites/__Allowed/__Guestsall default to({})increate(), soallow_logon()returns 1 unconditionally for everyone regardless of this patch. Added anyway per the standing policy, and it's ready the moment any site/name restriction is ever configured. - Admin seeding (\S1.5): registered
fluffos/Mud@2026through the normal registration flow (verified live, see \S5). This lib's security model is a directory-based privs/group scheme, not awiz_statusmapping -- granted admin by addingfluffosto the existing(SECURE)group entry insecure/cfg/groups.cfg(adminname->adminname: fluffos, colon-separated permaster.lpc's ownload_access()parser).adminnameis the ORIGINAL archive's own unclaimed template placeholder (no matching save file exists undersecure/save/users/, unlike the real historicaldescartesaccount that IS present there) -- left in place rather than removed, so the deviation from a truly pristine template stays minimal and documented. Verified live via_call/_eval(bothsecure/cmds/creator/, ambassador-tier-and-up) producing correct results asfluffos--check_access()'s privs logic grants SECURE-privs objects unrestricted read/write, satisfying this project's usual "wizard command actually works" bar even though this lib has no single canonicalupdate-style command to test against (its wizard toolset is the_call/_eval/_reffamily instead).
5. Live verification
Booted the real native driver (~/src/fluffos/build-debug/src/driver
config.fluffos, cd'd into libs/foundation1 first) repeatedly; zero
fatal errors, "Accepting telnet connections on 0.0.0.0:40248." /
"Initializations complete." every time. Full registration -> world
entry -> command verification, via scripts/mudclient.py, one
continuous session:
name: wizbaz -> y -> password (Mud@2026) -> confirm -> gender: female ->
display name: <blank, defaults to Wizbaz> -> email: [email protected] ->
real name: <blank, defaults to "Unknown"> -> [news pager: <return>] ->
look -> status -> inventory -> quitAll correct: entered domains/Standard/center (the "Pool Room" --
"You find yourself standing in a large cavern, lit softly by glowing
lichen...") with its make_pools()-cloned pool object present, look
re-displayed the same room correctly, status (this lib's real
score-equivalent -- there is no score command anywhere in this
minimalist archive, matching its own README's "no ... stats system"
description, \S2.3-style) printed hp: 1/10 sp: 1/10 mp: 1/10
experience: 0, inventory printed You are empty-handed., and quit
cleanly printed Please come back another time!. Also verified the
name-validation gate (BANISH_D->valid_name() correctly rejects
non-lowercase-alphabetic names, e.g. a name containing a digit) and the
admin account end-to-end (registration, (SECURE) grant, live
_call me;move;/domains/Standard/center returning MOVE_OK (0) plus a
correct look afterward). log/errors/* only ever accumulated the
harmless nosave-function/prototype-mismatch warnings cataloged in
\S3 -- no runtime crash signatures.
Environment note, not a Foundation I bug: in this session's sandbox,
a driver launched even with setsid nohup ... & disown (\S10.8's
documented workaround for "plain nohup ... & has died from stray
SIGTERMs between tool calls") still reliably stopped listening/exited
cleanly (no crash trace, no log signature) around the ~100-130 second
mark regardless of activity -- reproduced 3 times, including one run
where nothing at all was sent to it. Every actual registration/
gameplay verification above completed well inside that window in one
continuous client session, so this didn't block verification, but a
future session re-testing this lib (or debugging via a long-sit \S10.0
boot-watch) should budget for restarting the driver more often than
usual and not read a disappearance alone as a Foundation I-side crash
without also checking whether the process merely aged out of this
particular sandbox's process lifetime.
WASM status: not attempted this session (wasm_status left "").
深度功能测试 / §10.7 deep functional test (2026-08-31)
Round-two pass per the queue directive to hold every recently-onboarded
lib to the §10.7 bar. Since this lib has no combat/skills/stats system
(confirmed in \S1-5 above, matching its own README), this pass targeted
the actual engine features onboarding hadn't yet exercised: mail, help,
bug reports, finger, and a real quit/reconnect cycle. Booted
~/src/fluffos/build-debug/src/driver config.fluffos from
libs/foundation1/, tested via scripts/tmux_mud.sh (two concurrent
sessions, fluffos the seeded admin and a fresh throwaway mortal
xiaoyan), restarting the driver as needed for this sandbox's
documented ~100-130s lifetime quirk (\S5) and to pick up each fix.
Bugs found and fixed
All of the following share ONE root cause, a single systemic pattern —
see the new AGENTS.md \S8.23 entry for the full writeup and confirmed
recurrence in the nightmare3 sibling lib:
A (: "funcname" :) single-string functional literal was valid MudOS
syntax in 1993-95 (this lib's own doc/lpc/types/function states it
verbatim: "makes a function pair that is equivalent to (: this_object(),
"funcname" :)"), but this driver's compiler no longer honors that —
calling such a "function" just returns the string, the named function
is never invoked. This silently broke every edit()/more()
"when you're done, call me back" hook written that way:
secure/std/post.lpc:1355,1361,1415— mail compose/forward/reply'sthis_player()->edit(tmp, (:"complete_send":), (:"abort":)). Live repro:mail xiaoyanasfluffos, wrote a message, typed.to finish — no send-confirmation menu appeared, and xiaoyan's mailbox showed "Folder is new with zero letters" (the message was silently discarded, no error, no feedback). Fixed:(: complete_send :)(already prototyped inpost.h), abort callback changed to plain0(falls back toedit()'s own existing default no-op rather than inventing a new function). Verified fixed: same flow now shows "e)dit, f)orget, s)end", sending produces "Mail successfully sent!", and xiaoyan's mailbox correctly shows the letter and can read it.secure/std/post.lpc:823— reading a mail letter'sthis_player()->more(..., "mail", (: "end_read" :)). Live repro: reading a short letter (no--More--paging needed) dropped straight to the raw>prompt instead of the expectedCommand (i for index menu):follow-up. Fixed:(: end_read :)(needed a NEW prototype added topost.h, sinceend_readis defined after its point of use and forward references need an explicit prototype on this driver — a bare-identifier fix without adding the prototype produces a NEW compile error,Undefined variable, confirmed the hard way). Verified fixed live.secure/std/post.lpc:511— the mailer's own?/help command'sthis_player()->more(..., "help", (: "end_help" :)). Same class, fixed the same way ((: end_help :), new prototype added topost.h); not separately live-repro'd beyond the compile check since it's the identical pattern toend_readin the same file.daemon/help.lpc:264— the TOP-LEVELhelpcommand's menu-driven flow:this_player()->more(tmp, "help", (menu ? (: "endmore" :) : 0), ...). Live repro:help-> pick category -> pick topic ("postal") -> page through with--More---> after the last page, dropped straight to>instead of "Hit <return> to continue:" back into the category menu. This is the general help system used by EVERY player for EVERY topic reached via the menu (not just the mail-specific help above). Fixed:(: endmore :)(prototype already present inhelp.h). Verified fixed: the same flow now correctly shows "Hit <return> to continue:" and returns to "Selection:".std/user/editor.lpc:64— the shared player-body editor mixin's in-editor-help (~hwhile composing ANYTHING — mail, bug reports, bulletin posts):this_object()->more(EDITOR_HELP, "help", (: "return_to_edit" :)). Fixed:(: return_to_edit :)(needed a new prototype added toeditor.h). Not separately live-repro'd (low-traffic sub-feature of every editor session, same confirmed-working pattern as the others).secure/cmds/mortal/_bug.lpc:29— the player-facingbugcommand'sprevious_object()->edit(..., (:"end_edit":), (:"abort":)). Live repro:bug(no argument, triggers the interactive-editor path), typed a report, typed.— nothing printed on completion (though the report WAS still logged tolog/bugsin this specific case, sincereport()happened to run from inside the callback's own body before the bug bit — don't assume silent output always means nothing happened). Fixed:(: end_edit :), (: abort :)(needed a NEW prototype forend_edit— the file already had a stale, unrelatedvoid end_text();prototype left over from what looks like an old rename, itself dead/unreferenced anywhere, left alone). Verified fixed:bug-> compose ->.now correctly prints "Bug reported! Thank you!" immediately.cmds/adm/_register.lpc:23andsecure/cmds/creator/_changelog.lpc:34— the adminregister <site>and creatorchangelog <dir>commands' own interactive-editor callbacks, identical pattern. Fixed the same way (added fresh prototypes to each single-file command, since neither had one). Verified by lpcc compile only (both are low-traffic admin/creator-only tools; the pattern is now proven correct on 3 independently-tested siblings above, so a compile-clean fix here was judged sufficient rather than re-deriving live repro for every site).
Left unfixed, judged low-impact: secure/std/post.lpc:34-36's
set_prevent_get/drop/put( (: "remove"/"drop" :) ) are the SAME broken
construct, but the caller (std/Object.lpc's allow_get()) only checks
the callback's return value for TRUTHINESS — a string is truthy
regardless of content — so the intended "block this action" behavior
still works correctly; only the descriptive denial message is silently
dropped. The affected object (the postal mailbox) is also set_invis(1)
so a player can't normally target it with get/drop/put at all,
making this a barely-reachable cosmetic gap. Left as-is per this lib's
own existing NOTES.md precedent for similarly low-impact cosmetic
nosave/warning-only issues.
A separate, unrelated bug found via finger
secure/daemon/finger.lpc's user_finger_display() had a
missing-braces bug: if(this_player() && adminp(this_player())) if(...) tmp = ...; else tmp = ...; drow = tmp;
— the trailing drow = tmp; was written as if it were inside the outer
if, but with no braces it's an UNCONDITIONAL statement that runs for
EVERY finger, admin viewer or not. Live repro: finger fluffos as the
non-admin xiaoyan printed Male IdeaExchanger Coder of the HackersOn
since : ... — missing the title line entirely AND missing the newline
before "On since", because drow got silently reset to the stale,
newline-less tmp value from several lines earlier instead of keeping
the properly-built (title + bio + trailing \n) string. Fixed by
adding braces around the admin-only email-line block (line ~47-52) so
drow = tmp; only executes when actually appending the email line for
an admin viewer. Verified fixed: finger fluffos as xiaoyan now
correctly prints the title line, the bio line, "Last on: ..." (or "On
since : ..." if actively connected), unread-mail count, and "No Plan."
each on its own line.
Confirmed working (no fix needed)
- Mail (IIPS 3.1) end-to-end: send, receive, read, folder listing, unread markers — full round trip between
fluffosandxiaoyanafter the fixes above. - Help system: menu navigation across categories, topic display,
--More--pagination (tested a 5-page topic),qto exit cleanly. bugcommand: both the direct-text path (bug <text>, no editor) and the interactive-editor path.finger,news,who,date,version— all correct output.- Registration (repeated from a fresh second account,
xiaoyan, per \S5'swizbazprecedent) and a realquit-> "Please come back another time!" -> clean disconnect. - A ~190-second continuous idle-connected session (\S10.0 long-sit watch, held via
mudclient.py --idle 30) produced zero fatal errors and the driver survived the full duration and remained responsive afterward — notably LONGER than \S5's previously-documented ~100-130s sandbox lifetime quirk, so that quirk is apparently not deterministic (or was specific to a particular earlier run); still worth budgeting for on a future session regardless. - The Intermud-3 socket (
daemon/intermud.lpc, \S3) did not crash anything across 5 separate driver boots this session, including the ~190s long-sit — confirms the \S3 caution (real outbound UDP, don't reboot-loop this lib) without finding any NEW crash risk.
Observed but NOT fixed — flagged for the record, not a clear bug
log/runtime accumulated several thousand *Denied write permission in
save_object(). entries this session, all from daemon/intermud.lpc's
eventRead() (lines ~121/145, save_object(SAVE_INTERMUD) after a
startup-reply/mudlist packet) via secure/std/client.lpc's
eventReadCallback. Two things both need a human's attention but
neither felt safe to "fix" under this pass's driver-API-misuse-only
scope: (1) the daemon apparently can NEVER persist its Intermud
mudlist/nameserver state under this lib's directory-privs security
model (a real functional gap, but fixing it means deciding how a
system daemon should be granted write access under this lib's
own custom, install-it-yourself privs scheme — a security-design
judgment call, not a clear typo); (2) the sheer VOLUME of packets
received in a ~20-minute session (thousands) from the hardcoded 1996
nameserver address is surprising and not something the LPC code
controls either way (no crash or unbounded growth observed, just a lot
of log noise) — flagged as an environment/network observation, not
a lib bug.
Flagged unverified
- Message boards (
std/bboard.lpc): confirmed (again, per \S3) that this is dead code with zero live instances anywhere in this archive — no command creates or reaches a board object, so there is nothing to functionally test. Not a gap introduced by this port. - WASM: measured 2026-09-03 — see the WASM measurement section below.
WASM measurement (2026-09-03)
meta.json was already playable from the 2026-08-31 deploy-unblock
(2ec643a7c45); the README still said "not attempted." Cold-boot under
the shared ~/src/fluffos/build-wasm failed on the eager simul_efun
calling socket_status() from dump_socket_status() in
secure/SimulEfun/SimulEfun.lpc — same lima §1.3(c) / wilderness class.
Guarded the body with #ifdef __PACKAGE_SOCKETS__ (empty-string stub
otherwise). After that, scripts/wasm_client.js logged in as
fluffos / Mud@2026 into the Pool Room cavern, look showed lichen,
status reported hp: 1/10, and quit printed "Please come back
another time!" Shop/combat/death were not exercised this pass.
Shop + 拜师 (2026-09-04 librarian slice)
Content-absent, not a programming bug. This is the 1995 "minimalist LPC
Library" snapshot: no combat, no stats, no currency, no vendor objects,
and no apprentice/guild verbs. secure/etc/shops is leftover Nightmare
path list (/d/standard/supply.c, wizard cartesia/winona shops) — none
of those rooms exist in this archive (zero *shop* / *supply* under
work/, no do_buy/cmd_buy). Live on 40248 as seeded admin
fluffos / Mud@2026 into the Pool Room cavern: buy torch, list,
shop, and apprentice all printed What?. Help's player-general
topics are mail/prompt/terminal — no shop. Left as-is.
深度功能测试(§10.7,2026-10-03)— site-like tree, every compile warning
Method: a tree built the way the site builds it (scripts/pristine_tree.sh:
tracked files only, plus work/log and the directories
scripts/wasm_keep_dirs.txt records for the slug), not the old working tree
that earlier boots had filled with directories. Registration, the one-line
reports, the editor-based bug, mail, passwd, chfn, suicide, tells,
the soul, ~90 further commands, and disconnects at the name, password,
confirm, gender, email and news-pager prompts and inside the editor and the
mailer. Final run (fresh tree from HEAD): 25/25 verify checks, the chat /
passwd / suicide / disconnect battery all pass, 89 commands with no error.
1. New characters could not be created on the site. After the y at
"Do you really wish ... to be your name?" login.lpc calls
log_file("new_players", ...), which (called from a user object) writes
/log/secure/new_players. The zip never carries log/, and the keep list
had no entry for foundation1, so the write raised Wrong permissions for
opening file ... "No such file or directory" and new_user() aborted
before input_to("choose_password"): a bare > prompt, no account. The
live fluffos-boot.js showed keepDirs: []. Fix: gen_keep_dirs.py
--merge foundation1 foundation2 recorded the 15 + 6 log/ directories
the lib uses (log/secure, log/errors, log/etc/*, log/reports,
...).
2. tmp/ missing: bug without text (editor mode) raised the same
Wrong permissions ... /tmp/<name>.bug error. Added tmp/.gitkeep.
3. secure/save/letters/ missing: mail composed fine, then *Could not
open /secure/save/letters/<n>/<id>.o.tmp for a save; the letters daemon
mkdirs the one-digit bucket but not its parent. Added
secure/save/letters/.gitkeep. Also added realms/.gitkeep: user_path()
is /realms/<name>/ and eval/~ need that directory, which a creator
makes with one mkdir.
4. [0..-2] is empty in this driver (negative range ends do not count
from the end; the form is [0..<2]). uptime printed a lone ., and the
~ / ~name expansion in absolute_path() produced "", so cd ~ and
ls ~ went to / even with a home directory. Both fixed to [0..<2].
5. Every compile warning fixed at its source (the first visitor used to
get ~400 lines of them, because everyone is a creator and log_error
prints to creators). lpcc --batch over all 275 files now reports none:
- 374 nosave function declarations (the .c to .lpc conversion turned
every static into nosave; FluffOS accepts nosave on variables
only): protected (the historic static meaning), private ones lose
the redundant nosave; reset() stays public because the reset
command calls it from outside. A rejected external call is logged by the
driver as apply() with insufficient permission; zero of those appeared
in any run (only a deliberate probe).
- 98+ unused locals removed; get_stack() listed the function name twice
and never the program name (stack0 was the unused variable).
- (: "remove" :) in the post box is a function returning the string
"remove", not a pointer to remove() (old MudOS meant the latter): now
(: remove :); (: "drop" :) called a drop() that exists nowhere and
so always answered 0, now an explicit prevent_put() returning 0. Same
in Examples/room/search and Examples/virtual/start ((: search_brush :)
with a forward prototype).
- prototypes that disagreed with the definition (query_enters,
query_reply_text, date), unknown escapes (\| in the centred
sprintf, \. in two sscanf patterns, \<, \P), empty if bodies
and a no-op statement in master / intermud / network, the services
daemon's MSG_SYSTEM spelled two ways.
- secure/daemon/users.lpc inherited DAEMON and OB_USER, so
/std/clean_up came in twice (four ambiguous functions and a redeclared
__NoClean); it inherits OB_USER only and spells out what DAEMON
added (remove() with the valid_apply check, query_prevent_shadow).
- #pragma no_warnings is not used: a mudlib does not carry it.
6. Still failing to compile, unchanged (archive content gaps, section 3):
Examples/etc/{chest,flu,match,torch} and secure/etc/cowtown.
7. Native only, not touched: daemon/intermud saves /save/intermud.o, which
secure/cfg/write.cfg does not allow and the tree does not have, so every
I3 packet from a router logs Denied write permission in save_object()
(hundreds per hour in log/runtime). The site has no sockets.
2026-10-03 addendum — an unused-local deletion that broke an inactive branch
domains/Examples/room/search.lpc search_brush(): the compile-warning sweep deleted
object ob; as unused, but ob is used inside the #ifdef OB_TORCH branch the driver does
not compile here. Restored inside the branch. The same scope-aware check (a deleted
declaration whose name still occurs under an #if in the same function) found nothing else
in this lib; scripts/lpc_warnings.py now skips such locals (KB 04 §6.10).
深度功能测试(§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 前后对比,主控载入段只少了这两行。
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/SimulEfun/SimulEfun.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).
add_action() registrations of functions nobody defines (2026-10-21, KB 06 §7.349)
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 2 registrations in 2 files (scripts/lpc_fix_dead_add_action.py), the verbs are left to their other handlers: /domains/Examples/virtual/start.lpc (doclimb), /std/bboard.lpc (cmd_save). Most frequent: doclimb x1, cmd_save x1.
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. 1 statement(s) in 1 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 1 changed files, HEAD vs tree: 1 load at HEAD, 1 load now (0 newly loading, 0 regress).
The void, the freezer and every new workroom led to a room that does not exist (2026-10-22, KB 06 §7.257 addendum)
domains/Standard/xtra/void.lpc (the room a player lands in when a move fails), xtra/freezer.lpc, std/obj/workroom.lpc (the template of every new wizard's workroom) and cmds/creator/_pupdate.lpc (parks the contents of a dissolved room) named /domains/standard/square / /domains/standard/void: lower-case standard (the directory is Standard) and a "square" this lib never shipped (Foundation's central room is /domains/Standard/center, ROOM_START of secure/include/rooms.h). The void's any_hook sends every player without a start setting there on the first command, so a player in the void got *call_other() couldn't find object '/domains/standard/square' for ever (seen by the goto probe after a failed goto), and the square exit of the freezer and of a workroom went nowhere. The four files use ROOM_START / ROOM_VOID of the lib's own header now. Proof (scripts/lpcc_eval.py, fresh process, eval object inside /domains/Standard): HEAD's three rooms answer query_exit("square") = /domains/standard/square with no file; the tree's answer /domains/Standard/center, 1,698 bytes, loads; _pupdate compiles in both. Not touched: cmds/ambassador/_law.lpc COUNCIL_CHAMBER "/domains/standard/council" (a room nobody shipped: content) and the building tutorials in doc/ that still describe the Nightmare square.
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 1 subdirectory of log/: log/chan (secure/daemon/chat.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() makes the directory itself (measured, or read from the source: the nitan family's assure_file()), so only the directories written by a direct write_file() / save_object() are listed. log/chan 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.