Dock 9

✅ 可玩

dock9

更新 fdf581a 2026-09-12 源码 下载 ZIP 上游 dyher/dock9

▶ 开始游玩 · Play Now

A small English-language LPC mudlib built on the same LPUniversity/Sapidlib teaching-engine base as this project's lpuni -- a full login/registration/security/command-dispatch/help/mail/news/channel/Intermud-3 engine, with a small but genuinely original starting area: the dock town of Trepi, an island city trading across the Trepi Strait with Nulens, a 'relatively recently discovered continent' now being colonized by the empires of Erplak, an old continent from beyond the Besling Ocean. New characters start on Dock 9 itself -- 'one of the more magical places on the world,' where 'for as long as anyone can recall, people have appeared, lost and confused' -- among a network of numbered docks (7, 8, 10, 11, 12) connected by piers, cargo warehouses, and a customs house. A helpful greeter-sailor NPC welcomes each new arrival by name and, on request, teleports them to a separate floating-island training area (Make); a second, wandering 'unsteady sailor' NPC demonstrates subdomain wandering while visibly drunk on Nulens's lax law enforcement, name-dropping a place called Calliet. There is no combat/stats system at all. Also makes a real outbound Intermud-3 network connection at boot, same as lpuni/imud/tmi2/skylib.

README

A small English-language LPC mudlib built on the same LPUniversity/ Sapidlib teaching-engine base as this project's lpuni -- a complete login/registration/security/command-dispatch/help/mail/news/channel/ Intermud-3 engine, plus a small, genuinely original starting area: the dock town of Trepi.

Source: dyher/dock9 on GitHub. Slug dock9, number 964, port 40266.

Highlights

Registration flow

Please select a name: -> (new account?) Would you like to create it? yes -> password -> confirm password -> public e-mail address -> [Hit any key to continue] -> [Hit enter to continue] -> lands in Dock 9, the starting room.

Names may only contain the letters a-z (no Chinese support -- this is an English-only engine, same as lpuni).

Admin account

Verified live: update <path> against a real adm/obj/master.lpc file succeeds.

Status

Boots clean: zero compile errors on a fresh driver boot, zero entries in log/debug.log. Verified live with a real driver session and a raw socket client: fresh registration all the way into the world, inventory/ help/look/ls/news as a brand-new player's first commands, movement through the full bespoke Trepi/docks area (with the greeter NPC and real room content), the admin update command, a clean quit, and a successful reconnect. 207 of 220 .lpc files pass a full lpcc_check.sh batch compile sweep; the 13 that don't are all either non-LPC HTML templates that merely share the .lpc extension, files meant to be #included into simul_efun rather than compiled standalone, or an object whose create() legitimately needs a live player context -- see NOTES.md \S6 for the full triage.

WASM status: playable. Shared WASM driver. dump_socket_status() in adm/simul_efun/socket.lpc is stubbed without PACKAGE_SOCKETS (same treatment as lpuni). Admin login (fluffos / MudAt2026) into Training Ground, plus look / quit, verified with scripts/wasm_client.js. http_d.lpc socket_error is a graceful preload skip. Play: https://mudlibs.fluffos.info/dock9/

Local run

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

Game port: 40266.

Note: booting this lib makes a real outbound network connection (see Highlights above) -- avoid scripting repeated/automated boots.

NOTES · 移植与修复记录

Dock 9 -- porting notes

