Star Wars Mud

✅ 可玩

swmud

更新 a36792c 2026-10-27 源码 下载 ZIP 上游 coupdeforce/swmud

▶ 开始游玩 · Play Now

A Star Wars-themed conversion built on top of the Lima mudlib (this collection's own libs/lima, 164) -- its own README describes it as a set of 'additions and changes to the base mudlib' meant to be installed over a full Lima install, which is how this copy was assembled. Ships a genuine Star Wars race roster (13 playable races spanning several human ethnicities plus aliens such as Wookiees and Twi'leks), its own combat/limb/wound system, guild-style character classes (Jedi, smuggler, bounty hunter, and more), crafting, and weapon/armor/lightsaber item trees. New characters land in a working fallback starting room, since the original Star-Wars-specific starting area was never included in this particular git archive. Makes a genuine outbound network connection to the public Intermud-3 router at every boot.

README

A Star Wars-themed LPC mudlib built as an extension of the Lima mudlib (<https://limalib.dev>, FluffOS's own official reference/framework mudlib). Its own README describes it exactly: "This project only includes additions and changes to the base mudlib. It must be installed over the current Lima mudlib." So that's how it's built here: a full copy of this project's already-converted libs/lima/work/ (164, port 40212) with swmud's own ~980-file tree overlaid on top (852 new files, 233 overriding same-path Lima files).

Source: git clone https://github.com/coupdeforce/swmud (last pushed 2016-02-16, commit cf33a51). Slug swmud, number 180, port 40227.

Highlights

Registration flow

new name -> "Is '<Name>' correct?" -> password -> confirm password -> gender (male/female) -> email -> real name -> homepage (optional) -> race selection (type a race name, or list/help races/help stats) -> point-buy attributes (a <stat> [n] / s <stat> [n], done when satisfied) -> dominant hand (left/right) -> team (imperial/rebel/ neutral) -> lands in Void with a starting body, 1000 XP, and a $N the Bantha Fodder title.

Admin account

Created through the mudlib's own real first-boot bootstrap (Lima's "whoever registers first on an empty install becomes admin" convention, inherited by swmud's sw_body_handle_new_logon()) -- not a save-file hand-edit. Verified live: who shows Role Admin Wizard; update /std/race/wookiee and admtool (including its wizard-gated priv 1 submenu) both succeed.

Status

Boots clean. Verified live with a real driver session and a raw socket client: full registration (both a wizard-bootstrap account and a second, ordinary account), race selection across multiple races, point-buy attribute allocation, look/score/who/inventory, admin update/ admtool, and a clean quit, all producing correct output.

1731 of 1860 .lpc files pass a full lpcc_check.sh-style batch compile sweep. Nearly all of the 129 that don't are orphaned Lima demo content (domains/'s stock example rooms/monsters/spells, std/sword.lpc, std/race/unused/troll.lpc, etc. -- content swmud's own game world never uses and that predates this port) hitting the same "swmud rewrote the item/combat API out from under Lima's original demo files" incompatibility documented repeatedly in NOTES.md. See NOTES.md for the full breakdown and what's left unfixed on purpose.

WASM status: playable. The specialized Lima-flavor WASM driver (same NO_ADD_ACTION/NO_WIZARDS/NO_LIGHT build as native lima/swmud) is now packaged per-lib via scripts/custom_drivers/lima_swmud/, so the site can actually boot this game. Registration, race selection, and world entry were verified in-browser; see NOTES.md 「站点基础设施缺口已补上」.

⚠️ Makes a real outbound network connection at boot. `daemons/
imud_d.lpc` preloads and connects to the public Intermud-3 router
network at every boot (ADMIN_EMAIL in include/config.h is a real-
looking value, so Lima's placeholder-based protective guard doesn't
block it). Do not include this lib in high-frequency automated re-boot
loops. See NOTES.md.

Local run

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

Important: this lib needs the same specialized Lima-flavor driver build as libs/lima/ (~/src/fluffos-lima/build-debug, built with NO_ADD_ACTION/NO_WIZARDS/NO_LIGHT defined, OLD_ED/PACKAGE_UIDS undefined) -- it will NOT boot on this project's shared default driver at ~/src/fluffos/build-debug. See libs/lima/NOTES.md for how that driver was built, if the checkout needs to be recreated.

Game port: 40227.

NOTES · 移植与修复记录

swmud -- Star Wars Mud

Source: git clone https://github.com/coupdeforce/swmud (HEAD at clone time: cf33a51, last pushed 2016-02-16, cloned 2026-08-26). Number 180, slug swmud, port 40227. Status: done (clean boot, real registration + character creation + look/score/who/inventory/admin update/ admtool/quit all verified against a live driver).

Architecture: this is not a standalone mudlib

swmud's own README says it outright: "This project only includes additions and changes to the base mudlib. It must be installed over the current Lima mudlib." Confirmed by inspection -- the raw clone is ~980 files with no secure/simul_efun.lpc combiner, no check_config.lpc, no get_root_uid()/master security scaffolding, and macro tables (include/mudlib.h, classes.h, commands.h, daemons.h) that reference dozens of paths outside the archive. This project happens to already have a full working copy of Lima itself onboarded (libs/lima/, 164, git clone https://github.com/fluffos/lima) from earlier this session, so the AGENTS.md \S2.1 "recognize the lineage" playbook applies directly, just at a larger scale than usual:

1. work/ = a full copy of libs/lima/work/ (already fixed for this driver -- see libs/lima/NOTES.md), with every file from swmud's own converted tree overlaid on top. 852 files are swmud-only additions; 233 override a same-path Lima file outright (secure/master.lpc, secure/daemons/secure_d.lpc, std/body.lpc and 14 of its body/* submodules, std/adversary/*, most of cmds/verbs/cmds/player, the trans/cmds "wish" shell command tree, include/*.h, etc). 2. Because this driver requires a different build for anything Lima-based (NO_ADD_ACTION/NO_WIZARDS/NO_LIGHT defined, OLD_ED/PACKAGE_UIDS undefined -- see libs/lima/NOTES.md \S "驱动编译选项冲突"), swmud reuses that exact driver checkout, ~/src/fluffos-lima/build-debug. It will not boot on this project's shared default driver. 3. Encoding: swmud's own raw clone is 100% ASCII/UTF-8 except one Latin-1 copyright symbol (©) in help/wizard/coding/parser, fixed with iconv -f ISO-8859-1 -t UTF-8 (note: plain -f latin-1 fails with "failed to start conversion processing" on this machine's iconv -- use the full charset name ISO-8859-1). scripts/convert_lib.sh ran with UTF-8 as the source encoding (i.e. rename + reference-fixup only) against a staging copy, then that staging tree was rsync'd onto the pre-populated work/ (never run convert_lib.sh directly against work/ here -- it deletes and recreates its target from scratch, which would destroy the Lima base).

