info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
小雪初晴(游戏内品牌"SnowMud")由 CuteRabbit Studio(JackyBoy)为 CCTX 与 SDXL 社群开发,取材温瑞安武侠小说世界观——有别于本合集常见的金庸/古龙地图模板,药王谷、温家/温寨、龙门等都是温派小说里的真实地标;字节级比对显示,这份档案约 2000 个文件的地图有 99.6% 与姊妹档案 `xxcqii`("小雪初晴II",地图规模更大、多出约 150 个文件)逐字节相同,是同一世界观的一个子集版本而非各自独立开发的续作;两者的管理员账号引导机制也不同:这份档案通过 `securd.lpc` 里硬编码的创始人账号授权,`xxcqii` 则走标准的 `wizlist` 文本文件机制,管理员登录后称号直接显示"【天神】",是这批档案里权限称号最直白的一个。
English
Branded in-game as "SnowMud," developed by CuteRabbit Studio (JackyBoy) for the CCTX and SDXL communities and set in the world of Wen Ruian's wuxia novels -- a setting distinct from the more common Jin Yong/Gu Long map templates elsewhere in this collection, with real landmarks like the Medicine King Valley (药王谷), the Wen family estate (温家/温寨), and Dragon Gate (龙门). A byte-level check confirms this archive's entire ~2,000-file map is a 99.6%-identical subset of sibling xxcqii's larger map -- xxcqii ("Second Snow Clearing") extends this same world with roughly 150 additional files rather than being an independently-built sequel. The two also seed their admin account differently: this archive hardcodes a founder id in securd.lpc rather than reading the standard wizlist file, and shows the highest-ranking title in this batch, "【天神】" (God), directly on login.
README
内容亮点
- 和姊妹档案
xxcqii共享同一套温瑞安世界观地图(药王谷、温家场 景、龙门等),但管理员账号引导机制完全不同:这份档案通过securd.lpc里硬编码的创始人账号(jackyboy)授权,xxcqii则是标准的wizlist文本文件机制。 - 管理员登录后称号直接显示"【天神】",是这批档案里权限称号最直白 的一个。
- 这份档案原始压缩包里完全没有
adm/etc/目录,导致任何一次连 线(不只是 WASM 环境)在英文名字提示之前就会被访客计数器的write_file()静默炸掉,连注册流程都进不去——这是一个真实存在、 会影响原生部署的缺陷,不是测试环境特有的问题(详见下方 bug 修复 说明)。 - 深度功能测试发现 WASM 阶段创建
adm/etc/目录时遗漏了一个文件 (motd),导致每一次注册在幕后仍然静默失败——角色对象正常 创建、存盘,却从未被放进任何房间,look只会看到"灰蒙蒙一片", 没有任何报错。另发现同类连锁 bug(natured.lpc缺失的昼夜数据 表)、§8.9 食物/饮水年龄检查错对象、printf 调试残留、2 处 §7.68 复活软锁,以及一个从未被记录过的 bug:登录对象自身的set()权限保护过严,导致邮箱注册流程生成的新密码从未真正生效(详见 NOTES.md 和 AGENTS.md §7.9/§7.82)。
- 更正(2026-08-05):上面提到的"7.68 复活软锁"修复已经撤销——经重新评估,鬼魂"不在场"时放弃复活流程更可能是有意的游戏设计(多数这类档案里鬼魂本身就无法自行移动,离开是一种游荡机制,回来时 init() 会重新触发流程),不是需要强制重试的 bug;详见 NOTES.md。
本次修复的关键 bug
真正会挡住每一次连线的 bug:这份档案里根本没有 adm/etc/ 这
个目录(连原始压缩包里都没有)。logind.lpc 一连线就会呼叫
write_file(USERS, ...)(访问人次计数器,每次连线都会执行,在英
文名字提示之前),但 write_file() 能自动创建缺失的文件,创
建不了缺失的目录,所以这一步每次都会报"Wrong permissions for
opening file /adm/etc/users for overwrite, No such file or
directory"。这个错误没有被任何地方拦截,驱动就静默把连线直接扔进
了裸的指令提示符——注册流程的 input_to() 根本没被呼叫过,之后
输入什么都是"什么?"(无法识别指令)。这个 bug 在原生开机下也会一
模一样地发生,不只是 WASM 下的问题。已经创建 adm/etc/ 目录,附
带初始化的 users/iduser 计数器文件(都是"0")。
顺带确认(§7.56 双档案陷阱):SECURITY_D 真正指向的是
/adm/daemons/securd,不是那份引用(这份代码库里其实没用到
的)WIZLIST 文本文件的分身档案 securityd.lpc。securd.lpc 通
过 restore_list() 里硬编码的 set("wiz_status/jackyboy",
"(admin)") 来引导管理员账号(和这一轮之前修的 sj 是同一个模
式),已经在旁边加上 set("wiz_status/fluffos", "(admin)")。
管理员账号 / Admin account
- ID:
fluffos - 密码 / Password: 注册时自设(临时密码 + 确认)
- 权限 / Level:
(admin),注册完成时连线横幅已经直接显示"目 前权限:(admin)",人物称号也是最高级的"【天神】"。
警告:对外公开架设前请务必修改此密码。
本地运行
cd libs/xxcq
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40135。
NOTES · 移植与修复记录
WASM 修复摘要(迁移自 meta.json 的 group_note)
小雪初晴,游戏内品牌名"SnowMud",CuteRabbit Studio(JackyBoy)为 CCTX 和 SDXL 制作的一款以温瑞安小说为题材的 MUD。WASM 修复了一个真正会卡死游戏的 bug:这份压缩包里任何地方都没有 adm/etc/ 这个目录(连 raw/ 里也确认不存在),导致 logind.lpc 最开头的 write_file(USERS, ...) 呼叫(访客计数器自增,每一次连线、在 id 提示出现之前都会执行)抛出"Wrong permissions for opening file /adm/etc/users for overwrite, No such file or directory"——write_file() 能创建缺失的档案,但创建不了缺失的目录,所以这是每一次连线都会触发的未捕获错误,驱动会静默吞掉它,直接把裸连线丢进一个交互式指令提示符,从未呼叫 input_to() 进入注册流程(之后任何输入都显示成无法识别的"什么?"指令)。这在原生启动下也会一模一样地失败,不只是 WASM 环境的问题。已通过创建 libs/xxcq/work/adm/etc/ 目录并预置 users/iduser 计数器档案(均为 '0')修复。另外通过源码确认(§7.56 双档案歧义提醒)SECURITY_D 实际指向 /adm/daemons/securd,而不是那份引用了(这份代码库里根本没用到的)WIZLIST 文本档案的诱饵档案 .../securityd.lpc——securd.lpc 是通过 restore_list() 里一行硬编码的 set('wiz_status/jackyboy', '(admin)') 呼叫来播种管理员账号的(和本轮之前修过的 sj 那份档案是同一种形态),而不是读取 wizlist 档案;已加上一行同样的 set('wiz_status/fluffos', '(admin)')。注册流程在一次连续的 WASM 客户端会话里完整验证过:GB/BIG5 选择→英文 id→y/n 确认创建→中文名字→临时密码+确认→接受天赋赠礼(y)→性别(m/f)→带着完整角色属性表进入游戏世界,连线横幅里已经直接显示"目前权限:(admin)",还有"【天神】"最高阶层称号,全程没有任何意外错误。LPC 格式化工具对全部 2886 个档案运行(写入 2828 个,28 个报错,30 个未改动)。没有 :: 父类呼叫拆分命中,没有 CJK 重新加空格命中,没有 case 标签带尾随注释的候选。唯一一个存在的 map.lpc 档案确认内容完全相同(只是空白差异)。格式化后用同样的完整注册流程重新验证过——干净,管理员权限依然是 (admin)。
§10.7 深度功能测试(本次新增)
此前只做过一次连续会话内的注册流程验证。本次实际深入游玩后发现:WASM
修复阶段创建 adm/etc/ 目录时遗漏了一个文件,导致每一次注册实际上
仍然会在幕后彻底失败——只是这次失败得比之前更隐蔽,不会中断连接,而
是让角色永远卡在"灰蒙蒙一片、什么也没有"的无环境状态。这是本次会话里
发现的最严重的一类 bug,同时还额外发现了 §8.9、printf 调试残留、2 处
§7.68、以及一个从未被记录过的登录对象权限过严 bug。
修复 1(本次最严重的发现):adm/etc/motd 缺失导致每次注册都静默失败
adm/daemons/logind.lpc 的 enter_world() 里有一行完全没有防护的
write(read_file(MOTD));(MOTD = /adm/etc/motd)。WASM 阶段虽然
创建了 adm/etc/users/iduser,却没注意到还需要一个 motd 文件——
read_file() 对不存在的文件返回 0(不是字符串),write(0) 会让
receive_message() 抛出未捕获的 *Bad argument 1 to receive(),而这
一行恰好出现在同一个函数里、真正把角色 move() 到起始房间那一行代
码之前——所以每一次注册,函数都会在这里意外中断,角色对象已经创建、
已经存盘,但从未被放进任何房间。之后无论连线多少次、输入什么指令,
look 都只会看到"你的四周灰蒙蒙地一片,什么也没有。",没有任何报错
提示玩家,连管理员账号自己第一次注册也是这个下场。这是本档案原始压缩
包里真实存在的缺陷(原生开机下也一样),不是 WASM 沙箱特有的问题;
WASM 阶段已经处理了同类问题的一半(补齐了 adm/etc/ 目录本身),但
遗漏了这个具体文件,掩盖了背后这个真正的代码级 bug。已按 AGENTS.md
§7.9 的标准做法加上 stringp() 判断修复,不再依赖恰好存在这个可选的
欢迎消息文件。
修复 2:adm/daemons/natured.lpc 的同类连锁 bug——缺失的昼夜数据表
同样的根因还影响了另一个完全独立的档案:natured.lpc 的
read_table("/adm/etc/nature/day_phase") 在这份压缩包里同样彻底不存
在,explode(read_file(file), "\n") 在没有防护的情况下崩溃
*Bad argument 1 to explode()——第一次被惰性编译触发的时机恰好是巫师
起始房间自动 look 的时候。加上 stringp() 防护后问题并没有完全解
决:day_phase 变成了空数组,而 init_day_phase()/
update_day_phase()/outdoor_room_description()/outdoor_room_
outcolor() 这四处都假设这个数组至少有一项,sizeof(day_phase)-1 会
变成 -1("Array index must be positive or zero"),取模 %
sizeof(day_phase) 还会除以零——每一个户外房间的 look 都会崩溃。最
终修法是在读取数据表的地方就填入一个最小的兜底单条目("天色如常"),
让下游所有假设"至少一条"的索引/取模运算保持有效,而不是逐个访问点单
独加判断(详见 AGENTS.md §7.9 的追加记录)。
修复 3:§8.9 食物/饮水年龄检查错对象
logind.lpc 的 enter_world():
if (!user->query("food") && !user->query("water") && ob->query("age") == 14)
——ob 是登录对象,ob->query("age") 永远是 undefined,这道门槛永久
为假,每个新角色的食物/饮水会静默永远保持 0。已改成
user->query("age") == 14,实测新角色食物/饮水栏正确显示为满格。
修复 4:printf 调试残留
get_name() 在设置角色中文名字之后紧跟一行
printf("%O\n", ob);,会把登录对象的原始引用(如
/clone/user/login#0)泄露给每一个刚输入完中文名字的新玩家。已删除。
修复 5:两处 §7.68 复活软锁(d/death/npc/{b,w}gargoyle.lpc)
death_stage(object ob, int stage) 原代码
if (!ob || !present(ob)) return; 把"鬼魂对象已经不存在了"和"鬼魂此
刻只是暂时不在这个房间里"混为一谈,一旦判定瞬间鬼魂碰巧不在场就永久
放弃后续引导,把鬼魂永久卡在鬼门关。按标准修法拆开:!ob 才是真正放
弃,!present 改为 5 秒后重试。
修复 6(此前完全没有暴露过的新 bug):登录对象自身的 set() 权限保护过严,导致邮箱注册的密码更新静默失效
clone/user/login.lpc 给自己的 set() 加了 nomask 保护("Protect
login object's data against hackers"):只允许 geteuid(previous_
object()) == ROOT_UID 的调用者写入。但真正核心的邮箱注册流程
inherit/room/regroom.lpc 的 do_register()——不是玩家自造的内容,
是系统本身的注册房间——运行在房间自己的普通域 euid(这里是
"Domain")下,并不是 root,于是它对 linkob->set("password", ...)
和 linkob->set("email", ...) 的调用全部被无声拒绝,只在玩家屏幕上
打印一行 login set is error!Domain(打印之后并不中断,后续代码
照常执行,所以整个注册看起来完全正常:"你是第1个注册的朋友!发送到
...的邮件已经加入发送队列!")。实际效果:系统生成并通过邮件发送给
玩家的新随机密码从未真正生效,玩家的密码一直停留在最初自选的临时密
码上,和"忘记密码请查收邮件"这套流程的承诺完全不符。修复没有直接放
宽 login.lpc 自己的保护(那样会重新打开它自己注释里承认的"漏洞"),
而是给已经拥有 root euid 的 logind.lpc(create() 里
seteuid(getuid()))加了一个小的转发函数 set_login_field(),
regroom.lpc 改成通过这个函数间接写入。已实测确认:修复后同样的注
册流程不再打印任何 login set is error/password seting is fail。
检查、确认不适用的已知 bug 类别
- §7.78 CHARACTER 的 F_* 混入档缺 F_DBASE inherit:
inherit/char/ char.lpc是和 shujian3/hy2002/jh2006 完全相同形状的 ES2 结构,这 几个同宗档案都已经用真实测试排除过这个 bug(裸 set/query 实际写入 了真正的 dbase),本次时间关系未重复验证。
未能完成的部分(诚实记录)
由于地图上能立即找到的敌对 NPC 几乎都被原作者自己注释掉了("objects"
清单整段包在 /* ... */ 里,如 d/bianliang/guandao10.lpc 的
bigwolf),本次深度测试完成了完整的注册→移动→户外场景验证,但没有
找到一个可以快速安全触发的实战/死亡目标,因此没有做成活体的战斗/死
亡/复活验证——bgargoyle.lpc/wgargoyle.lpc 的 §7.68 修复只做到静态
代码审查确认,留给未来 pass 补做。
更正(2026-08-05):§7.68 复活软锁"修复"已撤销
上面提到的"鬼魂离开/不在场时被永久放弃复活流程"曾被当作 AGENTS.md
§7.68 记录的一类 bug 修复(把单次判定改成每 5 秒重试)。经用户指出并
重新审视:这更可能是有意的游戏设计,不是 bug——大多数这类档案里
鬼魂根本无法自行移动,所以"不在场"要么从未真正发生,要么是"离开去
在阴间游荡,想回来时再走回这个房间、流程会通过 init() 重新从头开始"
这种有意为之的宽松机制,而不是需要强制追上玩家的错误。强行重试还可能
引入新问题:如果鬼魂之后又走回这个房间,旧的重试和 init() 重新触发的
新一轮流程可能同时运行,导致对话重叠错乱。已把这处改动撤销,恢复成
原始的 if (!ob || !present(ob)) return; 单次判定写法(bmxkx2001
除外——那份档案里这确实是一个真实存在、经过实际复现验证的 bug:鬼魂
本身完全无法移动,是另一个不相关的 NPC 强行把鬼魂拖走导致的)。详见
AGENTS.md §7.68 顶部的撤销说明。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 20 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
深度功能测试第二轮 / Deep functional test round two (2026-08-15, post driver-upgrade re-test)
驱动于 2026-08-12 升级后的重测。标准检查清单发现并修复五处问题:
1. config.fluffos:maximum evaluation cost 从 700000(已知
风险区间)提升到 5000000。
2. cmds/ang/update.lpc(AGENTS.md §7.106):缺少
environment(me) && 前置防护,补上。
3. adm/simul_efun/file.lpc:log_file() 没有 assure_file()
目录预建保护,补上调用及前向声明;cat() 补上
read_file() || "" 空值防护。
4. adm/obj/master.lpc::log_error()(AGENTS.md §7.10/§7.103):
if (this_player(1)) efun::write(...) 完全没有警告过滤门,任何
编译警告都会原样广播给连线玩家。补上
strsrch(message, "arning:") == -1 判定。
5. clone/user/user.lpc::reconnect()(AGENTS.md §7.108,第十八条
独立确认的血统):adm/daemons/logind.lpc 有同款
exec(old_link, user); 踢掉重复登录写法,reconnect() 缺少
enable_commands()。按 §7.108 记录的写法预防性修复,现场用两个
真实连线复现"保持第一个连线不断开→第二个连线登录→答 y 踢掉旧连
线"验证:score 修复后立即正常显示完整角色档案。
方法论记录(非 bug):既有 fluffos 账号密码未知,改用新账号测试
与本 session 早前在 sj 上遇到的情况一样:已有存档的 fluffos 账
号(第一轮用"临时密码"通过真实注册流程播种,但 NOTES.md 没有记录
具体密码值)用标准密码 Mud@2026 登录失败。管理员授权在
securd.lpc::restore_list() 里是纯代码层硬编码(set("wiz_status/
fluffos", "(admin)")),不依赖密码正确与否。仿照 sj 的处理方
式,新增一行 set("wiz_status/fluffosb", "(admin)"),用全新 id
fluffosb 走真实注册流程验证(密码 Mud@2026),本轮所有测试均
用这个新账号完成。原 fluffos 账号存档原样保留,未触碰。
现场验证摘要
驱动干净启动,fluffosb 走完整注册流程(GB 选择→id→中文名"秦
风"→临时密码→天赋确认→性别)后确认 目前权限:(admin),update
/adm/daemons/logind 成功验证真实写入权限,现场未观察到编译警告
泄漏(确认 log_error 修复生效)。踢掉重复登录重连路径现场验证通过
(见上)。debug.log 全程干净(196 行,无真实错误)。
本轮修改的文件
config.fluffoswork/adm/daemons/securd.lpcwork/adm/obj/master.lpcwork/adm/simul_efun/file.lpcwork/cmds/ang/update.lpc
§7.100 扫描修复(ROOM 基类多余 replace_program())
#define ROOM "/inherit/room/room":删除 792 处多余的、独立成行的
replace_program(ROOM);(保留 inherit ROOM;),与 xxcqii/
xxcqii2 同一血统同一形状(本档案是它们的祖先/同系原始档案)。
clone/misc/roommaker.lpc 同样有两套模板——"造一间空房间"的
heredoc 本来干净,"克隆我所在的房间"命令的字符串拼接模板把同一枚
多余的 replace_program(ROOM); 烤进了每一个新克隆的房间,已同步
修正。已用 build-debug 驱动干净启动验证(0 个新增编译错误,端
口正常监听;启动时的 domain_stats/author_stats 统计文件缺失
警告是预先存在的良性提示);未做完整 §10.7 深度游玩测试。
work/clone/user/user.lpc
§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): 4 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.
Round three (2026-09-04): shop + 浣花拜师
New angle vs prior rounds. Native build-debug driver, port 40135.
First send is GB/Big5 g. Admin fluffosb/Mud@2026 (秦风, title
【天神】; the older fluffos save still has an unknown password, left
untouched). Lands at 巫师公会.
Shop — 叁合楼 buy jitui paid and received
goto /d/bianliang/sanhelou. 店小二 (d/bianliang/npc/xiaoer2.lpc,
F_DEALER) list shows 烤鸡腿 八十文铜板 (plus 酒袋/包子/酱牛肉).
clone /clone/money/silver (一两) then buy jitui →
你从店小二那里买下了一根烤鸡腿. Inventory: 烤鸡腿 + 二十文铜板
(100 − 80). Relog kept the 20 coins; the food item itself is not
autoload (existing design). No 丐帮 dealer gate in this F_DEALER.
拜师 — 李子木 / 浣花剑派, persisted
goto /d/huanhua/huzu first crashed (below). After the room guard:
虎啸堂 loads 李子木 (li zimu). bai li → 好吧…决定收你为弟子 /
恭喜您成为浣花剑派的第六代弟子. score shows 浣花剑派第六代弟子、
虎组组员 and 你的师父是李子木. Cold-boot relog (same driver still
up, clean quit + login) still shows that title and master. save
printed 档案储存完毕.
The organic 家丁 path (ask jia ding about 拜师 at
d/huanhua/gate.lpc) was not walked: the gate's objects entry
npc/zuyuan_q is a missing file (same content gap as zuyuan_h
below). doording.lpc is a real 家丁 with that inquiry, but is not
the object the gate actually places.
Programming bugs fixed this pass
1. §7.25 make_inventory() on a missing path
(inherit/room/room.lpc). huzu.lpc lists
__DIR__ "npc/zuyuan_h" (file does not exist; zuyuan_q at the
gate is the same gap) then __DIR__ "npc/li". new() returned 0;
0->move() threw *Bad argument 1 to EFUN call_other() and
aborted reset(), so 李子木 never spawned and goto failed.
Guard: if (!objectp(ob)) return 0; plus objectp() before
is_character() in both reset() branches. Live: 虎啸堂 then
showed 李子木 and accepted bai li. Missing zuyuan_* files
left as content gap.
2. do_list() used the whole key array as a mapping index
(feature/dealer.lpc, same shape as xxcqii §7.151).
j = tmp[goods] printed 0件布衣 for the 店小二's worn cloth.
Changed to j = tmp[goods[i]]. After update /feature/dealer +
room refresh, list showed 1件布衣; catalog prices unchanged.
深度功能测试(§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-05)— here-document terminators
scripts/lpc_fix_heredoc_terminator.py --all --apply: 3 here-document block(s) repaired (3 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/tangmen/nzlangn1.lpc, d/longmen/xuehepai/chenei.lpc, d/huanhua/wuroad2.lpc. The terminator must start its line (the lexer's own test, KB 04 §6.10); a glued one lost its newline in the GBK -> UTF-8 conversion together with a half character, which stays out.
深度功能测试(§10.7,2026-10-04)— paths named in another case
The archive comes from a case-insensitive host: files are stored as BAGUA0.lpc, longjing.C, d/SHAOLIN/, the code names them in lower
case (__DIR__ "bagua0", "tea/longjing", "/d/shaolin/..."), so the rooms, NPCs and items behind them could not be loaded (a .C file is
not an .lpc file; KB 03 §4.5). scripts/lpc_case_paths.py --convert --apply xxcq: 1 file(s) renamed to lower case (.C -> .lpc,
.H -> .h) and 1 path reference(s) in 1 file(s) rewritten to the new spelling; 0 skipped, 0 ambiguous. Load check of the 1 objects that were renamed, edited or name a renamed path (HEAD against the working tree, an object is the same under any spelling of its path): 1 PASS -> 1 PASS, 0 that loaded before fail now, 0 more load.
1 files moved (1 .HTM); mostly under www/html/WARNING.HTM (1). A rescan with scripts/lpc_warnings.py xxcq is still due: the renamed files are compiled for the first time.
深度功能测试(§10.7,2026-10-05)— room base survives a raising NPC
scripts/lpc_resilient_room.py --apply: make_inventory() of inherit/room/room.lpc loads each inventory object as if (catch(ob = new(file)) || !objectp(ob)) return 0;. new() of a file that is not there returns 0 (the room just lacks that NPC), but new() of a file whose create() raises (a skill file for set_skill() this archive never had, a carry_object() item that is not there, a header that never shipped) raised inside the room and the room did not load at all. The wrong-case renames of this lib (the section above) make the NPCs behind them reachable for the first time, so a room that holds one with such a content gap would have gone from "NPC missing" to "room missing"; now it loads without that NPC, as it did before. The room base compiles and loads with the change (HEAD against the working tree). KB 03 §4.5.
深度功能测试(§10.7,2026-10-10)— 编译警告整理
scripts/lpc_warnings.py xxcq --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 203 个 .lpc 和 1 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):192 -> 192 通过,无回退;改过的头文件的 1 个包含者 0 -> 0 通过。启动时警告 0 -> 0;新进程登录(输入名字)正常。
深度功能测试(§10.7,2026-10-10)— 编译警告整理的剩余部分;三个从未能编译的文件
整理后余下的警告逐个处理,其中三处是从未能编译的文件:
kungfu/skill/wenjia-daofa/wryd.lpc(温家刀法「温柔一刀」):if (me->query_skill("force")) > random(...)括号放错,整个 perform 是语法错误,从来用不了;改为if (me->query_skill("force") > random(...))。d/wenjia/obj/xiangshi.lpc:me->die;少了括号(驱动把->die当成类成员访问,编译失败);改为me->die();。d/wenjia/{skill,wenjia}/xiangshi_poison.lpc两份草稿少一个右括号(实际生效的是kungfu/condition/xiangshi_poison.lpc),补上。d/longmen/xuehepai/obj/xueyi.lpc(血衣)同时继承CLOTH和F_UNIQUE,两者都有init();自己的init()整个在/* */注释里,驱动取 cloth 的那份,F_UNIQUE的持有者检查(check_owner())从不执行。补一个init()两者都调。inherit/private_room.lpc、recordable_room.lpc的int setup()与基类void setup()不符(没有调用者用返回值),改为void;d/murong/hssg.lpc同一函数里把skill声明了两次且都没用,删去;d/murong/test.lpc的int lianing = 0;是遮住全局变量的局部变量,改为给全局赋值;wenliangyu.lpc在F_DEALER(它已继承NPC)之外又继承NPC(33 条重复声明),services/temp.lpc多余的F_CLEAN_UP,删去。
改动的 11 个文件 HEAD 与工作树各载入一次:5 -> 10 通过,无回退,警告 42 -> 0。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。22 个文件 22 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。 - 2026-10-14(同一次重扫):
include/daemons.h里重定义的宏先#undef。原报警告的 22 个对象重编译后没有警告。
内力/精力当作伤害种类(2026-10-15,KB 06 §7.241 第二类)
本库 feature/damage.lpc 的 receive_damage() 不接受 jingli/neili/force 等种类,me->receive_damage("jingli", 30) 之类在练功、绝招、吸取时报 F_DAMAGE: 伤害种类错误。1 处改为 add(type, -N)(scripts/lpc_fix_damage_calls.py 第二步)。
其他体系的属性名(2026-10-15,KB 06 §7.241 第三类,--any-file)
26 个文件来自侠客行体系:本库把 jing 改叫 jingli(receive_damage() 只接受 jingli/qi/douzhi/shenzhi),receive_damage("jing", ...) 报 伤害种类错误。按 jing→jingli、kee→qi、force→neili 改名;其中本库自己的 cmds/skill/learn.lpc:玩家教玩家时扣教者的 jing 报错,学不动时 query("jing") 永远为 0(scripts/lpc_port_es_props.py --any-file)。
治疗/疗伤的属性名(2026-10-15,KB 06 §7.241)
receive_heal()/receive_curing() 也只接受本库的种类。1 个文件(多为只调用 receive_heal/receive_curing 的药物、NPC)来自其他体系,按本库的属性名改名(scripts/lpc_port_es_props.py --any-file)。
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。17 处补上 if (!mapp(fam)) fam = ([ ]);(刚 recruit_apprentice() 之后的除外)。
技能按另一门武功的等级取招(2026-10-15,KB 06 §7.243)
这些技能文件是从别的武功复制改名来的,query_action() 仍用原武功的等级查招式表:只会本技能的人等级为 0,取不到招,每次出招都报错。改为读本技能自己的等级(scripts/lpc_fix_action_level.py):bupangguan。
招式表最低等级之下没有招(2026-10-15,KB 06 §7.243)
招式表的最低 "lvl" 高于 0(如全部为 100),技能等级未到之前取不到招,每次出招都报错。循环后补 if (!a_action) a_action = action[0];(lpc_fix_action_level.py --fallback):wenjia-daofa。
技能名拼错(2026-10-15,KB 06 §7.249)
query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):yiqi-guanriyue→yiqiguan-riyue(yiqi-guanriyue/heal)。
NPC 带着本库没有的物品,载入失败(2026-10-15,KB 06 §7.73)
19 个文件、19 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/d/wenjia/obj/chouduan、/clone/weapon/blade、/d/wenjia/obj/grnduanda)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 0 个,改后 5 个(5 个原来载入不了),没有退步;其余 14 个另有原因(多为 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 载入不了,刷出它的房间也少一个人。本库有 42 个对象设了本库没有文件的武功(8 个名字,最多的是 zhixin、yanling-shenfa、shenyuan-gong)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:30 个里 29 个通过,其余 1 个停在别的错误上(d/murong/npc/job-dizi:*Bad argument 1 to EFUN call_other())。(scripts/lpc_guard_set_skill.py)
通向不存在房间的出口,按对面房间的回头出口改正(2026-10-18,KB 06 §7.257)
房间出口 "west": __DIR__"xxx" 指向的文件本库没有,玩家走过去就被卡住(笔误、改过名的房间,或者没带进档案的编号房间)。对面的房间存在、并且它相反方向的出口正好指回这个房间,说明出口原本就是通向它:/d/baituo/tangwu 的 north:yuanzi → tuitang。改动后每个出口都能解析到本库的文件(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 文件。
被注释吞掉的 inherit / include(2026-10-19,KB 06 §7.274)
/adm/daemons/securd.lpc:#include <login.h>。这些指令接在上一行 // 注释的末尾(档案里丢了换行),成了注释的一部分,驱动看不到:缺 inherit 的技能报 Undefined function offensive_target,缺 #include <weapon.h> 的兵器报语法错误。scripts/lpc_fix_swallowed_directive.py 把换行放回去。载入检查 1 个文件,0 个原来编译不过、现在载入,没有退步。
手工收尾(2026-10-19,KB 04 §6.10)
d/murong/npc/skill.h:mapping *skills=({ … })是慕容复能传授的武功表,和 NPC 基类feature/skill.lpc的mapping skills(本人的武功等级)同名;驱动让后声明的盖掉先声明的(define_variable),murong-fu.lpc的ask_learn()读到的一直是这张表,后声明的这份还被强制成nosave。把表改名skill_table并明写nosave,ask_learn()里mapping skills = skill_table[random(sizeof(skill_table))];一行跟着改(初始化表达式里的名字指全局表,局部的skills在初始化之后才进入作用域:rule_new_local_def_with_init)。d/murong/npc/dizi.h:开头mapping skills; mapping learned;是从基类抄来的,job-dizi.lpc一次也没用到,删掉(Redeclaration of global variable 'skills'、'learned')。
逐个与 HEAD 对比载入,没有退步。
x == "a" || "b":后面的字符串常量永远为真(2026-10-19,KB 06 §7.281)
if (arg == "yuyou" || "yu you") 里的 "yu you" 是非空字符串,永远为真,整个条件对任何 arg 都成立;作者要的是每个字符串都和变量比较。具体是:驿站把任何输入都当成合法的去处;valid_enable() 对任何 usage 都返回真。在每个字符串前补上变量和运算符(scripts/lpc_fix_literal_or.py;forrest2.lpc 是 dir == "north" && "south",手工改成 (dir == "north" || dir == "south") && ...):/inherit/room/yizhan.lpc(7 处)、/kungfu/skill/tianyu-bufa.lpc(1 处)。
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)。具体是:百日十龙丸怎么 eat 都只得到“你要吃什么?”。改动:/d/longmen/xuehepai/obj/bairiwan.lpc(1 处)、/d/wenjia/square.lpc(2 处)。另有 /d/murong/npc/laoda.lpc(1 处) 的条件下面做的是别的事(== 和 || 也可能是本意),保持原样。
member_array('/', str) 改为 strsrch(str, "/"):字符字面量的 member_array 按字节搜索(2026-10-20,KB 06 §7.283)
member_array('/', str) 里的 '/' 是整数,驱动按 UTF-8 字节搜索,返回字节偏移(带第三个参数时起点也是字节);最新的 FluffOS(923cdbc0)在字符串里有非 ASCII 文字时,对每个调用点警告一次(Warning: member_array(int, string) searches BYTES)。而 str[0..idx-1] 按字符切,中文字符串会在错误的位置截断。改为 strsrch(str, "/")(找不到同样返回 -1,按字符计数;scripts/lpc_fix_member_array_char.py,JSON 编码器等少数几处手改)。改动:/adm/daemons/vrm_server.lpc(1 处)、/cmds/adm/sameip.lpc(1 处)。
boss 指令:/open/shit.txt 不在存档里,write(0) 报错(2026-10-20,KB 06 §7.285)
boss 1 先清屏、屏蔽所有消息,再 msg = read_file("/open/shit.txt"); write(msg);。/open/ 是巫师目录,存档里没有这个文件,read_file() 返回 0,write(0) 报 Bad argument 1 to receive(),玩家的屏幕还被屏蔽着。改为 if (stringp(msg)) write(msg);(cmds/usr/boss.lpc)。
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,共 6 处)。改动:/d/huanhua/npc/obj/saozhou.lpc(1 处)、/d/murong/hhyuan.lpc(1 处)、/d/yaowang/lufeng.lpc(1 处)、/d/yaowang/npc/lufeng.lpc(1 处)、/feature/skill.lpc(2 处)。
登录密码超过 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)。
简易 cat() 把 simul_efun 对象的 euid 改成了玩家:存档时 Too deep recursion(2026-10-20,KB 06 §7.313)
adm/simul_efun/file.lpc 的 cat() 是 seteuid(geteuid(this_player())); write(read_file(file));,它在 simul_efun 对象里执行:玩家敲过一次 help 之后,这个对象的 euid 就一直是那个玩家的。它自己的 log_file() → assure_file() → file_size(LOG_DIR + file) 于是按玩家权限过 valid_read(),被拒绝,securityd 再用 log_file() 记一笔拒绝,又走回 file_size()……直到调用深度耗尽。第二次连线(角色已保存)时第一次被拒的是 restore_object 读 /data/login/s/<id>.o,之后控制台出现两条 Too deep recursion.(securityd.lpc:173 和 :247),log/read_fail.log 写满同一行,角色不再被保存。现在 cat() 先记下 simul_efun 自己的 euid,用调用者的权限读文件(catch() 里读),读完再还原,read_file() 返回 0 时不 write()。实测:第一次连线跑 help,立刻第二次连线,无递归、无 read_fail.log。xxcq 的 securityd 没有走到同一条路,同一份 cat() 一并改了(潜在的)。
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(共 1 个文件)。
nodjoin:没有参数时 present(0, ...)(2026-10-21,KB 06 §7.336)
cmds/skill/nodjoin.lpc(recruit.c 的副本)第一句就是 ob = present(arg, environment(me));,裸命令 nodjoin 时 arg 是 0,present(0, ...) 在这个驱动里报 Bad argument 1 to present(),而且排在 userp(me) 的拒绝之前(玩家本来就用不了这个命令)。先拒绝玩家,再 if (!arg || !objectp(ob = present(arg, environment(me)))) return notify_fail("指令格式 : recruit|shou [cancel]|<对象>\n");(帮助里的那一行)。xxcq、xxcqii、xxcqii2 三份同样的改法。
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/adm/blockade.lpc、/cmds/adm/recover.lpc。
obj.h.es2:blockade / recover 包含的头文件档案里没有(2026-10-21,KB 04 §6.23)
blockade.lpc、recover*.lpc #include <obj.h.es2>(OBJ_*、NPC_* 路径的 ES2 头文件,hy5、xkx100 等 33 个库带着),本库的 include 目录没有它,这些指令从来编译不了(Cannot #include obj.h.es2),data/keep 的修补也无从生效。指令本身不用里面的宏;从同系的 xkx100 复制一份头文件,加载检查 HEAD 与工作树对照:2 个对象原来加载失败,现在通过,没有退步。
xupdate:file[sizeof(file)] != '/' 永远为真(2026-10-21,KB 06 §7.343)
管理员扫描里 xupdate(xyj2000)从根目录 / 开始递归时报 Bad argument 2 to foreach Expected: array Got: 0.:UpdateDir() 用 file[sizeof(file)] != '/' 判断路径是否已经以 / 结尾,下标等于长度读到的是 0,条件永远成立,/ 变成 //,get_dir("//") 答 0。改成 file[<1] != '/'。同一个写法在 xrm、rm、rmdir1、rmhir 里有 55 处,没有动:对普通路径新旧写法的行为相同,唯一改变的是 xrm /,旧写法让它死在第一个 get_dir("//"),改了它就能递归删光整个游戏目录。
socket:dump_socket_status() 在本库没有(2026-10-21,KB 06 §7.291)
管理员扫描里 socket 指令(/cmds/hufa/socket.lpc)编译不了:write(dump_socket_status()); 用的是 MudOS 有、本驱动没有的 efun。2026-10-20 的补丁只按名字找了巫师目录里的同名指令,这一条在 cmds/bos、cmds/hufa、cmds/island、cmds/ser 这类目录里;用 scripts/lpc_add_sefun.py dump_socket_status 在 /adm/obj/simul_efun.lpc 末尾补了一个由 socket_status() 构造表格的 simul_efun(没有 sockets 包的构建,比如 WASM,只打印表头)。指令 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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 28 条注册(24 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/bianliang/npc/tiejiang.lpc(do_buy、do_list)、/d/huafu/obj/gaoliang.lpc(do_bann)、/d/moto.lpc(do_list)、/d/murong/npc/murong-fu.lpc(do_jiaoliang)、/d/quanli/npc/dujue.lpc(do_join)、/d/quanli/npc/futianyi.lpc(do_join)、/d/quanli/npc/guan.lpc(do_join)、/d/quanli/npc/kang.lpc(do_join)、/d/quanli/npc/kong.lpc(do_join)、/d/quanli/npc/liang.lpc(do_join) 等共 24 个文件。最多的函数名:do_join 18 处、do_list 2 处、do_buy 1 处、do_bann 1 处、do_jiaoliang 1 处。没有动(读过):/d/quanli/npc/hua.lpc 的 do_join(对象本身编译不过,登记留着,等对象能加载再处理)。
(: 函数 :) 指针指向对象自己没有定义的函数(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。 还剩 1 个函数没有补(item_desc 1):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/d/murong/hole.lpc:look_out。 另有 1 处函数体被作者整段注释掉(指针没跟着删),是有意关掉的,没动。
运行期目录 log/nosave 不存在,写日志的 write_file() 直接报错(2026-10-22,KB 03 §4.3 补充)
这个库的 12 个文件里有 15 处 write_file("/log/nosave/...") / log_file("nosave/...")(static 改 nosave 时连字符串里的日志路径一起改了,原来是 log/static/):nosave/CRASHES、nosave/promotion、nosave/CALL_PLAYER 等。压缩包和站点包里没有这个目录,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/wenjia_b;/d/wenjia/wenjia/wenjia/square.lpc)注释掉并写明原因(别的房间里同样的语句本来就是作者自己注释掉的)。证明和 45 个库的整体加载检查见 KB 06 §7.357。
(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)
inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 2 个文件里 3 行只含这个指针的行("window": (: look_out :), "震天鼓": (: ask_me :), "gu": (: ask_me :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。
汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 28 处(2026-10-22,KB 06 §7.360)
原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字);各派掌门的辈分判断(name[0..1] == "澄")永远不成立。
本库 28 处(20 个文件):拼名字的赋值 19 处、与汉字常量比较 9 处。例:name[2..3] → name[1..1](/kungfu/class/shaolin/baocan.lpc:156);name[0..1] → name[0..0](/kungfu/class/shaolin/tong.h:68);s1[0..1] == "。" → s1[0..0] == "。"(/adm/npc/luban.lpc:2007)。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,不要重跑。
长度检查按字节写,旁边的提示按汉字写:8 处(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 你好吗)。本库 8 处(4 个文件):if ((strlen(name) < 2) || (strlen(name) > 8)) {(/adm/daemons/logind.lpc:643);if ((strlen(name) < 2) || (strlen(name) > 12)) {(/adm/daemons/logind1.lpc:606)。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 区间 4 个文件。证明(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 是两字符牌码,未改。
深度功能测试(2026-10-27,§6.10 未知 pragma save_binary/optimize)
删除本库中 FluffOS 不认的 #pragma save_binary / #pragma optimize… 行。