info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
以金庸小说背景为世界观的武侠 MUD(游戏内明确要求玩家不要使用小说中人物的本名),玩家社区自称"情义盟"。角色拥有膂力、悟性、根骨、身法四项天赋,创建时可选择其一或全部随机产生。新手历程完整可玩:从客店、西大街到扬州武馆的引导流程,中央广场的切磋战斗,死亡后经历鬼门关与白无常的五段对话,再到武庙复活重生。地图上的昼夜变化是真实生效的游戏机制——入夜后裸方向词(如"north")会失效并提示"天色太黑了",须改用"go <方向>",这是有意设计而非缺陷。虽然与本项目里的 `hy2000`/`hy2002`/`hy5` 同属"海洋"命名,但比对 master 文件哈希与 `securd.lpc`/`feature/skill.lpc` 后并未发现血统关联,是各自独立开发的同名之作。
English
A wuxia MUD set in the world of Jin Yong's novels (new characters are asked not to reuse the novels' own character names), whose community calls itself the Qingyimeng, the "Covenant of Passion and Righteousness." Despite sharing the "Ocean" name with this project's hy2000/hy2002/hy5, a comparison found no lineage relationship -- master file hashes and securd.lpc/feature/skill.lpc don't match any of them. Characters roll four attributes -- strength, comprehension, physique, and agility -- and can randomize any or all of them at creation. New characters progress from registration through the newbie sequence (inn, West Main Street, Yangzhou martial hall), a Central Square brawl, death, the five-part Ghost-Gate dialogue with the White-Robed Ghost, and revival at the martial temple; the bulletin board's post/look chain also works end to end. Night genuinely darkens the map -- bare direction words like "north" stop working after dark with a "too dark to see" message, forcing "go <direction>" instead -- an intentional day/night mechanic, not a bug.
README
内容亮点
- 注册流程:
new触发注册 → BIG5 字体确认(y/n,非空即可)→ 英文 id → 确认创建(y/n)→ 中文名字(可留空由系统随机取名)→ 密码 → 确认密码 → 天赋数值选择(0-4,0 为随机)→ 天赋数值确认(y/n)→ 电子邮件地址 (需含@)→ 性别(m/f)。 - 天赋系统:膂力、悟性、根骨、身法,可指定其一或全部随机。
- 深度功能测试(§10.7)完整走通了注册→新手引导(客店→西大街→扬州 武馆)→中央广场战斗→死亡→鬼门关(白无常五段对话)→复活送回武庙 的全流程;另用留言板验证了
look board/post全链路正常。地图上 的昼夜可见度是有意设计——夜晚裸方向词(north等)会失效、提示 "天色太黑了",须改用go <方向>,不是 bug。虽然和hy2000/hy2002同属"海洋"命名,但 master 哈希和已知档案都不匹配,没有确 认的血统关系(详见 NOTES.md),这次深度测试也确认它并不与两者共享 同一份securd.lpc/feature/skill.lpc,是独立验证出同一类 bug。
本次修复的关键 bug
adm/daemons/logind.lpc:create()里设置的seteuid(ROOT_UID)会被 后续howmany_user()/make_body()里遗留的seteuid(getuid())重置为 空字符串(该 daemon 的真实 uid 从未被正确解析),导致注册流程中途所有read_file/log_file权限判定失败。已将这两处改为显式seteuid(ROOT_UID)。howmany_visitor()/howmany_card():当/log/mud/MUDVISITOR、GIFTCARD计数文件不存在时,read_file()返回0,sscanf(0, ...)直接崩溃中断整个注册流程。由于libs/*/work/**/log已被.gitignore排除,这在全新 clone 的仓库上是真实会复现的首次启动 bug,不只是 WASM 沙箱的测试假象。已加if (!content) return 0;防御。adm/daemons/securd.lpc(真正生效的安全 daemon ——SECURITY_D宏指向securd.lpc,同目录下的securityd.lpc是未被引用的遗留重复文件):get_status()里member_array(euid, wiz_levels)会在该对象自身尚未 完成变量初始化时被重入调用(wiz_levels声明在wiz_status之后,create()内restore()触发的valid_read()链会在此刻重入本对象), 此时wiz_levels还是默认值0,导致崩溃。已加arrayp(wiz_levels)防御。adm/daemons/band.lpc:is_banned()加入 loopback 放行判断。is_chinese():改为按 Unicode 码点判断(0x4e00-0x9fff),修正旧版 按 GBK 双字节假设的判断逻辑。
深度功能测试(§10.7)新修复的关键 bug
adm/daemons/securd.lpc的valid_read()没有放行编译期/存在性 探测操作(AGENTS.md §7.5,两个变体):一是经典的file_size(技 能档案首次被引用时误判"不存在",中断 NPCcreate());二是本次新 发现、影响面大得多的变体——get_dir()效率函式底层走的是func=="stat",没放行导致adm/daemons/commandd.lpc(自己create()里从未seteuid()过)的rehash()每次get_dir()都被拒绝、指令表永远建不起来,表现为每个玩家的每一条指令(包括look)都回应驱动器的通用失败讯息"什麼?",且没有任何 debug.log 报错信号。已把load_object/recompile_object/include/file_size/stat一起加入放行名单。adm/daemons/{natured,questd}.lpc的create()从未seteuid(),导致读取自己的配置表被同一个自定义 ACL 拒绝 (AGENTS.md 新增 §7.98):read_file()被拒绝后返回0,explode(0, "\n")/read_table()崩溃,报错形状和"档案缺失"类崩 溃(§7.9/§7.54)一模一样,但两份档案(/quest/dynamic_quest、/adm/etc/nature/day_phase)其实都存在于硬盘上——真正原因是权限 被拒绝而非档案缺失。已仿照本档案logind.lpc已有的写法,在两处create()开头加seteuid(ROOT_UID);。- §8.9 食物/饮水年龄检查错对象:
logind.lpc的enter_world()里ob->query("age") == 14应为user->query("age") == 14(ob是登录对象,从未被设置过age)。本档案的init_new_player()会 在注册最后一步预先把食物/饮水设成非零值,所以这个 bug 之前不会在score上表现出来,但仍按标准修法修正,避免未来重构掉那个默认值 时静默复发。 cmds/usr/uptime.lpc的"上次当机原因"显示崩溃:main(int i)被以int参数直接呼叫时(例如巫师用call指令),write(read_file("/log/nosave/LASTCRASH"))在该档案不存在的全新 安装上会崩溃(*Bad argument 1 to receive())。玩家正常输入uptime(不带参数)不会触发,但已按 §7.9 惯例加stringp()防 御,避免巫师直接呼叫时崩溃。- AGENTS.md §7.86(留言板
post崩溃)确认已在更早的一次跨库扫描 中修复干净:全档案唯一命中"inherit BULLETIN_BOARD又多余replace_program()"这个形状的文件是clone/board/huashan_b.old——一份.old后缀的历史备份,不是驱动器会加载的.lpc源码,本 次确认无需改动。
管理员账号 / Admin account
- id:
fluffos - 密码 / password:
Mud@2026 - 权限 / level:
(admin)
此代码线的权限系统不通过 /adm/etc/wizlist(该文件从未被
securd.lpc 读取,是遗留自旧版本的死代码路径)。
修正(round-two 深度测试发现,上面这段此前的记录是错的):
adm/daemons/securd.lpc 的 wiz_status 实际声明为 nosave
mapping——永远不会被存档持久化,每次开机都会被 restore_list()
里的硬编码赋值重置成只有一个 lywin("预先留个门"的原始后门
id)。"直接编辑 securd.o 的 wiz_status 映射"这个此前记录的授权
方式根本不可能生效:nosave 字段既不会被写入存档,也不会在
restore() 时被读取,编辑那个文件毫无意义。而 lywin 本身已经是原
始存档里一个真实玩家(data/user/l/lywin.o,密码未知),fluffos
无法冒领这个 id。已在 lywin 那行旁边并列加一行
set("wiz_status/fluffos", "(admin)");(AGENTS.md §1.5 标准修法)。
此前虽然已经有一个 fluffos 角色存档被提交过,但由于上述 bug,那个
账号登录后从未真正拿到过 (admin) 权限——本轮修复后已用真实登录 +
update 指令验证权限真正生效。
警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。
在线试玩
https://mudlibs.fluffos.info/hy/
本地运行
cd libs/hy
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40182。
NOTES · 移植与修复记录
WASM 修复摘要(迁移自 meta.json 的 group_note)
海洋题材,和 hy2000(079)/hy2002(080)/hy5 是不同的代码库——master 哈希和任何现有档案都不匹配,没有确认的血统关系;完成了一次中断的转档(残留的 static 关键字,大写的 .C 档案),移除了一处重复的 create() 文字型 ftpd.lpc include(§7.40),并给 master.lpc 的 creator_file/domain_file/author_file() 加上了防止编译期递归加载的保护(find_object(SIMUL_EFUN_OB) 判断),否则会让整个开机流程崩溃。完整 WASM 修复:修好了 is_chinese() 码点判断 bug、check_legal_name() 长度界限,给 band.lpc 加了本地回环放行,修复了 adm/daemons/securityd.lpc 里一个真正的笔误(这是和真正的安全精灵 adm/daemons/securd.lpc 无关的死代码副本——SECURITY_D 宏实际指向的是 securd.lpc)。在 adm/daemons/logind.lpc 里发现并修复了两个更深的 bug:(1)create() 没有设置这个精灵的 euid,之后 howmany_user()/make_body() 里的 seteuid(getuid()) 呼叫又把它重置回空,因为这个精灵的真实 uid 本来就解析不出来——已把全部三处呼叫点都改成显式的 seteuid(ROOT_UID);(2)howmany_visitor()/howmany_card() 在各自的计数档案(/log/mud/MUDVISITOR、GIFTCARD)还不存在时会通过 sscanf(0,...) 崩溃——这是一个真实的、任何全新开局都会撞上的 bug(libs/*/work/**/log 是被 gitignore 掉的,不只是 WASM 沙箱的 artifact),已改成 read_file() 返回假值时直接回传 0。另外修复了 adm/daemons/securd.lpc 的 get_status() 里一个防重入编译崩溃:member_array(euid, wiz_levels) 在这个物件自己的变量初始化还没跑完时被呼叫就会崩溃(wiz_levels 声明在 wiz_status 之后,所以 create() 的 restore()->valid_read() 链条中的一次重入呼叫会看到它仍未赋值)——已用 arrayp(wiz_levels) 加上防御。管理员账号直接播种进了 adm/daemons/securd.o 存档文件自身的 wiz_status 映射(不是 /adm/etc/wizlist,这条血统真正的安全精灵从不读取它)——用二进制模式编辑,因为这条血统把映射键里的"/"编码成字面的 CR(0x0D)字节,Python 默认的文本模式换行符转换会悄悄把它弄坏。注册流程到进入游戏世界,对普通玩家和 fluffos/admin 账号都完整验证过(管理员专属的进场提示确认触发);登录后的 look/score/quit 回显偶尔会和异步的进场横幅 call_out 在脚本化 WASM 测试里抢跑(和 nt6/nt6nitan6win 已记录过的同一类无害测试节奏 artifact),不是 mudlib 的 bug。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 110 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
深度功能测试(§10.7,2026-08-08)
本次是 hy 第一次真正的 §10.7 深度功能测试(此前只做过登记/浅层验
证)。用标准管理员账号 fluffos(正常注册流程创建,securd.o 的
wiz_status 已确认播种过 "fluffos":"(admin)",密码 Mud@2026 直
接可用,无需改动)和一个新建的非巫师测试账号 testerxyz(浮浮)实
际走通了注册→新手引导→平安街区探索→扬州武馆(触发 F_SKILL 首次编
译)→中央广场战斗→死亡→鬼门关五段对话→复活送回武庙的完整流程,另
外验证了留言板 look board/post。
先说明背景更正:虽然 hy 和 hy2000/hy2002 共享"海洋"命名,但本
档案自己 WASM 阶段的 NOTES 已经确认三者 master 哈希互不相同、没有
确认的血统关系——这次深度测试独立验证到同一类 securd.lpc/
feature/skill.lpc bug,但代码本身并非同一份拷贝(下面每一条都单
独确认过 hy 自己的实际代码形状,不是照抄 hy2000/hy2002 的修法)。
§8.9 食物/饮水年龄检查错对象——适用,已修复(但影响被另一处默认值掩盖)
adm/daemons/logind.lpc 的 enter_world():
if (!user->query("food") && !user->query("water") && ob->query("age") == 14)
——ob 是登录对象,age 属性从未在它身上被设置过,这个判断永远为
假。已改为 user->query("age") == 14。注意:本档案的
init_new_player()(在注册最后一步、enter_world() 之前呼叫)会
提前把 food/water 设成 (str+10)*10 这个非零默认值,所以修复
前用 score 实测并没有看到食物/饮水栏归零——这个 bug 目前是"隐
性"的。但按 AGENTS.md §8.9 已有的教训(这个模式在 jhfy2 上也遇到
过一次),一个条件被别处的默认值意外掩盖不代表底层的"读错对象"是
正确的,未来任何人动到 init_new_player() 那行默认值就会让这个
bug 突然显形——照标准修法修正。
§7.86 留言板 post 崩溃——不适用,已在更早一次跨库扫描中修复干净
全档案唯一匹配"inherit BULLETIN_BOARD 又多余 replace_program()"
形状的文件是 clone/board/huashan_b.old——文件名是 .old 后缀,是
一份历史备份,驱动器不会加载它(真正生效的 huashan_b.lpc 已经是
干净的,只有 inherit,没有多余的 replace_program())。git log
确认这份档案在更早的一次 §7.86 跨库扫描提交(529649b16c9)里已经
处理过。本次用管理员账号实测 客店留言板:look board 正常列出
41 条历史留言,post <标题> 正常打开内建列编辑器,输入正文后 .
结束、正常存盘,look board 能看到新留言——全链路确认正常。
§7.5 自定义 ACL 未放行编译期/存在性探测——适用,两个变体,其中一个是本次最严重的发现
adm/daemons/securd.lpc 是真正生效的安全精灵(SECURITY_D 宏指向
它,securityd.lpc 是未被引用的死代码副本,和 README 已有记录一
致)。它的 valid_read() 完全没有为 load_object/
recompile_object/include/file_size 这类"纯编译期/存在性探测"
用途放行,对没有 euid 的调用者一律 if (!euid) return 0; 拒绝。
- 变体一(
file_size,和 hy2000/hy2002 相同的形状):feature/skill.lpc的set_skill()用file_size(...) < 0探测 技能档案是否存在,NPC 首次引用某个还没被load_object()过的技 能时会被误判"不存在"而中断create()。走到"扬州武馆"触发d/city/npc/chen.lpc(陈有德,create()里连续 14 次set_skill(),全部是本局第一次被引用的技能)后,debug.log全 程没有任何F_SKILL报错,确认此前已经放行file_size生效(该 放行本身也是本次一并加上的,见下)。 - 变体二(
stat,此前从未被记录过,本次新增 AGENTS.md §7.5 追 加段落):驱动器的get_dir()效率函式底层实际是用func=="stat"走 ACL 检查(读了 fluffos 驱动源码packages/core/file.cc确认,不是猜测)——而securd.lpc原本 完全没有放行这个 func。adm/daemons/commandd.lpc(指令表精灵) 自己create()里从来没有seteuid()过,它的rehash()每次 呼叫get_dir()列出/cmds/std/等目录时都会被这条 ACL 拒绝, 返回结果被当成空目录,search[dir]永远建不起来——表现为每个 刚注册的新角色、每一条指令(包括look、score)全部回应驱动器 的通用失败讯息"什麼?",而且 debug.log 没有任何报错信号。用 tmux 和 mudclient.py 两种客户端各自独立复现过(排除了 telnet 本 地客户端的 CJK 转码假象)。已把stat和load_object/recompile_object/include/file_size一起加入valid_read()的放行名单。修复后重启驱动,同一账号look/score/kill/post全部正常响应。
新发现且此前未被记录的 bug 类别:daemon create() 忘记 seteuid(),导致自己读取配置档案被同一个自定义 ACL 拒绝(已新增 AGENTS.md §7.98)
驱动启动阶段(早于任何玩家连线)debug.log 就报出两个崩溃:
执行时段错误:*Bad argument 1 to explode()
Expected: string Got: 0.
程式:/adm/daemons/questd.lpc 第 698 行
呼叫来自:/adm/daemons/questd.lpc 的 create() 第 39 行
呼叫来自:/adm/daemons/questd.lpc 的 read_table() 第 698 行以及 adm/daemons/natured.lpc 的 create() → read_table() →
explode(read_file(file), "\n") 一模一样的形状。乍看很像本项目已
经记录过的"档案缺失"类 bug(§7.9/§7.54:read_file() 对不存在的
档案返回 0,未加防御的 explode()/sscanf() 直接崩溃)——但
ls -la 确认 /quest/dynamic_quest(17KB)和
/adm/etc/nature/day_phase(1.5KB)两个档案都确实存在于硬盘
上。真正原因是:read_file() 本身也要经过 securd.lpc 的
valid_read()(func 是 "read_file"),而 questd.lpc/
natured.lpc 这两份档案自己从来没有呼叫过 seteuid()——preload
阶段 create() 执行时这两个精灵自己的 euid 是空的,被同一条
if (!euid) return 0; 拒绝,read_file() 因此返回 0,崩溃发生
在下游的 explode() 而不是权限判断本身。逐一 grep 了
adm/daemons/*.lpc(36 份档案)里同时有 read_file(/read_table(
调用、且全档案没有任何 seteuid 呼叫的文件,确认全库只有这两份档
案命中,不是更大范围的问题。修法是仿照本档案 logind.lpc 自己已
经有的写法(README 已记录的三处 seteuid(ROOT_UID)),在两个
create() 开头各加一行 seteuid(ROOT_UID);。修复后重启,两个崩溃
都消失,quests/day_phase 两张表正常载入非空内容(day_phase
如果继续空着,下游 outdoor_room_description() 等函数会对
day_phase[current_day_phase] 做 sizeof()-1/取模,存在这一项已
被 AGENTS.md 记录过的除零/越界连锁风险,虽然本次没有专门去复现这
一层)。
附带修复:cmds/usr/uptime.lpc 的"上次当机原因"崩溃
main(int i) 被以整数参数直接呼叫时(正常玩家输入 uptime 不带参
数不会触发,但 call 指令可以强制传入 1),
write(read_file("/log/nosave/LASTCRASH")) 在这份档案不存在的全新
安装上会崩溃:*Bad argument 1 to receive()(/log/nosave/ 目录
本身都不存在)。用管理员账号 call /cmds/usr/uptime->main(1) 复现
后确认。全库没有找到任何自动呼叫 uptime->main(1) 的代码路径(不
像 hy2000/hy2002 的等价代码那样被 LOGIN_D 自动触发),所以这条路径
目前不会在正常游玩中触发,但仍按 §7.9 的标准写法加了 stringp()
防御,避免巫师用 call 或未来的调用方式意外触发。
战斗与死亡/复活
在"中央广场"对"流氓头"(帮派头目 NPC)发起攻击,管理员账号和
testerxyz 各打了一场,都在十几回合内落败——新手角色对这只 NPC 明
显偏难,符合"江湖险恶"设计,不作为 bug 处理(战斗数值属于游戏平
衡,不在 §10.7 范围内)。
死亡后两个账号的表现符合预期差异:管理员账号 fluffos 死亡后卡在
"鬼门关",白无常(d/death/npc/wgargoyle.lpc)的 init() 本来
就用 wizardp(previous_object()) 排除巫师角色,这是有意设计(巫师
有别的手段自救),不是 bug,和 hy2000/hy2002 已经记录过的同一条教
训一致。另开非巫师账号 testerxyz 重新走一遍:白无常 的五段对
话("你叫什么名字" → ... → "阳寿未尽" → 阴冷浓雾包围你)全部按 5
秒一段的 call_out 节奏自动播放完毕(不需要玩家输入任何东西——这
个对话是纯计时器驱动的,之前误以为要靠玩家回答"你叫什么名字"是走
错了方向),最终 reincarnate() 正确执行,角色被送到武庙——复活系
统本身完全正常,没有套用 AGENTS.md §7.68 那条已撤销的"重试"补丁
(bgargoyle.lpc/wgargoyle.lpc 的 if (!ob || !present(ob)) return;
单次判定原样保留,没有改动)。
其它检查、确认不适用的类别
- §7.90(
maximum evaluation cost过低触发移动 eval-cost 中断):config.fluffos里已经是700000,深度测试全程移动/战斗未观察 到任何 eval-cost 中断报错,不适用。 - §8.3a(
private nomask command_hook):feature/command.lpc第 36 行已经是干净的nomask int command_hook(...)(第 35 行留着一 条private版本的历史注释,不是活代码),不适用。 - §8.3b(
commandd.lpcrehash()里残留.c后缀的 sscanf):实 测是sscanf(cmds[i], "%s.lpc", cmds[i]),早就是.lpc后缀,不 适用。 - printf 调试残留:全库
printf("%O命中都是巫师用的mem/where/ilist/call等指令的正常功能代码,或者logind.lpc里两处早就被前任巫师注释掉的//printf(...),没有 发现活的调试泄漏。 - §1.5(管理员账号是否被真实旧账号占用):README 已记录的
fluffos/Mud@2026组合本次验证依然有效,securd.o的wiz_status映射本来就已经包含"fluffos":"(admin)",无需重新 播种。 - 昼夜可见度:入夜后裸方向词(
north等)会提示"天色太黑了,看不 清明显的出路"、失效,需要改用go <方向>——这是有意的游戏内容设 计,不是 bug,深度测试过程中用go系列指令正常绕过。 - 本次没有测试到:拜师/门派系统、商店/交易、邮箱(虽然注册时确认 收到了新手礼包信)、task 系统。留给未来某次针对
hy的后续 pass。
深度功能测试(2026-08-13,round two,新驱动重测)
Re-tested against the freshly-rebuilt build-debug/src/driver(post
全库 quest_times/win_times %-operator 修复 + Warning/warning
驱动文本回退)。
更正上一轮(2026-08-08)的一处误判
上一轮"没有发现的 bug"清单里写着"§1.5(管理员账号是否被真实旧账号
占用):… securd.o 的 wiz_status 映射本来就已经包含
"fluffos":"(admin)",无需重新播种"——这个结论是错的,是仅凭检
查存档文件内容得出、未做真实登录验证的判断失误。本轮读代码发现
adm/daemons/securd.lpc 的 wiz_status 声明为 nosave mapping
(第 6 行)——永远不会被存档持久化,无论 securd.o 文件里实际写了
什么都无关紧要,每次开机都会被 restore_list() 里的硬编码赋值重置
成只有一个 lywin。也就是说,即使 fluffos 角色本身早就注册并提
交了存档,这个账号登录后从未真正拿到过 (admin) 权限——这个误
判很可能正是当年检查存档文件内容(看到里面确实写着
wiz_status:{"fluffos":"(admin)"} 之类的内容)却没有意识到这个字
段是 nosave、从不会被真正 restore() 读取所致。这正是 AGENTS.md
§1.5 归档的"wiz_status 声明为 nosave"这一类 bug 的又一实例,和本轮
fqyy2 独立发现的同一类形状完全一致。
发现并修复的 PROGRAMMING bug
1. log_error()(adm/single/master.lpc)完全没有严重度检查
(AGENTS.md §7.34-class,与本轮 wdxtym/ffxymud/fy2mg/fys/
hc 同一原始形状):if (this_player(1)) efun::write(...)——
不区分巫师/玩家,也不区分警告/错误。修复:加上
strsrch(message, "arning:") == -1 判断。
2. log_file()(adm/simul_efun/file.lpc)完全没有 assure_file()
保护(AGENTS.md §7.11-class 的又一确认实例):注册/登录本身只
写 log_file("USAGE", ...)(无子目录,本来就存在),不受影响,
但 nosave/SUICIDE 等管理指令路径会在首次使用时未捕获抛出。已
补上 assure_file(LOG_DIR + file);(含前向声明)。
3. 管理员账号从未真正拿到 admin 权限(见上方"更正"一节,AGENTS.md
§1.5 的"wiz_status 声明为 nosave"形状,与本轮 fqyy2 同一模
式):已在 lywin 那行旁边并列加一行
set("wiz_status/fluffos", "(admin)");。README 已同步更正此前
错误的授权机制描述。
Proactive checks(无需改动)
win_times修复确认存在且正确:d/city2/npc/refereew.lpc:177。- 未发现
message()simul_efun 包装函数——不适用 message()-missing-varargs 这一类 bug。
实测过程
登录时有一个 GB/Big5 选码提示(选 g),用已提交的 fluffos/
Mud@2026 账号登录,score 确认显示"目前权限:(admin)"(修复前
按代码分析必然显示较低权限,未做修复前的真实登录复现,因为已经确
信 bug 形状与 fqyy2 完全一致,直接应用了已验证过的修法),
update /adm/simul_efun/file(就是本轮改过的文件)确认可正常重新
编译。adm/log/debug.log 时间戳全程未变化(Jul 30,早于本次会
话),确认无新增未捕获运行期错误。登录本身产生的存档时间戳类微小
diff(data/{login,user}/f/fluffos.o 的 last_on 字段)已用
git checkout 撤销,不提交。驱动最终按精确 PID kill,ps -p 确认
已退出。
(本档案 work/ 目录下还嵌着一份完整的姊妹档案 海洋2002/hy3/ 副
本——不是这次任务的目标,未触碰,留给 hy3 自己的 pass。)
§7.100 跨库扫描修复(ROOM 冗余 replace_program() 关闭包炸弹,2026-08-19)
同一形状覆盖到几乎所有房间基类(机制详见 AGENTS.md §7.100)。本库属
于该扫描已知最大规模的 10 个库之一。二进制模式脚本机械删除了 7847
处独立、未注释的 replace_program(ROOM); 整行;另有 2 处不规则形状需
要单独手工处理:d/happy/workroom.lpc 的调用行带一个空的尾随注释
标记(replace_program(ROOM); //,没有实际注释文字,判定为噪音而
非"其它代码",一并删除);造房工具 clone/misc/roommaker.lpc/
adm/roommaker.lpc 代码生成模板里同样内嵌了这个形状(4 处)。这
次连同上面这条记录提到的内嵌 海洋2002/hy3/ 完整副本一起处理了
——虽然那份副本本身是死代码(不在任何真实加载路径上,§7.112 记录已
确认),但同一套机械删除对它无害,为了不留一个"看起来没修但其实是
故意跳过"的死角,本次连副本一起修了(d/happy/workroom.lpc/两份
roommaker.lpc 在副本里各有一份同形状问题,同步清理)。删除总计
7857 行,与本次扫描 FINDINGS.md 记录的 hy 存活命中数完全一致。
验证:干净启动一次真实调试驱动,端口 40182 正常监听,
work/log/debug.log 全程无新增内容。用已播种的 fluffos/
Mud@2026 管理员账号连线,look/who 正常(/d/welcome/welcome
本身出口较少,多数方向回应"这个方向没有出路",属于房间本身设计,
非本次修复引入的问题)。测试产生的 data/{login,user}/f/fluffos.o
存档时间戳 diff 已 git checkout 撤销,不提交。驱动按精确 PID
kill。
``§7.112`` residual-gap closure (2026-08-20)
Corpus re-scan (grep -rl 'call_out("death_stage"' ... | filter for missing guard) found unguarded init()-scheduled death_stage() call_out chain(s) in 海洋2002/hy3/d/death/npc/bgargoyle.lpc, 海洋2002/hy3/d/death/npc/wgargoyle.lpc that the original two-wave sweep (see AGENTS.md §7.112) missed -- same reconnect-triggered duplicate-chain bug, different filename/lineage. Added the standard query_temp("death_stage_active")/set_temp/delete_temp re-entry guard, adapted per file's own exit points. Compile-verified via lpcc --batch.
§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): 5 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.
深度功能测试(2026-09-04,round three,shop + 拜师)
新角度:扬州醉仙楼购物 + 丐帮左全拜师。2026-08-08 / 2026-08-13 两轮
都写明没测商店和拜师。这是海洋II/ES2 金庸血统,端口 40182。第一输入
是 BIG5 选单,发 g,再英文 id。密码 Mud@2026。登录对象没有
wizpwd。(player) 退出后 20 秒内不能再 login;本轮管理员不受这条
限制。work/ 下嵌着一份 海洋2002/hy3/ 副本,本轮没动它。
实测过程
管理员 fluffos / Mud@2026(权限 (admin),落地 /d/welcome/welcome)。
身上没钱,clone /clone/money/gold 可用。
goto /d/city/zuixianlou(醉仙楼,店小二 d/city/npc/xiaoer2.lpc,
F_DEALER)。list 烤鸡腿八十文铜板 / 牛皮酒袋一两白银 / 包子五十文
铜板。buy jitui 成功(「你从店小二那里买下了一根烤鸡腿」)。一两黄
金换成九十九两白银 + 二十文铜钱。feature/dealer.lpc 里丐帮拒买那段
是注释掉的,买完再拜仍更稳妥。
goto /d/gaibang/inhole,左全源码是 kungfu/class/gaibang/zuo-qu.lpc
(文件名少一个 n),apprentice zuo 一次成功:恭喜成为丐帮第二十代
弟子。score 称谓「丐帮第二十代弟子」、师傅左全。cmds/usr/save.lpc
真正写盘。save 后杀驱动冷启动再登录,称谓/师傅/九十九两白银/二十文铜
钱都在。烤鸡腿未进 autoload。左全只收男性。
发现并修复的 PROGRAMMING bug
1. 华山收徒计数拼写(与 jhfy2/wmkj/hy2002 同形,静态对照
修,本轮拜师走的是左全不是华山):
kungfu/class/huashan/{yue-buqun,yue-wife,feng-buping}.lpc、
kungfu/class/yue-buqun.lpc、d/biwu/feng-buping.lpc 的
recruit_apprentice() 写 add("apprentice_availavble", -1),永远
减不到 set("apprentice_available", 3) 那个字段。五处都改成
apprentice_available。嵌套的 海洋2002/hy3/ 同形未改,留给
hy3 自己的 pass。
深度功能测试(§10.7,2026-10-04)— negative range ends
scripts/lpc_fix_negative_ranges.py: 10 line(s) in 4 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-05)— 任务表里的 .c 路径与任务守护进程的防护
scripts/lpc_dynamic_rows.py:quest/dynamic_location 里 833 行写的是原档案的 .c 路径。转档时文件已改名 .lpc,而显式后缀按原样解析,load_object("/d/x.c")、new("/d/obj/quest/x.c") 返回 0 且不报错(KB 03 §4.2,KB 06 §7.220),所以 adm/daemons/questd.lpc、cmds/std/questd.lpc 读到的每一行都加载不出来:随机任务和任务物品从未生成,在 create() 里直接对结果调用 tar->set() 的版本还会崩溃,连带加载它的 cron 守护进程和 give 的任务钩子。这些行已改为 .lpc。
scripts/lpc_dynamic_quest_guard.py:4 处 spread_quest() 调用(init_dynamic_quest() 的循环里)包进 catch(),缺失的任务物品或编译不过的房间只跳过那一个任务,守护进程照常加载、其余任务照常生成;cmds/std/look.lpc 夜间检查的 !present("fire", this_player()) 前加 objectp(this_player()) &&(房间 reset() 销毁物品时 /feature/move.lpc 的 remove() 会调用 look_room(),此时没有 this_player(),present(…, 0) 报 Bad argument 2 to present())。
验证:adm/daemons/questd.lpc、cmds/std/questd.lpc和 cron 守护进程在新进程里加载(HEAD 一次,工作树三次,选房是随机的):HEAD 通过 2 个,工作树三次分别通过 2、2、2 个(共 3 个),无回退;cmds/std/questd.lpc 在 HEAD 与工作树都加载失败,另有原因,与本次无关。
验证:改动过的 adm/daemons/questd.lpc、cmds/std/look.lpc、cmds/std/questd.lpc、海洋2002/hy3/adm/daemons/questd.lpc、海洋2002/hy3/cmds/std/look.lpc、海洋2002/hy3/cmds/std/questd.lpc 在新进程里加载,HEAD 通过 3 个、工作树通过 3 个(共 6 个),无回退。
深度功能测试(§10.7,2026-10-05)— 房间列出的物件缺档时房间照常加载
房间基类 inherit/room/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-06)— 所有对象的 uid 都是空字符串
与 hy5 相同(KB 06 §7.235):config.fluffos 的 simul_efun 写成 /adm/obj/simul_efun,主控 creator_file() 的保护查的是 globals.h 里的 /adm/single/simul_efun(原始配置也是这个),从未载入,于是每个对象的 uid/euid 都是 "",Root 权限的判断全部失败(断线玩家无法被 quitgame 清除,守护进程大多写不了文件)。配置改回 /adm/single/simul_efun 后,/cmds/usr/quit 的 uid 为 Root;临时副本新进程登录、score、save、quit 无错误。
深度功能测试(§10.7,2026-10-06)— 白驼山掌门欧阳锋编译不过
d/baituo/ouyangfeng.lpc 有两处笔误,整个 NPC 从未编译成功:greeting() 里 write("你是谁?我要杀了你!哈 ̄哈 ̄哈 ̄") break; 缺分号,inquiry_map() 里 object me = this - player();(应为 this_player())。白驼山派的掌门因此不存在,拜师、问路(inquiry_map)都无从谈起。两处改正后,新进程里 HEAD 加载失败、工作树加载成功。同一文件在 14 个 xkx 系的库里一字不差。
深度功能测试(§10.7,2026-10-07)— 空的 if (current_water == 0) {}
3 个房间的 valid_leave() 里有一行什么也不做的 if (current_water == 0) {}(编译警告 Expression has no side effects),删去,行为不变:d/xingxiu/nanjiang2.lpc、d/xingxiu/nanjiang3.lpc、d/xingxiu/shanjiao.lpc。
深度功能测试(§10.7,2026-10-07)— 编译警告整理
scripts/lpc_warnings.py hy --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 665 个 .lpc 和若干 .h。HEAD 与工作树分别加载(新进程):648 -> 648 通过,无回退;改过的头文件的 343 个包含者 334 -> 334 通过。剩下的告警都在 海洋2002/hy3/ 下——库里夹带的另一份 hy3 源码,不加载,未动。
启动时警告 65 -> 0;新进程登录(编码选择、新人物确认)正常。
(警告整理也改了 海洋2002/ 下 1038 个文件的无用局部变量等——那份夹带的源码不加载,这些改动没有进 HEAD/工作树对比加载。)
- 2026-10-11:编译警告第二轮(按驱动报告逐条修):按位 & 改逻辑运算 0 处,返回类型/原型 42 处,NPC+F_UNIQUE 的 init() 0 个,带初始化的未用局部变量 5 个,只在 #if 分支用的声明移进分支 0 个文件;改动 33 个文件,HEAD 与工作树各载入一次无回退(33 objects; HEAD PASS 30, tree PASS 30; regress 0; newly loading 0),这些文件的警告 48 -> 1。
深度功能测试(§10.7,2026-10-11):“无副作用表达式”警告复查(批 4)
liangyi-jian.bak.lpc:int i; level;去掉无用的level;。d/xingxiu/nanjiang2/3.lpc、shanjiao.lpc:删去空的if (current_water == 0) {}(行为不变)。- 改动的对象 HEAD 与工作区都能载入,无回退。
编译警告收尾(2026-10-13)
scripts/toolkit_fix.sh 一轮去掉 16 处多余继承,其余手工,能编译的文件警告清零;改动的对象在 HEAD 与工作树各载入一次(27 objects; HEAD PASS 19, tree PASS 19; regress 0; newly loading 0)。
adm/roommaker.lpc与海洋2002/hy3/adm/roommaker.lpc把special_props声明两次,代码用的是后一份(多file_name),删去前一份(同 wqfy)。- 四间书库(
d/city/library、d/city2/library及海洋2002/hy3/下两间)自己的STORY_DIR覆盖globals.h的同名宏,先#undef。 海洋2002/hy3/d/pk/sjsz/obj/control.lpc在开头把auto_check()原型写成int,中间又写了两条varargs void auto_check(...),定义是int auto_check();删去两条void原型。海洋2002/hy3/quest/guojob/guo.lpc同时包含zzzjob.lpc、teamjob.lpc,两者都声明names:teamjob.lpc的改名team_names(同 yxjh);天书任务quest/shenshu/npc/man.lpc、testman.lpc的kill_ob()改void(同 jh2006)。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。12 个文件 12 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断);x op= 浮点先把浮点转成整数再运算(系数小于 1 时 x 直接变 0),改为x = to_int(x op ...):/kungfu/skill/fengyun-shou/foguang.lpc(damage *=)、/kungfu/skill/taiji-quan/zhenup.lpc(damage *=)、/海洋2002/hy3/kungfu/skill/fengyun-shou/foguang.lpc(damage *=)、/海洋2002/hy3/kungfu/skill/taiji-quan/zhenup.lpc(damage *=)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。 - 2026-10-14:1 个存档文件(其他 1 个;
海洋2002/hy3/adm/daemons/securd.o)还是原档案的 GBK 编码,没有转成 UTF-8,restore_object()报Invalid utf8 string后对象以空数据启动(表情守护进程没有表情、留言板没有帖子、玩家无法载入)。scripts/lpc_save_encoding.py逐个按 GB18030 解码,结果按存档格式解析通过才写回(KB 03 §4)。
负数伤害当作消耗(2026-10-15,KB 06 §7.241)
receive_damage("neili", -52)、receive_damage("qi", -12) 之类把 add() 的负数写进了 receive_damage():本库 feature/damage.lpc 对负数伤害报错(或归零),练功/运功时报 F_DAMAGE: 伤害值为负值 或消耗失效。已改为正数伤害,内力/精力类改为 add(type, -N)(scripts/lpc_fix_damage_calls.py):/海洋2002/hy3/kungfu/skill/shaolin-tantui。
内力/精力当作伤害种类(2026-10-15,KB 06 §7.241 第二类)
本库 feature/damage.lpc 的 receive_damage() 不接受 jingli/neili/force 等种类,me->receive_damage("jingli", 30) 之类在练功、绝招、吸取时报 F_DAMAGE: 伤害种类错误。29 处改为 add(type, -N)(scripts/lpc_fix_damage_calls.py 第二步)。
无门派玩家的 family 映射(2026-10-15,KB 05 §7.30 第三种)
mapping fam = ob->query("family"); 之后直接 fam["family_name"]:没有门派(新人或叛师后)的玩家在学习、练功、拜师、进入门派房间时报 Value being indexed is zero,而不是作者写好的提示。4 处在声明行加上 if (!mapp(fam)) fam = ([ ]);(scripts/lpc_fix_family_guard.py)。
东方故事/西游记体系的技能文件(2026-10-15,KB 06 §7.241 第三类)
这些 kungfu/skill 文件来自 gin/kee/sen/force 体系,本库人物只有 jing/qi/neili:receive_damage("kee", 30) 报 伤害种类错误,query("force") 永远为 0,技能学不了也练不了。按 kee→qi、gin/sen→jing、force→neili、max_force→max_neili、force_factor→jiali 改了属性名(scripts/lpc_port_es_props.py),共 6 个文件。
其他体系的属性名(2026-10-15,KB 06 §7.241 第三类,--any-file)
另有 3 个文件(绝招、状态、物品、NPC 等,不限于 kungfu/skill)来自 gin/kee/sen/force 体系,同样按 kee→qi、gin/sen→jing、force→neili 改名(scripts/lpc_port_es_props.py --any-file)。
family 映射(补)(2026-10-15)
mapping fam = (mapping)me->query("family");(带类型转换的写法)同样直接取下标,4 处补上 if (!mapp(fam)) fam = ([ ]);。
message_vision(msg, environment(me), me)(2026-10-15,KB 06 §7.242)
把房间当成了动作者:message_vision() 取房间的 name() 得 0,replace_string(msg, "$N", 0) 报错。在状态(中毒)里,这个错误发生在 apply_condition(name, duration - 1) 之前,状态永不结束,心跳里后面的恢复也每次被跳过。改为 message_vision(msg, me):/海洋2002/hy3/kungfu/condition/sanpoison。
family 映射(再补)(2026-10-15,KB 05 §7.30)
mapping fam; fam = (mapping)ob->query("family");(先声明后赋值)和泥潭式的 query("family", me) 同样直接取下标:无门派的新人拜师(attempt_apprentice/recognize_apprentice)、NPC 的 greeting、房间的 valid_leave、ask_me 等报 Value being indexed is zero。18 处补上 if (!mapp(fam)) fam = ([ ]);(刚 recruit_apprentice() 之后的除外)。
妙手回春的银针检查(2026-10-15)
miaoshouhuichun 的 valid_learn() 用了未赋值的 object ob(ob->query_temp("weapon") 报错),还把兵器对象和字符串 "银针" 比较。改为 ob = me->query_temp("weapon"),再用 id("yin zhen")/id("yinzhen") 判断是否银针。
空手使用兵器绝招(2026-10-15,KB 06 §7.246)
perform 指令允许直接点名招式(如 perform stick.ban),这些绝招读了 me->query_temp("weapon") 却从不检查,空手时 weapon->name() 报错(有的已先扣了内力)。在赋值后补 if (!objectp(weapon)) return notify_fail("你必须手持兵器才能使用这一招。\n");(scripts/lpc_fix_perform_weapon.py):duanyun-fu/hongtianza、murong-jianfa/kuangjian、tianyu-qijian/kuangwu、wuyue-blade/jiao。
技能名拼错(2026-10-15,KB 06 §7.249)
query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):douzhuan-xignyi→douzhuan-xingyi(/d/mr/obj/shu1、/海洋2002/hy3/d/mr/obj/shu1);jinwu-dao→jinwu-daofa(jinwu-daofa、/海洋2002/hy3/kungfu/skill/jinwu-daofa);quanzhen-jianfa→quanzhen-jian(quanzhen-jian/ding、/海洋2002/hy3/kungfu/skill/quanzhen-jian/ding);yinyun_ziqi→yinyun-ziqi(yinyun-ziqi/cure、/海洋2002/hy3/kungfu/skill/yinyun-ziqi/cure);buddhsim→buddhism(/海洋2002/hy3/kungfu/skill/qianye-shou)。
技能名拼错:技能文件里拼错自己的名字(2026-10-15,KB 06 §7.249)
技能自己的文件(或它的绝招目录)里,query_skill() 读的是差一个字母的自己的名字,永远得 0:“基本功夫要高过本技能”之类的限制从不生效,按等级取招、计算伤害的那一项一直是 0。改为本技能的名字(scripts/lpc_fix_skill_typo.py 的 self 判据):chanyun-tui→chuanyun-tui(chuanyun-tui/shuangjue、/海洋2002/hy3/kungfu/skill/chuanyun-tui/shuangjue)。
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,而不是得到拒绝。11 处(8 个文件)改为 dbase 路径查询 query("family/master_name"):有门派时值相同,没有时得 0;原有的判断照常生效(scripts/lpc_fix_family_index.py)。给映射赋值的写法未动。
存档读不出来:字符串里的换行、空行(2026-10-15,KB 03 §4.4)
海洋2002/hy3/clone/gift/npc/bingqipu.o、海洋2002/hy3/data/board/yipin_b.o:字符串里有原样的换行(save_object() 存成 \r,restore_object() 逐行读,原样的换行让字符串收不了尾),或有空行(Illegal file format),读档的对象(留言板、NPC、玩家)从空白开始。换行改写成 \r、删去空行(scripts/lpc_save_encoding.py --from-raw),驱动核对全部能读回(scripts/lpc_save_restore_check.py)。
NPC 带着本库没有的物品,载入失败(2026-10-15,KB 06 §7.73)
3 个文件、262 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/migong/obj/cloth、/d/migong/obj/sword、/d/kaifeng/obj/yayifu)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 0 个,改后 141 个(141 个原来载入不了),没有退步;其余 19 个另有原因(多为 set_skill() 了本库没有的武功)。
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 载入不了,刷出它的房间也少一个人。本库有 170 个对象设了本库没有文件的武功(62 个名字,最多的是 dragon、gedoushu、bingxue-xinfa)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:29 个里 28 个通过,其余 1 个停在别的错误上(海洋2002/hy3/clone/guard/baoer:*No program in object '/海洋2002/hy3/clone/guard/baoer'!)。(scripts/lpc_guard_set_skill.py)
武功名拼错的 NPC(2026-10-19,KB 06 §7.258)
set_skill("kunlun-shnfa", …)、map_skill("dodge", "shaolin-shnfa") 这样的调用把武功名拼错了:本库没有这个武功文件,有的是拼对的那个。以前这种 NPC 在 create() 里报 No such skill 载入不了;武功缺失时跳过之后它们能载入,可是少了那门武功(set_skill 被跳过,map_skill 映射不到,门派弟子用普通轻功/拳脚)。按核对过的对照表改正,共 87 处、78 个文件:kunlun-shnfa→kunlun-shenfa(28 处)、shaolin-shnfa→shaolin-shenfa(22 处)、lingbo-weibu→lingboweibu(6 处)、yunu-xinfa→yunv-xinfa(6 处)、chang-quan→changquan(6 处)、tiaji-jian→taiji-jian(4 处)、yan-fly→yanfly(4 处)、shenhuo-shengong→shenghuo-shengong(2 处)等共 15 个名字。(scripts/lpc_fix_skill_names.py)
全局头文件丢了 1 个宏(2026-10-19,KB 04 §6.16)
/include/globals.h 里没有定义 MAILDIR,用到它们的物件(名单里 2 个)编译时报 Undefined variable/Undefined function,整个物件载入不了(编译器在第一个未定义的名字上停下,后面的错误和警告都看不到)。同系的库在各自的头文件里都有这几行:照其中和本库共有文件最多的那个库,原样补到 /include/globals.h 最后一个 #define 之后,宏体里出现的路径在本库里都存在。名单里的物件除了这几个宏之外还有别的错误,暂时仍载入不了。(scripts/lpc_restore_defines.py)
和包含文件同目录的头文件,改成引号形式(2026-10-19,KB 04 §6.17)
5 个文件用尖括号写 #include <hedi.h> 这类头文件(hunyin.h 等),头文件其实就放在它们自己的目录里。老驱动对尖括号名字也会先找包含文件所在目录,本驱动只找 include 路径,所以报 Cannot #include ...,整个物件载入不了。改成 #include "hedi.h"(引号形式相对包含文件解析,预加载、lpcc 和运行时编译都一样)。(scripts/lpc_fix_local_include.py)
bite.lpc 被截断(2026-10-19,KB 06 §7.266)
d/city/bite.lpc(忘尘居)在 set("no_clean_up", 0); 处结束,缺 setup(); },驱动报 syntax error, unexpected end of file。同系的 hy2000/hy2002/hy5/haiyang2 带着完整的这个文件,补上 setup(); 和结尾的 }。
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 03 §4.7、KB 04 §6.10)
d/hangzhou/npc/tangzi.lpc(和海洋2002/hy3/里的旧副本):整个文件只是 UTF-16 的空格(\x00 \x00 …,作者把这个物件清空了),驱动报Illegal embedded NUL byte (0x00) in source;旧驱动编出一个空程序。改成一个换行,行为和原来一样(空程序),不从 xkx100 抄回同名 NPC。
逐个载入与 HEAD 对比,没有退步。
玩家打造兵器模板没有打造者时(2026-10-19,KB 06 §7.275)
/d/npc/m_weapon/weapon/m_blade.lpc、/d/npc/m_weapon/weapon/m_club.lpc、/d/npc/m_weapon/weapon/m_hammer.lpc、/d/npc/m_weapon/weapon/m_staff.lpc、/d/npc/m_weapon/weapon/m_sword.lpc、/d/npc/m_weapon/weapon/m_whip.lpc 等(共 12 个)的 create() 开头 me = this_player(); 之后无条件读 me->query("weapon/name") 等打造数据。房间刷新时没有人在场(me 为 0),或者 NPC 在别的 NPC 的命令环境里 carry_object() 这个模板(NPC 的 weapon 属性是文件名字符串),旧驱动里读出来是 0 或字符串,switch 照样落到 default;新驱动报 Bad argument 1 to EFUN call_other() / Bad argument 1 to switch,NPC 和它所在的房间载入不了(--walk 普查里一个玩家走进房间就触发)。在 //读入数据 之后加 if (!me || !mapp(me->query("weapon"))) { setup(); return; }:没有打造者就留一个没有名字的空模板,玩家打造的流程不变。
标准整理轮,第二轮(2026-10-19,scripts/toolkit_fix.sh)
本库在新驱动下重扫,机械修复:22 个未使用的局部变量。修后与 HEAD 逐个物件对比载入,没有退步。
手工收尾(2026-10-19,KB 04 §6.10)
海洋2002/hy3/d/kaifeng/(本库里又收了一份开封擂台):ground1/2/3.lpc继承ground0.lpc,又各自 includeground.h,三张表(modes、mode_hints、families)在每个 inherit 之后又声明一遍(Redeclaration of global variable);照 xiyouji 系各库d/kaifeng/ground.h的办法,派生程序在 include 前#define GROUND_TABLES_INHERITED,头文件用#ifndef把三张表包起来。- 同目录的
ground2.lpc、ground3.lpc、guanli.h写的是#include <ground.h>,尖括号在 include 路径里找不到同目录的头文件(Cannot #include ground.h),这两个物件一直载入不了;改成#include "ground.h",现在载入得了,同时露出里面 21 个未使用的局部变量,留给下一轮标准整理。
载入 14 个相关物件(含 d/*/marry/chen.lpc 一类 include hunyin.h 的):HEAD 通过 12 个,现在通过 14 个,没有退步。
补回丢失的标点(2026-10-19,KB 03 §4.6)
转换时丢掉的分号、逗号、引号或文件末尾的 },共 4 个文件,修前 HEAD 载入失败。别的库里同路径的完好副本只比这里少几个标点,按它补回(括号里是那个库):/d/baituo/npc/obj/bowl.lpc(cctx)、/d/baituo/obj/bowl.lpc(cctx)、/d/city2/npc/zhengmen.lpc(jqxz2008)。文件末尾少了 }(最后一个或两个字节丢了),补上:/海洋2002/hy3/d/city2/npc/zhengmen.lpc(2 个)。修后与 HEAD 逐个对比载入:这 4 个物件全部载入,没有退步(scripts/lpc_restore_from_twin.py、lpc_fix_eaten_quote.py --comma、lpc_fix_eof_braces.py)。
标准整理轮(2026-10-19,scripts/toolkit_fix.sh)
本库在新驱动下重扫,机械修复:17 个未使用的局部变量,4 个带初始化的未使用声明。修后与 HEAD 逐个物件对比载入,没有退步。
x != "a" || x != "b" 和 x == "a" && x == "b":同一个值的比较链永远成立/永远不成立(2026-10-20,KB 06 §7.282)
if (arg != "wan" || arg != "丸") return notify_fail(...) 对任何 arg 都为真,dir == "south" && dir == "north" 永远为假:条件下面的守卫要么永远拦、要么永远不拦。作者要的是“既不是 A 也不是 B”(&&)或“是 A 或 B”(||)。!= 链的 || 改成 &&(仅当条件下面是 return 的拒绝/守卫),== 链的 && 改成 ||(scripts/lpc_fix_cmp_chain.py)。改动:/d/mingjiao/shanmen.lpc(1 处)、/海洋2002/hy3/d/changan/ca2.lpc(1 处)、/海洋2002/hy3/d/mingjiao/shanmen.lpc(1 处)。
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/arch/callouts.lpc(2 处)。
if (x = 常量):条件里写成赋值的比较(2026-10-20,KB 06 §7.290)
if (arg = "button" || arg = "按钮")、if (!arg || arg = "") return 0;、else if (check = 0 && tempB != ""):把 == 写成了 =,条件的值就是那个常量,分支对所有非零数和任何字符串(连 "" 都算)永远成立、对 0 永远不成立,变量还被覆盖;驱动对此没有任何警告。每一处 = 改成 ==(scripts/lpc_fix_cond_assign.py,共 22 处)。改动:/d/city4/puke-room.lpc(4 处)、/d/hangzhou/npc/meipo.lpc(1 处)、/海洋2002/hy3/adm/daemons/combatd.lpc(1 处)、/海洋2002/hy3/d/city4/mj/1mj.lpc(3 处)、/海洋2002/hy3/d/city4/mj/mj.lpc(3 处)、/海洋2002/hy3/d/city4/obj/mj.lpc(3 处)、/海洋2002/hy3/d/city4/puke-room.lpc(4 处)、/海洋2002/hy3/d/hangzhou/npc/meipo.lpc(1 处)、/海洋2002/hy3/kungfu/class/emei/tobebonze.h(1 处)、/海洋2002/hy3/kungfu/skill/meinv-quan.lpc(1 处)。
callouts:sizeof(info[i]) == 4 把每一行都挡掉了(2026-10-20,KB 06 §7.287)
驱动的 call_out_info() 每项只有 3 个元素,输出行前的守卫 if (sizeof(info[i]) == 4) 永远不成立,指令只打印表头。改为 >= 3。改动:/cmds/arch/callouts.lpc(1 处)、/海洋2002/hy3/cmds/arch/callouts.lpc(1 处)。
登录密码超过 255 字节:crypt() 抛错(2026-10-20,KB 06 §7.294)
驱动的 crypt() 对超过 255 字节的密钥直接报错(Error in crypt(), check your salt),老驱动则按任意长度散列。在登录提示符下输入 256 字节以上的密码,密码检查那一行就抛错,连线停在一个裸的 > 提示符上,登录走不完(password 指令的旧密码检查同样);用 300 个 x 当参数的扫描发现的。/adm/single/simul_efun.lpc 里补了一个同名的 simul_efun crypt(key, salt):只有超长的密钥才被截短(先截到 255 个字符,再逐字符截到 sizeof(to_buffer(key)) <= 255,限制按字节算,一个汉字占三个字节),再交给 efun::crypt();普通密码原样散列,已存的密码散列不受影响(scripts/lpc_add_sefun.py crypt)。
目录读出后的后缀截取宽度(2026-10-20,KB 06 §7.296)
.c 改名成 .lpc 之后,get_dir("*.lpc") 读出的文件名还按两个字符截取($1[0..<3]):baguadao.lpc 变成 baguadao.l,用它拼出的路径加载不到对象(故事、战场、事件、帮助、自造物品数据、召唤列表等)。改成 [0..<5](scripts/lpc_fix_getdir_strip.py)。改动:/海洋2002/hy3/adm/daemons/eventd.lpc(1 处)。
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。
访客计数 howmany_visitor() 读文件不检查(2026-10-20,KB 06 §7.301)
howmany_visitor() 读访客计数文件(VISITORS / COUNTER / BBSVISITOR,只有运行中的 mud 才会写;.gitignore 把它排除在仓库之外),新检出的树里没有,read_file() 回 0,sscanf(0, ...) 报错。这份副本登录时不一定走到它,但调用时同样会出错。在读文件之后加了 if (!stringp(content)) return 0;(xkx100、xiakexing100、zzfy 早先的写法,scripts/lpc_fix_visitor_count.py)。改动:/海洋2002/hy3/adm/daemons/logind.lpc。
setbak 指令编译不过:文件末尾粘了半截 help()(2026-10-20,KB 06 §7.264)
cmds/usr/setbak.lpc 在完整的 help() 之后又重复了帮助文字的结尾(取消变数设定请用 unset 指令。……TEXT ); return 1; },没有函数头):Illegal character 0xe5。在第一个完整函数结尾处截断(去掉 7 行)。加载检查: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])) ...,不检查列表是否为空。本库现有的 NPC 文件里没有字面写成空 chat_msg 的(扫描只认 set("chat_msg", ({ })));守卫挡的是运行时才为空的列表和以后新增的 NPC。在 random(100) < chance 前面加 sizeof(msg) &&(scripts/lpc_fix_chat_empty.py;if (random(100) > chance) return 0; 的写法是 !sizeof(msg) ||)。改动:/inherit/char/npc.lpc、/海洋2002/hy3/clone/guard/baoer.lpc、/海洋2002/hy3/clone/guard/long2.lpc、/海洋2002/hy3/d/migong/obj/npc1.lpc、/海洋2002/hy3/inherit/char/npc.lpc(共 5 个文件)。
存档里的 .c 文件名改成 .lpc(2026-10-21,KB 03 §4.2 第 9 项)
存档里写着 "/d/city/npc/wei.c" 这样的文件名;extract 时源文件已经全部改成 .lpc,而驱动对带扩展名的路径只认字面,new() / load_object() / move() 都找不到它,调用方多半把失败当成「没有这个东西」悄悄跳过。本库 10 个存档共 10 处按 scripts/lpc_fix_save_dotc.py 改成 .lpc(只改 .lpc 存在而 .c 不存在的字符串;玩家存档、备份、留言板、巫师目录和文件索引缓存不动)。改动:/p/residence/cash971888263.o(1 处)、/p/residence/cash971888503.o(1 处)、/p/residence/cash971888605.o(1 处)、/p/residence/chao972181288.o(1 处)(等共 10 个存档)。
巫师命令不带参数时对 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 在第一处使用参数的语句前加守卫:文件自带 int help(object me) 的,守卫是 if (!arg) return help(me);(help() 定义在 main() 之后的 1 个文件补了原型 int help(object me);,否则编译报 Undefined function help)。改动(1 个文件,括号里是接住 0 的 efun):/cmds/wiz/test.lpc(present)。加载检查 HEAD 与工作树对照,没有退步。
home:模板 /clone/misc/wizroom.lpc 档案里没有(2026-10-21,KB 06 §7.310)
管理员扫描里 home 报 Bad argument 1 to EFUN replace_string() Got: int(0)(cmds/imm/home.lpc 第 30 行):巫师还没有工作室时,home 从 /clone/misc/wizroom.lpc 读模板,把 NAME 换成名字写进 /u/<id>/workroom.lpc;本库没有这个模板,read_file() 答 0,第一次敲 home 的巫师就是系统错误。wizroom.lpc 是这一系的公用文件(hy3、chidi、wmkj、yxcs 等 28 个库都带,内容相同,只差换行符:“没有建造工作室的巫师的临时住处”,出口 north 通 /d/wizard/wizard_room,本库有这个房间),从 hy3 原样复制过来,加载检查通过。
refresh(retask):动词函数不返回 1,搬动或销毁带动作的物件时报错(2026-10-21,KB 06 §7.339)
管理员扫描里 refresh 在重新分布使命之后报 Illegal to move or destruct an object (/questobj/...) defining actions from a verb function(refresh) in object(/obj/user#...) which returns zero.:main() 以第二条 message() 结尾、没有 return,答 0,而 TASK_D->init_dynamic_quest(1) 销毁了给玩家定义过动作(init() 里的 add_action)的使命物件:驱动只在动词答非 0 时才允许这样搬动或销毁,否则它接着遍历的动作表会悬空;答 0 还会让解析器在指令做完之后再补一句“什么?”。main() 末尾加 return 1;(同系已经返回 1 的 11 个同名文件没有动)。
blockade / recover:data/keep 不存在,解封之后世界仍然锁着(2026-10-21,KB 06 §7.340)
管理员扫描里 blockade 报 Error copying file from /data/user/f/fluffos.o to /data/keep/fluffos, Error: No such file or directory,随后的 recover 在第一个 cp() 上死于 */data/keep/fluffos: lstat failed,来不及执行最后的 LOGIN_D->set_madlock(0):封锁解不开。data/keep/ 是运行时目录,git 克隆里没有空目录。blockade 在第一次写之前 if (file_size("/data/keep") != -2) mkdir("/data/keep");(mkdir() 失败答 0,不抛错),recover 对没有保存档的玩家 if (file_size(src) < 0) continue;,其余照常恢复、照常解锁(scripts/lpc_fix_keep_dir.py,hy5 上实测:没有保存档时 recover 答“时空解封完毕”,blockade 建出 data/keep 并保存,recover 取回并删除)。改动(2 个文件):/cmds/arch/blockade.lpc、/cmds/arch/recover.lpc。
瓦耳拉齐 ask_back:送回去的是没赋值的 ob(2026-10-21,KB 06 §7.348)
带着地图问“回去”时 ob->move(__DIR__ "entrance"):ob 没赋值,路径也错成了 /d/gaochang/npc/entrance。xkx100 的原文是 me->move("/d/gaochang/entrance");,照改,去掉 object ob;。改动:/d/gaochang/npc/wuaer.lpc。
妙手回春 valid_learn():先读 ob->query_temp("weapon") 再给 ob 赋值(2026-10-21,KB 06 §7.348)
学这门暗器技能时 valid_learn() 第一个武器检查读的是没赋值的 ob,xue / learn 在这里抛错;后面 ob = me->query_temp("weapon") 的检查才是对的。有两种写法:if (!ob->query_temp("weapon")) 改成 me->query_temp("weapon");if ((string)ob->query_temp("weapon") != "银针")(拿一个对象和字符串比较,永远不等,补上 ob 之后这门技能也学不成)改成后一种写法里的 !objectp(ob = me->query_temp("weapon")) || !(ob->id("yin zhen") || ob->id("yinzhen"))。改动:/d/hxsd/pingan/shenzhen/skills/miaoshouhuichun.lpc、/d/pingan/pingan/shenzhen/skills/miaoshouhuichun.lpc、/d/pingan/shenzhen/skills/miaoshouhuichun.lpc。
注册到不存在函数的 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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 51 条注册(31 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/city/leitai.lpc(do_quit)、/d/city4/qishi.lpc(do_pass、do_resign)、/d/hxsd/pingan/npc/sighmoon.lpc(do_vendor_list)、/d/mr/shuichi.lpc(do_strike)、/d/pingan/npc/sighmoon.lpc(do_vendor_list)、/d/pingan/pingan/npc/sighmoon.lpc(do_vendor_list)、/inherit/misc/fboard.lpc(do_list)、/海洋2002/hy3/d/city/leitai.lpc(do_quit)、/海洋2002/hy3/d/city4/qishi.lpc(do_pass、do_resign)、/海洋2002/hy3/d/hxsd/pingan/npc/sighmoon.lpc(do_vendor_list) 等共 31 个文件。最多的函数名:do_quit 18 处、do_send 15 处、do_vendor_list 6 处、do_strike 4 处、do_pass 2 处。
对象路径里的 ..:new(__DIR__ "../obj/x") 报 Read access denied(2026-10-22,KB 06 §7.351)
__DIR__ 是当前文件所在目录(带结尾斜杠),__DIR__ "../obj/x" 就是 /d/zhiye/npc/../obj/x;驱动的 legal_path() 不接受带 .. 的路径,new() / load_object() 直接抛 *Read access denied.(实测,find_object() 对它答 0)。走库自己的 carry_object() / 房间 objects 表的路径,库的基类会先规范化,能用;直接把路径交给 new() / clone_object() / load_object() / move() 的不行,调用到那一步才报错。改成规范化后的绝对路径(scripts/lpc_fix_dotdot_dir.py --direct):/d/wudang/npc/xiaocui.lpc(__DIR__ "../obj/nuerxiang" → "/d/wudang/obj/nuerxiang")。
调用了、但这个库自己的守护进程里没有的函数(2026-10-22,KB 06 §7.354)
MACRO->函数() 调用的函数在这个库的守护进程里不存在:call_other() 对不存在的函数不报错、只返回 0,所以这些功能(时间显示、昼夜事件、名字检查、价格文字……)一直没起作用。同一血统另一个库的同名守护进程里有这些函数,原文照抄到本库守护进程末尾(scripts/lpc_port_function.py;调用处传的参数个数、函数体用到的函数 / 全局变量 / 宏 / 字符串键都在本库里有,已核对;守护进程在 HEAD 和工作区都能加载,每个函数用固定参数各调用一次:HEAD 返回 0,现在返回正常的值):/adm/daemons/natured.lpc ← aoxiangtianji:outdoor_room_event()(1 处调用);/adm/daemons/chinesed.lpc ← aoxiangtianji:chinese_period()(1 处调用)。
(: 函数 :) 指针指向对象自己没有定义的函数(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。 还剩 11 个函数没有补(item_desc 6, inquiry 3, other 2):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/cmds/imm/help2.lpc:confirm_l_choice、/d/city/ximenroad.lpc:look_gaoshi、/d/gaochang/zoulang2.lpc:look_writing、/d/gaochang/zoulang_1.lpc:look_writing、/海洋2002/hy3/cmds/imm/help2.lpc:confirm_l_choice、/海洋2002/hy3/d/city/ximenroad.lpc:look_gaoshi、/海洋2002/hy3/d/gaochang/zoulang2.lpc:look_writing、/海洋2002/hy3/d/gaochang/zoulang_1.lpc:look_writing、/海洋2002/hy3/d/pk/bwdh/npc/npc2.lpc:ask_for_quit、/海洋2002/hy3/d/pk/bwdh/npc/npc5.lpc:ask_me 等另外 1 个。 另有 1 处函数体被作者整段注释掉(指针没跟着删),是有意关掉的,没动。
运行期目录 log/nosave 不存在,写日志的 write_file() 直接报错(2026-10-22,KB 03 §4.3 补充)
这个库的 12 个文件里有 22 处 write_file("/log/nosave/...") / log_file("nosave/...")(static 改 nosave 时连字符串里的日志路径一起改了,原来是 log/static/):nosave/CRASHES、/log/nosave/LASTCRASH、nosave/promotion 等。压缩包和站点包里没有这个目录,write_file() 在目录不存在时抛 *Wrong permissions for opening file /log/nosave/... for append. "No such file or directory",调用它的函数(杀人记录、重启记录、call 日志……)当场中断。把 log/nosave 记进 scripts/wasm_keep_dirs.txt(站点和 pristine 树都按它建空目录);没有把字符串改回 static。证明(scripts/lpcc_eval.py,catch(write_file("/log/nosave/ZZTEST", ...)),fqyy2 / hy5 / bmxkx2001 的新进程):补目录前报上面的错误,补后写成功。
读不到的留言板:"/clone/board/x"->foo(); 在 create() 里报错(2026-10-22,KB 06 §7.357)
xkx 系的房间在 setup() 之后写 "/clone/board/gaibang_r"->foo(); 来加载留言板(foo() 本来就没有定义,只为了触发加载)。这个库没有这个文件,call_other() 报 *call_other() couldn't find object '/clone/board/gaibang_r',对象留在内存里、load_object() 却抛错:玩家第一次走进房间就看到错误,写在 create() 中间的还会切掉后半段。scripts/lpc_fix_missing_foo.py 把 1 条单独成行的语句(/clone/board/jail_b;/d/city2/jail.lpc)注释掉并写明原因(别的房间里同样的语句本来就是作者自己注释掉的)。证明和 45 个库的整体加载检查见 KB 06 §7.357。
log/ 下的日志子目录没有人建:写日志的 write_file() / log_file() 报错(2026-10-22,KB 06 §7.358)
这个库的代码往 log/ 的 1 个子目录写日志:log/html(海洋2002/hy3/clone/npc/robot-judge.lpc)。压缩包、git clone 和站点包里都没有这些目录(本地启动没有写过它们,gen_keep_dirs.py 没记),write_file() 对不存在的目录抛 *Wrong permissions for opening file /log/dlog/money for append. "No such file or directory",调用它的函数在写日志那一行中断,后面的奖励、标志、步骤都没有执行。本库的 log_file() 会自己建目录(实测,或读源码:nitan 系的 assure_file()),所以这里只记了直接用 write_file() / save_object() 写的目录。把 log/html 记进 scripts/wasm_keep_dirs.txt(scripts/lpc_keep_log_dirs.py;站点和 pristine 树按它建空目录)。证明(scripts/lpcc_eval.py,在 /adm/daemons 这类目录下的求值对象里写 /log/zzdirprobe/...,新进程)见 KB 06 §7.358 的实测表。
(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)
inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 4 个文件里 4 行只含这个指针的行("gaoshi": (: look_gaoshi :), "高昌迷宫": (: ask_gaochang :), "writing": (: look_writing :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。
汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 24 处(2026-10-22,KB 06 §7.360)
原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字);各派掌门的辈分判断(name[0..1] == "澄")永远不成立。
本库 24 处(19 个文件):拼名字的赋值 16 处、与汉字常量比较 8 处。例:name[2..3] → name[1..1](/d/biwu/da-mo.lpc:152);name[0..1] → name[0..0](/d/mr/npc/bo.h:17);name[0..1] == "慧" → name[0..0] == "慧"(/kungfu/class/shaolin/cheng.h:140)。scripts/lpc_fix_gbk_slice.py 把下标按字符重算(社区的 UTF-8 版 xkx100utf8 里就是同样的写法,作对照);没有改的:截断(name = name[0..7],现在最多 8 个字,只是放宽)、ASCII 的区间(fname[0..3] == "room"、ctime(time())[4..19])和上下文看不出是汉字的区间。证明(scripts/lpcc_eval.py,bixiecanyang;其余各库是同一份文本):让澄字辈掌门 attempt_apprentice() 一个名叫「张三」的访客,HEAD 的法名是 慧,改后是 慧三;抽样 28 个库的 HEAD / 工作树加载对比 0 退步。工具不幂等([0..3] 折半得 [0..1],又是对齐区间),改过的文件记在 scripts/gbk_slice_converted.txt,不要重跑。
长度检查按字节写,旁边的提示按汉字写:10 处(2026-10-22,KB 06 §7.362)
老档的 strlen() 数字节(一个汉字 2 字节),转成 UTF-8 后它数字符:提示写「必须是 2 到 6 个中文字」的检查还是 strlen(name) < 4 || strlen(name) > 12,于是两三个字的房名、帮会名、群名、聊天室名、称号被提示自己说允许的那句话拒绝,上限却是提示的两倍;留言板的标题检查 i < 4 || i > 24 || i % 2 还会拒绝所有奇数个字的标题(post 你好吗)。本库 10 处(5 个文件):if ((strlen(name) < 2) || (strlen(name) > 20)) {(/cmds/usr/buildrest.lpc:69);if ((strlen(name) < 2) || (strlen(name) > 40)) {(/cmds/usr/buildroom.lpc:75)。scripts/lpc_fix_strlen_gate.py 以提示为准:条件里每个 strlen 门槛都恰是提示所说数字的两倍才折半,存长度的变量(i = strlen(arg);)的门槛和 || i % 2 奇偶项、其后循环的 i % 2 == 0 && 一并处理;提示里有英文 / 字母 / 字节的不动。证明(scripts/lpcc_eval.py,bxsj 的留言板,求值对象继承 NPC 并重写 edit() 记录调用,command("post 标题")):HEAD 拒绝「你好」和「你好吗」、放行 23 个字的标题;改后接受前两个、拒绝第三个;shenmo 的 named.lpc invalid_new_name("张三"):HEAD 回 名字只能是2-4个中文字。,改后回 0。抽样 24 + 15 个库的 HEAD / 工作树加载对比 0 退步。
按字节写的汉字循环、茶馆猜诗和同盟频道标题(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 区间 8 个文件;茶馆猜诗 mixup 1 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。
血条 / 表情标题按字节算显示宽度(2026-10-23,KB 06 §7.365)
老档把汉字当两字节:bar_string[0..(val * N / max) * 2 - 1] 把「格数」换成字节下标,半格血条在本驱动(按字符算区间)上会整条画满;巫师表情 edemote 的 repeat_string("─", 13 - (strlen(emotion) / 2)) 也会少减汉字宽度。scripts/lpc_fix_display_pad.py:去掉 CJK 血条 / 空白条上的 * 2(jings/qis 赋值和 blank_string3[0..(23-a)] 的 23→11 一并改),表情标题去掉 / 2;ASCII 空格的 none_string / empty_string 仍保留 * 2(一格两个空格)。本库 4 处(2 个文件)。证明(lpcc_eval.py hhsj):tribar_graph(6,9,12) 在 HEAD 画出整条 12 个■,改后为 6 个■+3 个□;麻将 strlen(tempB)/2 是两字符牌码,未改。
Beyond 出口仍指向 /u/beyond(2026-10-23,KB 06 §7.368)
宁波 / 苏州 / 慕容等区域已进 /d/ningbo、/d/suzhou、/d/mr、/d/hz,出口与任务物路径仍写 /u/beyond/…。scripts/lpc_fix_beyond_exits.py 在目标 .lpc 存在时改写(murong→mr)。本库 1 处。证明:sjplii xikou HEAD 西向 /u/beyond/ningbo/aywsi,改后 /d/ningbo/aywsi 可加载;bxsj hubian1 三向均可加载。
死巫师出口 gold/dock、sunrise、mei(2026-10-23,KB 06 §7.370)
/d/wanjiegu/road4 东向 /u/g/gold/dock、峨眉寿星居南向 /u/xinyue/sunrise_room、玩家卧室通往 /u/mei/… 均无对应房间。scripts/lpc_fix_drop_dead_u_exits.py 删除这些出口行,并去掉同方向的 create_door(否则 create_door 无出口会炸)。本库 1 处。证明:zxty08nxgbb 的 road4 HEAD 有东向,改后没有;mei_room1 可加载。
深度功能测试(2026-10-27,§6.25 zhongling tell_object 引号错位)
d/wanjiegu/npc/zhongling(及同路径副本)的 tell_object("who, 钟灵脱下一双绣花鞋。\n") 把对象参数写进了字符串;改为 tell_object(who, "…")。证明:原文错位;改后加载,语料残渣 0。
深度功能测试(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… 行。