Merentha FluffOS Lib

✅ 可玩

Merentha

merentha

更新 368a3d0 2026-09-04 源码 下载 ZIP

▶ 开始游玩 · Play Now

A classic English-language LPMud lib explicitly based on the popular Nightmare 3 lib, per its own description aiming to have "the Nightmare feel and robustness without the complications of verbs or weird lfun names." New characters wake in a race-selection limbo room (a wall of frozen male/female bodies in wall compartments, one pair per race), pick a gender and race with `become <gender> <race>` from six options (human, giant, gnome, elf, dwarf, fairy), and land in the Cabeiri starting town with its central square, guild halls (fighter/mage/healer/monk), and general supply shop. Ships a full skill-point system (30+ skills from swordplay to alchemy to bargaining), a limb-based body model, an Intermud-3 daemon, and a wizard-oriented `/cmds/admin` toolset.

README

Merentha is a classic English-language LPMud lib explicitly based on the popular Nightmare 3 lib -- per its own description, aiming for "the Nightmare feel and robustness without the complications of verbs or weird lfun names." This collection already carries nightmare3 (160) itself, so the two are worth comparing side by side.

Highlights

Provenance

The original lpmuds.net distribution site is now defunct. This archive (merentha_fluffos_v2.zip, a maintained, already-FluffOS-adapted release) was recovered from the Wayback Machine snapshot <https://web.archive.org/web/20120225071653id_/http://lpmuds.net/files/merentha_fluffos_v2.zip> and content-verified before porting.

What was fixed

Only one driver-compatibility fix was needed anywhere in the ~140-file mudlib: this driver build requires get_root_uid()/get_bb_uid() applies on the master object (PACKAGE_UIDS support), which this archive never defined -- the driver otherwise exits immediately after loading the master object, before any mudlib content ever runs. Fixed by porting the same two-line pair already used by the closely-related nightmare3 lib in this collection. See NOTES.md for the full diagnosis, plus a documented (not fixed, out of scope) cosmetic display glitch in the score/inventory panel border rendering.

Registration flow

name -> confirm keep name (y/n) -> password (4-8 characters) -> confirm password -> name capitalization format (default: capitalized name) -> straight into the game world's race-selection room -> become <gender> <race> (e.g. become male human) -> Cabeiri.

Admin account

Registered through the normal registration flow, then granted admin by editing the account's own save file directly (this lineage's set_position() setter is itself gated behind already-being-an-admin). Verified live: the login banner shows "You are logged in as an administrator", and update /cmds/admin/update succeeds.

Warning: Mud2026 is a public default password for local play only.
Change it before exposing this host publicly.

Status

