Foundation II LPC Library

✅ 可玩

Foundation II

foundation2

更新 f1b9b2e 2026-10-22 源码 下载 ZIP

▶ 开始游玩 · Play Now

A stripped-down classic descendant of the Nightmare mudlib lineage from the mid-1990s, positioned by its own boot banner as an engine for hardcore LPC programmers, not a beginner-friendly game world -- it ships no combat, limbs, or stats system at all. What shipped and works: a real first-boot admin-setup installation wizard, full login/registration, security/privs access control, command dispatch, help, mail, message boards, and Intermud-3 networking, plus one small starting domain. Two optional example/teaching content trees included in the archive reference an incompatible, differently-named room/item API from a different snapshot of this engine and don't compile, so they're not playable content.

README

A stripped-down, classic descendant of the Nightmare mudlib lineage, originally released by George Reese ("Descartes of Borg") in the mid- 1990s. Per the mud's own boot banner: "This mud is NOT for newcomers to LPC. You must be hardcore." Foundation II ships no combat, limbs, or stats system at all -- it's a minimal, complete engine (login/ registration, security, command dispatch, help, mail, message boards, Intermud-3) meant as a clean base for LPC programmers to build their own game world on top of, not a playable-out-of-the-box game.

Source: a maintained, already-FluffOS-adapted archive (foundation2_fluffos_v1.zip) from lpmuds.net, a now-defunct LPMud archive site. Recovered via the Wayback Machine (<https://web.archive.org/web/20160306101019if_/http://lpmuds.net/files/foundation/foundation2_fluffos_v1.zip>) since the original site is down. Slug foundation2, number 174, port 40221.

Highlights

Registration flow

new name -> "Do you really wish <name> to be your name? (y/n)" -> password (5+ letters) -> confirm password -> gender (male/female) -> display name (blank = default) -> email (required, must look like user@host) -> real name (optional, blank OK) -> two news screens (<return> to continue) -> lands in the Standard domain's starting cavern.

Every account (mortal or wizard) saves under the same secure/save/ creators/ tree -- this engine has no separate mortal/wizard save-file split by default.

Admin account

Created through the mud's own first-boot installation wizard (see NOTES.md), not a save-file hand-edit. Verified live: update <path> (this project's canonical ACL/compile-permission check) against a real cmds/player file succeeds.

Status

Boots clean: zero compile errors across repeated fresh-driver boots. Verified live with a real driver session and a raw socket client: the first-boot admin installation wizard, a brand-new ordinary character's full registration, and look/inventory/who/save/a soul command (smile)/the wizard update command all producing correct output, and a clean quit. This engine has no combat/stats system, so there is no score command to verify (see NOTES.md for what substitutes for it in this port's verification pass).

223 of 246 .lpc files pass a full lpcc_check.sh batch compile sweep; the 23 that don't are all non-preloaded, non-boot-critical example/ teaching content with a pre-existing content/API mismatch predating this port (see NOTES.md \S3) -- expected for a deliberately minimal engine like this one, not a sign of missing conversion work.

WASM status: playable. Login as fluffos into the starting cavern and inventory are verified under the shared WASM driver (NOTES.md WASM status update). Play: https://mudlibs.fluffos.info/foundation2/

Local run

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

Game port: 40221.

NOTES · 移植与修复记录

Foundation II -- porting notes