Because two independently-evolved snapshots of a large, deeply inheritance-based codebase are being combined, most of the real bugs below are API mismatches between vintages -- a file on one side of the merge calls a function/macro/class that the other side renamed, dropped, or never had -- rather than classic single-archive conversion bugs. Two consistent patterns run through almost everything below:

Real bugs fixed (driver-compat / programming, not content)

1. Dropped macro definitions (the single biggest bug class)

Systematic sweeps (grep every inherit BARE_MACRO;, new(MACRO...), and MACRO->call() site against every #define in include/*.h) found and fixed:

All confirmed by checking the target file actually exists in the merged tree before adding the macro back -- none of these paths were invented.

2. Dropped simul_efun functions

swmud's own secure/simul_efun/{misc,objcalls,path,string}.lpc (4 of the 4 simul_efun source files it ships) replaced Lima's originals wholesale, but are an older/leaner snapshot missing several utility functions that both Lima-base files *and swmud's own files* still call: lima_version()/identify() (misc.lpc; called by secure/ error_handler.lpc and the version command), file_domain() (path.lpc; called by daemons/domain_d.lpc), and punctuate()/tidy_list()/ time_to_string()/weight_to_string()/filepath_ellipsis() (string.lpc; called by 14+ files including cmds/wiz/calls.lpc and swmud's own std/modules/m_companion.lpc). Ported the exact implementations forward from libs/lima/work/secure/simul_efun/*.lpc (unmodified copy-paste, credited in-file) rather than reinventing them.

Also found the exact same already-known bug Lima itself had and fixed upstream: secure/simul_efun/string.lpc's create() builds an "every byte value" lookup table via chr(i) for i in [0,256); this driver's sprintf("%c", 0) (used by the chr() simul_efun override itself) errors on a \0/out-of-range codepoint. Lima's own current string.lpc already fixed this (for (i = 1; i < 255; i++)); swmud's copy had reverted to the original [0, 256) range. Applied the exact same fix Lima uses, verbatim, rather than re-deriving a different one.

3. TYPE array NAME / bare array dialect (§7's recurring bug class 7)

980 sites across swmud's own files used two flavors of the classic old- MudOS array-declaration dialect this driver doesn't accept: TYPE array NAME (405 hand-written + machine-verified sites, e.g. string array names;) and **bare array as a shorthand for mixed *** (a second, narrower dialect specific to this archive -- ~200 more sites, e.g. array args..., array handle_ob(...), class command_info array module_commands()). A two-pass scripted sweep converted both to modern TYPE *name / mixed *name syntax. Two sweep-tooling bugs surfaced and were caught by re-running lpcc_check.sh and fixing forward rather than trusting the first pass:

4. inherit chain size too small once merged (masqueraded as infinite recursion)

/std/body.lpc failed every load attempt with `*Inherit chain too deep:

N for every N` up to 200 tried -- looked like true runaway recursion,

but the real cause (found by bisecting) was a missing file: std/body.lpc has inherit __DIR__ "body/help";, and std/body/ help.lpc does not exist anywhere in this archive or in Lima's base. Compiling a missing inherit target routes through master.lpc's compile_object() virtual-object fallback, which (per its own doc comment) walks up the path looking for a parent .lpc to call virtual_create() on -- for /std/body/help that parent is /std/ body.lpc itself, so resolving the missing inherit re-entered compiling /std/body.lpc, which needed to resolve the same missing inherit again, forever. Removed the dead inherit __DIR__ "body/help"; line (content genuinely absent, not invented) and also inherit __DIR__ "body/wiz_position"; on the same line block -- confirmed *not* just missing but actively dead: std/body/wizfuncs.lpc's own header says "230319, Tsath: Merged wiz_positions into this file," and it already defines query_wiz_position()/set_wiz_position(). inherit chain size was still bumped from Lima's 30 to 60 (this project's common ceiling, used by 137 other libs) as normal headroom for the now-much- larger combined class hierarchy -- it just wasn't the real bug.

One related, still-orphaned file: std/modules/m_exit_obj.lpc (from Lima's base, unreferenced anywhere in the merged tree -- grep confirms nothing inherits M_EXIT_OBJ) calls a 3-argument eval_dest(), but swmud's own replacement std/modules/m_exit.lpc defines a completely different 1-argument eval_dest() (also private, matching the AGENTS.md \S7.48 cross-file-private pattern -- loosened to protected on general principle, though no live caller actually needs it). Left m_exit_obj.lpc as a known lpcc_check.sh failure: it's genuinely dead code with an incompatible signature from a different API vintage, not something to guess a 3-argument implementation for.

5. First-ever player registration crash (real, 100%-reproducible)

The single most severe bug found: every fresh install of this archive crashes on the very first character ever created. secure/user/ sw_body.lpc's sw_body_handle_new_logon() auto-promotes the first-ever registrant to admin (a direct port of Lima's own bootstrap convention) *before* calling create_body(). Inside std/body.lpc's create() -> adversary::create() -> mudlib_setup() -> refresh_stats() -> std/adversary/health/limbs.lpc's set_max_health(), the racial-stat rollout calls check_wizard_set() (an anti-cheat guard meant to stop a *different*, low-ranked wizard from remotely tampering with another player's stats). That guard's thing != link check is unconditionally true at this exact point in the call chain, for *every* new character of *any* account, because link is only ever assigned later, inside enter_game()/init_cmd_hook() -- which hasn't run yet during the body's own constructor. For an ordinary (non-wizard) new account this is harmless (wizardp(thing) is falsy, so the guard short-circuits), but for the bootstrap-admin account -- unavoidable on a fresh install, and also would hit any wizard creating a new character if this mudlib ever supported more than one character per account -- wizardp(thing) is truthy and the guard fires: *Invalid attempt by <name> to set 's max health to <n>. This took real trace-level debugging to isolate (naive write()/printf() debug statements silently vanished at exactly this point in the call stack, because this_player() context is ambiguous mid-construction; switching to write_file()-based logging and reading /log/runtime's actual stack trace was what nailed the true call chain -- see git history of this fix for the full reasoning if this class of bug recurs elsewhere). Fixed with a one-line, minimal guard: only apply the anti-cheat check once link is actually established (if (link && ...) instead of just if (...)), preserving the real anti-cheat behavior for its intended post-login use case.

6. Division by zero in the Character Creation attribute screen

std/modules/m_bodystats.lpc's query_for() (the "Force" stat, with a Star-Wars-specific environmental bonus term) divides by cur_for unconditionally -- but cur_for is legitimately 0 for every freshly rolled, not-yet-allocated character, which is exactly the state the attribute-point-buy screen displays. Crashed the Character Creation "Attributes" screen for every single new character on every single render until the player had already put at least one point into Force. Fixed with a guard returning cur_for unmodified (0) rather than dividing by it, on the reasoning that a character with 0 force sensitivity shouldn't receive an environmental force bonus anyway.

7. team_alignment used but never declared

obj/mudlib/createmenu.lpc's Character Creation "Team" step (imperial/rebel/neutral) calls this_body()->set_team_alignment(n), and secure/daemons/{user_d,finger_d}.lpc's who/finger display code reads "team_alignment" back as a plain global variable via this driver's fetch_variable() reflection mechanism -- but no team_alignment variable, nor set_team_alignment()/query_team_alignment() functions, exist anywhere in the archive. The setter calls were pure no-ops (call_other to an undefined function silently returns 0), and every who/finger lookup on *any* character crashed outright (*Variable named 'team_alignment' does not exist or is not visible!) since the variable genuinely doesn't exist to reflect. Added the missing int team_alignment; variable plus matching accessors to std/ body.lpc, mirroring how race_name already works as a plain reflected variable on the race classes. (The broader legal_body_query list in user_d.lpc also names 8 jedi-sponsorship fields -- jedi_sponsor, sponsored_by, etc -- that are similarly never declared anywhere; left alone since they're outside the everyday who query set that actually gets exercised, per secure/daemons/finger_d.lpc's own shorter 12-field list, so they're not a live crash. Flagged here in case a future jedi-sponsor-specific command surfaces the same gap.)

8. race_name missing from 4 leftover Lima races -> crashed who

std/race/{human,elf,orc,documentation}.lpc are unmodified Lima base files (byte-identical, confirmed via diff) that hardcode query_race() to return a literal string instead of exposing a race_name variable the way every one of swmud's *own* races does (human_corellian, wookiee, defel, etc. all declare string race_name = "...";). who/finger's variable-reflection lookup on race_name crashed for any character of one of these 4 races the same way \S7 did for team_alignment. Since these 4 races are also non-canonical for a Star Wars game (a generic "human"/"elf"/"orc" alongside swmud's own 13 real races, one of which -- human_corellian et al -- already covers "human" properly) and were the same 4 files needing the stat_roll_mods class-inherit fix below, moved them to std/race/unused/ (an existing, already-used-for-this-purpose directory -- human_dathomirian.lpc was already there) alongside troll.lpc (\S9). race_d.lpc's own get_dir(DIR_RACES + "/*.lpc") glob is non-recursive, so this cleanly removes them from the selectable race list without deleting any content.

9. Missing inherit CLASS_STATMODS; broke 5 races' stat-roll class

std/race/{human,elf,orc,troll,documentation}.lpc (all unmodified Lima files) declare a local class stat_roll_mods query_roll_mods() function using a class type defined in std/classes/statmods.lpc, but never inherit CLASS_STATMODS; to bring that class definition into scope -- a real, lpcc_check.sh-confirmed compile failure (Undefined class 'stat_roll_mods') for all 5. Added the missing inherit to all 5. 4 of the 5 (everything except troll.lpc) now compile clean and were kept loadable (before being moved to unused/ per \S8 above, for the separate race_name reason). troll.lpc has a second, deeper incompatibility (below) and stays broken.

10. troll.lpc (leftover Lima demo race) broke 3 *real* swmud races

std/race/troll.lpc additionally calls set_to_hit_bonus()/ set_weapon_class() -- functions that existed in Lima's old combat system but don't exist anywhere in swmud's rewritten std/adversary/* combat model (\S"Architecture" above). This alone would just mean troll fails to load and gets skipped -- except daemons/race_d.lpc's create() loop does if (!load_object(tmp)) { continue; }, and on this driver a failed compile inside load_object() throws an error that propagates past that if/continue guard instead of returning falsy, aborting the entire foreach early. Since get_dir() returns races in filename order and troll.lpc sorts right before twilek.lpc, verpine.lpc, and wookiee.lpc, this silently dropped 3 of swmud's own *real, working, Star-Wars-canonical* races from the selectable list every single boot -- not a cosmetic listing bug, an actual "3 valid races become permanently unselectable" bug, caused entirely by one broken leftover demo file sorting adjacent to them. Fixed by moving troll.lpc to std/race/unused/ (same mechanism as \S8) rather than trying to guess a set_weapon_class()/set_to_hit_bonus() implementation for content that was never part of this game to begin with. Verified live: the race list now shows all 13 real races, including wookiee/twi'lek/verpine, and each is independently selectable.

11. WIZARD_START pointed at broken Lima demo content

include/config.h's WIZARD_START (upstream value: "/domains/std/ Wizroom", Lima's stock "Grand Hall" demo room) fails at runtime: its set_objects() places a virtual "map" object at /domains/std/ map.lpc, which calls set_untouched_desc() -- again, a function that doesn't exist anywhere in swmud's rewritten object API (same pattern as \S10). This crashed move_to_start() for the wizard-fallback path specifically -- i.e. exactly the path this project's own admin-account verification convention exercises. Repointed WIZARD_START at /d/common/void (a real, working room this archive does ship) instead of inventing replacement demo content. This is a deployment substitution, not a "fix" of swmud's own design, since \S12 below means the *real* wizard starting room was never included in the archive either way; ordinary (non-wizard) players already fall through move_ to_start()'s own built-in fallback chain to this same Void room for that reason, so wizards now land in the same well-defined place.

12. The real starting area doesn't exist in this archive at all

include/config.h's START/NEW_PLAYER_START point at /d/coru/main ("Coruscant", the intended real starting location) -- but neither that file nor even a d/coru/ directory exist anywhere in the raw clone or its git history. This is confirmed not a conversion artifact: the d/ tree (swmud's real, non-Lima game-content root) has real, working subtrees for items/weapons/armor/monster templates (d/arm, d/buffs, d/com, d/const, d/mon, d/obj, d/weap) but no room/area content anywhere -- confirming the actual playable map was never committed to this git repository at all (plausibly built live on the original production server and never checked in, a common split for "engine code" vs "game content" in MUD codebases). This is a genuine, severe, pre-existing content gap -- not something fixable within this project's "fix programming bugs, don't invent content" mandate. Fortunately std/body/start.lpc's move_to_start() already has a sensible built-in fallback chain (configured start -> WIZARD_START/ START -> VOID_ROOM) ending at a real, well-written room (/d/common/void.lpc, "Void... This is where players and objects are moved to when something goes wrong. If you end up here, please let a wizard know."), so every new character still lands somewhere coherent and playable rather than crashing -- this was the intended behavior of swmud's own error-handling design, not a workaround this port invented.

13. secure/user/sw_body.lpc's new-character shell path clamp broke wizard commands

Every brand-new character's shell (incarnate(), in the if (is_new) block) unconditionally overwrote its shell's "path" variable to ({"/cmds/player", "/cmds/guild"}). secure/user/shell.lpc's start_shell() already correctly picks WIZ_SHELL (the Lima "wish" shell, /trans/obj/wish, whose own create() smartly defaults path to WIZ_CMD_DIRS/ADMIN_CMD_DIRS for a wizard account) vs PLAYER_SHELL based on wizardp() -- but the very next thing incarnate() did was stomp that smart default back down to the plain player command set, for every new character regardless of wizard status. Combined with \S5's bootstrap-admin-before-create_body() ordering, this permanently locked the very first player on a fresh install (guaranteed to be a wizard) out of every wizard/admin command (update, admtool, ...) forever, since this mudlib is single- character-per-account (no way to "recreate" the character with a wider path later). Fixed by only clamping the path when !wizardp(query_ userid()), letting a wizard's shell keep whatever WIZ_SHELL's own constructor already set up. Verified live: update /std/race/wookiee and admtool (through its wizard-gated priv 1 submenu) both work correctly for the bootstrap-admin account after this fix.

14. NO_NEW_PLAYERS -- deployment change, not a bug fix

include/config.h ships with #define NO_NEW_PLAYERS (an intentional upstream "closed beta" gate -- new registrations are rejected outright except via a pre-approved guest slot). Since this project's whole point is letting site visitors register and actually play, #undef'd it. Documented in-file as a deliberate deployment substitution, matching this project's established convention for this exact situation.

15. Missing <lpscript.h> header (removed, not invented)

swmud's own include/mudlib.h replaced Lima's #include "custom_mudlib.h" (an empty upstream placeholder) with #include <lpscript.h> -- a header that doesn't exist anywhere in this archive *or* in Lima's base, breaking compilation of every single file (it's pulled in via the global include chain). The only 3 references to LPscript anywhere in the merged tree (daemons.h's LPSCRIPT_D path macro, secure/master.lpc's compile_object() handling a hypothetical *.scr file extension, and this include line) need zero symbols from the missing header -- there are no .scr files anywhere in the archive either, so the code path is dead in practice. Removed the #include line rather than fabricating a header for a feature this archive never actually ships content for.

Known, deliberately unfixed gaps (content/design, not bugs)

⚠️ Real outbound network connection at boot (Intermud-3)

Unlike almost every other lib in this corpus (all sandboxed, no outbound connections -- see AGENTS.md \S1.3c/\S7.6), daemons/imud_d.lpc preloads at boot and makes a genuine outbound socket connection to the public Intermud-3 router network. imud_d.lpc's create() has a protective check that refuses to load if ADMIN_EMAIL is still Lima's placeholder "[email protected]" -- exactly the guard that made libs/lima/'s own imud_d refuse to load (see libs/lima/NOTES.md). swmud's include/config.h sets ADMIN_EMAIL to a real-looking value ("[email protected]"), so that guard does not trigger here: create() proceeds to call trigger_reconnect("router") unconditionally at every boot, same as libs/imud/'s intentional demo behavior (see libs/imud/NOTES.md). This means this lib should not be swept into high-frequency automated re-boot loops (round-two/round- three-style re-test cron, lpcc_check.sh-adjacent live-boot sweeps) the way the rest of this project's sandboxed corpus safely can be -- each boot is a real network event against a real third party.

Admin account

Bootstrapped through the mudlib's own real first-boot logic (not a save-file hand-edit): registered fluffos/Mud@2026 as the very first account on a completely clean data/{links,players,pshells,wshells} tree, which sw_body_handle_new_logon() auto-promotes to admin/wizard per Lima's own convention (see \S5 above for the crash this triggered and its fix). Completed full Character Creation (Corellian, right- handed, Rebel alignment) and verified: who shows [Apprentice] Fluffos the Bantha Fodder (Corellian) with Role Admin Wizard; update /std/race/wookiee succeeds (Updated and loaded.); admtool opens and its wizard-gated priv 1 -> administration submenu (showing [admin]- tagged options) is reachable. A second, ordinary (non-wizard) test account (testplayer, Wookiee, Imperial) was also registered end-to-end to confirm the bootstrap-specific fixes above don't regress the normal player path.

Verification record

scripts/mudclient.py-equivalent raw Python socket sessions through the real ~/src/fluffos-lima/build-debug/src/driver, multiple independent boots across the debugging process above: full registration (username, "is this correct?" confirmation, password+confirm, gender, email, real name, homepage) -> race selection (verified against corellian and wookiee, both non-human and human-ethnicity races) -> point-buy attribute allocation -> dominant hand -> team alignment -> lands in Void with a starting body -> look (room description + exits correct) -> score (race/gender/guild/XP/HP/credits all correct) -> who (roster line with role tag, race, location) -> inventory -> admin update/ admtool (bootstrap-admin account only) -> quit (clean disconnect back to the User Menu, debug.log free of new errors). No crashes, zero silent failures, across all of the above after the fixes in this document.

wasm_status 审计(2026-09-01):内容在专用 WASM 驱动上完全可玩、但与 lima 同样卡在站点基础设施上,结论 noboot

meta.json 的 wasm_status 此前一直留空(README 的 Status 一节写 "WASM status: not attempted")。本次批量审计(见 [[project_wasm_status_audit]])补上这一步,结论直接参照本收藏 libs/lima(164)自己 2026-09-01 那次审计的先例——见 libs/lima/NOTES.md 的「wasm_status 审计」一节。

复用 lima 那次审计已经建好的专用 WASM 驱动 (~/src/fluffos-lima/build-wasm/src/{fluffos.js,fluffos.wasm}, NO_ADD_ACTION/NO_WIZARDS/NO_LIGHT 定义、OLD_ED/PACKAGE_UIDS 未定义,与本 lib 的 README.md/config.fluffos 一贯记载的驱动 要求完全一致,不需要重新编译)。第一次起跑就命中和 lima 自己 secure/simul_efun/misc.lpc 一模一样的 bug:swmud 自己独立重写过 的同名文件(secure/simul_efun/misc.lpc,与 lima 原版逐行对比 差异很大,是 swmud 自己的版本,不是继承 lima 的)里 dump_socket_status() 同样无条件调用 socket_status(),WASM 构建 默认不带 sockets 包,导致 secure/simul_efun 整个编译失败,驱动 拒绝启动。用 lima 已验证过的同一手法修复(#ifdef __PACKAGE_SOCKETS__ 包一层,退化返回空字符串)。

修复后完整验证:fluffos_boot 成功,登入菜单正常显示"Welcome to Star Wars MUD!",走完 new name → 确认 → 密码×2 → 性别 → 邮箱/ 真实姓名/主页(可跳过)→ 种族选择列表正常显示 13 个种族选项,全程 无未捕获错误——这个 lib 的内容本身在专用 WASM 驱动上是完全可玩 的,obj/secure/socket.lpc/secure/daemons/ftp_d.lpc/ daemons/imud/finger.lpc 等几个非 simul_efun 级的外围文件编译 失败只是正常的预载跳过,不影响真实登入。

但和 lima 一样卡在同一个站点基础设施缺口上:这个项目的站点打包 管线(write_play_page.sh/build_site.sh)给所有 lib 共用 同一份 WASM 驱动二进制,没有任何按 lib 切换驱动配置的机制——部署 到线上时,swmud 仍然会撞上和用共享驱动跑原生版一模一样的"驱动 配置不匹配"问题(NO_ADD_ACTION/PACKAGE_UIDS 等宏的差异会导致 mudlib 自身的启动期检查失败)。标记 playable 会误导站点实际能 跑什么,因此和 lima 一样定为 noboot——这是站点基础设施层面的 缺口,不是这个 lib 自身内容的问题,等以后站点支持按 lib 覆盖驱动 时可以直接翻正,不需要重新调查。

站点基础设施缺口已补上(2026-09-02/03):wasm_status 翻正为 playable

见 libs/lima/NOTES.md"站点基础设施缺口已补上"一节——write_play_page.sh/ build_site.sh 加了 per-lib 自定义驱动目录支持,lima/swmud 共用同一份已提交的驱动产物(scripts/custom_drivers/lima_swmud/)。

本地范围内测试站点(只打包这两个 lib)用 Playwright 通过真实 #cmd 输入框验证:fluffos_boot 成功,"Welcome to Star Wars MUD!" 正常 显示,走完 new name → 确认 → 密码×2,控制台无 JS 报错——和本节 前面用 wasm_client.js 跑通的结果一致。meta.json 的 wasm_status 从 noboot 改为 playable。

Deep functional test (first full §10.7 pass, 2026-09-03)

First continuous playthrough. Prior work was onboarding + the WASM driver-override flip — never a §10.7 pass. Admin fluffos/Mud@2026 against ~/src/fluffos-lima/build-debug. Unique world content still does not exist (/d/coru/main absent; players land in /d/common/void). Tested Void, the lima leftover Monster_Room guild-guard path, quit → user-menu → re-enter, and a brief combat attempt.

Bug 1: leftover debug writes on every move

std/modules/m_smartmove.lpc::move_to() printed The destination is a string. / The destination is an object. on every move attempt. Lima's copy of this module does not have those writes — they are swmud debug leftovers. out from Void (the only exit, pointed at the missing START) then correctly says Construction blocks your path. Verified on a fresh driver after removing the two write()s: only the construction message remains.

Bug 2: lima §10.7 guild-guard / GUILD_D fixes never reached this overlay

swmud's work/ was copied from lima *before* lima's 2026-08-27 §10.7 pass, so std/guild_guard.lpc and daemons/guild_d.lpc still had the unguarded query_guild_allies() and the get_dir()-returns-0 load_missions()/load_favors() crash. Ported lima's two guards.

On this lib that was not enough: swmud's std/body.lpc does not inherit body/guilds (lima does, under USE_GUILDS), so guilds_belong() is a missing apply and returns 0. 0 & ({}) then threw *Bad argument 2 to &. Added an arrayp() guard. Verified live: goto /domains/std/Monster_Room → north now prints the intended block (The a guard pushes you back. "Guild members only", she growls.) instead of the go-verb parse crash. Caught Non-existant guild - sorcery. still lands in /log/catch (same mudlib error handler : 1 visibility lima documented — not a new leak).

Bug 3: user-menu s/p after quit crashed (single-body vs multi-body API)

swmud's secure/user.lpc is the older single-body_fname vintage and does not inherit user/bodies (lima does; inheriting it here would nomask-collide on set_body_fname). Lima's usermenu still calls query_bodies() / query_selected_body() / set_selected_body() / enter_game(name, fname).

Before the shim: s was *Bad argument 1 to keys() (query_bodies() returned 0); after a first query_bodies stub, s hit *Array index must be positive or zero because n_gen is nosave and defaults to -1; p was *Bad argument 1 to EFUN call_other() because enter_game() did not exist on the user object.

Shims on secure/user/sw_body.lpc: present the one body_fname as a one-entry bodies map (gender clamped to 0..3), plus a varargs enter_game() that forwards to sw_body_handle_existing_logon(0). Verified live: quit → s lists [1*] Fluffos → p lands back in Void with a working prompt.

Observed, not fixed (programming, but needs a careful skill-table read)

kill guard in Monster_Room: repeating *can't find skill unarmed from std/adversary/skills.lpc (SKILL_D->query_skill("unarmed") is 0). Default unarmed skill_used is "unarmed" (m_damage_source_body.lpc); formula code and the lima trainer use "combat/melee/unarmed". This is lima-demo combat on swmud's rewritten skill table — not invented content to "fix" by adding a skill. Left documented; a later pass should decide whether to rename the default or register the short name.

Checklist

Driver killed by exact PID after the pass. Test-session save/log churn (data/links, data/players, data/daemons/guild.o, data/secure/LOG) was reverted and not committed.

深度功能测试(§10.7,2026-10-04)— #pragma no_warnings removed, every compile warning fixed

include/mudlib.h (every file that includes it) and the three example rooms domains/std/{Attic,Church,wiz_hall}.lpc opened with #pragma no_warnings. With the pragma gone a scan on the Lima-family driver build (~/src/fluffos-lima/build-debug/src/lpcc) showed 1287 distinct diagnostics (1733 of 1860 files compile). Now: 164, all errors, in the 121 files that still do not compile; 0 warnings in any file that compiles (one is left in the CGI WWW/cgi/who.lpc, which needs class members finger does not have).

lpc_warnings.py swmud --fix did the mechanical classes (60 varargs, 36 bare returns, 13 escapes, 88 unused locals); lpc_fix_unused_init.py kept the calls of the unused initializers as statements. Then, by root cause (KB 04 §6.10):

Live on a pristine tree with the Lima driver: the seeded fluffos admin runs the wish shell (alias, aliases, prompt, history, ls, pwd), clones and carries the hammer, backpack (worn), health scanner, syringe and holster, and clones bank_accountant, accountant, greeter, wolf and beek; a fresh character registers through race, attributes, hand and team and reaches the game (score, who, skills, inventory). Not mine, seen on the way: domains/std/trainer cannot be cloned (m_trainer calls add_option(), which nothing defines) and the troll stops on /std/sword.lpc (set_damage_type() with one argument).

Left: the 121 files that do not compile are Lima's example domain domains/std (37; it calls set_weapon_class() and set_value(), which this game's weapon API replaced), std/effect/*, retired std classes and the two CGI scripts.

深度功能测试(§10.7,2026-10-04)— two uncaught errors in a row left the session mute

secure/user/inputsys.lpc re-armed the player's input_to() (modal_recapture()) only after the input handler returned, so an uncaught error in a handler lost it. The next line then reached process_input(), and a second error there made the driver stop calling process_input() for the connection for good (comm.cc: if (!ret) ip->iflags &= ~HAS_PROCESS_INPUT); the Lima command set has no add_action commands, so every later line was dropped and the player saw only the driver's > . Reproduced on the unchanged tree: clone a file whose create() is int *a; a[0] = 1; twice, then look. A player who repeats one failing command hits the same thing, and only a reconnect helps.

dispatch_modal_input() now wraps the handler call and the ! escape in catch() and re-arms the input as before, which is what upstream Lima already does. error_handler() still prints the error and Trace written to (the log is /log/catch now, not /log/runtime). Live on a pristine tree: four errors in a row (and one more after a look), then look, i and !look answer. KB 06 §7.217.

Follow-up (2026-10-04, later): domains/std/rooms/caves/Sloping_Tunnel called the retired set_default_exit() (only the non-compiling room/exits module defines it) and did not load; it calls set_default_error() like its sibling caves, and north prints "You can't find an exit in that direction."

2026-10-04 (later) — a deleted local declared again

scripts/lpc_audit_removed_locals.py over the warning sweep found secure/user/login.lpc's string *foo; deleted although #ifdef WELCOME_DIR reads it; it is declared inside that branch again (a build that defines WELCOME_DIR compiles). The scan is unchanged: 1740 files compile, 120 do not.

深度功能测试(§10.7,2026-10-05)— here-document terminators

scripts/lpc_fix_heredoc_terminator.py --all --apply: 1 here-document block(s) repaired (0 with the terminator glued to the last text line, 1 with it indented), so End of file in text block no longer hides the room or object behind them: std/modules/m_react.lpc. The terminator must start its line (the lexer's own test, KB 04 §6.10); a glued one lost its newline in the GBK -> UTF-8 conversion together with a half character, which stays out.

深度功能测试(§10.7,2026-10-04)— paths named in another case

The archive comes from a case-insensitive host: files are stored as BAGUA0.lpc, longjing.C, d/SHAOLIN/, the code names them in lower case (__DIR__ "bagua0", "tea/longjing", "/d/shaolin/..."), so the rooms, NPCs and items behind them could not be loaded (a .C file is not an .lpc file; KB 03 §4.5). scripts/lpc_case_paths.py --convert --apply swmud: 2 file(s) renamed to lower case (.C -> .lpc, .H -> .h) and 2 path reference(s) in 2 file(s) rewritten to the new spelling; 0 skipped, 0 ambiguous. Load check of the 0 objects that were renamed, edited or name a renamed path (HEAD against the working tree, an object is the same under any spelling of its path): 0 PASS -> 0 PASS, 0 that loaded before fail now, 0 more load.

2 files moved (2 .lpc); mostly under domains/std/rooms (1), domains/std/Wizroom.lpc (1). A rescan with scripts/lpc_warnings.py swmud is still due: the renamed files are compiled for the first time.

深度功能测试(§10.7,2026-10-05)— 主控 author_file()/domain_file()

本库驱动(~/src/fluffos-lima,没有 PACKAGE_UIDS)的 mudlib_stats 包在主控载入时调用 domain_file("/") 和 author_file(<主控文件>)。主控没有这两个 apply,所以每次启动都打印 ... using 'BACKBONE' as fallback / ... using 'NONAME' as fallback 两行。secure/master.lpc 补上 LIMA 上游自己的写法(limalib/lima f85dba3a):/domains/<名>/ 下的文件属于该域,/wiz/<名>/ 下的属于该巫师,其余为 "std" / "mudlib";去掉了上游把 master 记在 beek 名下的那一行。本库没有代码读取作者或域统计(KB 06 §7.222)。lpcc config.fluffos /nonexistent.lpc 前后对比,只少了这两行。

2026-10-05 — T array a, b 转换遗漏

std/object/description.lpc 的 plural_discarded_message 由原文 protected string array discarded_message, plural_discarded_message; 转换时丢了 *,补回(该变量未被使用,KB 03)。

深度功能测试(§10.7,2026-10-08)— .lpc 后缀的定宽切片

.c 改名 .lpc 时只改了字符串没改切片宽度:f[<2..] == ".lpc" 拿两个字符比四个字符,永远不成立(!= 永远成立),按后缀筛文件、去后缀的代码因此从不运行或截成 x.l(KB 06 §7.238)。scripts/lpc_lpc_suffix_slice.py --apply 改了 6 处比较、3 处去后缀(如 www/cgi/autodoc.lpc);改动的文件 HEAD 与工作树分别加载(新进程),无回退。

- std/modules/m_space_object.lpc moved ships with location_x += (direction_x * velocity) / 1000.0 into int coordinates. The new driver converts the step to an int first, so a step below 1 per heart beat (a slow ship, or the small component of a diagonal heading) was lost every tick and the ship drifted off its heading or stood still. The coordinates are float now, so steps accumulate, and the int getters return to_int(). - cmds/guild/jedi/mwipe.lpc adjustment *= resistance (a factor below 1) gave 0. - The drunk stance penalty (std/adversary/condition.lpc, 1.2) was an int. - std/fluid.lpc held float masses in ints. - Damage, healing, experience and layout figures spell to_int() (same values). - Gameplay-visible: ship movement at low speed and on diagonals, the Jedi mind-wipe adjustment, and the drunk penalty. - The 31 changed objects load, with no regressions.

深度功能测试(2026-10-21,play 扫描):以第一个注册的管理员跑 264 个命令,控制台只剩两条已记录的

用 lima 专用驱动(PLAY_DRIVER=~/src/fluffos-lima/build-debug/src/driver scripts/play_boot.sh swmud 40227;scripts/pristine_tree.sh 把没有补丁的子模块工作树也搭成测试树)启动,按 README 的注册流程手工走一遍创建(姓名、y、密码两遍、性别、邮箱、真名、种族、颜色 n、属性点买满 45 点、done、right、neutral;第一个注册的就是管理员),再 scripts/play_sweep_lib.py swmud --no-boot --account NAME:Test1234:261 行,16 条发现,控制台两条,都是上面“Lima 原版演示内容被 swmud 改写后失去对应 API”和 daemons/imud/finger.lpc 的 class finger 成员问题(materials 载入 /domains/std/crafting/* 时 /std/armour 编译失败,mudlist 载入 imud 的 finger 服务),两条本来就记在前面的“没有修”里。其余是 needed-escape(分页器、编辑器、对话)。

(: 函数 :) 指针指向对象自己没有定义的函数(2026-10-22,KB 06 §7.355)

item_desc / inquiry / chat_msg_combat 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:编译不报错,用到时驱动报 *Undefined lfun pointer called: 函数名(look 某物、ask NPC about 话题 直接出错,战斗 / 闲聊里的指针在 NPC 的 chat() 里出错,这一拍心跳的后半段被跳过)。scripts/lpc_scan_callout_targets.py --kinds=lfun。 还剩 2 个函数没有补(other 2):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/obj/mudlib/nicknamemenu.lpc:new_template、/obj/mudlib/nicknamemenu.lpc:rcv_new_alias_name。

Remaining constant assignments used as conditions (2026-10-27, KB 06 §7.290 Also)

m_set if (set = "") became == (lpc_fix_cond_assign.py; KB 06 §7.290 Also).