Boots clean: zero fatal errors, a compile-sweep of 142/143 files passing (the one non-pass is an inert plain-text documentation file misnamed with a .lpc extension by this project's blanket rename, not real LPC source -- see NOTES.md). Full registration, race/gender selection (become male human), look/score/inventory/quit, and re-login as a returning character all verified end-to-end with a real driver session and a raw socket client. Admin account verified with a wizard-only command.

WASM status: playable. Login as fluffos, look, and score are verified under the shared WASM driver (NOTES.md WASM status update). Play: https://mudlibs.fluffos.info/merentha/

Local run

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

Game port: 40223.

NOTES · 移植与修复记录

merentha (176)

Provenance

merentha_fluffos_v2.zip from lpmuds.net/files/ (a maintained, already-FluffOS-adapted release bundling a FluffOS 2.9-ds2.03 driver source snapshot alongside the mudlib -- the driver snapshot was ignored; this project uses its own driver at ~/src/fluffos). The original lpmuds.net site is now defunct; this archive was recovered from the Wayback Machine snapshot <https://web.archive.org/web/20120225071653id_/http://lpmuds.net/files/merentha_fluffos_v2.zip> and was already content-verified (real, non-corrupted LPC source, e.g. merentha_fluffos_v2/bin/local_options) before this pass began.

Mudlib root: merentha_fluffos_v2/lib/ (the archive also ships bin/, win32/, and the fluffos-2.9-ds2.03/ driver-source tree, all ignored).

Merentha is explicitly derived from Nightmare 3 -- its own description advertises "the Nightmare feel and robustness without the complications of verbs or weird lfun names." This collection already has nightmare3 (160), which needed essentially zero driver-compat fixes; Merentha turned out to need exactly one.

Conversion (scripts/convert_lib.sh)

Ran unconditionally per AGENTS.md convention even though the archive was already clean English UTF-8/ASCII (verified: already_utf8=756 converted=0 lossy=0 across all 757 files):

Proactive checklist (AGENTS.md \S2.2) -- none of the 6 known

recurring bug classes from this archive family applied here

Checked all six classes flagged from sibling lpmuds.net-family archives (genesis, finalrealms, foundation2, discworld, tmi2) before the first boot attempt; none were present:

1. add_action() with a bare function value instead of a string -- every add_action() call in the tree uses a string literal. 2. ed_start()/ed_cmd()/query_ed_mode() (new editor API) -- zero hits anywhere in the tree; the ed command isn't part of this mudlib's admin toolset at all. 3. .c->.lpc rename breaking fixed-width filename-slice arithmetic in a command-dispatch table -- commandHook() in std/inheritables/user.lpc resolves commands via file_exists(path+cmd_path+".lpc"), not by slicing filenames pulled from a get_dir() listing, so the rename is a non-issue here. 4. ref used as an ordinary identifier -- the only \bref\b hits in the whole tree are inside doc/old/lpc/constructs/ref.lpc, a plain-text help-file describing the LPC ref keyword itself (not real code -- see below). 5. Missing creator_file() master apply -- already defined in daemon/master.lpc (delegates to /sefun/sefun), and works. (What *was* missing turned out to be a different pair, see below.) 6. explode() on SANE_EXPLODE_STRING builds -- master.lpc contains no explode() calls at all.

The one real fix: missing get_root_uid()/get_bb_uid()

This driver build has PACKAGE_UIDS enabled, which requires get_root_uid()/get_bb_uid() applies on the master object -- vm/internal/master.cc's set_master() calls exit(-1) immediately if either is missing or returns a non-string, with only a debug_message ("No function get_root_uid() in master object...") as a clue, no crash trace. Neither was ever defined anywhere in this archive (unlike creator_file(), which was), so the driver silently exited (255) right after loading master.lpc, before ever reaching any of the actual mudlib content -- this is exactly the same driver requirement (and exact same fix shape) documented for foundation2 (174) in this collection, and the closely-related nightmare3 (160) already carries the identical two-line pair with the identical values. Ported as-is into daemon/master.lpc right next to the existing creator_file():

string get_root_uid() { return "Root"; }
string get_bb_uid() { return "Backbone"; }

This was the *only* driver-compat fix needed anywhere in the ~140-file mudlib. Confirmed via lpcc --batch: 142/143 pass after this one change (the remaining "failure" is inert doc content, see below).

Compile-sweep tail: doc/old/lpc/constructs/ref.lpc (expected, not a

bug)

The only file that fails to "compile" in the lpcc --batch sweep:

/doc/old/lpc/constructs/ref.lpc:1:7: error: syntax error, unexpected L_REF

This isn't LPC source at all -- it's a plain-text help/documentation file (using TITLE()/PRE/ENDPRE markup consumed by the man/help daemons as raw text, not compiled) that describes the ref keyword construct, and happens to sit in a doc/ tree that got the project's blanket .c->.lpc rename along with everything else. Confirmed dead for compilation purposes: nothing anywhere in the tree inherits or load_object()s it; the doc/ help system only ever read_file()s these as text. Left as-is -- not a conversion bug, and not reachable via the compiler at all under real play.

Cosmetic pre-existing content bug, NOT fixed (out of this project's

scope per AGENTS.md's programming-bugs-only rule)

score (and any other UI panel built with sefun/strings.lpc's border() helper, e.g. money/inventory display) shows the literal string __BORDER_LINE__ as a padded content row instead of a dashed separator line:

|  Score  : 0 (level 1 child)                                             |
| __BORDER_LINE__                                                         |
|  Vitals : Health 91(91)       Magic 90(90)        Stamina 95(95)        |

cmds/player/score.lpc builds its message with a sentinel line string line="__BORDER_LINE__"; interleaved between sections, relying on border() splitting the *wrapped* text back into an array with explode(wrap(str,...), "\n") and matching strs[y]=="__BORDER_LINE__" exactly to swap in a real dashed line. In practice the wrap() call (via the terminal_colour() efun, which reflows/pads text to a target width) doesn't preserve that line as an exact standalone array element, so the equality check never matches and the sentinel falls through to the generic "format this as a content row" branch instead, printing the raw placeholder text with padding.

This is a display-only cosmetic glitch (every underlying value -- level, HP/MP/SP, stats, money -- displays correctly) that this project's standing scope rule (fix programming/driver-compat bugs, not content/design issues -- and there's no error signature here, just a cosmetic mismatch) puts out of scope to "fix" by rewriting the mudlib's own line-splitting logic. Documented here rather than patched.

Boot + play verification

Boots clean: lpcc --batch 142/143 pass (see above), zero fatal errors on driver boot, log/errors_* show only pre-existing warnings (nosave on functions -- a driver pedantry warning, not an error) plus the doc-file line noted above.

Verified live over a raw Python socket client against the real driver (~/src/fluffos/build-debug/src/driver config.fluffos), full registration flow: name -> confirm keep name (y/n) -> password (4-8 chars) -> confirm password -> name capitalization format -> drops straight into the game world (a race-selection limbo room, not a separate out-of-world lobby). Then, in a second full run: become male human (this mudlib's race/gender-selection command, one of six races: human/giant/gnome/elf/dwarf/fairy) -> moves into a "becoming" transition room -> look, score, inventory all produce correct, well-formatted output (money correctly shows the starting 100 gold granted by become) -> quit saves cleanly and disconnects.

Re-login as a returning character (existing name -> password prompt -> correct password accepted) also verified working.

One easy-to-misread transient during testing, not a bug: an early test run disconnected via a raw socket close instead of quit, leaving a live (never-saved) in-memory player object resident under that name. Subsequent logins to that same name reconnected to the *stale live object* (reconnect()/"link-dead" recovery -- an intentional Merentha feature so a dropped connection doesn't lose your live character) rather than loading fresh save data, which looked at first like a save-file edit not taking effect. A full driver restart (clearing all in-memory state) confirmed the save file itself was correct all along.

Admin account seeding

fluffos / Mud2026 registered through the normal registration flow (see above). Grant mechanism: sefun/preds.lpc's admin_p(ob) treats any __Position value other than the literal string "player" as an administrator; std/inheritables/user.lpc's own set_position() setter is itself gated behind admin_p() (a chicken-and-egg problem for the very first admin), so the account's own save file, save/users/f/fluffos.o, was edited directly (per AGENTS.md \S1.5: "Prefer editing the *data* ... over code") to change __Position "player" to __Position "admin".

Verified after a full driver restart (see the stale-object note above): login banner shows "You are logged in as an administrator", the prompt switches to the admin <cwd> > form, /cmds/admin/ commands become reachable via commandHook()'s path search, and update /cmds/admin/update returns "Update Successful." -- confirms both the rank grant and that the admin-only command path resolves and compiles/reloads correctly.

Networking: local UDP daemon, but no live outbound traffic by default

daemon/network.lpc (preloaded at boot) binds a local UDP socket (MERENTHA_INTERMUD, query_host_port()+6) and schedules a periodic send_ping_request() call-out -- but that function only pings muds already present in its own __MUDS peer table, restored from /save/daemons/network. The shipped save data has __MUDS ([]) (empty), so with no peer mud ever registered, no outbound packets are actually sent; the socket bind is purely passive. Unlike imud/tmi2 in this collection (which hardcode a live public router IP and register with a real Intermud-3 network on every boot), Merentha's peer-discovery protocol here is opt-in and inert out of the box -- no live-network caveat needed for repeated automated re-boot testing.

WASM status update (2026-08-26, another session)

Promoted wasm_status from "" to playable. Booted clean under WASM with zero fixes needed -- unlike most sibling libs from this same source, no eager simul_efun/master socket call was present. The only sockets-related output is daemon/intermud.lpc's own deliberate #error guard (refuses to load without the sockets package, working as designed, not a bug). Verified with a scripted WASM session: login (fluffos/Mud2026), look, and score (reproducing the exact same cosmetic __BORDER_LINE__ display quirk already documented above, confirming consistent behavior between native and WASM). quit wasn't recaptured distinctly in this transcript but is already verified clean under native testing above.

Deep functional test (round two), 2026-08-27

Full continuous playthrough per AGENTS.md §10.7, against ~/src/fluffos/build-debug/src/driver booted from libs/merentha/. Read doc/news/welcome, doc/CLASSES, doc/SKILLS, and the in-game help menu first; the intended path is: register -> land in the race-selection room -> become <gender> <race> -> an optional personality-quiz room (begin, five multiple-choice questions that nudge starting skills) -> Cabeiri square (guild halls, a supply shop). Registered a real English-named test character (Corwin, later also Aldric/Brannor/Delwyn as throwaways) via a raw Python socket script; look/score/i checked after registration, after become, after guild sign-up, after combat, and after every quit/reconnect cycle.

SEVERE bug found and fixed: orphaned password-check clone leaks a

live heartbeat that silently corrupts every character's save data on reconnect

New AGENTS.md entry §7.150 (full writeup there). Summary: std/login/login.lpc's check_password() clones a throwaway new(USER) object purely to restore_object() the save file and compare the password hash. Whenever a player reconnects to an already-resident (link-dead) copy of their character -- the completely ordinary case of a client disconnecting without typing quit -- both query_password() and query_override() reassign the local __User variable to the found live object, silently discarding the reference to the just-created verification clone without destructing it. std/inheritables/living.lpc's create() unconditionally does set_heart_beat(1); for every clone of the class with no "is this a real playable character or a scratch object" distinction, so the orphan keeps ticking forever. std/inheritables/user.lpc's heart_beat() does if(autosave>25) save_player(query_name()); autosave++; -- once the tick counter passes 25 (~25 real seconds), the orphan saves every subsequent tick, forever, using the single, increasingly-stale snapshot it loaded at creation time, overwriting the real, currently-live character's save file under the identical name. Every unclean reconnect (a dropped connection, the normal failure mode for any real client) leaks one more such orphan, all racing to stomp the same file.

Caught live, not by code review: repeated score/sign up/combat progress on a real test character (level 1 "child" -> fighter, level 2, after killing the helper NPC) visibly vanished from the save file within about a second of a clean quit -- the quit's own save was correct, but a still-ticking orphan from several reconnects earlier immediately overwrote it. Confirmed the exact mechanism via the admin account's eval command and the objects()/ query_heart_beat() efuns: after ordinary play spanning six reconnect attempts, six independent /std/inheritables/user clones shared the name aldric, five of them still ticking with no relationship to any interactive session.

Fix (std/login/login.lpc): destruct the orphaned clone immediately before each of the two __User=find_player(...) reassignments (if(__User && __User!=find_player(player_name(__Name))) destruct(__User);), plus a new net_dead() apply on the login handler itself that destructs __User if a connection drops while still mid-password-prompt (a case the two call-site fixes don't cover on their own).

Verified live, end-to-end, with a fresh corwin test character:

Secondary fix: seeded admin account was missing its /realms/

directory, silently breaking the eval admin command

Per this file's own "Admin account seeding" section above, fluffos was promoted to admin by hand-editing its save file directly (since set_position() is itself gated behind admin_p() -- the standard chicken-and-egg bootstrap problem for a lib with no working auto-promote-first-admin mechanism, and merentha genuinely has none, so §7.149 does not apply here). The *normal* promotion path, cmds/admin/promote.lpc, also does mkdir("/realms/"+ob->query_name()); as its last step -- since the hand-edit skipped this, fluffos was missing /realms/fluffos/, and cmds/admin/eval.lpc (which write_file()s a scratch .lpc into that directory on every use) failed outright: Error: *Wrong permissions for opening file /realms/fluffos/eval.lpc for append. "No such file or directory". Every OTHER admin command tested (update, score, stat, call) worked correctly with full privileges -- this was specifically an eval-only gap from an incomplete manual seed, not a broader §7.149- style "admin promotion doesn't actually work" problem. Fixed by creating the missing directory (mkdir -p work/realms/fluffos, matching what promote.lpc itself would have done); verified eval works correctly afterward (used extensively for the diagnosis above).

Cross-cutting bug-pattern sweep (§7.80/118, §7.121, §8.3a, §7.112,

§7.122-149): clean

Grepped systematically for every standing pattern from the current AGENTS.md checklist. None present in this lib:

Observations, NOT fixed (content/design judgment calls)

Test evidence left in place

Seeded admin fluffos/Mud2026 (verified live: admin-only prompt, /cmds/admin/ command path, update, stat, call, and now eval all working). All throwaway test characters (corwin, aldric, brannor, delwyn) and their save files were deleted before this commit; git status shows only the std/login/login.lpc fix and the new (now non-empty, via a leftover diagnostic eval.lpc matching the existing realms/petrarch/ precedent) realms/fluffos/ directory.