Source: foundation2_fluffos_v1.zip, a maintained, already-FluffOS-adapted archive from lpmuds.net (a now-defunct LPMud archive site). The live site is down; this exact archive was recovered from the Wayback Machine (<https://web.archive.org/web/20160306101019if_/http://lpmuds.net/files/foundation/foundation2_fluffos_v1.zip>) and content-verified (valid zip, non-corrupted) before this session started. Slug foundation2, number 174, port 40221. The zip bundles a FluffOS 2.9-ds2.13 driver source snapshot (fluffos-2.9-ds2.13/), plus bin/, win32/, old/ build/install scaffolding -- all ignored, this project uses its own driver. The real mudlib root is the zip's lib/ directory.

Foundation II is a stripped-down classic descendant of the Nightmare mudlib lineage: no combat/limbs system at all, positioned (per its own boot banner) as a minimal base "NOT for newcomers to LPC. You must be hardcore" -- aimed at LPC programmers building their own game on top of a bare engine, not a playable-out-of-the-box game world. This matches what shipped: a working login/registration/security/command-dispatch/help/mail/board/ Intermud-3 engine, one small "Standard" starting-cavern domain, and two optional teaching-only content trees (domains/Examples/, domains/School/) that reference a different, incompatible content API (see \S3) -- there is no score/combat/stats system anywhere to test.

1. Conversion

English-language archive (not GBK-encoded) -- scripts/convert_lib.sh against raw/foundation2_fluffos_v1/lib confirmed this: 1126/1160 files already valid UTF-8, only 1 lossy conversion (doc/faq/lpmud, a handful of stray invalid bytes in a plain-text FAQ file, not code), 244 files renamed .c->.lpc, 67 literal .c" references fixed, 13 local angle-bracket #includes converted to quotes, 118 files touched by the static->nosave sweep.

One file needed the same static->nosave treatment by hand: secure/lib/login.real is not .c/.lpc (so the mechanical sweep skipped it) but is real, live LPC source -- the mudlib's own first-boot admin-setup wizard (secure/lib/login.lpc, see \S5) cp()s it over login.lpc at the end of setup and it becomes the mud's actual ongoing login/registration object from then on. Its 45 static declarations were converted the same way by hand, then verified by compiling it standalone under its real target path before ever letting the install flow copy it into place live.

2. Compile-time driver-compat fixes

- Follow-on bug in the fix's own sscanf() use: creator_file(str) strips a clone-instance suffix via sscanf(str, "%s#%d", base, ext), same idiom as nightmare3's version -- but nightmare3's version never hits the case where str has no # at all in a context that matters, while every one of this driver's *own* boot-time preload calls does. An unmatched %s target keeps its int-0 default (not ""), so base stayed the integer 0 for any non-cloned path, and passing that into file_privs()'s file[0..1] string-indexing crashed with "Cannot index type 'int' using [ .. ] operator" -- caught by preload()'s own catch(), so non-fatal, but every single preloaded daemon hit it (confirmed via log/catch). Fixed by falling back to base = str whenever sscanf() doesn't report a full 2-field match.

- secure/cmds/creator/grant.lpc: file[strlen(file)-1] != "/" compares a single character (an int) against a string literal -- this driver's strict-types pass makes that a hard "!= always true" compile error (not just a warning), unlike whatever drove the original archive. Fixed to the character literal != '/'. - secure/cmds/creator/banish.lpc: used UID_BANISH (a real constant, defined in secure/include/security.h) without including that header -- had a literal duplicate #include <daemons.h> instead (one of the two copies replaced with the missing include). - secure/cmds/creator/memcheck.lpc: inherit DAEMON; -- DAEMON is not a macro anywhere in this codebase (the real one, used everywhere else, is LIB_DAEMON); a lone pre-existing typo, not something this port introduced. Fixed to LIB_DAEMON.

3. Bundled example/teaching content targets a different, incompatible API (not fixed -- pre-existing content mismatch)

23 of 246 .lpc files still fail lpcc_check.sh's batch sweep (down from 227 before the fixes in \S2); every one of them is non-preloaded, non-boot- critical teaching/example content, and every failure traces to the same root cause: this content was written against a different room/item API than the one this engine actually ships.

None of these 23 files are in secure/cfg/preload.cfg, referenced from the Standard starting domain, or reachable from any command a normal player or the seeded admin would run -- confirmed via a full live boot and play session with zero related errors.

4. First-boot admin-setup wizard (not a bug -- this engine's own onboarding flow)

Unlike most archives in this project (which need a wizard account hand-seeded via a save-file edit or a normal-registration + config-edit combo), Foundation II ships its own genuine one-time superuser setup flow: secure/lib/login.lpc starts as a small self-contained "installation process" wizard (name -> password -> confirm -> capitalized-name -> gender -> real name -> email), and on completion it clones the real, full-featured login/registration object over itself (cp("/secure/lib/login.real", "/secure/lib/login.lpc"), see \S1), destructs the temporary admin object and the master object (forcing a clean reload of both), and disconnects with "Login as your admin character." From then on the mud behaves like any other -- new characters register through the normal flow (name -> confirm (y/n) -> password -> confirm -> gender -> display name (blank OK, defaults to the account name) -> email, which must match user@host and is NOT optional/skippable -> real name (optional, blank OK) -> two "Press <return> to continue" news screens -> world entry).

The project's admin account was created through this exact first-boot flow (not a save-file hand-edit): id fluffos, password Mud@2026, granted ASSIST+SECURE group membership in secure/cfg/groups.cfg automatically by the setup flow itself (it renames the archive's placeholder admin group entries to the chosen admin's name), and given a real per-account "realm" directory (realms/fluffos/workroom.lpc, copied from domains/Standard/xtra/workroom.lpc by the same flow). Verified live with update <path> (this project's canonical ACL/compile check) against a real cmds/player file.

Note for anyone re-running this install flow: every account in this engine saves under secure/save/creators/, not a separate mortal-vs- wizard split -- #ifdef PLAYERS branches throughout the codebase (e.g. creator_file()'s own base-name check) are dead in this build (PLAYERS is never defined), consistent with this being a bare "for LPC experts" engine with no mortal/wizard distinction baked in by default.

5. Live outbound network side effect at boot (by design, not a bug -- same class as imud, see AGENTS.md \S2.3)

daemon/intermud.c (Intermud-3 protocol support, preloaded unconditionally via secure/cfg/preload.cfg) is guarded by #ifndef __PACKAGE_SOCKETS__ #error ...#endif -- and this project's driver build has the sockets package enabled (confirmed via nm: f_socket_create is present), so the guard doesn't fire. Its create() unconditionally schedules call_out((: Setup :), 2), and Setup() calls eventCreateSocket() against a hardcoded real address (204.209.44.3:8080, the historic *i4 Intermud-3 router) roughly 2 seconds after every boot. This is genuine, intentional upstream functionality (not something to patch away), but -- exactly like imud (AGENTS.md \S2.3) -- it means every boot of this lib makes a real outbound connection attempt to a real third party. That router is a 1990s-era service and near-certainly long defunct by now (no connection-success evidence was seen in any boot log this session), but this should not be swept into high-frequency automated re-boot/re-test loops the way this project's other (fully sandboxed) libs safely can be.

6. Boot and play test

Booted ~/src/fluffos/build-debug/src/driver config.fluffos repeatedly; zero compile errors, Initializations complete., accepting telnet connections on 40221 every time. Verified via a raw Python socket client (scripts/mudclient.py), end to end:

Zero uncaught runtime errors in log/runtime/log/catch across this session's boots and play-testing (the log/catch entries seen mid-session were creator_file() traces from the lpcc_check.sh batch runs that share this lib's log/ directory, not from the live driver -- confirmed by their file-modification time not advancing while the real driver was up).

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