Source: dyher/dock9 on GitHub (git clone https://github.com/dyher/dock9), cloned into raw/dock9/. Built on the same LPUniversity/Sapidlib engine as this project's already-onboarded lpuni -- same adm/obj/master.lpc lineage, same Groups/access ACL scheme, same login/registration flow, same %^TAG%^ pinkfish colour system. The real mudlib root is the repo's lib/ directory; bin/, driver/ (a bundled MudOS/FluffOS/rth driver source snapshot), and etc/ (build/install scaffolding) are all ignored, same as every other archive in this project.

Genuinely distinct bespoke content, not a duplicate of lpuni: lib/areas/trepi/docks/ (24 files across obj/npc/room) -- a small, fully authored dock-town area ("Trepi") with real room descriptions, exits, an NPC, and author/date comments, none of it present in lpuni's own starting area. Small footprint, so this was a quick onboarding relative to a full raw-archive bring-up.

Slug dock9, number 964, port 40266. No slug/port/number collision with any existing lib (checked before starting). No LDMud tells found (no efun::-as-scope-resolution outside the expected simul_efun-override files, no #pragma strict_types, no LDMud-only applies) -- confirmed via direct grep of the raw source, consistent with prior research.

1. Conversion

English-language archive (not GBK-encoded), same as lpuni -- scripts/convert_lib.sh against raw/dock9/lib confirmed this: 1395/1413 files already valid UTF-8, only 2 lossy conversions (a stray non-ASCII byte in doc/license and in one archived Intermud-3 chat log under data/daemons/history_d/, neither code), 14 skipped as genuine binaries. 218 .lpc files after the .c->.lpc rename, 174 literal .c" references fixed, 0 local angle-bracket #includes needed converting, 1 file touched by the static->nosave sweep.

The archive also ships a handful of stray editor backup files (*.c.~1~, *.c~) committed upstream in the repo's own initial commit -- e.g. std/user/mobile.c.~1~, four files under areas/trepi/docks/{npc,room}/. Left as-is: they don't end in .c or .lpc so neither the driver's own file resolution nor scripts/lpcc_check.sh ever touches them; they're harmless dead weight carried over from upstream, not a conversion artifact.

2. Compile-time driver-compat fixes

``lpc string get_root_uid() { return "[admin]"; } string get_bb_uid() { return "[backbone]"; } string creator_file(string str) { string ret; ret = privs_file(str); if (!stringp(ret) || ret == "") ret = get_bb_uid(); return ret; } ` (needed a forward prototype for privs_file(), since creator_file() was inserted above its definition in master.lpc`).

- areas/trepi/docks/room/dock11.lpc: a stray extra pair of empty quotes before a real string literal -- ({"dock","dock 11"}):""This is Dock 11... -- syntax error, fixed by removing the stray "". - areas/trepi/docks/room/dock7.lpc: a missing comma between two set_items() mapping entries (one multi-line string literal ran straight into the next ({...}):"..." entry with no separator) -- fixed by adding the comma. - areas/make/management/towns.lpc: mismatched closing brace/parenthesis on set_chats(({ ... }}); (should close as })); to match the (({ opener) -- fixed.

(Confirmed this driver DOES support C-style adjacent string-literal auto-concatenation across lines with no +/comma between them -- several other room files in this same area rely on that, and none of them errored -- so that shape is not a bug and was left untouched.)

3. Runtime bugs found via live boot + play testing

Two bugs in this exact shape were already catalogued in AGENTS.md \S7.140 from onboarding lpuni (same master/valid.lpc + login.lpc lineage), and both are also present here -- ported the same fixes proactively rather than waiting to rediscover them live:

One further, previously-uncatalogued bug found live (see the new AGENTS.md \S7.179 entry -- this is genuinely wider than just this lib, see below):

4. Checked, present but inert / not present

5. Live outbound network side effect at boot (by design, not a bug -- same class as lpuni/imud/tmi2/skylib)

Same as lpuni: adm/daemons/chmodules/i3.lpc's Intermud-3 module makes a real outbound socket connection to the public *i4 router within a few seconds of boot, and receives real mudlist packets back from other real muds currently on that network (observed live: a real reply describing Lima 1.1a3 from 192.9.170.24). This mudlib doesn't implement a mudlist service handler (i3_rec_unsupported() logs it and moves on), so this is harmless but IS a genuine live network event against a real third party -- do not sweep this lib into high-frequency automated re-boot loops.

6. lpcc_check.sh batch-compile results

207/220 pass. The 13 that fail are all expected, not bugs:

IMPORTANT: while re-testing this lib, don't run lpcc_check.sh concurrently with a live driver instance still bound to port 40266 -- adm/daemons/http_d.lpc will spuriously fail to load with a port-bind conflict, which looks like (but isn't) a real regression. Kill any running driver first.

7. Boot and play test

Boots clean (native driver, ~/src/fluffos/build-debug/src/driver config.fluffos from libs/dock9/): "Loading preload files..." through every daemon, then "Accepting telnet connections on 0.0.0.0:40266." / "Initializations complete." with zero errors in log/debug.log.

Verified live with a raw Python socket client through the full flow:

WASM status: not attempted this session (wasm_status left "") -- scope was native-only per this onboarding's assignment.

Admin account

深度功能测试 / §10.7 deep functional test (2026-08-31)

Round-two pass, adapted for this lib's real scope (no combat/stats system, per \S0). One continuous session, native driver (~/src/fluffos/build-debug/src/driver config.fluffos from libs/dock9/, port 40266), driven via scripts/tmux_mud.sh across several parallel sessions (the seeded admin fluffos/MudAt2026, plus two throwaway fresh registrations mailerone/mailertwo created specifically to exercise mail/channel between two real accounts and cleaned up before committing). Everything the original onboarding already covered (registration through world entry, movement through Trepi/docks, inventory/help, quit/reconnect, news, the letter-bucket save-directory fix) was not re-run in full; this pass targeted what NOTES.md flagged as not yet deeply tested.

Six bugs found and fixed

1. adm/daemons/mail_d.lpc + adm/obj/master/valid.lpc -- the entire mail system was completely unusable, in two compounding ways (new AGENTS.md \S7.190 -- full root-cause writeup there):

2. adm/daemons/mail_d.lpc's own internal "only the real mail client may call this" guard was ALSO broken, independently, by the standard \S6.3 .c->.lpc rename-slice bug -- send_message()/ delete_message() both compared `file_name(previous_object())[0..index

OBJ_MAIL_CLIENT[0..<3], where OBJ_MAIL_CLIENT is "/obj/mudlib/mail_clients/mail_client.lpc" -- [0..<3] only strips 2 characters, correct for the ORIGINAL .c extension but leaving a stray .l on the right-hand side after the rename, so the two sides could never match. Every legitimate call through the real mail client was rejected with Error [mail]: You can only do that through the mail client!, masking bug \#1 above until that was fixed first. Widened both occurrences to [0..<5] (correctly drops the full 4-character .lpc extension). Same bug confirmed still present, unfixed, in lpuni's own copy of this exact file (also flagged, not fixed, per AGENTS.md \S7.190).

3. adm/obj/master.lpc's log_file() had no missing-directory guard, so the FIRST-ever force/makedev/revdev/nuke each boot crashed -- LOG_FORCE/LOG_PROMOTE/LOG_NUKE (in include/logs.h) all resolve to a path one level below LOG_DIR (adm/force, adm/promote, adm/nuke), inside a /log/adm/ subdirectory this archive never ships. write_file() doesn't create missing parent directories, so the very first invocation of any of those four admin commands each boot threw Error: *Wrong permissions for opening file /log/adm/force for append.: No such file or directory (the command's own effect -- e.g. force actually forcing the target -- still happened; only the after-the-fact logging side effect crashed, visibly, to the admin). Fixed with a directory_exists()/mkdir() guard in log_file() before the write_file() call, same pattern already used in adm/obj/login.lpc's home/data-directory setup. Verified live: force, makedev, and revdev (against the throwaway mailerone account) all ran clean with zero error traces post-fix, and log/adm/force/log/adm/promote now contain the expected log lines. Same latent gap also exists in lpuni's identical log_file() -- masked there only because that archive happens to ship a pre-created (empty) /log/adm/ directory, same "masked by a pre-existing directory" shape as the already-known /data/users/ letter-bucket bug from this lib's own onboarding section (\S3) -- not fixed there, just noted for the next agent, same as that earlier precedent.

4. Three wizard commands unconditionally crashed instead of failing gracefully whenever an Intermud-3 service module wasn't loaded (the normal state in this archive, since it implements no mudlist service handler per \S5) -- cmds/wiz/mudinfo.lpc, cmds/wiz/finger.lpc (the user@mud remote-finger form), and cmds/wiz/i3seen.lpc all did find_object(I3_MUDLIST or I3_UCACHE) immediately followed by an unguarded -> call, with no null-check -- crashing every single invocation with Bad argument 1 to EFUN call_other() Expected: object, string, array, Got: int(0) instead of the already-correct "Mudlist unavailable."/"module is currently not loaded" fallback this codebase's OWN sibling implementations use correctly (cmds/wiz/mudlist.lpc, cmds/std/tell.lpc, cmds/std/reply.lpc, cmds/wiz/rwho.lpc all guard the identical lookup correctly). Fixed all three by adding the missing null-check, matching the working sibling pattern. Verified live (pre-fix crash reproduced first in every case): mudinfo test, finger fluffos@dock9, and i3seen fluffos now all correctly print a graceful unavailable-module message. Checked lpuni for the same pattern -- NOT present there; lpuni's cmds/std/mudinfo.lpc/cmds/std/finger.lpc are an earlier, differently-written revision (load_object() with its own guard, or no I3 mudlist lookup at all) -- this looks like a regression introduced when dock9's later revision (dated 2007, Tricky @ RtH) was rewritten against find_object(), not a lineage-wide bug, so no AGENTS.md entry filed for this one.

5. adm/daemons/soul_d.lpc had the identical unguarded-find_object() bug in two places, reachable via any targeted emote at an "@mud"-style target -- find_object(I3_UCACHE)->getUserCache() (used to resolve an i3 username match for the emote target) and, separately, find_object(I3_UCACHE)->getGender(...) (used to pick a $PT/$ST pronoun for the message), both with no null-guard. Fixed both: the first now defaults to an empty mapping (([])) when the module isn't loaded, which this function's own existing logic already handles correctly (falls through to "no i3 match found"); the second now defaults gender to -1, which the surrounding switch already treats as its existing neutral "its"/"it" default case. Not independently reproduced with a full remote-i3 round trip (would need a live peer mud with a matching username), but the crash mechanism is identical to \#4 above and the fix is a direct, mechanical application of the same pattern.

6. std/object/object.lpc's set_chats() had a stray write(this_object()); debug leftover, executed on EVERY call to this universally-inherited base-class method (any room, NPC, or item that sets ambient chat/emote messages) -- printed the raw object reference (OBJ(/path/to/file), or OBJ(/path/to/file#N) for a clone) directly to whichever player's connection happened to trigger that particular object's first compile/create() each boot, with no relationship whatsoever to the function's actual job of storing the chat list and interval. Reproduced live: moving south from Dock 9 into Pier, Trepi for the first time in a fresh boot printed OBJ(/areas/trepi/docks/npc/greeter_sailor)OBJ(/areas/trepi/docks/npc/greeter_sailor#8)OBJ(/areas/trepi/docks/room/pier3) with no separators, immediately before the real "You move to Pier, Trepi." message (three separate set_chats() calls: the NPC's blueprint create(), the NPC's actual clone, and the room's own ambient-chat setup). Fixed by deleting the stray line. Verified live post-fix: the same first-ever move into a not-yet-compiled room (Dock 12, this time) produced clean output with no leaked object references. Checked lpuni's own copy of this file -- does NOT have this line, so this looks like a dock9-specific debug leftover from this archive's own author rather than an inherited lineage bug; no AGENTS.md entry filed.

What was tested and confirmed working

Flagged unverified

Test accounts

mailerone/mailertwo (both password-protected throwaway accounts created solely to exercise mail/channel between two real sessions) were deleted -- save files, /home/m/ workroom copies, and dev journal links -- before committing, keeping only the pre-existing seeded fluffos admin account, per this project's established cleanup convention (see lpuni's own \S "Deep functional test round two" for the precedent). adm/etc/access/adm/etc/groups picked up a purely-cosmetic mapping-iteration-order diff from the makedev/revdev test cycle (no actual ACL content changed) and were reverted rather than committed. data/daemons/history_d/ and data/daemons/chmodules/i3.o (real Intermud-3 chat history and router connection state, growing on every live boot) were added to the project's root .gitignore, mirroring the existing imud precedent, and the pre-fix churn in those paths was reverted rather than committed.

WASM measurement (2026-09-03)

meta.json was already playable from the 2026-08-31 deploy-unblock; the README still said "not attempted." Cold-boot under the shared ~/src/fluffos/build-wasm failed on adm/simul_efun/socket.lpc calling socket_status() from dump_socket_status() — same class as lpuni. Stubbed the header behind #ifndef __PACKAGE_SOCKETS__. After that, scripts/wasm_client.js logged in as fluffos / MudAt2026 into Training Ground, look and quit both worked. http_d.lpc socket_error is a graceful preload skip (not on the login path). I3 still tries an outbound connect at boot — do not loop-reboot. Shop/combat/death were not exercised this pass.

Shop + 拜师 (2026-09-04 librarian slice)

Content-absent, not a programming bug. LPUniversity/Sapidlib engine with only the small Trepi docks footprint — no vendor rooms, no buy/list player command, no apprentice/guild verbs (same as lpuni: no combat or stats). Live on 40266 as seeded admin fluffos / MudAt2026 in Training Ground: buy torch, list, shop, and apprentice all printed What?; help buy is Unable to find helpfile for: buy. Left as-is.