info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
侠客英雄传 II/III「欢乐天地」(别名"武艺:无尽豪情"),是一个把金庸武侠、日本奇幻小说《罗德岛战记》和漫画元素熔于一炉的世界观:门派场景沿用华山、武当、峨嵋、昆仑、丐帮、少林等传统武侠格局,但 `d/lodoss/` 区域原样搬入了《罗德岛战记》里阿拉尼亚(Alania)、弗雷姆(Frame)等异国王国的地理设定,是这批档案里最跳脱传统武侠框架的一个;角色创建除了选性别,还要在 0-5 号种族里挑一个,而不是直接加入单一汉人门派体系,呼应这种跨文化混搭的世界观。
English
Legend of the Chivalrous Hero II/III, subtitled "Happy Land" and also styled "Wuyi: Boundless Heroism." The sect map is standard Jin Yong wuxia (Huashan, Wudang, Emei, Kunlun, the Beggars' Sect, Shaolin, and more), but one zone, d/lodoss/, drops in the geography of the Japanese fantasy novel Record of Lodoss War almost verbatim -- the kingdoms of Alania and Frame -- making this one of the more genre-blending archives in the collection. Character creation reflects the mashup: after gender, players also pick a race (0-5) rather than joining a single Han-Chinese sect hierarchy from the start.
README
内容亮点
- 世界观细节见上方简介:金庸门派场景 + 《罗德岛战记》地理 + 种族选择的三合一混搭。
本次修复的关键 bug
§8.1 GBK 字节区间 is_chinese() bug,出现在两个地方:
1. adm/daemons/chinesed.lpc 的 is_chinese()(CHINESE_D 真正
的实现,logind.lpc 委托它做判断)只检查 str[0] 的原始字节区
间,还要求总长度是偶数,在这个驱动按 UTF8 码点索引字符串的情况
下永远判定失败。
2. adm/daemons/logind.lpc 自己的 check_legal_name() 也有一样的
i%2 奇偶门槛,长度上限是字节数写法(2-12,原意是提示文字里说
的"1 到 6 个中文字")。
已把 chinesed.lpc 的 is_chinese() 改成逐码点 0x4e00-0x9fff 区
间检查,check_legal_name() 改成逐字符呼叫
CHINESE_D->is_chinese(name[i..i]),长度上限改成字符数 1-6。
测试注意事项
性别/种族选择紧跟在 /std/char.lpc 首次编译(带出
feature/damage.lpc/feature/skill.lpc 等一大堆编译警告)之后,
和 xhcii 记录过的情况一样,会有测试客户端的计时竞态——"m"偶尔
会被当成无法识别的指令,不管 --idle 设多长。多送一次"m"/"0"就
能稳定通过;已经用一次完全干净、注册过程零异常的成功运行确认过这
纯粹是测试工具的计时问题,不是 mudlib 本身的缺陷。
管理员账号 / Admin account
- ID:
fluffos - 密码 / Password: 注册时自设
- 权限 / Level:
(admin),通过/adm/etc/wizlist授予,wizlist命令确认显示"目前权限:(admin)",且fluffos出现在 最高权限组里。
警告:对外公开架设前请务必修改此密码。
本地运行
cd libs/xkyxciii
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40118。
NOTES · 移植与修复记录
WASM 修复摘要(迁移自 meta.json 的 group_note)
侠客英雄传 II/III("欢乐天地",五邑无尽豪情),一个金庸小说/欧洲中世纪/漫画风混搭的世界观设定。WASM 修复了两处经典的 §8.1 GBK 字节区间 is_chinese() bug:adm/daemons/chinesed.lpc 的 is_chinese()(CHINESE_D 委托的真正实现——只检查 str[0] 的原始字节区间,还要求总长度是偶数,在 UTF8 码点索引下永远失败)和 adm/daemons/logind.lpc 自己的 check_legal_name()(i%2 奇偶门槛,加上按字节数算的长度界限 2-12,本意是按用户提示"1 到 6 个中文字"应为 1-6 个字符)。已把 chinesed.lpc 的 is_chinese() 重写成逐码点 0x4e00-0x9fff 区间循环,check_legal_name() 改成通过 CHINESE_D->is_chinese(name[i..i]) 逐字符检查,长度界限修正为 1-6。管理员账号播种:fluffos (admin) 加入 adm/etc/wizlist(已通过源码确认 SECURITY_D 真的会在开机时读取 WIZLIST;wiz_levels 顶层是 (admin))。注册流程在多次连续的 WASM 客户端会话里完整验证过:英文 id→y/n 确认创建→中文名字→密码+确认→性别(m/f)→种族选择(0-5)→带着完整角色属性表和可用的 score/look 指令进入游戏世界,全程没有任何意外错误。测试笔记:性别/种族选择正好接在 /std/char.lpc 首次编译的大量负荷之后(加载 feature/damage.lpc、feature/skill.lpc 等),引发了和 xhcii 上记载过的同一类测试工具时序竞争——不论 --idle 设多少,'m' 有时会被当作无法识别的输入吞掉;补发一次重复的 'm'/'0' 能可靠绕过,这是客户端时序上的假象,不是 mudlib 本身的缺陷(一次完全干净的运行在注册流程里没有捕获到任何异常,可以证实这一点)。管理员权限已直接通过 'wizlist' 指令输出确认"目前权限:(admin)",fluffos 出现在最高阶层里。LPC 格式化工具对全部 3178 个档案运行(写入 3145 个,12 个报错,21 个未改动)。没有 :: 父类呼叫拆分命中,没有 CJK 重新加空格命中,没有 case 标签带尾随注释的候选,这份快照里没有 map.lpc 档案。格式化后用同样的完整注册流程重新验证过——干净,管理员权限依然是 (admin)。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 4 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
深度功能测试(2026-08-17,round one)——严重 bug:AGENTS.md §7.11 类,新角色永久没有出生房间
第一次对这份档案做完整 §10.7 深度游玩测试,发现并修复一个严重的、 每个新角色都会撞上的 bug——不计其数(这份档案没有任何存活的角色曾 经真正进入过一个房间)。
- 症状:无论用普通玩家账号还是巫师账号,走完完整注册流程(英 文id→中文名字→密码→电子邮件→性别→种族)之后,
score正常显示 完整属性面板,但look只回传cmds/std/look.lpc自己对environment(me)为空时的兜底文字"你的四周灰蒙蒙地一片,什麽也 没有。"——角色事实上从未被放进任何房间。走east(起始房间自 己声明的真实出口)也只得到"什麼?",goto(巫师指令)在这个角 色身上执行时会因为environment(me)是 0 而在cmds/imm/goto.lpc第 11 行的file_name(where_me)抛出 "*Bad argument 1 to file_name()"。断线重连、换用全新账号、换用 巫师账号,症状完全一致,可稳定复现。 - 根因:
adm/daemons/logind.lpc的enter_world(),在cat(MOTD)/房间选择/user->move(startroom)那一段代码之前, 有一段"经验值/潜能值异常侦测"逻辑——combat_exp和exp差值 ≥1000,或potential和pot差值 ≥500,就会log_file("nosave/WARNING", ...),完全没有catch()保护。 这份档案的adm/simul_efun/file.lpc里log_file()的实现是 裸write_file(LOG_DIR + file, text),同样没有目录存在性检查; 而这份档案的原始压缩包从未打包过/log/nosave/这个目录。 结果就是:几乎每一个新创建的角色(combat_exp/exp、potential/pot默认值的落差天然就会超过这两个阈值)在enter_world()执行到这个日志调用时就会因为 "Wrong permissions for opening file /log/nosave/WARNING for append" 而未捕获地中止整个函式——包括它之后的一切,尤其是user->move(startroom)。角色从此永久卡在没有环境的状态,score等不依赖environment()的指令仍然正常,制造出"部分功 能正常,部分彻底失效"的迷惑现象。这正是 AGENTS.md §7.11 已经归 档的"未打包的运行时目录 + 无保护的 write_file()/log_file()"类, 但打击面比该条目里记录过的任何一个先例都更广——它不是命中某个 边角功能,而是命中每一个新角色的世界入口本身。 - 修复:
adm/simul_efun/file.lpc里log_file()补一行assure_file(LOG_DIR + file);(这份档案自己已经有assure_file()这个帮助函式,委托给FILE_D->assure_file()), 和 AGENTS.md §7.11 里xajhxo那个实例用的是完全一致的修复手 法。由于assure_file()在同一个档案里的文字位置晚于log_file(),这个编译器不会自动解析后向引用(和 §7.11xajhxo条目记录的现象完全一样)——额外加了一行前向声明void assure_file(string file);,否则编译报 "Undefined function assure_file" 并直接打断整个simul_efun的载入(连驱动开机都会失败,lpcc_check.sh全档 案批量编译检查直接从 total=3178 变成 total=0)。 - 现场验证:重开一个全新驱动进程,两个完全独立的角色(普通 玩家
qintestz/秦侠三,和管理员fluffos/巫侠)都干净地 走完注册流程,look正确显示起始房间"如何移动"(新手教程室) 的完整描述、出口east、以及会主动打招呼的 NPC"小花狗";debug.log全程零 "Wrong permissions"/"nosave" 记录。用巫师 账号goto到有主动攻击型 NPC 的场景(扬州"异娼庙"的"疯狗"), 完整交手数十回合,攻防/命中/闪避判定全部正常,疯狗被击晕 ("跌在地上一动也不动了")。因为这个 bug 已经严重到本轮大部分 时间都花在定位它上,死亡/复活循环和留言板发帖本轮未覆盖,留给 下一轮。 - 和早前 WASM 阶段记录的矛盾:本文件顶部的 WASM 修复摘要写 着"带着完整角色属性表和可用的 score/look 指令进入游戏世界,全 程没有任何意外错误"——这条记录和本轮观察到的情况矛盾。由于这 段"经验值异常侦测"逻辑是基于角色具体属性差值的条件触发,不 是每次注册都 100% 必然命中,推测早前那次测试用的角色属性差值凑 巧落在阈值以下,侥幸没有撞上——不代表这份档案曾经真的对所有角 色都安全过。这也是本次能找到这个 bug 的关键:早前的测试只覆盖 了"注册流程走没走通",没有像本轮这样进一步验证"注册完之后
look是否真的显示了一个房间"。
深度功能测试(2026-08-17,round two)——补完死亡/复活、留言板,另发现并修复两个新问题
补完上一轮留下的死亡/复活循环和留言板发帖测试,过程中额外发现并 修复两个此前未覆盖到的问题。
- AGENTS.md §7.90 类新实例,
/d/jiangjunfu/main-05/main-06真实的 eval-cost 崩溃:上一轮lpcc_check.sh批量编译扫描曾 在这两个房间报过 eval-cost 命中,当时判断优先级不够、未深入调 查(详见feedback_lpcc_sweep_eval_cost_artifact的方法论教 训——批量编译的共享 eval-cost 预算可能对本身廉价的档案产生假阳 性)。本轮用巫师账号goto /d/jiangjunfu/main-05复现,确认是 真实崩溃而非批量编译假象:"执行时段错误:*Too long evaluation. Execution aborted.",崩在房间create()载入多个"将军"NPC、每 个 NPC 的create()又要wear()装备的连锁编译成本上。这份档 案的config.fluffos和大多数本项目档案一样用着maximum evaluation cost : 700000(本项目最常见的模板默认值, 但同时也是本项目实际使用范围里偏低的一档)。修复:按 AGENTS.md §7.90 已确立的手法,调到5000000(本项目内 30+ 档 案已经在用的同一个值)。重启驱动后现场验证:main-05和main-06两个房间都能干净载入,NPC 列表正常显示,debug.log里零 "Too long evaluation"/"cost limit reached" 记 录。 cmds/imm/goto.lpc的file_name(environment(me))未做空值 保护:本轮为搭建死亡测试环境,用一个刚触发过上一轮 bug 的 (测试用)巫师账号做goto时意外复现——environment(me)为 空时,goto函式第 11 行无条件对其取file_name(),在检查arg参数之前就先崩了("*Bad argument 1 to file_name(); Expected: object Got: 0")。这个变量ll_me只在函式末尾的一条日志格式化 字串里用到,完全没必要在函式最前面无条件计算——后果是任何巫师 一旦自己的环境意外变成空(不论是本档案 §7.11 那类 bug 还是其他 未知原因),goto本身作为"自救"工具反而永久失效,连去别的房 间重新落脚都做不到。修复:判空后再取file_name()(where_me ? (string)file_name(where_me) : "无环境")。跨库检 查同一段代码(ll_me = (string)file_name(where_me))只在xkyxciii和xkyx3b(很可能是同源姊妹档案)命中,命中率太低, 未作为跨库扫描立项,仅在本档案修复并记录,供以后遇到同源档案时 参考。- 死亡/复活循环现场验证:巫师账号在中央广场
clone出一个 "扬武将军"NPC,force <测试角色> to kill yangwu逼真实战斗一 击致死("秦侠死死了。"),重新连线后角色正确复活于REVIVE_ROOM(/d/center/chenghuangmiao,城隍庙),score显示的死亡惩罚符合预期:气血精气神清零至个位数、潜能 从 500 降到 251、经验从 4000 降到 3961、"死亡"计数器正确 +1。 全程无崩溃、无卡死、无未捕获错误。 - 留言板发帖/阅读现场验证:巫师账号在中央广场对
中央广场留言板执行post <标题>,走完编辑器交互 (.结束输入),提示"留言完毕。",read <序号>能正常读出 历史留言内容。debug.log全程干净。(测试产生的留言已从work/data/board/guangchang.o存档里撤销,不作为本次改动的一 部分提交。) - 本轮结论:round one 修复的 §7.11 类 bug 是这份档案迄今为止影响 面最大的问题;round two 补完的死亡/复活/留言板全部正常,另外顺 手确认并修复了两个此前搁置的小问题(真实的 §7.90 eval-cost 崩 溃、goto 自救失效)。这份档案目前没有已知未处理的发现。
深度功能测试(2026-08-18,round three)——发现并修复:信箱系统全域失效
本轮目标是比 round two 更深入地补完经济、拜师(宗门)、通信(邮
件)等此前未覆盖的子系统。重开全新驱动进程,用管理员账号
fluffos/Mud@2026(round two 留下的旧号密码未知/未追踪,已删
除重新注册)和一个全新普通玩家账号 qintestv/秦测试三
(纯字母 id——注意这份档案的 check_legal_id() 不允许数字,试
过带数字的 id 会被直接拒绝,这是设计限制不是 bug)分别验证。
- 注册流程回归:
qintestv完整走完英文id→y/n确认→中文名字 →密码+确认→电子邮件→性别→种族→进入游戏,look正确显示新手 培训室及出口、NPC,score属性正常——确认 round one 修复的 §7.11 类 bug(新角色永久没有出生房间)依然稳固,未回归。 - 经济系统(当铺)现场验证:
/d/center/hockshop当铺老板 (hockman,继承feature/pawnowner.lpc)的value、sell、pawn、redeem/ransom四个指令全部正常:value vase正确报价,sell vase按 80% 估值换钱,pawn vase按 40% 估值当出并给当票,ransom vase正确赎回物品并扣费。古玩 店curio_seller(继承feature/vendor.lpc)的list指令正常显示可购商品。全程debug.log干净。 - 拜师(宗门)机制现场验证:
goto /d/wudangshan/wd-lwc后对 真实宗门大师 NPC 宋远桥(song_yuanqiao.lpc)执行apprentice song yuanqiao(注意实际 id 是"song yuanqiao"带 空格,不是文件名的下划线写法,也不能用中文名"宋远桥"本身——feature/name.lpc的id()只认set_name()第二个参数指定 的 id 阵列,不会自动把第一个参数(显示名)加进去,这是这份档 案里所有具名角色的通用行为,不是 bug)。测试账号胆识 (cor)/定力(cps)都是 16,低于song_yuanqiao.lpc里attempt_apprentice()要求的 20 门槛,NPC 正确拒绝("学剑之 人必须胆大心细,依我看……的资质似乎不宜?")——这是源码里明确 的属性门槛判定,属于设计,不是 bug。apprentice cancel也正 常工作。 - 真实 bug:
adm/daemons/logind.lpc的enter_world()里一行 多余的destruct(mail_box)导致信箱/邮件系统对每一个玩家、每 一次登录都彻底失效:obj/misc/mailbox.lpc是一个完整的邮件 子系统(send/mail/forward/check/from/read/discard共 7 个指令,长描述里还专门列出了使用说明),设计上 应该在登录时挂到玩家身上并常驻(提供指令、在init()里报告 未读信件数)。但enter_world()里紧接着创建/取得这个信箱对 象之后,无条件对它调用了destruct(mail_box)——不管这个信箱 是刚创建的还是玩家早就有的、里面存了信的那个,一律当场销毁。 后果:init()里注册的add_action全部随对象销毁失效,玩家 当场就失去所有邮件指令,check/from/read/discard/mail/send/forward全部返回"什麼?"(未知指令)——现场用 管理员账号验证:修复前,全新登录后立即check/from/mail qintestz三个指令均返回"什麼?",i指令的物品栏列表里也完 全看不到信箱这个物件。信件本身的磁盘存档(F_SAVE,按USER_DIR + id[0..0] + "/" + id + "/mail"路径)不受影响(因 为存信/读信/丢信各自会显式调用save()),所以没有数据丢失风 险,但整个邮件功能对所有玩家、所有会话都是彻底哑火的——这是一 个全局性、每次必现的功能失效,不是边角案例。
- 修复:删掉 enter_world() 里那行多余的
destruct(mail_box);,保留前面创建/取得信箱并 move() 进
玩家身上的逻辑不变(这正是触发 init() 挂载指令、显示未读
信件数的必要步骤)。
- 现场验证:重启全新驱动进程,修复前先复现(check/
from/mail qintestz 均"什麼?",i 里没有信箱),应用修
复后重新登录,check/from 都能正常报告"你的信箱中目前没
有任何信件",i 里出现"巫侠的信箱 (mailbox)"物件;完整走了
一次 mail fluffos(给自己写信)→标题→正文→.结束→y
保留副本→check(正确显示信件列表)→read 1(正确显示标
题/寄信人/正文)→discard 1(正确丢弃,check 数量正确减
一)。全程 debug.log 零错误。(给自己写信且选择"保留副
本"会看到 2 封相同的信——这是 confirm_copy() 自留一份加
send_mail() 内部再存一份到同一个信箱的自然结果,只在"自己
寄给自己"这种边角用法下出现,不影响正常的"寄给别人"流程,未
作为 bug 处理。)
- 跨库排查:对全仓库 227 个档案的 logind.lpc 做了
grep -rl "destruct(mail_box)" 全库扫描,命中且仅命中两份
档案:本档案 xkyxciii 和它的姊妹档案 xkyx3b(round two
笔记里已经记录过这两份档案同源,共享同一段 goto.lpc 自救
失效 bug)。xkyx3b 尚未验证过是否真的触发(未启动它的驱动
测试),但源码里那一行 destruct(mail_box); 与本档案修复前
完全一致,逻辑上必然有同样的问题。因为全库范围只找到这两个
实例(低于跨库批量扫描立项的门槛),本轮只修复了
xkyxciii,未touch xkyx3b,留给下次测到 xkyx3b 时参考
本条记录直接验证/修复。
- 邮件、当铺、拜师之外:留言板/死亡复活已在 round two 验证过 未重复测试;补充确认了
redeem/ransom(当铺赎回)此前两轮 都没测过的指令,功能正常。 - 本轮结论:找到并修复了一个全局性、每次必现、影响全部玩家的真 实 bug(邮件系统失效),是本档案迄今为止修复过的 bug 里第二严 重的(仅次于 round one 的出生房间 bug)——同样是"score 等基础 指令一切正常,但一整块子系统悄无声息地失效"的模式。经济(当 铺/古玩店)、拜师门槛判定,均验证正常、无新发现。
§7.100 sweep (2026-08-19): redundant replace_program(ROOM); landmine
Same corpus-wide bug as documented at AGENTS.md §7.100: rooms
inheriting ROOM (/std/room) had a redundant, harmful
replace_program(ROOM); call right after inherit ROOM; in create(),
setting a permanent "pending replace" flag that crashes the object the
first time anything binds a closure to it. This lib had 1,452 live
occurrences (survey-ranked #89 of 166 candidates >=100, tied with
sibling lib xkyx3b). Fixed with the sweep's binary-mode script
(fix_710_room.py); git diff --numstat totals (0 insertions, 1452
deletions) match the survey's live-occurrence count exactly. This lib
has no in-game room-building tool at all (roommaker.lpc or
equivalent doesn't exist), so there was no factory-bug variant to fix.
No work/data/ room-source false-negative found. Verified via a clean
build-debug boot (zero "cannot replace"/"cannot bind" debug.log
lines, port 40118 listening) plus a live spot-check: no player save
data exists yet in data/login/ at all (a genuinely fresh, never-
registered state despite the seeded fluffos/lywin wizlist
entries), so a fresh test account was registered through the full flow
(id → confirm → Chinese name → password → email → gender → race) and
landed at the new-player training room; look/quit both worked,
debug.log stayed clean throughout (only harmless first-compile-burst
Warning noise, matching this lib's own documented pattern). Test
account's save directory deleted before committing; an accidental
rm -rf of the ADJACENT data/login/s/ directory's tracked
.donotdelete placeholder file was caught via git status and
restored with git restore before committing.
§7.30 uninitialized-mapping accessor sweep (2026-08-20)
Corpus-wide mechanical sweep of the feature/skill.lpc shared-lineage
bug (confirmed independently on xiakexing2017/jqxz2015/haiyang2
via round-four testing): 3 accessor(s) in this file returned a raw
never-initialized mapping instance variable (defaults to int 0,
not ([]), until first assigned), crashing any unguarded
keys()/sizeof()/indexing caller for a fresh/untrained character.
Fixed at the accessor level (mapp(x) ? x : ([])) per the documented
remedy. Verified via lpcc --batch static compile check only (not a
live boot) as part of a large mechanical sweep; not individually
functionally re-tested live on this lib.
深度功能测试(§10.7,2026-10-04)— negative range ends
scripts/lpc_fix_negative_ranges.py: 5 line(s) in 2 file(s) count a range from the end with < (x[a..-1] -> x[a..<1], x[-2..] -> x[<2..]; KB 06 §7.209). In this driver a negative constant end gave "" / ({ }) and a negative start the whole value, so each of these returned the wrong slice; the compile warning A negative constant as the second element of arr[x..y] is gone from the files that compile. Range lvalues (s[0..-1] = text, the prepend idiom) are left alone. A paired compile of the changed files at HEAD and in the working tree showed no new error.
深度功能测试(§10.7,2026-10-04)— undefined macro and simul_efun gaps
message_combatd(msg, me, you)is called by 1 file(s) (the perform files) and defined nowhere in this archive (a simul_efun of the original server): 1 of them did not compile (Undefined function message_combatd).adm/simul_efun/message.lpcnow hasvarargs void message_combatd(string msg, object me, object you, mixed extra)callingmessage_vision(), placed aftermessage_vision's definition (KB 04 §6.2). All callers compile.
深度功能测试(§10.7,2026-10-05)— here-document terminators
scripts/lpc_fix_heredoc_terminator.py --all --apply: 1 here-document block(s) repaired (1 with the terminator glued to the last text line, 0 with it indented), so End of file in text block no longer hides the room or object behind them: d/huashan/yongdao2.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-05)— tail() simul_efun
scripts/lpc_add_tail.py --apply: cmds/apr/tail.lpc calls tail(file), an efun of the MudOS this archive ran on that FluffOS does not have (Undefined function tail: the command did not compile). adm/simul_efun/file.lpc now defines void tail(string file): the last 10 lines of the file, written to the player. Load check (HEAD against the working tree): 1 of the 1 tail command file(s) failed to load at HEAD, all load now, and the simul_efun file loads. KB 04 §6.2.
深度功能测试(§10.7,2026-10-05)— 房间列出的物件缺档时房间照常加载
房间基类 std/room.lpc:make_inventory() 用 catch() 包住 new(file),做不出来就返回 0;reset() 的 case 1: 拿到 0 就跳过这一项(if (!objectp(ob[list[i]])) break;)。档案里没有的 NPC 或物品(或 create() 出错的物件)原先会让列出它的房间整个加载失败(Bad argument 1 to environment(),或对 0 的 call_other()),连带这个房间后面的区域;现在房间照常加载,只是少了那个物件,下次 reset() 再试(KB 05 §7.25;scripts/lpc_resilient_room.py、scripts/lpc_room_reset_guard.py)。
验证:探针(scripts/lpc_room_reset_probe.py:房间基类列出一个不存在的物件,再调用 reset())HEAD 单件 崩溃、多件 崩溃,工作树 单件 OK、多件 OK。
深度功能测试(§10.7,2026-10-07)— 编译警告整理
scripts/lpc_warnings.py xkyxciii --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 214 个 .lpc。HEAD 与工作树分别加载(新进程):206 -> 206 通过,无回退。
启动时警告 37 -> 0;新进程登录(新人物确认)正常。
深度功能测试(§10.7,2026-10-08)— .lpc 后缀的定宽切片
.c 改名 .lpc 时只改了字符串没改切片宽度:f[<2..] == ".lpc" 拿两个字符比四个字符,永远不成立(!= 永远成立),按后缀筛文件、去后缀的代码因此从不运行或截成 x.l(KB 06 §7.238)。scripts/lpc_lpc_suffix_slice.py --apply 改了 1 处比较、0 处去后缀(如 adm/daemons/natured.lpc);改动的文件 HEAD 与工作树分别加载(新进程),无回退。
- 2026-10-10:编译警告第二轮(按驱动报告逐条修):按位 & 改逻辑运算 0 处,返回类型/原型 2 处,NPC+F_UNIQUE 的 init() 0 个,带初始化的未用局部变量 1 个,只在 #if 分支用的声明移进分支 0 个文件;改动 2 个文件,HEAD 与工作树各载入一次无回退(2 objects; HEAD PASS 0, tree PASS 0; regress 0; newly loading 0),这些文件的警告 2 -> 0。
- 2026-10-10(续):第二轮编译警告整理(
lpc_fix_unused_init.py)删掉了只在不启用的#if分支里用到的局部变量声明(如include/net/ftpdsupp.h中#ifdef GUEST_WIZARD_FTP分支的file),启用该选项时会编译失败;声明已放回所用的分支里,工具已改为跳过这种情况(交给lpc_move_decl.py)。
编译警告收尾(2026-10-12)
能编译的文件警告清零(74 条)。继承 SKILL 的全部 163 个对象加上改动文件 HEAD 与工作树各载入一次:HEAD PASS 162,tree PASS 162,regress 0。
std/skill.lpc开头的mapping action = ([ "action": "", "force": 0, ... ])是一份招式栏位说明,基类自己的代码从来不读它(perform_action()用的是同名局部变量),而 71 个武功都声明自己的mapping *action招式表,于是每个都报Redeclaration of global variable 'action'。把这段改成注释;真正拿它当缓冲用的只有daemon/skill/baigu-claw.lpc和example.lpc(query_action()里填action["action"]等再返回),这两个文件各自声明一份同样初值的mapping action,行为不变。d/beijing2/zhang/npc/dog.lpc:chat_msg里写成(: "random_move" :)(返回字符串常量的函数指针),狗从不走动;改成(: random_move :)。d/center/npc/biantai-kuangmo.lpc、d/quanzhou/npc/biantai-kuangmo.lpc:自己记目标的object killer与feature/attack.lpc的仇人名单string *killer同名,改名target_ob;killa.lpc两份函数体后多余的};。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。12 个文件 13 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断);x op= 浮点先把浮点转成整数再运算(系数小于 1 时 x 直接变 0),改为x = to_int(x op ...):/adm/daemons/combatd.lpc(damage -=)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。
- 2026-10-14:2 个文件用了颜色宏(
HIW、NOR)却没有#include <ansi.h>(首个错误syntax error, unexpected L_STRING);开头补上(KB 04 §6.1、§6.3)。新进程比对 HEAD 与工作树:2 个对象,新增载入 0,回退 0。
family 映射内联取下标(2026-10-15,KB 05 §7.30)
ob->query("family")["master_name"]、me->query("family")["family_name"](泥潭式为 query("family", ob)[...])直接对门派映射取下标:没有门派的人(新玩家、叛师者、大多数 NPC)在 NPC 的打招呼和问话、绝招的门派判断、房间的 valid_leave 里报 Value being indexed is zero,而不是得到拒绝。4 处(1 个文件)改为 dbase 路径查询 query("family/master_name"):有门派时值相同,没有时得 0;原有的判断照常生效(scripts/lpc_fix_family_index.py)。给映射赋值的写法未动。
NPC 带着本库没有的物品,载入失败(2026-10-15,KB 06 §7.73)
5 个文件、5 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/quanzhou/npc/obj/yellow_cloth、/d/quanzhou/npc/obj/green_cloth、/d/quanzhou/npc/obj/yayifu)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 0 个,改后 5 个(5 个原来载入不了),没有退步。
去掉的预定义宏 SAVE_EXTENSION(2026-10-18,KB 04 §6.11)
老版 MudOS 给每个 LPC 对象预先定义了 SAVE_EXTENSION(".o");FluffOS 的 ChangeLog(COMPAT_BUSTER 一条)把它删了,改用 __SAVE_EXTENSION__。本库没有自己 #define 这个宏,用到它的文件一律 Undefined variable 'SAVE_EXTENSION',整个对象载入不了。改成 __SAVE_EXTENSION__(scripts/lpc_fix_predefined_macro.py):adm/daemons/network/netmail.lpc、include/net/ftpdsupp.h。netmail 是网络邮件守护进程(本库缺 mailer.h 等头文件,改后仍载入不了)。改后用 lpcc 逐个载入这些对象:0 个通过,1 个还有别的错误(adm/daemons/network/netmail:/adm/daemons/network/netmail.lpc:17: error: Cannot #include mailer.h)。
set_skill() 遇到本库没有文件的武功不再抛错(2026-10-18,KB 06 §7.256)
feature/skill.lpc 里的 set_skill()、map_skill()、prepare_skill() 碰到没有武功文件的名字就 error("F_SKILL: No such skill (…)"),调用它们的 NPC create() 整个中断:NPC 载入不了,刷出它的房间也少一个人。本库有 14 个对象设了本库没有文件的武功(4 个名字,最多的是 kunlun-force、kunlun-zhang、kunlun-steps)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:14 个里 14 个通过。(scripts/lpc_guard_set_skill.py)
通向不存在房间的出口,按对面房间的回头出口改正(2026-10-18,KB 06 §7.257)
房间出口 "west": __DIR__"xxx" 指向的文件本库没有,玩家走过去就被卡住(笔误、改过名的房间,或者没带进档案的编号房间)。对面的房间存在、并且它相反方向的出口正好指回这个房间,说明出口原本就是通向它:/d/dali/d-jiangjun-s 的 south:d-xiaogongyuan → d-xiaohuayuan。改动后每个出口都能解析到本库的文件(scripts/lpc_fix_dead_exits.py;目标在运行时本来就能载入的库——虚拟房间——不动)。
RANK_D 对 0 的称呼(2026-10-19,KB 06 §7.273)
adm/daemons/rankd.lpc 的 query_respect()、query_rude()、query_self()、query_self_rude() 里 4 个函数开头补上 if (!objectp(ob)) ob = this_object();。NPC 在 create() 里写 "…" + RANK_D->query_respect(this_player()) + "…"(inquiry 对话、chat_msg),驱动自己跑 reset() 时没有人在场,this_player() 是 0。旧驱动里 0->query("age") 返回 0,称呼走默认分支;新驱动直接报 Bad argument 1 to EFUN call_other(),房间的 make_inventory() 吞掉错误,被打死的 NPC 再也刷新不回来。用 scripts/lpc_reach_census.py --walk 找到(xkx100 的武当弟子、一灯、汴梁书生…);修在守护程序里,不动 1,800 个 NPC 文件。
两个斜杠开头的物件路径(2026-10-19,KB 06 §7.276)
1 个文件、1 处物件路径写成 //…(如 carry_object("//clone/cloth/hic_cloth")):/obj/wizard/workroom.lpc。当年的驱动不介意(路径先被规范化);新驱动把原样的字符串交给 master 的 valid_read(),securityd 按 //clone 查目录,NPC 的 euid 没有权限,报 Read access denied.,NPC 载入不了。改成一个斜杠。
行编辑器指令编译失败:(: this_object(), ({ "do_to", me, arg }) :)(2026-10-20,KB 06 §7.284)
Illegal to use local variable in functional.:call_other 形式的函数指针 (: ob, ({ "函数", 参数 }) :) 的数组里只能是常量,写了局部变量 me / arg 整个指令就编译不过,to 这类先开编辑器、再回调自己的指令因此从未能用。改成 (: call_other, this_object(), "do_to", me, arg :)(参数在建立指针时绑定,编辑器再把输入的文字接在最后,do_to(me, arg, text) 收到全文;scripts/lpc_fix_functional_locals.py)。改动:/cmds/usr/to.lpc(1 处)。
socket / dumpsocket 指令:dump_socket_status() 驱动里没有(2026-10-20,KB 06 §7.291)
这些巫师指令 write(dump_socket_status());,MudOS 的 efun 在 FluffOS 里不存在,指令编译不过。在模拟 efun 文件里补了 dump_socket_status():用 socket_status() 排出同样的表(没有 sockets 包的构建,比如 WASM,只有表头;scripts/lpc_add_sefun.py dump_socket_status)。
登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)
驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/adm/obj/simul_efun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。
sefun cat():缺文件时 write(0) 报错(2026-10-20,KB 06 §7.297)
void cat(string file) { write(read_file(file)); }:文件不存在(压缩包没带的欢迎/公告文件,或只有运行中的 mud 才会写的日志)时 read_file() 回 0,write(0) 在本驱动里报 Bad argument 1 to receive(),调用者(登录对话、cat 指令)被中断;老驱动什么也不打印。改成 write(read_file(file) || "");(scripts/lpc_fix_cat_unchecked.py)。改动:/adm/simul_efun/file.lpc。
翻页器 start_more(read_file(...)):文件缺失时 explode(0)(2026-10-20,KB 06 §7.300)
东方故事系的翻页器 void start_more(string msg) { ... explode(msg, "\n") ... } 被这样调用:start_more(read_file(HELP_DIR + "help/topics"))、start_more(read_file(NEWS))。压缩包没带的文件使 read_file() 回 0,explode(0, ...) 在本驱动里报 Bad argument 1 to explode()(不带参数的 help 指令就是这样出错的),老驱动把 0 当空文本。在 start_more 开头加了 if (!stringp(msg)) return;(scripts/lpc_fix_start_more.py)。改动:/feature/more.lpc。
teamkill 指令编译不过:条件少了右括号(2026-10-20,KB 04 §6.25)
cmds/std/teamkill.lpc 第 43 行 if (userp(obj) && (int)obj->query("combat_exp") < LOWER_LIMIT && !me->query("licensed_to_kill") 的条件没有收尾的 ),下一行的 return notify_fail(...) 就成了条件里的语法错误(syntax error, unexpected L_RETURN),指令从来没有加载过。补上 ),其余照原样(同一目录的 kill.lpc 里这个条件多一个 || obj->query("age") < 16 分支,没有搬过来)。xkyx3b 和 xkyxciii 的同一行相同,同样修改。加载检查:各 1/1,HEAD FAIL → tree PASS。
NPC 的 chat() 在 chat_msg 为空数组时下标越界(2026-10-21,KB 06 §7.334)
NPC 基类的 chat() 是 if (arrayp(msg = query(...))) { if (random(100) < chance) { rnd = random(sizeof(msg)); if (stringp(msg[rnd])) ...,不检查列表是否为空。本库有 4 个 NPC 文件写成 set("chat_msg", ({ }))(或 chat_msg_combat)并带正的 chat_chance(例如 /d/center/npc/biantai-kuangmo.lpc、/d/gaibang/npc/traitor.lpc),每次心跳有 chat_chance% 的概率 random(0) 为 0、msg[0] 报 Array index out of bounds(驱动捕获,心跳继续,但每次一条运行时错误),这个 NPC 在内存里就一直发生。在 random(100) < chance 前面加 sizeof(msg) &&(scripts/lpc_fix_chat_empty.py;if (random(100) > chance) return 0; 的写法是 !sizeof(msg) ||)。改动:/std/char/npc.lpc(共 1 个文件)。
不带参数的指令对 0 调用 efun:不在 wiz / adm 目录里的指令(2026-10-21,KB 06 §7.336)
指令敲成不带参数时,驱动把 0 传给字符串参数;下面的指令第一句就把这个 0 交给 efun,运行时报 Bad argument 1 to ...。它们在 cmds/std、cmds/usr、cmds/bos、cmds/app 这类目录里,上一轮的扫描只看巫师目录。用 scripts/lpcc_bare.py 不登录、在一次 lpcc 求值里以 0 调用 main() 逐个确认后,由 scripts/lpc_fix_bare_arg.py --all-dirs 在第一处使用参数的语句前加守卫:文件自带 int help(object me) 的,守卫是 if (!arg) return help(me);(help() 定义在 main() 之后的 1 个文件补了原型 int help(object me);,否则编译报 Undefined function help);没有 help() 的是 return notify_fail("…\n");。改动(1 个文件,括号里是接住 0 的 efun):/cmds/apr/test.lpc(present)。加载检查 HEAD 与工作树对照,没有退步。
注册到不存在函数的 add_action(2026-10-21,KB 06 §7.349)
add_action("函数", "动词") 绑定的函数,在对象本身、它包含的头文件、它继承的父类和继承它的子类里都没有定义(scripts/lpc_scan_callout_targets.py 静态解析,子类用传递闭包复核,scripts/lpc_verify_handlers.py 运行时 function_exists() 复核)。驱动从玩家最新的一条动作往回找,遇到这样的句子就报 *Function for verb 'drop' not found.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 4 条注册(3 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/center/gongzi.lpc(do_vendor_list)、/d/center/npc/gongzi.lpc(do_vendor_list)、/obj/npc/meng-zhu.old.lpc(do_answer)。最多的函数名:do_vendor_list 2 处、do_answer 2 处。没有动(读过):/d/beijing2/zhang/obj/room7.lpc 的 do_get(对象本身编译不过,登记留着,等对象能加载再处理)。
按字节写的汉字循环、茶馆猜诗和同盟频道标题(2026-10-23,KB 06 §7.363)
老档按字节做「一个汉字」:while (i--) if (i % 2 == 0 && !is_chinese(name[i..<0])) 只检查偶数下标,奇数位上的拉丁字母(张a)会过;茶馆猜诗 mixup() 用 strlen(str)/2 再 j=j+j、str[k..k+1] 交换两字节;泥潭系 channeld 的 switch (strlen(fname)) case 4/6/8/10 和 strlen(family)==4/6 按字节长度给同盟 / 门派名加空格。本驱动的 strlen() 和区间都按字符算。scripts/lpc_fix_byte_char.py:去掉奇偶项、把区间改成 name[i..i],按字符重写 mixup() 并把 strlen(first)>=14 折成 7,频道标题的 case / 门派长度改成字符数(对照 aoxiangtianji)。本库 汉字循环 / is_chinese 区间 1 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。
深度功能测试(2026-10-27,§6.25 one-arg message_vision)
message_vision("…") 补第二参(me / TP / this_player())。
深度功能测试(2026-10-27,§6.10 未知 pragma save_binary/optimize)
删除本库中 FluffOS 不认的 #pragma save_binary / #pragma optimize… 行。