Lima Mudlib

✅ 可玩

Lima

lima

🔑 fluffos / Mud@2026 更新 99acc1d 2026-10-22 源码 下载 ZIP 上游 limalib/lima

▶ 开始游玩 · Play Now

LIMA 社区仍在维护的参考 mudlib(源码: <https://github.com/limalib/lima>,官网 <https://www.limamudlib.dev/>)。这不是一款武侠/仙侠游戏——和本项目收录的其它中文 mudlib 不是同一个宗谱,是一个独立的、英文的、面向开发者的现代 LPC 框架,收录进本项目是作为 "当代 FluffOS mudlib 长什么样"的参照样本。

English

The maintained LIMA mudlib (limalib/lima, limamudlib.dev) -- an English-language, developer-facing framework built to demonstrate what a modern LPC mudlib looks like, quite unlike the classic Chinese wuxia lineages that make up most of this collection. Rather than registering one verb per add_action, Lima understands full natural-language sentences ('take the red potion from the box'), secures files and commands through a capability-based system of privileges/domains/protections instead of the traditional uid/root model, and gives every wizard a Unix-like shell (variables, pipes, globbing) rather than a fixed command table. New characters begin in the Grand Hall, a wizards' meeting room with polished floorboards and rough-hewn beams, flanked by a sulfurous northwest passage, a passage to the south that smells like something recently died, and an incongruous elevator door to the west; a glowing portal leads to the separate mortal starting area. Bundled demo content includes a nine-room 'Sandy Beach' pirate treasure-hunting puzzle (explicitly marked by its own author as not meant for a real, live mud) and optional contributed modules for bulletin boards, marriage, and in-room object creation. New characters are granted wizard powers automatically (AUTO_WIZ), the upstream project's own default for a framework meant to be explored and extended, not played as a finished game.

README

github.com/fluffos/lima 已于 2024-10-08 归档,不再是活跃上游。本馆按 submodule-patch 跟踪 limalib/lima:work/ 是该仓库的 submodule(mudlib 根目录是 work/lib,上游保持 .c),本馆 FluffOS 兼容在 patches/,管理员播种在 overlay/。真源码 bug 往 limalib/lima 提(本馆已合入的 #49 / #50 / #51 在上游 2026-09-03 HEAD ffed9c588ee5)。

架构说明

Lima 在几乎每个层面都和本项目里的经典 LPMud 系 mudlib 不同:

详见 NOTES.md,里面有完整的转换记录、发现并修复的驱动兼容性 bug、 以及为什么这个 lib 需要一个专门编译的驱动。

特殊要求:需要专用驱动

这个 lib 不能用本项目其它 ~240 个 lib 共用的默认驱动 (~/src/fluffos/build-debug)启动——Lima 自带的 secure/check_config.c 会在启动时检查驱动是否按它要求的编译期开关 (NO_LIGHT/NO_ADD_ACTION/NO_WIZARDS 已定义,OLD_ED/ PACKAGE_UIDS 未定义)编译,不满足就直接报错中止,而本项目的共享 驱动是按相反的开关编译的(其余所有 lib 都需要那一套)。

已经在 ~/src/fluffos-lima(~/src/fluffos 的一个独立 git worktree, 同一份源码不同的编译配置)按 Lima 的要求单独编译出了一份驱动,运行 时要用:

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

(如果 ~/src/fluffos-lima 不存在,NOTES.md 里有完整的重建步骤, 一次性编译大约 5-10 分钟。)

游戏端口:40212。原生(native)驱动已验证。站点现在按 lib 覆盖 WASM 驱动(scripts/custom_drivers/lima_swmud/,与 swmud 共用), 浏览器试玩已走通注册→选种族→进入 Grand Hall,wasm_status 为 playable。详见 NOTES.md「站点基础设施缺口已补上」。

本地运行

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

连接后按提示注册(英文 id + 密码 + 性别/邮箱/真实姓名等基本信息), 在用户菜单里用 c 创建角色、选择种族,s 选中、p 进入游戏。核心 指令:look、score(状态面板)、inventory、who、quit。

管理员账号 / Admin account

警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。

NOTES · 移植与修复记录

lima — Lima Mudlib

来源:活跃上游是 limalib/lima(HEAD 98431aa05cc9,scheduled safe rebase 2026-10-05 from eefaff66; 官网 <https://www.limamudlib.dev/>)。本馆可玩树最初从已归档的 fluffos/lima 克隆(dbcef2a Update fluffos/lima to 1.1a2 (#33), 2026-08-24)。编号 164,端口 40212。状态:done(干净启动,真实注册 流程 + 角色创建 + look/score/inventory/who/quit 全部验证可用; 管理员账号 fluffos 已播种并验证 update/admtool 等高权限指令)。 hosting 为 submodule-patch。leftover 684:work/ 已切成 limalib/lima submodule(pin 98431aa05cc9),config.fluffos 的 mudlib directory 指向 work/lib。

leftover 684:lima work/ 切到 limalib/lima submodule

按 leftover 682/683:非 fluffos 活跃上游保持他们的 .c,本馆兼容进 patches/。work/ 现在是 limalib/lima 的 gitlink,不再是本馆 .lpc 格式化树。catalog-only 补丁:

站点 zip 在 staging 上 apply patches + rsync overlay,所以下载仍是 可玩、可复现的源码树。autodoc.c 的行内 END 标记仍不编译,http_d 未 preload,不影响游玩。

leftover 683:归档的 fluffos/lima 不是 hosting 目标

fluffos/lima 2024-10-08 已 archived。按 leftover 682 的规则,归档 = 不再活跃,不能当 fluffos-upstream。活跃上游是同一 LIMA 社区的 limalib/lima(本馆 PR #49/#50/#51 已于 2026-09-03 合入)。meta.json 改为 hosting: submodule-patch,pin ffed9c588ee5。leftover 684 已把 work/ 切成该 submodule(见上一节)。

这个 lib 的架构,写给不熟悉 Lima 的读者

Lima 不是本项目里常见的"侠客行/金庸群侠传"系语系(LPMud 经典宗谱), 而是 FluffOS 驱动项目自己维护的官方参考 mudlib(<https://limalib.dev>), 英文,面向开发者/巫师的框架式设计,和本项目收录的绝大多数中文武侠 mudlib 在几乎每个层面上都不同:

§7.46 的后续:LIMA 系驱动编译选项冲突,这次真的解决了

AGENTS.md §7.46 早先记录过一个结论:"基于 LIMA 代码库的 mudlib 需要 本项目共享驱动没有的编译期开关(NO_LIGHT/NO_ADD_ACTION/ NO_WIZARDS/undef OLD_ED/undef PACKAGE_UIDS),不在源码层面可修, 需要一个单独编译的驱动 —— 当时判定为 out of scope"(案例:sgzmudsgz, 一个基于 LIMA 代码库的三国志 MUD)。这次直接拿到了 LIMA 本尊,所以把 这件事真正做掉了:

重要:libs/lima/ 不能用 ~/src/fluffos/build-debug(本项目其余 ~240 个 lib 共用的默认驱动)启动——secure/check_config.c 会在 simul_efun 编译时因为上述六项全部不满足而报错并中止启动。必须用 ~/src/fluffos-lima/build-debug/src/driver。这个 worktree 是本 session 新建的,不在 git 版本控制内(~/src/,不是 libs/lima/ 下), 如果这台机器/这个 checkout 丢失了要重新按上面步骤建一次——大概 5-10 分钟的一次性编译,之后复用。

WASM 状态当时标为 partial (native only):要让这个 lib 在浏览器里跑, 还需要在 emcmake cmake --preset wasm 的基础上叠加同样的 local_options/PACKAGE_UIDS=OFF 改动,构建一个专用的 wasm 驱动—— 留作未来工作,这次 session 的任务范围(见协调者的原始指令)没有要求 WASM 通道。(2026-09-01 更新:这个专用 wasm 驱动后来真的建出来了、 且验证可用——见下方「wasm_status 审计」一节。partial 本身也不是这个 项目 wasm_status 枚举里的合法值之一,scripts/gen_site_index.py 的 EXCLUDE_STATUSES 把它当"deliberately deprioritized, e.g. ds386"处理 而不是"boots but WASM未验证"——当时这样写等于把这个 lib 从站点彻底 排除,而不是标成"限制",这正是本次审计要修的那类 wasm_status 误标。)

wasm_status 审计(2026-09-01):专用 WASM 驱动建成、但站点基础设施还接不上

之前 meta.json 的 wasm_status 一直留空——本节记录 2026-09-01 那次 批量审计([[project_wasm_status_audit]])对这个 lib 的完整调查过程和 最终结论:noboot(不是 playable,原因见下)。

专用 WASM 驱动:编译成功,mudlib 本身完全可玩

复用已有的 ~/src/fluffos-lima(git worktree,src/local_options 已经 按上面的六项要求改好):

cd ~/src/fluffos-lima
source ~/src/emsdk/emsdk_env.sh
cmake --preset native-tools && cmake --build --preset native-tools -- -j8
emcmake cmake --preset wasm -DPACKAGE_UIDS=OFF
cmake --build --preset wasm -- -j8

(/opt/wasm-deps 下的 ICU/zlib 已经是全项目共享、编译好的,不需要 重新跑 tools/wasm/build-deps.sh。)一次性编译成功,产出 ~/src/fluffos-lima/build-wasm/src/{fluffos.js,fluffos.wasm}。

第一次用 scripts/wasm_client.js 起跑立即命中和 ds386/dsII/dsIII 同一类"sockets 包缺失导致 simul_efun 编译失败"的 bug(AGENTS.md §1.3(c) 那一类,这次是首次在 lima 自身代码里发现,而不是三国/西游同人 mudlib 里的变体):secure/simul_efun/misc.lpc 的 dump_socket_status() 无条件调用 socket_status()——这个 efun 只在编译了 sockets 包的 驱动上存在(WASM 构建默认不带 sockets 包,浏览器沙箱里没有真实 socket 的语义),所以这不只是运行时缺失,而是编译期就报 "Undefined function socket_status",导致整个 secure/simul_efun 编译失败(*No program in object '/secure/simul_efun/misc'!), 驱动直接拒绝启动("The simul_efun ... and master ... objects must be loadable")。修复(#ifdef __PACKAGE_SOCKETS__ 包一层,没有这个包时 退化成返回空字符串,和 ds386 已有的同类修复手法一致):

string dump_socket_status()
{
#ifdef __PACKAGE_SOCKETS__
    ... (原有实现,逐行不变) ...
#else
    return "";
#endif
}

修复后用 wasm_client.js 完整跑通一次真实会话:fluffos/Mud@2026 登录 → 用户菜单等到 auto-login 10 秒倒计时自动进入游戏(也可以直接 输入 p 跳过倒计时)→ 落地 Grand Hall → score 显示完整的属性/ 经验/负重面板 → quit 干净退出("You have left Lima Mud."),全程 driver_stdout.log 无一条运行期错误。这套 mudlib 内容本身在专用 WASM 驱动上是完全可玩的——之前 README 写的"WASM 通道未构建"这句话 现在已经不成立了,专用驱动确实建出来了且验证通过。

原生驱动(~/src/fluffos-lima/build-debug/src/driver)用同样的 fluffos/Mud@2026 账号复测,无回归。

但站点实际部署用的是唯一一份共享驱动,这个专用驱动接不进去

问题出在这个项目的站点打包管线,不在 lima 自己身上: scripts/write_play_page.sh 把每个 lib 的 play.html 写进站点时, 里面的驱动脚本引用是写死的相对路径 ../_driver/fluffos.js——_driver/ 是整个站点唯一一份共享的 WASM 驱动产物(来自 ~/src/fluffos/build-wasm),所有 ~240 个 lib 共用同一份二进制, 没有任何"这个 lib 用另一份驱动"的每-lib 覆盖机制(build_site.sh/ write_play_page.sh 通读一遍,找不到任何 per-lib driver 路径参数)。

secure/check_config.lpc 的六项检查(NO_LIGHT/NO_ADD_ACTION/ NO_WIZARDS/OLD_ED/PACKAGE_UIDS)在任何驱动上、在 create() 里都会无条件跑一遍,不分 native 还是 WASM——用共享站点驱动(没有按 lima 的要求编译)跑 lima,这个检查一定会 error() 掉,跟本地这次 用专用驱动之前遇到的、AGENTS.md §7.46 记录的"驱动编译选项冲突"是 同一个错误。也就是说:即使这次把专用 WASM 驱动建出来了,部署到 mudlibs.fluffos.info 上的那份 lima 实际上还是会用共享驱动去启动, 一样会在 simul_efun 编译期报错中止——wasm_status: playable 会是 一句谎言,不代表访客点开这个 lib 的页面真的能玩。

结论:wasm_status 设为 noboot

按审计任务给出的定义("noboot: 即使 native 能跑,WASM 下也跑不 起来")——这里的"WASM"应该理解成"这个项目实际部署的 WASM 环境",也就是 共享驱动,而不是"技术上存在某种 WASM 驱动配置能跑"。专用驱动的存在 证明了 mudlib 内容层不是问题,问题完全在站点基础设施的"一份驱动打包 所有 lib"这个假设上,这已经超出本次审计("修正个别 lib 的 wasm_status 字段")的范围,属于一次单独的站点管线改造工作(给 write_play_page.sh/build_site.sh 加一个 per-lib 驱动目录覆盖 参数,再单独为 lima 归档 ~/src/fluffos-lima/build-wasm 的产物)。 把这个专用驱动的完整构建步骤记在上面,方便未来那次改造直接复用, 不用重新摸索 check_config.lpc 的六项要求或重新踩一遍 __PACKAGE_SOCKETS__ 这个坑。

secure/simul_efun/misc.lpc 的这处修复本身不依赖站点基础设施,已经 提交进 libs/lima/work/——不管未来站点管线怎么改,这个修复都是 必需的前置条件,先做掉不亏。

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

上一节末尾说的"未来那次改造"这次做掉了:write_play_page.sh 新增 一个可选的第 5 个参数(自定义驱动目录),给出时把该目录下的 fluffos.js/fluffos.wasm 拷进这个 lib 自己的站点输出目录, play.html 里的驱动脚本引用也相应改成本地路径而非共享的 ../_driver/(telnet.js/vendor/xterm 这些和驱动编译选项无关的 文件仍然共享)。build_site.sh 新增一个 custom_driver_dir_for() 查找表,lima/swmud/spacemud 三个 slug 映射到 scripts/custom_drivers/lima_swmud/——本节上面记录的那份专用驱动 产物(~/src/fluffos-lima/build-wasm/src/{fluffos.js,fluffos.wasm}, 2026-08-24 的 fluffos/fluffos@721878c 构建)直接提交进了这个目录, 不需要在 CI 里重新编译(CI 没有装 emsdk,见 AGENTS.md §1.6)。

本地用 pack_lib_zip.sh + write_play_page.sh <slug> ... <custom_dir> 搭了一个范围内(只有这两个 lib)的测试站点验证:fluffos.js/ fluffos.wasm 正确落地在 lib 自己的输出目录,play.html 引用也正确 指向本地而非 ../_driver/。用 Playwright 通过真实的 #cmd 输入框 走完整个流程——注册、性别/种族选择、创建角色、s+p 进入游戏—— 落地 Grand Hall,look 正常显示房间描述,和本节前面用 wasm_client.js 跑通的那次结果完全一致,控制台无 JS 报错。

meta.json 的 wasm_status 从 noboot 改为 playable。见 libs/swmud/NOTES.md 对应小节(同一份驱动,独立验证)。

转换步骤(对照 AGENTS.md §2/§2.3)

1. 克隆到 scratch 目录,lib/ 就是 mudlib 根(README 自带的 mudlib directory : ./lib);driver/(fluffos 子模块)、 resources/、build.sh 等构建脚手架按约定忽略,只取 lib/。 2. 编码:整个仓库 1969 个文件里只有 2 个非 UTF-8——WWW/lima.jpg (二进制图片,正常)、help/wizard/coding/parser(一处 Latin-1 版权符号 ©,iconv -f latin-1 -t utf-8 修复)。其余全部原生 ASCII/UTF-8,符合"最新 fluffos 兼容的现代仓库"预期,不需要 GB18030/BIG5 转换。 3. .c → .lpc:scripts/convert_lib.sh(用 UTF-8 作为"源编码" 跑一遍,等价于跳过转码、只做改名 + 引用修复),1095 个 .c 文件 全部重命名,118 处字面量 .c" 引用自动修复,0 处遗留。 - 发现一处 §4.2 条目 5("扩展名缺失文件 + 同名 .c 备份")的实例: secure/daemons/finger_d(无扩展名)与 finger_d.lpc(原 finger_d.c)内容不同。git log 确认 finger_d.c 是最近一次 commit(Update fluffos/lima to 1.1a2 #33)改过的版本,无扩展名 的那份自 #28 之后再没被碰过——是过时内容,已删除。 - static -> nosave 批量替换踩了 §4.3 描述的"字符串字面量误伤": domains/std/lima/workroom_ob.lpc 一句房间描述文本里恰好出现了 单词 static("a burst of static darts across your screen"), 被误替换成 nosave,已手动改回。include/global.h 里 #ifndef __SENSIBLE_MODIFIERS__ #define nosave static #define protected static #endif 的兼容 shim(§4.3 第二类碰撞)也被误伤成 #define nosave nosave,同样改回原文——不过这个 shim 本来就在 #ifndef __SENSIBLE_MODIFIERS__ 保护下,本项目驱动始终定义 __SENSIBLE_MODIFIERS__,这段代码从未真正执行,改不改都不影响 行为,纯粹是为了保持源码忠实。

编译期发现的真实 bug(而非内容/示例缺口)

以下几处是驱动兼容性/程序错误,符合本项目"只修程序 bug,不改 内容设计"的范围,已修复:

1. .c→.lpc 改名的连锁反应:命令查找机制本身就没跟上扩展名变长 (AGENTS.md §4.2 第4类"固定宽度切片而非扩展名操作",这次是同一类 bug 在上游自己的 .c-命名树里原本"恰好正确",被本项目的重命名 直接触发)。secure/daemons/cmd_d.lpc 的 cache_dir() 用 get_dir(dir+"?*.lpc") 拿到文件名后用 $1[0..<3] 砍掉扩展名—— 这个切片宽度是为两个字符的 .c 算的(<3 在这个驱动的区间 语法里表示"从末尾数第 3 个字符(含)到开头",对 5 字节的 foo.c 算出 foo),改名后文件变成 4 字节的 .lpc,同一个 <3 切出 score.l 这种带垃圾尾巴的"命令名",导致几乎所有裸词形式的 巫师/玩家命令全部失效——score、inventory 等在 shell 里输入 会报 I don't know the verb 'score',只有写全路径 /cmds/player/score 才能跑(score.lpc 本身完全正常,纯粹是 路径匹配层面的缓存 key 算错了)。这是这次转换里影响面最大的一个 bug——不修的话整个游戏对话式命令行基本不可用。同一切片模式在 cmd_d.lpc 另一处(smart_arg_parsing 里按绝对路径匹配后取 cmd_name)、daemons/spell_d.lpc(重建法术目录缓存)、 daemons/quest_d.lpc(显式 if (base[<2..]==".lpc") 判断之后却用 两字节宽度去砍)里各出现一次,一并改成 <5(对应 4 字节的 .lpc)。已用真实驱动重新启动、注册、score/inventory/update 等裸词命令验证全部恢复正常。全仓库还有 27 处同形态的 [0..<3] 切片,逐一读取上下文后确认其余都是无关的字符串截断(去掉末尾逗号、 "\n\n"、"::" 等),或是针对 .o(SAVE_EXTENSION,仍是 2 字节, 不受影响)——只有以上 4 处真的和 .c→.lpc 改名相关。 不提交上游:这是本项目自己 .c→.lpc 改名操作触发的问题, 上游 fluffos/lima/limalib/lima 原生文件名仍是两字节的 .c, [0..<3] 在那里本来就是对的——这是本项目的转换产物,不是 lima 自己源码里的 bug,不属于这次 upstream PR 审计范围(2026-08-31)。 2. set_droppable() 参数类型声明过窄:std/modules/m_gettable.lpc 自己的文档注释写"传函数或字符串等效于调用 set_dropmsg()",函数体 也确实用 functionp(g) || stringp(g) 判断,但签名却声明成 void set_droppable(int g)——这个驱动的静态类型检查因此拒绝 domains/std/lima/directory.lpc/workroom_ob.lpc(后者正是新巫师 自己的 workroom)里传字符串自定义"拿不下来"提示语的调用。参数类型 改成 mixed g,和函数体实际行为、文档注释保持一致。 已提交上游:fluffos/lima 已于 2024-10-08 被官方归档只读 (gh pr create 直接报 GraphQL 错误"Repository was archived so is read-only"),无法开 PR。经核实,同一 LIMA 社区(同一 Discord 频道 #LIMA、同名品牌"limamudlib.dev")已迁移到活跃维护的 limalib/lima(非 fork 关系但代码血缘一致,git clone 核对确认 m_gettable.c/m_exit.c/usermenu.c 三处逐字节相同,同一 bug 原样存在),已改为向 limalib/lima 提交: limalib/lima#49。 3. private 跨文件继承调用(AGENTS.md §7.48 标准案例): std/modules/m_exit.lpc 的 eval_dest() 声明为 private,却被 继承它的 std/modules/m_exit_obj.lpc 直接调用——这个驱动对 private 的强制比老 MudOS 严格(继承链内也不能跨文件调),改成 protected(比 private 宽松,不会引入新 bug,只会修好已经存在的 非法调用错误)。 已提交上游:同上,fluffos/lima 已归档,改为 limalib/lima#50。 4. 含内联注释的宏参数展开异常:daemons/spell_d.lpc 里 ENSURE(spell_name /* 注释 */) 这种"宏参数里带 C 风格块注释"的写法 触发这个驱动预处理器的一个真实 bug——ENSURE 宏没有正常展开,报 Undefined function ENSURE 后级联出语法错误,导致 daemons/spell_d.lpc 本身以及依赖它的 6 个法术文件全部编译失败 (fireball/magic_arrow/unlock/test/ale 及 stock-mage 分支)。 已构造最小复现(两行 LPC:ENSURE(x /* c */);)确认与具体文件内容 无关,是宏参数解析器本身在"参数里含 /* */"时的通用问题——不打算在 本项目范围内修驱动本身(那是 fluffos/fluffos 的事,需要走 PR), 采用等价、保留原意的写法规避:把注释挪到宏调用外面 (ENSURE(spell_name); /* 注释 */),两处均已修复并验证。 不属于本次 lima queue 审计范围:这是 fluffos/fluffos 驱动本身的 bug,不是 fluffos/lima/limalib/lima 源码的 bug—— 本次任务(2026-08-31)只审计 lima 的 mudlib 源码是否需要提 PR, 驱动仓库层面的 PR 不在这次的范围内,未提交。 5. 含"开括号后紧跟内容"的 heredoc(@TAG content...)在这个驱动的 词法分析器里同样有真实问题:std/race/troll.lpc (@LONG Trolls are...)和 WWW/cgi/autodoc.lpc(7 处 @END<title>...)在开始标记同一行紧跟正文时报 End of file in text block——已用两行最小复现确认(@LONG hi\nthis is\na test\nLONG; 能编译,把首行改成 @LONG hi 同一行接内容就不能)。 同样倾向于判定为驱动词法分析器的真实 bug(不在本项目范围内修驱动, fluffos/fluffos 需要走 PR),mudlib 侧用等价、保留原文内容的写法 规避:把开始标记单独放一行,正文从下一行开始。troll.lpc 已完全 修好(唯一一处),autodoc.lpc 的 7 处开始标记都修了,但该文件的 HTML 内容里结束标记本身也不是顶格(比如 <UL> END +,END 前面还有真实 HTML 内容),而这个驱动的 heredoc 结束标记 必须是整行的最前面——这是另一类、更深的方言差异(老式 LPC 允许 结束标记出现在缩进/行内任意位置,这个驱动不允许),不是简单挪一行 能解决的,autodoc.lpc 因此仍然编译失败。鉴于它只是一个默认未启用 (http_d 在 preload 里被注释掉)的巫师专用 CGI 文档生成工具, 不影响核心游玩,这里记录下来但不再深入修——留给以后需要真正用到 web 文档生成功能时再处理。 不属于本次 lima queue 审计范围:同上一条,这是 fluffos/fluffos 驱动词法分析器本身的 bug,不是 lima 源码的 bug,本次审计 (2026-08-31)未提交驱动仓库层面的 PR。 6. 赋值/比较符打错:obj/usermenu/usermenu.lpc 的 confirm_decision()——if (dec == "yes" || dec = "y"),第二个条件 是赋值不是比较,导致这个函数永远返回真,无视用户实际输入的 "确认删除角色"对话框形同虚设(remove_char 流程里的 yes/no 确认, 不管输入什么都会被当作"是")。改成 ==。 已提交上游:同上两条,fluffos/lima 已归档,改为 limalib/lima#51。

§2.2 On-sight checklist 结论

管理员账号播种

Lima 自己的安全模型是"能力(capability)+ 领域(domain)"式的,没有 本项目其它 lib 常见的"adm/etc/wizlist"或"(admin) 状态字符串", 而是 /data/secure/access.o(SECURE_D 的存档文件, ACCESS_SAVE="/data/secure/access")里的 wizards 和 domains["admin"] 两个 mapping。上游仓库自带的原始档案是一个"全新 安装、什么人都没有"的空白状态(wizards ([]),domains["admin"] ([]))——这本来就是设计使然:secure/user/sw_body.lpc 的 sw_body_handle_new_logon() 里有一段"没有任何 admin 时,把第一个创建 角色的人自动提升为 admin"的引导逻辑(自举机制),正常情况下靠"第一个 真人巫师创建角色"就能完成播种,不需要额外操作。

但本项目的标准约定是固定的 fluffos/Mud@2026 账号,而不是"谁先手快 谁当家"——为了不依赖"谁第一个连上来"这种时序,直接编辑 data/secure/access.o(纯文本的 save_object() 格式,逐行 变量名 值 ),把 wizards 加上 "fluffos":1,把 domains["admin"] 加上 "fluffos":2(2 = 领域领主/lord,和 add_domain_member(domain, member, lord) 的语义一致)。这样一来, fluffos 一注册就直接是 admin,同时因为 domains["admin"] 已经非空, "自动提升第一个人"的引导逻辑不会再对后续任何测试账号触发(已用 traveler/finaltest 两个测试角色验证:它们注册后只拿到普通 AUTO_WIZ 赠送的 Wizard 权限,who 里 Role 显示 Wizard,不是 Admin——引导逻辑确实只认第一次)。data/secure/access_backup.o (save_data() 顺带写的备份文件,启动时不读取)同步更新以保持一致, 测试期间产生的其它账号相关残留(traveler/finaltest 的存档、 data/referrals、data/secure/LOG、data/daemons/last_login.o)已 清理,只留 fluffos 一个种子账号。

验证:fluffos 登录后 who 显示 Role: Admin;update /std/race/human(读+写都要过 ACL 的规范检验)和 admtool (进入管理菜单,能看到 1 - priv 1 选项)均成功,说明播种的不只是 "看起来像 admin"的展示状态,写权限确实生效。

已知但未修的内容/示例缺口(按项目惯例:不是程序 bug 就不动)

编译扫描(lpcc --batch,1095 个 .lpc 文件)在上述 bug 修复后还剩 38 个失败,逐一读取错误信息确认全部是内容/示例本身的缺口,不是 驱动兼容性问题:

定义完成的验证记录

用 scripts/mudclient.py 通过真实驱动完整走过三轮独立账号 (questor→wanderer→traveler,以及最终的 fluffos/finaltest): 注册(id/密码/性别/邮箱/真实姓名/主页/推荐来源全部走完)→ 用户菜单 c 创建角色 → 种族选择(human/elf 两种都测过)→ s 选择角色 → p 进入游戏 → 落地在 Grand Hall(Lima Bean 巫师聚会场所,游戏内自带的 "新手/巫师起始房间",因为 AUTO_WIZ 的关系每个新角色都从这里开始)→ look(房间描述正确重复)→ score(ASCII-art 状态面板,含种族对应的 六维属性、经验、卡玛值)→ inventory("You are empty handed.")→ who(正确列出在线角色和权限等级)→ quit(正常退出, [announce] 频道正确广播离线消息,回到用户菜单,debug.log 全程 无报错)。管理员账号额外验证 update/admtool。整个流程干净、无 崩溃、无静默失败。

附录(2026-08-26):limalib/lima("modernized LIMA" fork)调查结论 —— 负面结果,未新建 lib

外部研究提到一个 GitHub 仓库 limalib/lima(<https://github.com/limalib/lima>, 自称 "Updated, maintained, modernized LIMA mudlib updated to run latest FluffOS driver. Kept in the original LIMA spirit.",官网 limamudlib.dev,文档 docs.limamudlib.dev,HEAD eb25dc6,2025-10-27), 怀疑这个"现代化"分支是否已经从根本上摆脱了 §7.46 记录的驱动编译期开关 冲突(本条目上文已经用的那五项:NO_LIGHT/NO_ADD_ACTION/NO_WIZARDS/ undef OLD_ED/undef PACKAGE_UIDS)。本次任务专门验证了这一点—— 结论:没有,问题原样保留,limalib/lima 和 libs/lima 用的 fluffos/lima 一样,需要同一套专用驱动才能启动,不构成新的可独立 onboard 的 lib,因此本次只写这条负面结论,不新建 libs/<slug>/ 目录。

验证过程

1. 代码血缘对比:limalib/lima 和本 lib 来源的 fluffos/lima 是两个独立的 GitHub 组织下的仓库(limalib/lima 的 defaultBranchRef/parent 显示不是 fluffos/lima 的 fork), 但目录结构和核心文件几乎一致——两边的 secure/check_config.c 逐行 diff 下来,除了版权头文字和版本号字符串("MudOS"→"FluffOS"、 config.lima→config.mud)之外,need() 检查列表完全相同, 证明是同一 LIMA 血缘的演进版本,不是独立重写。文件数 limalib/lima 1534 个 vs fluffos/lima 1969 个(前者砍掉了不少 demo 内容,新增了 obj/admtool、obj/tasktool、std/race、 cmds/guild 等框架性目录),属于同一代码库的功能增补/精简, 不是从零重写的替代架构。 2. 直接读 limalib/lima 自带的 lib/secure/check_config.c—— 这就是 LIMA 系 mudlib 用来拒绝在不兼容驱动上启动的自检文件。 除了沿用 §7.46 记录的五项(NO_LIGHT/NO_ADD_ACTION/NO_WIZARDS/ undef OLD_ED/undef PACKAGE_UIDS),还新增了几项检查 (SANE_EXPLODE_STRING 需定义、CAST_CALL_OTHERS 需未定义、 OLD_RANGE_BEHAVIOR 需未定义、MUDLIB_ERROR_HANDLER 需定义、 ARRAY_RESERVED_WORD 需未定义、PACKAGE_CONTRIB/PACKAGE_PARSER 需定义)——但这些新增项本项目共享驱动(~/src/fluffos/build-debug) 原本就满足,不构成新的冲突点;真正冲突的仍然是那五项经典要求, 一个字都没变。 3. 实测启动:用本项目的 scripts/convert_lib.sh 把 limalib/lima 的 lib/ 转成 .lpc(UTF-8 源码,1407 文件已是 UTF-8、0 需要转码,127 个二进制文件跳过,行为和 fluffos/lima 转换时几乎一样),配上一份仿照本 lib config.fluffos 改路径/端口 的临时配置,直接用本项目共享的标准驱动 (~/src/fluffos/build-debug/src/driver,未加任何特殊 flag)尝试 启动——simul_efun/master 加载阶段立即被 check_config.lpc 的 create() 用 error() 中止,实际报错文本:

`` *Bad driver configuration: ********************************************************** * You have incorrectly compiled the FluffOS driver. This * * driver is not compatible with the LIMA mudlib. Please * * make the following changes to 'local_options' ) in the * * driver source, and recompile. * ******************************************************** #define NO_LIGHT is required for LIMA libs. #define NO_ADD_ACTION is required for LIMA libs. #define NO_WIZARDS is required for LIMA libs. #undef OLD_ED is required for LIMA libs. #undef PACKAGE_UIDS is required for LIMA libs. ******************************************************** The simul_efun (/secure/simul_efun) and master (/secure/master) objects must be loadable. ``

和 §7.46/本 lib 上文记录的 fluffos/lima 症状完全一致——不是 "modernized" 架构层面移除了这个假设,只是版本号/驱动兼容性提示文字 更新了而已(README 里"modernized"、"run latest FluffOS driver" 说的是能跟着最新版 fluffos 驱动源码(adm/dist/fluffos 子模块跟踪 fluffos/fluffos.git)编译,而不是摆脱 NO_LIGHT/NO_ADD_ACTION/ NO_WIZARDS/OLD_ED/PACKAGE_UIDS 这套架构假设)。

有用的推论(供未来参考)

limalib/lima 要求的五项和本 lib 已经解决的 fluffos/lima 要求 完全相同,所以本 session 已经建好的 ~/src/fluffos-lima/build-debug(worktree,见上文"§7.46 的后续"一节) 理论上不需要任何改动就能同时满足 limalib/lima 的 check_config.c—— 如果未来真的要把 limalib/lima 也收进本项目(例如作为它自己独立的 lib,因为它的功能集/demo 内容和 fluffos/lima 已经有实质差异,不是 纯粹的重复),可以直接复用这个驱动 worktree,不必重新摸索 flag 组合。 但这次任务的范围只是回答"是否解决了驱动 flag 冲突"这一个问题, 结论是否定的,所以没有走完整的 onboard 流程(没有 convert_lib.sh 之后的编译修复循环、没有真实注册验证、没有分配 number/port、 没有新建 libs/<slug>/ 目录)。

深度功能测试 / Deep functional test (2026-08-27, §10.7)

这个 lib 之前只做过"能启动/编译"级别的验证(见上文"定义完成的验证 记录"一节)——本轮是第一次按 AGENTS.md §10.7 的方法论做一次真正连续 的真人playthrough(真实注册 → 探索 → 战斗 → 门派/公会交互 → quit/隔一段真实时间后重连 → 查日志),用专用的 ~/src/fluffos-lima worktree 驱动(已存在,直接复用,未重新编译)。

Lima 本身是 FluffOS 官方的框架/演示 mudlib,不是一款有完整门派/任务 系统的"游戏"——help newbie 就是原版英文 Zork 式通用教程,核心可测 系统是:注册/建角、AUTO_WIZ巫师身份、导航、skills(战斗中自动 熟练度成长)、kill战斗、GUILD_GUARD门禁、talk toNPC 对话、 quests、quit/重连。测试角色:账号 TestHero/TestPass123,角色 Aidan(human),全程走过注册六项资料 → 建角(种族选择)→ s选中 → p进入游戏,落地 Grand Hall;另建了一个非管理员的 questfinal/ Rowan 角色专门复测下面的 bug 修复(确认不是只有先前角色的残留状态 碰巧绕过了问题)——两个测试账号连同它们的存档在验证完成后都已按本 项目惯例清理(data/players/{a,r}/、data/links/{t,q}/、以及 GUILD_D 因这次测试而首次落盘的 data/daemons/guild.o),只保留种子 fluffos 管理员账号;data/secure/{access,access_backup}.o、 data/secure/LOG、data/daemons/last_login.o、data/news/* 里因登录/公会daemon初始化产生的残留字段也已用 git checkout 还原,最终 git status 只剩两处真实代码改动。

发现并修复的真实 bug

1. std/guild_guard.lpc 的 handle_blocks() 对一个从未真正被 GUILD_D 定义过的公会名,会把驱动的未捕获 error() 直接甩给玩家, 导致"从被公会守卫挡住的房间试图离开"这个完全普通的移动动作每次必 崩溃。

2. daemons/guild_d.lpc 的 load_missions()/load_favors() 两个函数在 GUILD_D 第一次被懒加载时必定崩溃,因为它们引用的两个 目录在这个仓库里根本不存在——修复 #1 直接触发并暴露了这个第二层 bug。

测试覆盖:干净通过的部分

已知但未修的观察项(不确定是否算 bug,按惯例只记录不改)

Upstream PR 审计(2026-08-31,standing queue 项目)

按 project_fluffos_org_upstream_pr_queue.md 的标准流程,把上文"编译 期发现的真实 bug"一节里已经在本地 work/ 修好的 6 处 bug 逐一核对 是否需要(也能否)提交回上游:

- 核实同一 LIMA 社区已迁移到 github.com/limalib/lima(非 fluffos/lima 的 GitHub fork 关系,但同一 Discord #LIMA 频道、 同一品牌"limamudlib.dev",且本文件前面"附录(2026-08-26)"一节 已经做过一次代码血缘对比,确认是同一 LIMA 代码库的延续版本,非 独立重写)——直接 clone 核对,std/modules/m_gettable.c/ std/modules/m_exit.c/obj/usermenu/usermenu.c 三个文件对应位置的三处 bug 逐字节存在,尚未修复(master/ stable 两个分支都确认过),且该仓库尚无任何提及这三处的 open/closed PR。判定为这三处 fluffos-org 遗留 bug 的实际可提交 对象,改为向 limalib/lima 提交: PR #49(set_droppable)、 PR #50(eval_dest private→protected)、 PR #51(confirm_decision 赋值/比较符)。

Shop (2026-09-04 librarian slice)

Biff's General Store (/domains/std/Shop) is live. Seeded admin fluffos / Mud@2026 (user menu p to enter; character already selected) started with empty pockets (money → "only lint"). eval this_body()->add_money("gold", 50.0) credited 50 gold. goto /domains/std/Shop, list showed Biff's stock: amber ale and red apple at "no gold", dull sword at 1 platinum, red/blue sticks at 2 copper. buy apple from biff completed but is a zero-price food (query_value() from uneaten eats × heal, heal is 0). buy sword from biff was a real paid sale: "You buy a dull sword for 1 platinum. You give 10 gold to Biff the Shopkeeper." Inventory: dull sword + red apple. money 50→40 gold (platinum factor 10). No programming bug.

拜师: N/A. This is a FluffOS reference/demo lib, not a sect/family game. Live probes: apprentice / become / 拜师 → "I don't know the verb"; join → parser "A valiant attempt"; help guild has no player topic (only wizard guild_d). GUILD_D stock guilds are still not loaded (same as the 2026-08-27 guild_guard/sorcery finding). School rooms are a wizard-coding tutorial, not an apprenticeship.

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

lib/include/mudlib.h (so every file that includes it) and the three example rooms lib/domains/std/room/{Attic,Church, wiz_hall}.c open with #pragma no_warnings upstream. The catalog's playable tree must not carry it (AGENTS.md), so patch 0003 removes the four lines and 0004–0006 fix what they hid. With the pragma gone the Lima-driver lpcc (~/src/fluffos-lima/build-debug/src/lpcc) printed 549 distinct diagnostics over the 1174 objects; on the patched tree there are 8, all errors in files that do not compile, and 0 warnings (lpcc --batch over every .c object of a fresh upstream copy with the six patches applied: 1159 load, 15 do not).

The patches are split by directory so that a pin bump that breaks one still leaves the others applying: 0004 is std/ (77 files), 0005 daemons/ secure/ obj/ trans/ WWW/ (53), 0006 cmds/ domains/ contrib/ (87). They were made in a scratch repo (scripts/submodule_patch_scratch.py build lima --below 3, then the usual lpc_warnings.py / lpc_name_winner.py / lpc_rename_ident.py / lpc_fix_unused_init.py passes on its .lpc-named copy, then export lima --stage std-fix-compile-warnings:std/ ...); the same two commands regenerate them against a new pin. rebase_upstreams.py holds a pin when a patch stops applying.

Root causes (KB 04 §6.10, the Lima-family classes again):

Live on a pristine tree with the Lima driver, baseline (upstream with patches 0001/0002) against the patched tree, the same 48-command battery as the seeded fluffos admin: clones of the troll, goblin, dragon, wolf, test dummy, Harry, Robert, four weapons, water, ale, keg, tankard, hamburger, candle, backpack, trainer, bank accountant, horse, camaro, cooking station, delivery boy and uniform; wield / drink / eat / light; smell and listen in the kitchen; the cave and the wizard room; help. The transcripts agree except for two objects that now load (special_longsword, Sloping_Tunnel); the console shows no warning and no error. Pre-existing, not touched: fighter_master raises "Non-existant guild - stock-fighter" in guild_d, and m_react's add_script() is upstream's own stub ("Don't you wish this function wasn't missing").

Left: 8 diagnostics in the files that do not compile: WWW/cgi/autodoc.c (inline @END text blocks), contrib/marriage/ {finger,finger_d}.c (the class member spouse the contrib expects the core finger class to have), domains/std/object/portable_board.c (BOARD_OB is defined by nothing in the tree) and std/behaviour/test.c (a three-line scratch file); another eleven objects fail to load in a batch without a diagnostic (they need arguments or a user: virtual rooms, menus, mailers, contrib/bboard/news_d).

- daemons/loot_d.lpc: count *= level / 16.0 converted the multiplier first, so below level 16 the loot count was 0. It is now count = to_int(count * (level / 16.0)). - std/adversary/condition.lpc: the drunk stance penalty STANCE_IMPACT_AT_30 (1.2) went into an int and had no effect. It is now a float, and the skill test argument uses to_int(). - std/container.lpc receive_object() and std/fluid.lpc will_fit() kept float masses in ints, so an item under 1 weighed nothing in the capacity check. They now use float receivers. - The rest spell their truncation to_int(), with the same values. - Gameplay-visible: low-level loot drops, the drunk stance penalty, and container capacity for light items now work as designed. - The 17 changed objects load before and after. The patch applies on its own on the pin and after 0001–0006.

深度功能测试(2026-10-21,play 扫描):以种子管理员 fluffos 跑 mortal 和 wizard 命令,clone 补了一条(patches/0008)

用 lima 专用驱动登录(PLAY_DRIVER=~/src/fluffos-lima/build-debug/src/driver;scripts/pristine_tree.sh 现在能把子模块工作树 + patches/ + overlay/ 搭成测试树,scripts/play_login.py 认得 [spcrwq] 菜单并选 p),scripts/play_sweep_lib.py lima --account fluffos:Mud@2026:mortal 命令 160 个(157 行),--wiz 再加 235 行,控制台只有两条。clone(wiz):没有参数又没有 cwf 时 new(arg[0]) 的参数是 0,FluffOS 报 Bad argument 1 to EFUN new() Expected: string, Got: int(0)(上游依赖旧驱动里 new(0) 答 0 再走 “Failed to load file.”);patches/0008-wiz-clone-without-a-file-name.patch 先检查 stringp(arg[0]),没有时打印 clone [filename],实测 0 条发现。treefor:是扫描喂给 eval 的垃圾输入编译出的警告(/tmp/EVAL.c),不是 lib 的问题。其余 needed-escape(分页器、编辑器、对话)和 1–2 条 stuck-reconnect 不是错误。

data/secure/ 没有人建:master 的 valid_write() / valid_read() 拒绝分支往 ACCESS_LOG 写日志(2026-10-22,KB 06 §7.358)

lib/secure/master.c 每次拒绝一个路径(valid_write() / valid_read())都往 ACCESS_LOG(/data/secure/LOG)写一行;干净的 git clone 里没有 data/secure/,拒绝分支里的 write_file() 就抛错。data/secure 已记进 scripts/wasm_keep_dirs.txt(lpc_keep_log_dirs.py --root=data;该扫描列出的另外 29 个 data/ 目录都读过了:留言板和彩票精灵在 save() / assure_file() 里自己建目录,passwordd 用 assure_file(),votebox 在巫师大厅,rwho_a 只在联网时写)。