Storm III (Xingxing's Revision)

✅ 可玩

风云三(星星修订版)— fy3xd

fy3xd

🔑 fluffos / Mud@2026 更新 66a3bd8 2026-10-28 源码 下载 ZIP

▶ 开始游玩 · Play Now

压缩包内附带的说明文档称之为"星星修订的风云III"——社区爱好者"星星" 整理修订的一个版本。逐文件核实发现"修订版"/"独立内容包"的说法有些夸大:其域文件与 `fy330`(风云III 3.0版)高达 96% 逐字节相同,基本就是同一个版本;与 `fy3dz`(风云典藏版)也有 91% 相同——`fy3dz` 是在同一套底座上进一步加工,新增了一处"bat"战场区域和一处新疆地图,并去掉了 `fy330`、`fy3xd` 仍保留的一些旧调试/管理后门残留。三者其实都是同一个"风云III"世界的先后快照,而非各自独立创作的游戏。游戏本身:古龙风格的江湖世界,从凤求凰客栈起步,选择民族与性别后即可闯荡江湖;根据其自带文档说明,这一快照相比同引擎最早的版本任务内容更丰富,游戏内帮助文档也更详细。

English

The readme bundled with the original archive calls this "Storm III, revised by Xingxing" — a version compiled and revised by a community enthusiast going by "Xingxing." A file-level check found the "revised"/"different content package" framing overstated: fy3xd's domain tree is 96% byte-identical to fy330 (Storm III v3.0) — essentially the same build — and 91% identical to fy3dz (Storm III Collector's Edition), which reworked the same base further by adding a "bat" battlefield zone and a Xinjiang region while dropping some old debug/admin leftovers fy330 and fy3xd both still carry. All three are snapshots of one "Storm III" world rather than separately authored games. Set in a Gu Long-style jianghu starting at the Fengqiuhuang Inn, choosing ethnicity and gender before heading out; per its own bundled documentation this snapshot carries somewhat richer quest content and more detailed in-game help text than the earliest cut of the same engine.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/fy3xd/

管理员账号 / Admin account

警告:公开架站前请务必修改此默认密码。

本地运行

cd libs/fy3xd
mkdir -p log   # 如尚未创建
~/src/fluffos/build-debug/src/driver config.fluffos

游戏端口:40089。

NOTES · 移植与修复记录

fy3xd — 风云Ⅲ (星星修订版) / "Fengyun III, Xingxing's revised edition"