Promoted wasm_status from "" to playable. Same root-cause class as ds386/discworld/deadsouls_fluffos/tmi2: secure/sefun/ sefun.lpc's own dump_socket_status() (part of the eagerly-loaded simul_efun object) called socket_status() unconditionally, undefined on this driver build (no sockets package). Gutted to a safe stub (diagnostic admin tool only, not on the boot/login path). No other socket-related eager-load failures found. Verified with a scripted WASM session: login (fluffos/Mud@2026), arrival in the correct starting cavern room, and inventory producing correct output. quit wasn't recaptured distinctly in this transcript but is already verified clean under native testing above and untouched by this fix.

深度功能测试(round two, 2026-08-27)

Full §10.7 continuous playthrough against the native debug driver (~/src/fluffos/build-debug/src/driver config.fluffos), never done for this lib before (only the original onboarding-tier boot/play pass in \S6/\S "WASM status update" above). This is a genuinely minimal engine (no combat/limbs/stats/score, one small starting room with no working exits -- see \S3), so the test surface is: login/registration, look/inventory, save, mail (IIPS), who, quit+reconnect, and a systematic grep sweep for every standing cross-cutting bug class tracked in AGENTS.md (\S7.121-\S7.135, \S8.3a).

Playthrough (admin fluffos/Mud@2026, plus a throwaway English test character Aldric, cleaned up before commit): full first-boot admin login and a brand-new registration (name -> confirm -> password -> confirm -> gender -> display name -> email -> real name -> news screen -> world) both landed cleanly in /domains/Standard/center with correct look/inventory output. save, the mail command (IIPS letter reader, empty folder, clean q-driven exit), and who all worked correctly. quit printed the normal "Please come back another time!" with zero log/runtime growth; reconnected after a real multi-minute wall-clock gap and got a genuinely fresh Setup() run (not a "Reconnected." link-restore), landing back in the same room with the same empty inventory -- no state loss, no duplication. Confirmed (per \S3) that the starting room's own pool-spawning code is commented out in domains/Standard/center.lpc's create(), and even if it were re-enabled both pool destinations are dead ends (one points at a non-existent /domains/Discussion/MudOS, the other at domains/School/room/entrance -- one of the already-documented incompatible-API School files that doesn't compile) -- so the starting cavern legitimately has zero working exits; this is pre-existing content/archive state, not something introduced by this port, and not a programming bug to invent a fix for.

Cross-cutting pattern sweep -- grepped systematically for every class in AGENTS.md \S7.121-\S7.135 and \S8.3a. Confirmed genuinely not applicable/structurally clean on this lib for: \S7.121 (only float usage in the whole tree is secure/sefun/percent.lpc's own correctly- float-declared percent()), \S8.3a (zero private command-dispatch/ call_out-target functions anywhere -- every add_action/call_out target checked is a plain public/nomask function), \S7.112 (no NPCs at all outside the non-compiling domains/Examples/npc), \S7.122 (lib/autosave.lpc's AutoLoad item-reload mechanism is correctly gated by lib/interactive.lpc's own Setup re-entry guard -- net_dead()/reconnect via boot_copy() never re-invokes Setup() at all, so the autoload-on-Setup path only ever runs once per genuinely fresh login), \S7.123 (the one raw grep hit, secure/cmds/player/tell.lpc:109's morse() local variable assignment, is inside a function body, not file scope -- false positive), \S7.124 (no percentage/threshold fields of this shape exist at all -- no combat/wimpy system), \S7.126 (no coordinate-area door save-data of this shape), \S7.129 (secure/sefun/communications.lpc's tell_room()/say()/shout() wrappers all explicitly convert a falsy/omitted exclude to ({}) before forwarding to message() -- already safe, unlike the buggy es1 shape this class describes), \S7.130 (disconnect handling goes through the net_dead() apply directly, no heart_beat()-based query_idle() polling anywhere), \S7.131 (lib/interactive.lpc:63 calls set_living_name(GetKeyName()) inside Setup(), and find_player()/find_living() lookups were directly exercised live via secure/lib/login.lpc's own reconnect- detection and secure/cmds/creator/call.lpc/whisper/tell -- already confirmed working), \S7.132 (no map() over a mapping anywhere in the tree -- every map() call site targets an array), \S7.133 (net_dead() -- the correct apply name for this driver -- is genuinely defined in lib/interactive.lpc/lib/creator.lpc/ lib/chat.lpc), \S7.134 (lib/room.lpc's own "extra descriptions contributed by present items" accumulator, ExtraLongs, IS initialized at its declaration site in create() -- structurally the same feature class as the arkadia/genesis bug but already safe here), \S7.135 (checked every lazily-guarded global family found via grep -- lib/nmsh.lpc's Nicknames/Aliases/Xverbs, lib/access.lpc's __ReadAccess/__WriteAccess, secure/daemon/master.lpc's Groups/ReadAccess/WriteAccess, daemon/banish.lpc's __Names/__TmpBanish/etc. -- every one is unconditionally initialized in its own file's create(), not merely guarded lazily per-accessor, so there's no "one accessor forgot the guard" gap to find).

New bug found and fixed: secure/daemon/events.lpc (the reboot- warning/scheduled-event daemon, EVENTS_D) defines GetEvents() and GetRebootInterval() as its real query accessors, but two OTHER files call a same-purpose function under a different name that is never defined anywhere in the whole tree: cmds/creator/events.lpc's cmd() (the player-facing events <wizard> command, reachable by any arch/creator) called EVENTS_D->query_events() at its second branch (the events command's first branch, no argument, already correctly calls GetEvents() -- so this is an internal naming inconsistency within the SAME file, not a guess), and secure/sefun/events.lpc's (dead, uncalled) event_pending() made the identical mistake. This predates this project's port entirely -- confirmed via raw/ foundation2_fluffos_v1/lib/{cmds/creator,secure/sefun}/events.c, the pristine 1990s archive already has this exact mismatch, byte for byte. On this driver, a call to an undefined function via -> returns a bare int(0), and (mapping)0's cast is a no-op (this driver's (type) casts are compile-time hints only, see the reference_lpc_ int_cast_is_compile_time_only note) -- so mp stayed int(0), and the very next line's keys(mp) crashed uncaught: reproduced live, events somebody as the seeded fluffos admin threw *Bad argument 1 to keys() Expected: mapping Got: 0 (log/runtime, /cmds/creator/ events line 36), aborting the command with no player-facing indication beyond the raw driver error text. Fix: both call sites changed from EVENTS_D->query_events() to EVENTS_D->GetEvents(), matching the daemon's own real, only accessor and the working sibling branch already right above it in the same file (cmds/creator/events.lpc:35, secure/sefun/events.lpc:16). Verified live post-fix: events somebody now correctly prints "somebody doesn't have any events pending." with zero log/runtime growth; a full re-boot-and-retest after running the \S9 LPC formatter on both touched files (all three formatter blind-spots checked clean: no :: mis-split, no case/comment merge -- neither file has a switch, no CJK/escape re-spacing) reconfirmed the fix.

Related gap found, NOT fixed (documented per this section's own "when unsure, don't guess" rule): cmds/player/nextreboot.lpc (a real, reachable player command) calls EVENTS_D->query_next_reboot(), which also does not exist anywhere in the tree -- and unlike query_events(), there is no matching same-purpose sibling function to redirect it to (GetRebootInterval() only returns the configured *interval* in hours, not an absolute next-reboot timestamp). This doesn't crash (the undefined-function call_other returns 0 and the command just prints a bogus "Wed Dec 31 16:00:00 1969 PST" instead -- confirmed live), but fixing it correctly would mean inventing new bookkeeping (e.g. tracking mud-start time and deriving boot_time + RebootInterval*3600) rather than just correcting a mistaken function name, and the entire auto-reboot subsystem it would report on is already dormant by original-archive design: secure/daemon/ events.lpc's own eventPollEvents() self-rescheduling poll loop -- the only code path that ever checks uptime() > RebootInterval*3600 and triggers eventReboot() -- is never started at all, because its sole call_out((: eventPollEvents :), 60) in create() is commented out. Confirmed via raw/foundation2_fluffos_v1/lib/secure/daemon/ events.c that this call_out was ALREADY commented out in the pristine 1990s archive, not something broken by this port. Left untouched as a pre-existing, structurally dormant feature gap rather than guessed at. (A third, related latent bug in the same dead subsystem, also pre-existing and also left untouched: secure/sefun/ events.lpc's event() wrapper calls EVENTS_D->AddEvent(f, when, reg) with 3 arguments of the wrong types against AddEvent()'s real 6-parameter (string c, string s, string f, mixed *a, int w, int r) signature -- but event() itself is never called by anything anywhere in the tree, so this is unreachable dead code, not a live crash.)

Evidence left in place: the seeded admin account secure/save/creators/f/fluffos.o (age/login-time counters naturally advanced by this session's repeated logins). The throwaway test character Aldric (secure/save/creators/a/aldric.o, secure/save/postal/a/aldric/) was deleted before committing, per this project's standing cleanup convention.

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

Content-absent, same class as foundation1. Bare 1995 engine: no combat, no stats, no currency, no vendor objects, no apprentice verbs. Live on 40221 as seeded admin fluffos / Mud@2026 in /domains/Standard/center: buy, list, shop, apprentice, join, become, and 拜师 all printed What?. look/inventory still work. Left as-is.

深度功能测试(§10.7,2026-10-03)— site-like tree, every compile warning

Same method as foundation1 (a tree built the way the site builds it: tracked files, work/log, and the directories wasm_keep_dirs.txt records; see that lib's 2026-10-03 section). Registration, bug -r (the ed-based tracker), mail, the soul and chat commands, ~60 further commands, and disconnects at every login prompt and inside the bug dialog, the mailer and the news pager. Fixed:

1. tmp/ missing: bug -r ran ed on tmp/<name>, then Couldn't open file for writing! -- no bug could ever be filed. Added tmp/.gitkeep. 2. secure/save/letters/ missing: mail composed, then *Could not open /secure/save/letters/<n>/<id>.o.tmp for a save (the daemon makes the digit directory but not its parent). Added secure/save/letters/.gitkeep. 3. log/ subdirectories: the keep list had no entry for the lib, so the site's new characters died at the y prompt exactly like foundation1 (gen_keep_dirs.py --merge), and the recorded set was still not complete: eval logs to /log/adm/eval, which no local boot had ever created (Wrong permissions ... "No such file or directory" after the command had run). master_log_file() now creates the missing parent directories of the log file it writes, so the list no longer has to be complete. 4. drop <thing you do not hold> fell through the unfinished "do money here later" branch and called CanDrop on 0: Bad argument 1 to EFUN call_other(). It now says "You do not have that." 5. magic called new("/realms/b/beek/magic")->eventMove(...); the object (a wizard's private game, not in the archive) does not exist, so new() returned 0 and the command raised instead of printing its own "Error in loading magic object." message. Null-checked. 6. Every compile warning fixed at its source (scripts/lpc_warnings.py --fix, KB 04 section 6.10): ~500 nosave function declarations (protected; private ones lose nosave; names another object calls stay public), 94 unused locals, get_stack() printing the function name twice (stack0 was the unused variable), interactive.lpc SetKeyName returning void against object.lpc's string, SetPassword with a bare return in a string function, the Examples start room's (: "search_brush" :). lpcc --batch over 246 files reports no warning; the 23 Examples / School / move objects of section 3 still fail with their missing-subsystem errors, unchanged. 7. Not a bug: chfn, passwd and suicide -- this lib has no passwd or suicide command at all (cmds/player, secure/cmds/player).

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

驱动的 mudlib_stats 包在主控载入时各调用一次 author_file(<主控文件>) 和 domain_file("/"),要求返回字符串。本库这两个 apply 只为 domains / realms 目录下的文件返回名字,其余返回 0,所以每次启动都打印 author_file() in the master file does not work, using root_uid as fallback 和 domain_file() in the master file does not work, using bb_ui as fallback 两行,网页版每位访客的终端里都看得到。secure/daemon/master.lpc 改成与 fluffos 组织维护的 Dead Souls 上游(deadsouls_fluffos)相同的写法:其余文件的域返回 "BACKBONE",作者返回 "NONAME"。域等于 backbone 域时驱动让对象沿用创建者的域,与返回 0 时相同;作者统计(author_stats())多出一行 NONAME,汇总 realms 目录以外的对象(KB 06 §7.222)。用 lpcc config.fluffos /nonexistent.lpc 前后对比,主控载入段只少了这两行。

2026-10-05 — 新玩家首次存档失败(KB 06 §7.227)

secure/daemon/master.lpc 的 compile_object() 用 else if 先建 players 目录、再建字母目录:父目录不存在时只建父目录,这位玩家第一次存档(设密码时)打开 players/<字母>/<名字>.o.tmp 失败。git 不跟踪这个目录下的文件,全新检出与网页版每位访客都会遇到。第二个 else if 改为 if(与 dsI 相同)。nightmare4 上用只含被跟踪文件的树实测注册→重登通过;本库主控编译通过。

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

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

Macros the global header lost (2026-10-19, KB 04 6.16)

/secure/include/global.h did not define VT_WEAPON; the objects that use them (1 on the list) stopped at Undefined variable/Undefined function, and since the compiler stops at the first undefined name the errors and warnings behind it stayed hidden. The sibling libs of the same lineage carry these lines in their headers: the copy of the sibling with the most files in common was added after the last #define of /secure/include/global.h; every path in a macro body exists in this lib. the listed objects have other errors as well and still do not load. (scripts/lpc_restore_defines.py)

call_out_info() 的每一项只有 3 个元素(2026-10-20,KB 06 §7.287)

驱动返回的是 ({ 物件, 函数, 延迟 }),不带 call_out 的参数,旧代码读第 4 项 info[i][3](callouts 指令、calloutd、cod 工具)一读就报 Array index out of bounds,指令从第一行起什么也打印不出来。改为 (sizeof(info[i]) > 3 ? info[i][3] : 0)(scripts/lpc_fix_callout_info.py)。改动:/cmds/creator/callouts.lpc(1 处)。

登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)

驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/secure/sefun/sefun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。

no_colours():FluffOS 的 terminal_colour() 会给空的 RESET 换上 ANSI 复位序列(2026-10-21,KB 06 §7.337)

/daemon/terminal 的 no_colours() 把 RESET 为空串的表交给 terminal_colour(),FluffOS 把空的 RESET 换成 ESC[49;49m ESC[0;10m 并写回映射,所以"去掉颜色"的文本里仍有转义序列(no_colours("%^CYAN%^[1]%^RESET%^ abc") 得到 [1]\e[49;49m\e[0;10m abc,用 scripts/lpcc_eval.py foundation2 --at /open/zzeval.lpc 验证),strip_colours() 少算宽度,arrange_string() 与 format_page() 会把含颜色码的格子截短。Foundation II 自带的菜单屏幕里格子大多不含颜色码,没有观察到可见症状;现在 RESET 先映射为退格符再删掉。修改:/daemon/terminal.lpc。

巫师命令不带参数时对 0 调用 efun(2026-10-21,KB 06 §7.336)

巫师(管理员)把命令敲成不带参数时,驱动把 0 传给字符串参数;下面的命令第一句就把这个 0 交给 efun,运行时报 Bad argument 1 to ...,这条指令从来没有用过。用 scripts/lpcc_bare.py 不登录、在一次 lpcc 求值里以 0 调用 main() 逐个确认后,由 scripts/lpc_fix_bare_arg.py 在第一处使用参数的语句前加守卫:没有 help() 的是 return notify_fail("…\n");。改动(1 个文件,括号里是接住 0 的 efun):/secure/cmds/creator/banish.lpc(lower_case)。加载检查 HEAD 与工作树对照,没有退步。

注册到不存在函数的 add_action,第二轮:宏路径父类和静态解析不了的对象(2026-10-21,KB 06 §7.349)

第一轮的静态解析读不了经宏路径继承父类的对象(#define ROOM STD_DIR "room/room" 这类定义的正文以另一个宏开头)和它解析不了的继承,这些对象当作 UNSURE 没有扫;第二轮修了解析器,重扫并对这些对象在运行时问 function_exists()。add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 1 条注册(1 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/lib/bboard.lpc(cmd_save)。最多的函数名:cmd_save 1 处。

战斗探针(2026-10-22,KB 08 §10.16):domains/Examples/npc/ 的两个示例 NPC 编译不过

scripts/play_combat_probe.py foundation2 --account ... --clone ... 克隆 /domains/Examples/npc/smiley 和 centaur,两个文件都是 inherit LIB_NPC;(9:9: syntax error):这个压缩包里没有 NPC 基类(lib/ 里没有 npc.lpc,lib.h 没有 LIB_NPC),示例 NPC 没法加载。没动(缺的是基类,不是笔误)。