Archive: 风云III修订版 .rar (archive #97; byte-identical duplicate 风云III修订版 (1).rar skipped per AGENTS.md's duplicate table). Port: 40089. Status: done (boots clean, full registration + post-login look/score verified end-to-end with a real Chinese name, across two independent connections).

What this is

Archive unpacks to a nested fy3fixed[1]/fy3fixed/ root (config at fy3fixed/config.fy3, master at fy3fixed/adm/obj/master.c) plus a top-level 小熊泥苑.txt site-branding text file (same "小熊泥苑" snapshot branding already seen on archives #33/#34, sjtx2/ sjpl2). readme.txt (converted): "这是星星修订的风云3,非常 稳定,任务丰富,内有详细的help文档,推荐!" — "This is Xingxing's revised Fengyun 3, very stable, rich in quests, has a detailed help doc set, recommended!", signed "titny". config.fy3's own name field (converted): 风云三(本地) ("Fengyun 3 (local)"). ~3919 raw files, 3714 renamed .lpc/.c source files (medium-sized lib, comparable to moniHuafu's 945 or zzfy's ~10k).

Lineage confirmation (evidence, not assumption)

Per the task's standing instruction, compared this archive's core files directly against every already-done "风云"(Fengyun)-themed lib (fengyun434 #14, fy2005 #16, fy2 #17, zzfy #30, moniHuafu #57) via md5sum/diff BEFORE any fix work:

Conclusion: this is the same 风云3 (Fengyun 3) engine core as zzfy/moniHuafu/fengyun434/fy2/fy2005 — specifically closer to zzfy's snapshot than moniHuafu's (near-identical master.c), with its own distinct site content/branding/quest-layer on top (matching the readme's "revised by 星星" framing — a themed community fork, same pattern as moniHuafu being a school-themed skin). Every fix below was ported directly from the already-verified zzfy/moniHuafu write-ups, then individually re-confirmed against this archive's actual source before applying (not assumed).

Fixes applied

AGENTS.md §15h (GBK byte-range Chinese detection) — identical shape to zzfy/moniHuafu, confirmed via the byte-identical chinese.c/similar logind.c:

AGENTS.md §15p (DNS/intermud daemon preload exclusion): adm/etc/ preload listed /adm/daemons/network/dns_master — removed proactively before first boot.

AGENTS.md §15u (dormant phone-home self-destruct): adm/daemons/ securityd.lpc has the exact same checking_status(mapping info) function already found and neutralized in moniHuafu — checks info["MSG"] against a hardcoded crypt() hash, and on match calls rmhirdir("") (recursively deletes the entire mudlib root) then shutdown(0). Unlike moniHuafu, this one IS referenced in-tree — adm/daemons/network/services/ gtell.c's intermud-tell relay calls SECURITY_D->checking_status(info) — but that relay is itself unreachable in this sandboxed environment (no inbound intermud gtell traffic, dns_master excluded per above, gtell.c not on preload and not called from anywhere else in the lib). Neutralized the destructive body anyway as cheap insurance (replaced with a log_file()-only stub), matching moniHuafu's fix exactly.

AGENTS.md §14 (valid_override 3-arg upgrade): master.lpc's valid_override(file, name) was 2-arg; adm/simul_efun/object.lpc (an #included fragment inside simul_efun.lpc) has an efun::destruct(ob) override, the exact shape §14 warns about. Upgraded to the 3-arg valid_override(file, name, main_file) signature, checking both file and main_file against SIMUL_EFUN_OB/MASTER_OB. Confirmed (via the lpcc sweep, see below) this only ever surfaces as a sweep-only artifact when object.lpc is compiled standalone — the real boot's composed simul_efun.lpc never hits it, same conclusion as every other lib that's checked this.

New collateral-damage instance of the known §3/moniHuafu static→ nosave string-literal collision: the blanket \bstatic\b→nosave sed rewrote 8 log_file("static/...") calls (in master.lpc, securityd.lpc, cmds/std/suicide.lpc, cmds/arch/call.lpc, cmds/arch/purge.lpc) to "nosave/...", even though work/log/static/ is a real pre-existing seed directory shipped with the archive (contains CRASHES, CALL_PLAYER, promotion, SUICIDE — genuine historical log entries). Confirmed via the same cross-check moniHuafu's write-up recommends (grep -rn '"nosave/' + find work/log -maxdepth 1 -type d showing a same-named static/ directory with real data in it) and reverted all 8 occurrences back to "static/..." with a scoped sed.

Two genuine pre-existing content bugs, found via the lpcc sweep and fixed (both confirmed present in the RAW pre-conversion archive too, not introduced by this pipeline): 1. d/fy/npc/gangster2.lpc's init(): used for(i=sizeof(ob)-1; ...) with i never declared (only object *ob; was declared) — a hard compile error (Undefined variable 'i', Illegal lvalue) that broke this NPC's aggro-on-room-entry logic entirely. Fixed by adding int i; alongside the existing object *ob; declaration. 2. AGENTS.md §15s (tell_room()/message() 2-arg exclude-argument bug): adm/simul_efun/message.lpc's varargs void tell_room(mixed ob, string str, object *exclude) passes a defaulted exclude (int 0 when called 2-arg, the dominant call form — 15 of 21 call sites in this lib) straight through to the real message() efun's 4th argument, which this driver rejects (Bad argument 4 to EFUN message(), Expected: object, array, Got: int(0)). This crashed /d/qianfo/tanghuai's reset()/make_inventory() (a mounted-horse NPC's init() calling tell_room() 2-arg) during the lpcc sweep. Fixed once at the shared wrapper (exclude || ({})) rather than touching any of the 21 call sites, per §15s's own guidance.

Both fixes re-verified with a standalone lpcc re-compile (both now compile with zero errors, only the usual harmless warnings) AND with a fresh full driver restart + registration re-test (see below) — the debug.log grep for the exact Bad argument 4 to.*message error string came up empty across two independent post-fix sessions.

What was checked and confirmed NOT needed

Interactive registration + post-login-command test — full transcript outcome

Read logind.lpc's actual logon()/get_id/confirm_id/get_name/ new_password/confirm_password/get_email/get_gender/get_nresp callback chain in full before scripting the test — no hidden BIG5/ client-version pre-id gate here (the first real prompt is the ordinary English-id prompt); check_legal_id requires 3-10 lowercase letters only (no digits — an early test attempt with a digit in the id, and a second attempt exceeding the 10-char cap, were both correctly rejected by the id-length/charset validator, not a bug).

Ran the complete flow in ONE continuous mudclient.py connection (twice, across a driver restart that picked up the gangster2/tell_room fixes, using different ids each time to avoid double-registering):

1. Connect → ASCII-art banner + poem + uptime/visitor-count banner → 您的英文名字: prompt. 2. qfengx → passes check_legal_id → 使用 qfengx 这个名字将会创造一个 新的人物,您确定吗(y/n)? 3. y → registration flavor-text banner → 您的中文名字: 4. Real Chinese name 秦风 → accepted immediately, NO rejection message (direct proof the §15h fix works — this exact 2-character Chinese name would previously have failed is_chinese()'s always-false GBK byte-range check) → 请设定您的密码: 5. Aa123456 (password) → 请再输入一次您的密码,以确认您没记错: 6. Aa123456 (confirm) → matches → 您的电子邮件地址: 7. [email protected] → 您要扮演男性(m)的角色或女性(f)的角色? 8. m → ethnicity menu (汉族/苗族/满族/蒙古族) → 请选择你在风云Ⅲ中的民族 (0,1,2,3): 9. 0 (汉族) → character created, enter_world() called → actual game room: "凤求凰客栈" (Fengqiuhuang Inn), full room description, one exit (west), an NPC (店小二/Waiter) greeting the new arrival with a random flavor line. 10. look: room re-displayed as part of the entry sequence (this lib's enter_world triggers an implicit look on arrival — confirmed the exact same room text appears, i.e. the look command dispatch genuinely executes, not just silently no-op'd). 11. score: produced the full character stat sheet — 普通百姓 秦风 (Qfengx), "你是一位汉族十岁的男性人类,...", all 12 attribute pairs (才智/体质/灵性/魅力/勇气/力量/耐力/韧性/速度/气量/运气/定力), combat stats (攻击力/防御力/杀伤力/保护力), 参数点 — unambiguous proof both look and score genuinely dispatch through add_action post-login (directly ruling out §15ae/§15ar, both of which were also confirmed absent by direct source inspection above). 12. quit → correctly dropped starting inventory (一件布衣/a set of cloth), printed "欢迎下次再来!" ("welcome back next time").

Confirmed both test characters (qinfeng+qinfengtwo from the first pre-fix session, qfengx from the post-fix session) saved correctly to data/user/<initial>/<id>/<id>.o and data/login/<initial>/<id>/<id>.o.

log/debug.log reviewed in full across both sessions (pre- and post-fix): zero fatal errors, zero Undefined function, zero Read access denied, zero Too deep recursion, zero Bad argument 4 to.*message (confirming the tell_room/message fix). The only non-clean-looking output is this lib's own log_error() mislabeling ordinary compile *warnings* (e.g. "Unused local variable", "Illegal to declare nosave function", "Unknown #pragma") as "编译时段错误:" (compile-time error) when broadcasting them to a connected wizard-status player — a pre-existing cosmetic quirk of this mudlib's error handler (same shape already documented for moniHuafu/wuhanzhan §15w), not something introduced by this port, and not a genuine error.

lpcc sweep

3714 files, 3698 pass / 16 fail initially (99.6%) — after fixing the 2 genuine bugs found (gangster2's missing int i;, the tell_room() message() exclude-argument bug), both re-verified clean via standalone lpcc re-compiles, leaving 14 confirmed-harmless failures:

Memory stayed healthy throughout (~9.4GB free / 18GB available on this 23GB host at sweep time, despite a concurrent sibling agent's driver + lpcc sweep also running on fy3dz — no OOM risk at this file count).

How to run

cd libs/fy3xd
mkdir -p log   # if not already present
~/src/fluffos/build-debug/src/driver config.fluffos
python3 ../../scripts/mudclient.py 127.0.0.1 40089 --timeout 25 --idle 0.8 \
  --send "" --send "<3-10 lowercase letters>" --send "y" \
  --send "<Chinese name>" --send "<password>" --send "<password>" \
  --send "<email>" --send "m" --send "0" \
  --send "look" --send "score" --send "quit"

Re-verification pass: driver rebuild + LPC formatter + WASM build

Process hygiene note

While this archive was being processed, a sibling agent was concurrently processing archive #98 (风云III典藏版.rar → fy3dz, a DIFFERENT archive despite the similar title — confirmed via ls archives/ and TODO.md's table, not a duplicate). Both drivers ran simultaneously on different ports (40089 here, the sibling's own assigned port there) with no interference. Per standing policy, this lib's driver was only ever killed by its own exact tracked PID, never via a broad pkill -f pattern that could have hit the sibling's identically-named process.

WASM-enablement pass (loopback / admin seeding)

WASM 修复摘要(迁移自 meta.json 的 group_note)

此前被错误标记为某个不存在于本项目任何档案的原始压缩包文件名的 duplicate_of;这里的内容其实是完全独立、可游玩的游戏(有自己的 -N 变体编号、自己的端口、自己的 README)——duplicate_of 已清除。

§7.86 跨库扫描修复(留言板 post 崩溃)

深度功能测试 / Deep functional test (2026-08-07)

第一次完整游玩测试(原生驱动)。测试角色 id fyxdtest,中文名 云梦仙, 苗族/女性,后拜入金钱帮第三代弟子门下(师父荆无命)。本轮 WASM 未重新 验证:emsdk 工具链下载硬编码指向 storage.googleapis.com,本次会话的 出口代理策略性拒绝该域名(403,已用 curl $HTTPS_PROXY/__agentproxy/status 确认是策略拒绝而非临时故障),本地无法构建 WASM 驱动。

与同引擎家族的 fy3dz(风云3典藏版)核心代码高度一致(README 及此前 的血统分析已确认),本轮开局前先按 fy3dz 自己深潜时记录的、明确标注 "很可能同样影响 fy3xd" 的两处已知 bug 做了主动排查,两处均确认存在并 按已验证过的同一手法修复;另外独立发现并修复了一处新的、注册流程里 的 §7.34 已知模式实例。

主动排查并修复:file_owner() 路径深度差一错误(AGENTS.md §7.26 已确认实例,fy3dz 记录中明确标注"值得检查 fy3xd")

``lpc if (sscanf(file, "/u/%s/%s/%s", dir, name, rest) == 3) { return name; } ` 只在路径恰好是 /u/<wizard>/<file>/<更多> 三段式时才生效,对这份档 案实际使用的 /u/<wizard>/npc/xxx.lpc(两段式:wizard 目录本身就 直接放 npc//obj/ 子目录)永远匹配不到 3 段,返回 0——任何在这 类嵌套内容上的编译诊断(哪怕只是一条"Unused local variable"警告) 都会让 master.lpc 的 log_error() 把日志写到一个错误路径,抛出 *Wrong permissions for opening file ... for append. "No such file or directory"`(被驱动接住不会崩溃,但会把 debug.log 灌满无关噪音)。

``lpc if (sscanf(file, "/u/%s/%s", name, rest) == 2) { return name; } ``

主动排查并修复:user_cwd()/user_path() 假设了这份档案从未使用过的按首字母分片的巫师目录布局

``lpc string user_cwd(string name) { return ("/u/" + name[0..0] + "/" + name); } ` (比如 guanwai 会解析成 /u/g/guanwai)。实测 work/u/ 目录列表 确认是扁平布局(chuenyu/ghost/guanwai/palace/ quicksand/resort/taoguan/wudang 直接平铺在 /u/ 下,没有任 何按首字母分片的中间层),与 fy3dz` 记录的情形完全一致。

``lpc string user_cwd(string name) { return ("/u/" + name); } ` 这个函数只有 3 处调用点(cmds/adm/cd.lpc、master.lpc 的 log_error()、path.lpc` 自身),均为巫师/管理指令,不影响普通玩 家游玩。

主动排查并加固:save.lpc 缺少与 quit.lpc 同款的 environment(me) 空指针防护

新发现并修复:注册流程中文名确认环节的 printf("%O", ob) 调试输出泄漏(AGENTS.md §7.34 已知模式的又一实例)

主动排查并提高:maximum evaluation cost 沿用了这份档案自己偏低的默认值

config.fluffos 原为 300000(低于本项目常见的 700000 模板默认值, 更远低于 fy3dz/zzfy 两个姊妹档案已经验证过安全的 5000000)。按 AGENTS.md §7.90 的既定判断标准("数值接近或低于 700000 时上手就查") 主动提高到 5000000,与两个姊妹档案对齐,避免潜在的首次房间/NPC 编 译触发 eval-cost 中止。本轮全程游玩未观察到任何 eval-cost 相关错误。

测试内容与结果

§7.100 扫描修复(ROOM 基类多余 replace_program())

#define ROOM "/std/room":删除 663 处多余的、独立成行的 replace_program(ROOM);(保留 inherit ROOM;),全部由脚本自动 删除。本库没有任何在游戏内建造房间的工具(roommaker/rmmaker 等名称均未找到),因此没有"工厂"侧需要修复。修复后全库仅剩 2 处 历史遗留的 //-注释掉实例,均确认无害、未改动。已用 build-debug 驱动干净启动验证(0 个新增编译错误,端口 40089 正常监听, debug.log 无新增 "cannot replace"/"cannot bind" 行);未做完整 §10.7 深度游玩测试。

§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.

深度功能测试(2026-09-03,第三轮)

新角度:2026-08-07 第一轮已经走完金钱帮拜师(荆无命)和 quit+reconnect, 没有测商店。本轮只补商店,并顺带发现 save 指令被影子文件挡住。

深度功能测试(§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)— tail() simul_efun

scripts/lpc_add_tail.py --apply: cmds/wiz/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)— 任务表里的 .c 路径与任务守护进程的防护

scripts/lpc_dynamic_rows.py:quest/dynamic_location 里 1052 行写的是原档案的 .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 读到的每一行都加载不出来:随机任务和任务物品从未生成,在 create() 里直接对结果调用 tar->set() 的版本还会崩溃,连带加载它的 cron 守护进程和 give 的任务钩子。这些行已改为 .lpc。 scripts/lpc_dynamic_quest_guard.py:1 处 spread_quest() 调用(init_dynamic_quest() 的循环里)包进 catch(),缺失的任务物品或编译不过的房间只跳过那一个任务,守护进程照常加载、其余任务照常生成。 验证:adm/daemons/questd.lpc和 cron 守护进程在新进程里加载(HEAD 一次,工作树三次,选房是随机的):HEAD 通过 2 个,工作树三次分别通过 2、2、2 个(共 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 fy3xd --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 288 个 .lpc。HEAD 与工作树分别加载(新进程):283 -> 283 通过,无回退。

启动时警告 61 -> 0;新进程登录(新人物确认)正常。

技能名拼错(2026-10-15,KB 06 §7.249)

query_skill() 里的技能名拼错了,或用的是本库改名前的旧名;本库没有这个技能,调用永远得 0:门槛(< N 就拒绝)永远过不去,相关功夫或绝招学不了、用不了;内功互斥(> N 就拒绝)从不生效;公式里这一项一直是 0。经汉字名(提示文字或中文名词典)或纯拼写差异(_/-、少一个连字符、相邻字母颠倒)核对后,改为本库的技能名(scripts/lpc_fix_skill_typo.py):putfiforce→putiforce(qiankunwuxing)。

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 载入不了,刷出它的房间也少一个人。本库有 1 个对象设了本库没有文件的武功(1 个名字,最多的是 jiayiqoforce)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:1 个里 1 个通过。(scripts/lpc_guard_set_skill.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 文件。

行编辑器指令编译失败:(: 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/wiz/to.lpc(1 处)。

登录密码超过 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。

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) ||)。改动:/std/char/npc.lpc(共 1 个文件)。

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 个同名文件没有动)。

注册到不存在函数的 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.,同一个动词的其他处理函数不再尝试(不是最新的一条就悄悄跳过)。删除了 2 条注册(2 个文件,scripts/lpc_fix_dead_add_action.py),动词留给别的处理函数:/d/huangshan/npc/xiangke.lpc(do_vendor_list)、/d/songshan/npc/xiangke.lpc(do_vendor_list)。最多的函数名:do_vendor_list 2 处。

(: 函数 :) 指针指向对象自己没有定义的函数(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 个函数没有补(chat_msg 1):原型还在函数体丢了、或者根本没写过,没有可以直接补回的捐赠(别的库同一路径的文件里没有这个函数,或者它还要用本库没有的辅助函数、签名不同),不编造:/d/fy/npc/gangster2.lpc:goaway。

(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)

inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 1 个文件里 1 行只含这个指针的行((: goaway :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。

汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 2 处(2026-10-22,KB 06 §7.360)

原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字);各派掌门的辈分判断(name[0..1] == "澄")永远不成立。

本库 2 处(2 个文件):拼名字的赋值 1 处、与汉字常量比较 1 处。例:name[0..1] → name[0..0](/d/qianfo/npc/master2.lpc:101);things[0..13] == "天南地北双飞客" → things[0..6] == "天南地北双飞客"(/d/tieflag/magroom.lpc:46)。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,不要重跑。

长度检查按字节写,旁边的提示按汉字写:6 处(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 你好吗)。本库 6 处(3 个文件):if ((strlen(name) < 2) || (strlen(name) > 40)) {(/cmds/std/buildroom.lpc:68)。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 个文件。证明(scripts/lpcc_eval.py):sjplii 的 check_legal_name 在 HEAD 接受「张a」、改后拒绝,两边都接受「张三」;xkx100 的 clubpoem->mixup("离离原上草") 得到「离离原草上」(真交换汉字);nitan_san 两字同盟名得到 【 兄 弟 】。关键对象加载通过。

无参数时命令对 0 的调用(2026-10-27,KB 06 §7.336 Also)

巫师/管理员命令在通过权限门之后仍会把 0 交给 sscanf / read_file / find_living 等。lpc_fix_bare_arg.py 在首次使用参数前加上 if (!arg)(或 help(me) / GetHelp())。本库本批改动见该提交。

深度功能测试(2026-10-27,§6.10 未知 pragma save_binary/optimize)

删除本库中 FluffOS 不认的 #pragma save_binary / #pragma optimize… 行。