info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
《逸俠之世界》(Daniel's World),ES II 引擎家族,台湾出品,和这批档案里的 `yxsj` 是同一份代码库在不同时间点的两个版本:同样的现代校园开局地图(`d/school/`,教室、中庭花园、实验大楼、厨房、脚踏车停放处)、同样收录台湾"霹雳布袋戏"元素的 `d/Pili/` 场景、同样的联络信箱与档案结构,但内容并非完全一致(例如 `adm/daemons/combatd.lpc` 相差约 80 行),是同一代码库不同时间点留下的独立快照,值得两份都保留而不是去重。
English
Daniel's World, the same ES II-family, Taiwan-produced game as this collection's yxsj — including its unusual modern-campus starting map (classrooms, courtyard garden, lab building, kitchen, bike rack) and the Taiwanese Pili Puppetry (霹靂布袋戲) glove-puppet scenes woven into the jianghu backdrop — but a distinct build from a different point in the codebase's history: same title, same contact email, and the same file structure, yet not a byte-identical repackaging (its combatd.lpc, for instance, differs from yxsj's by roughly 80 lines) — worth keeping as a separate entry rather than treating as a duplicate.
README
内容亮点
- 和
yxsj是同一份代码库在不同时间点的两个版本:同样的校园开局 地图(d/school/)、同样收录台湾"霹雳布袋戏"元素的d/Pili/场景。两者内容并非完全一致(比如combatd.lpc相差约 80 行), 值得都保留而不是去重。
重大编码问题(和 yxsj 一样,先修好才能继续)
这份档案也是 BIG5 编码,但最初的转换用了整个语料库默认的
GB18030,产生了看起来正常、实际语意不通的乱码(合法 UTF-8,但每
一句话都是错的汉字),而不是转码报错。用处理 yxsj 时扩充过的
scripts/convert_lib.sh(支持第三个"来源编码"参数)重新用 BIG5
转换了一遍,之前套用在乱码版本上的修复全部在新转出来的树上重做了
一遍。
本次修复的关键 bug
(和 yxsj 完全相同的模式)
1. 经典 §8.1 GBK 字节区间 is_chinese()/check_legal_name()
——改成逐码点的 0x4e00-0x9fff 判断,上下限从 2/12 减半成
1/6。
2. §7.12 tell_room() 裸露 exclude 参数——补上
exclude || ({})。
3. master.lpc 的 valid_write() 补上标准的
user == this_object() 短路判断(valid_read() 本来就无条件
回传 1;这份档案的 securityd.lpc 存档权限检查本来就是对的,
不用改)。
4. §7.50 is_killing(object) 与 is_killing(string id) 类型不
匹配,修好了和 yxsj 一样的 9 处呼叫点。
5. data/ 目录下 45 个存档档案是 CRLF 换行,统一转成了 LF。
adm/daemons/ftpd.lpc、adm/daemons/network/dns_master.lpc 都已
经在 adm/etc/preload 里被注释掉,仅有的呼叫者也都是巫师专用指
令,属于完全休眠,本次没有改动。
已知但未修复的问题
和 yxsj 一样:adm/daemons/chinesed.lpc、adm/daemons/emoted.lpc
开机时都会报一次被拦截、非致命的"Illegal mapping format while
restoring dict/emote";登陆过程会出现一次"apply() with
insufficient permission",但都不影响任何游戏功能。
管理员账号 / Admin account
- ID:
fluffos - 密码 / Password:
Mud@2026 - 权限 / Level:
(admin),通过/adm/etc/wizlist授予,登录 后自动显示"目前權限﹕(admin)"确认生效。
警告:对外公开架设前请务必修改此密码。
本地运行
cd libs/yxzsj
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40170。
NOTES · 移植与修复记录
WASM 修复摘要(迁移自 meta.json 的 group_note)
逸俠之世界(Daniel's World),ES II 血统,台湾出品。是 libs/yxsj 关系很近的手足档案(同样的标题、同样的 [email protected] 联系人、同样的 is_killing/check_legal_name bug 指纹、同样的文件布局),但确认不是逐字节相同的重复档案——即使做完空白/CRLF 归一化,内容依然有差异(比如 adm/daemons/combatd.lpc 有大约 80 行不同),而且这份快照的 securityd.lpc 的 save_object() 路径本来就已经修好了(query_save_file()+__SAVE_EXTENSION__),yxsj 那份才需要应用这处修复——是同一血统里不同时间点的构建版本,不是同一套文件的重新打包。先发现并修复了和 yxsj 相同的重大编码 bug:这份档案同样是 BIG5 编码,但最初的 convert_lib.sh 用了整个语料库通用的 GB18030 默认值,产生了静默乱码(合法的 UTF-8,但是真实存在却错误的中文码点),而不是报出转换错误。已用 BIG5 重新跑了 convert_lib.sh(见处理 yxsj 时做的工具扩展提交),并在重新转换出来的代码树上重做了每一处修复。应用的 WASM 专属修复(和 yxsj 完全一致的模式):(1)经典的 §8.1 is_chinese()/check_legal_name() 字节区间 bug,改成逐码点 0x4e00-0x9fff 检查,界限从 2/12 减半为 1/6。(2)§7.12 类的 tell_room() 裸 exclude 导致的 message() 崩溃,用 exclude || ({}) 保护修复。(3)master.lpc 的 valid_write() 缺少 'user == this_object()' 保护(valid_read() 本来就无条件回传 1;这份快照里 securityd.lpc 的 save_object() 检查本来就是对的,和 yxsj 不同)。(4)§7.50 类的 is_killing(object) 对 is_killing(string id) 修复了和 yxsj 完全相同的 9 处呼叫点(kungfu 类的 roar/nuke 档案、u/d/daniel/ 下的 NPC 档案、cmds/std/surrender.lpc)。(5)data/ 下 45 个 CRLF 换行的 .o 存档档案统一转换成 LF。adm/daemons/ftpd.lpc 和 adm/daemons/network/dns_master.lpc 都处于休眠状态(在 preload 里被注释掉,只有带保护的巫师指令呼叫者),保持原样。和 yxsj 相同的已知非阻断性问题,没有进一步深挖:chinesed.lpc/emoted.lpc 在启动时都会抛出一个被捕获的'Illegal mapping format while restoring dict/emote';登录过程中会出现一次针对 obj/user 的 command_hook 的短暂'apply() with insufficient permission... needs: private, has: hidden',但不会阻挡任何功能。管理员账号播种:fluffos (admin) 加入 adm/etc/wizlist。注册流程在格式化前后都用一个真实的中文名字完整验证过;管理员权限已通过'目前權限﹕(admin)'确认。LPC 格式化工具对全部 949 个档案运行(写入 919 个,21 个转档之前就存在的错误,9 个未改动)——数字和 yxsj 完全一样,符合这两份是同一代码库不同时间点快照的判断。
§7.86 跨库扫描修复(留言板 post 崩溃)
BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 11 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
§7.100 sweep (2026-08-19)
Fixed the corpus-wide inherit ROOM; ... replace_program(ROOM); redundant-replace bug (AGENTS.md §7.100). 185 live occurrences deleted: 184 via scripted sweep (fix_710_room.py), plus 1 hand-fixed roommaker-tool template (obj/roommaker.lpc, simple string-builder variant — a second, already-commented-out copy of the same template lower in the same file was correctly left untouched). 2 already-commented-out instances left untouched. No real .lpc source found under work/data/. Verified via build-debug driver boot: clean compile, zero new "cannot replace"/"cannot bind" debug.log lines; confirmed serving via raw-socket connect on port 40170.
§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.
Deep functional test (round two) — 2026-08-27
Full live continuous playthrough (build-debug driver, port 40170,
raw Python socket client): registered a real character (譚雲/Tanyun),
apprenticed to a sect NPC (柳淳風, 封山劍派/swordsman), learned a skill
via learn ... from ..., fought a wild NPC with the safe fight
command (ended in unconsciousness with a scheduled revive()
call_out, not death — confirmed working, not a soft-lock), did two
separate reconnect cycles (one abrupt-disconnect-then-reconnect while
a revive() timer was still pending, one explicit quit+fresh
relogin after a real wall-clock gap), and cross-checked find_player()
+ tell + the admin call command between two simultaneous
connections. Registered and confirmed the seeded admin account
(fluffos/Mud@2026) live for the first time (previously wizlist-only,
no save file existed) — 目前權限﹕(admin) confirmed, and score
correctly showed the admin-only extended attribute block.
Systematically grepped this lib's source for every standing
cross-cutting bug pattern in AGENTS.md (§4.3, §4.4, §7.11, §7.80/118,
§7.86/100, §7.103, §7.112, §7.121–§7.153) rather than spot-checking.
Found and fixed 6 real bugs, all from the already-documented pattern
list — same lineage, same bugs as yxsj's own round-two pass, since
these two libs share the ES2/daniel codebase at different points in
its history:
data/chinese.o(§4.4, BIG50x5C-collision): this snapshot's BIG5→UTF-8 conversion had the exact same collision asyxsj, but WORSE — not just the one trailing dict entry, but 10 separate string values ended in a0x5C-final BIG5 character (功/許) immediately before their closing quote (e.g."move":"...輕功\","celestial":"...神功\", plus 3 more mid-string cosmetic instances like"force":"內功\心法"). Confirmed via boot: before the fix,chinesed's dict restore silently zeroed (sameIllegal mapping format while restoring dictclass asyxsj, though this boot didn't even surface the warning — the corruption ran past EOF differently given the extra breaks), andlearn/skillscrash-risked on any entry past the first broken string. Re-derived the whole file fromraw/mudos/es2lib/data/chinese.owith a custom escape-aware BIG5 decoder (unescape\-doubling byte-for-byte, THEN BIG5-decode, THEN re-escape for LPC string syntax) rather than a blindiconv— diffed byte-for-byte against the naive conversion and confirmed the ONLY changes were removal of the 13 spurious backslashes, nothing else. Verified live: fresh boot showed nochinesedrestore warning at all, andlearn sword from master/skillsboth worked crash-free on the test character.- §4.3
static→nosavecollision, 7 call sites / 4 files (cmds/wiz/call.lpc'sCALL_PLAYER,adm/obj/master.lpc'sCRASHES×3,cmds/arch/purge.lpc'sPURGE×2,adm/daemons/securityd.lpc'spromotion) — identical shape and identical files toyxsj's fix, confirmed againstraw/'s"static/..."literals. Reverted all 7, and hardenedadm/simul_efun/file.lpc's sharedlog_file()with anassure_file()call beforewrite_file(), matchingyxsj. Verified live: logged in as admin (fluffos), rancall tanyun->query("name")andcall tanyun->query("class")against the live test character — both executed cleanly and appended correctly to the pre-existing (real 2000-era)log/static/CALL_PLAYERhistory file with no crash. - §7.103
log_error()broadcasting raw compile warnings (adm/obj/master.lpc): added thestrsrch(message,"warning:") == -1guard. Verified live: a lazy first-compile ofcmds/usr/quit.lpcduring testing threw multipleUnknown escape sequencewarnings (the ~129-instance cosmetic tail of the same §4.4 BIG5 collision, left unfixed here exactly as documented foryxsj— none of these are crash-causing) and none of them leaked to the connected test player's screen. - §7.86/§7.100 redundant
replace_program()(obj/board/wizard_j.lpc):inherit "/std/jboard"+replace_program("/std/jboard")increate()— same file, same shape asyxsj's fix (missed by the earlier macro-based corpus sweep since this board uses a literal path). Deleted the redundantreplace_program()call. Verified live as the admin test character: navigated to/d/wiz/jobroom, the board loaded without the "cannot bind an lfun fp to an object with a pending replace_program()" crash, andproject/read newboth worked end-to-end. - §7.153
std/jboard.lpc do_read(): identical missing-elseshape toyxsj—read new/read nextcomputed the correct index, then fell through into the unconditionalsscanf(arg, "%d", num)numeric parse, which always failed on the literal string"new"/"next"and returned "你要讀第幾個計畫的簡報﹖" instead of showing the note. Fixed by chaining it into the existingif/else ifinstead of a bare trailingif. Verified live:project <title>followed byread newnow correctly displays the just-posted note instead of erroring.
No other cross-cutting pattern from the AGENTS.md checklist matched
live source in this lib (checked explicitly, not just assumed clean):
§7.121 (no float-typed economy function found), §7.122/§7.132/§7.134/
§7.135/§7.136/§7.139/§7.144/§7.145/§7.146/§7.147/§7.148/§7.149/§7.151
(no matching shape found by grep), §7.129 (tell_room() wrapper
already has the exclude || ({}) guard from the earlier §7.12-class
onboarding fix), §7.133 (net_dead() IS defined on obj/user.lpc),
§7.150 (the login-verification object restores directly onto itself,
no throwaway clone; the leftover post-exec() shell object is
destructed lazily on the player's *next* disconnect/quit/wizard
update, confirmed via code read of obj/user.lpc's own net_dead()
and cmds/usr/quit.lpc/cmds/wiz/update.lpc — no active heart_beat on
the orphan in the meantime, so no resave-corruption path), §7.152 (no
remove_living_name()/unset_living_name call exists anywhere in this
codebase, so the living-name registration from initial login persists
across reconnect() without needing to be reset — confirmed
empirically: cross-connection tell tanyun ... from the admin account
worked immediately after tanyun had already gone through one full
netdead→revive→reconnect cycle).
Two harmless, pre-existing oddities noted but explicitly NOT touched
(design/leftover, not crashes): d/snow/schoolhall.lpc and
d/wiz/jobroom.lpc both end their create() with a
"obj/board/xxx"->foo()/->???() call to an undefined function on a
board object — apparently a preload idiom from the original codebase;
call_other to an undefined function is a silent no-op on this driver
(confirmed no error in either boot output or the caught-error handler
across this whole session), not a crash. Also: cmds/usr/recall.lpc
sends players to /d/wiz/entrance while cmds/usr/recall2.lpc (a
distinct, separately-registered command) sends players to
/d/snow/inn — both work correctly, left as-is since choosing which
should be "the" recall command is a content decision, not a bug.
This lib's actual playable world is a much smaller custom build (a
"school" starting zone plus a 雪亭鎮/snow-town zone with the ES2 sect
system) than doc/help/newbie's text describes (which lists many
more locations/sects than are reachable from the actual map) — a
content/documentation mismatch, not a programming bug, left unfixed
per the scope boundary.
Admin account fluffos/Mud@2026 confirmed seeded and working
(kept, with its live-registered save file). Test character 譚雲
(tanyun) and its test bulletin-board post removed before commit.
深度功能测试(2026-09-04,shop)
Live pass, port 40170. 2026-08-27 already apprenticed 譚雲 to 柳淳風
(封山劍派); this round is shop only. First send is the English id (no
GB/BIG5 menu). Admin fluffos / Mud@2026 lands at /d/school/gate.
Shop: goto /d/school/store (逸俠中學福利社). list is a static
price board; keys are English (dumpling, not 包子). clone
/obj/money/gold then buy dumpling succeeded: “你買下了一個包子”,
inventory gained 包子 plus change (銀/銅). std/room/store.lpc treats
any non-zero can_afford() as success, so the gold-only “零錢” return-2
path does not block this counter (unlike cmds/std/buy.lpc / es2's
waiter). No finance patch needed for this purchase.
/d/snow/npc/inn (飲風客棧) is not on the walkable map (no room
exits point here). goto hits make_inventory() →
call_other(0, …) because objects names /d/snow/npc/waiter and that
file is absent from both work/ and raw/ (also absent from sibling
yxsj). Not restored from es2 — this snapshot never shipped the NPC.
Walkable /d/snow/inn (義守客棧) has an empty objects map.
No code change. log_error() already gates on "warning:" (2026-08-27);
this login did not broadcast compile warnings.
深度功能测试(§10.7,2026-10-04)— negative range ends
scripts/lpc_fix_negative_ranges.py: 5 line(s) in 2 file(s) count a range from the end with < (x[a..-1] -> x[a..<1], x[-2..] -> x[<2..]; KB 06 §7.209). In this driver a negative constant end gave "" / ({ }) and a negative start the whole value, so each of these returned the wrong slice; the compile warning A negative constant as the second element of arr[x..y] is gone from the files that compile. Range lvalues (s[0..-1] = text, the prepend idiom) are left alone. A paired compile of the changed files at HEAD and in the working tree showed no new error.
深度功能测试(§10.7,2026-10-04)— stray backslash before a closing quote
A Big5 character whose second byte is 0x5C was written with one extra backslash after it (the old driver read 5C 5C as one backslash and so completed the character) and the UTF-8 conversion kept it (KB 03 §4.4). Before a closing quote it escapes the quote, the string runs into the next line and the file does not compile. scripts/lpc_fix_big5_quote_backslash.py deletes it when the character before it encodes to cp950 with 0x5C as second byte and the quote ends an expression. Here: cmds/std/enable.lpc ("force": "…功\", and "dodge"), so the enable command never loaded, and four if (temp == "…功\") lines in daemon/class/sage/{su-sk,bullets}/bullets.lpc, u/d/daniel/temp/bullets.lpc and u/d/daniel/npc/mastersu.lpc. enable.lpc loads now. The four bullets files still do not: their first message line has 功\\" (two backslashes) in front of a colour macro, which this fix does not touch; read them by hand.
深度功能测试(§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 yxzsj: 66 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 67 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 -> 67 PASS, 0 that loaded before fail now, 66 more load.
66 files moved (66 .lpc); mostly under d/Pili/glass (64), d/Pili/obj (2). A rescan with scripts/lpc_warnings.py yxzsj 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 std/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-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)— 房间列出的物件缺档时房间照常加载
房间基类 std/room.lpc: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、多件 OK。
深度功能测试(§10.7,2026-10-07)— 编译警告整理
scripts/lpc_warnings.py yxzsj --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 160 个 .lpc(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):143 -> 143 通过,无回退。启动时警告 13 -> 0;新进程登录(输入名字)正常。
编译警告收尾(2026-10-13)
scripts/lpc_twin_port.py adopt yxsj yxzsj 搬来 yxsj 同日整理(400db78f408)的 3 个文件,与本库逐 token 相同,能编译的文件警告清零;改动的对象在 HEAD 与工作树各载入一次,regress 0。
include/net/ftpdsupp.h的check_access()只在#ifdef GUEST_WIZARD_FTP分支用file,声明移进该分支。u/d/daniel/npc/chinmo.lpc、u/d/daniel/temp/corpse.lpc写明用sserver::clean_up(),与驱动原先的选择相同。
- 2026-10-14:新驱动(FluffOS 970d9f09)报
Float value truncated to int(#1426)。4 个文件 10 处:赋值、初始化、return 改写成to_int(...)(值不变,只是写明截断)。改动的对象新进程各载入一次,无回退(scripts/lpc_fix_float_trunc.py,KB 04 §6.10)。
- 2026-10-14:2 个文件因
Illegal to inherit after defining global variables整份不编译:FluffOS 要求所有inherit写在第一个全局变量之前,MudOS 不管。scripts/lpc_fix_inherit_order.py把写在inherit之前的全局变量(以及声明全局变量的头文件的#include)挪到最后一个inherit之后(KB 04 §6.3)。
表情档读不出来(2026-10-15,KB 03 §4.4)
data/emoted.o 中 grin(邪惡的笑)一条在原档里就坏了:"target_res"$P指著自己。說道﹕就是我﹗","others_target":"$N指著$n。",一个键丢了后半截,后面接的是别的表情的文字;restore_object() 报 Illegal mapping format,整个表情系统一个表情都没有。按本条自己的字段补回:target_rest 取 myself_rest 把 $P 换成 $N(同档 groan 等表情都是这样),others_target 取 target_self 把 $T 换成 $n(同系各库 grin 的 others_target 就是这句)。丢失的那个表情无法恢复。驱动核对能读回。
NPC 带着本库没有的物品,载入失败(2026-10-15,KB 06 §7.73)
1 个文件、2 处在 create() 里 carry_object("<本库没有的文件>")->wear()(或 wield()、set_amount()):carry_object() 得 0,接着的调用报 Bad argument 1 to EFUN call_other(),NPC 根本载入不了,该有它的房间里就少了这个人。多是从别的系统搬来的 NPC 用了原系统的物品路径(最常见:/u/e/eeman/eq/cloth、/u/e/eeman/weapon/sword)。每处改为 { object carried_ob = carry_object(...); if (carried_ob) carried_ob->wear(); }(scripts/lpc_guard_carry_object.py):NPC 照常出现,只是不带这件东西;文件补回来(或是虚拟对象)照样会带上。载入检查:改动的文件改前能载入 0 个,改后 0 个(0 个原来载入不了),没有退步;其余 1 个另有原因(多为 set_skill() 了本库没有的武功)。
去掉的预定义宏 SAVE_EXTENSION(2026-10-18,KB 04 §6.11)
老版 MudOS 给每个 LPC 对象预先定义了 SAVE_EXTENSION(".o");FluffOS 的 ChangeLog(COMPAT_BUSTER 一条)把它删了,改用 __SAVE_EXTENSION__。本库没有自己 #define 这个宏,用到它的文件一律 Undefined variable 'SAVE_EXTENSION',整个对象载入不了。改成 __SAVE_EXTENSION__(scripts/lpc_fix_predefined_macro.py):adm/daemons/network/netmail.lpc、include/net/ftpdsupp.h、obj/token.lpc。token 是帮派领袖信物;netmail 是网络邮件守护进程(本库缺 mailer.h 等头文件,改后仍载入不了)。改后用 lpcc 逐个载入这些对象:1 个通过,1 个还有别的错误(adm/daemons/network/netmail:/adm/daemons/network/netmail.lpc:17: error: Cannot #include mailer.h)。
set_skill() 遇到本库没有文件的武功不再抛错(2026-10-18,KB 06 §7.256)
feature/skill.lpc 里的 set_skill()、map_skill()、prepare_skill() 碰到没有武功文件的名字就 error("F_SKILL: No such skill (…)"),调用它们的 NPC create() 整个中断:NPC 载入不了,刷出它的房间也少一个人。本库有 2 个对象设了本库没有文件的武功(2 个名字,最多的是 iceforce、icekill)。三处都改成 return:这一门武功不给、不映射,对象照常载入,只是少了它(缺的武功本身仍是内容缺口)。抽样载入这些对象:2 个里 2 个通过。(scripts/lpc_guard_set_skill.py)
删掉 exert_function(整数)(2026-10-19,KB 06 §7.266)
/d/school/npc/healtech.lpc create() 里的 exert_function(10); 把整数传给了要字符串的直接调用(让 NPC 闲聊时自己运功),驱动报 Bad type for argument 1 of exert_function ( string vs int ),整个 NPC 载入不了。这是存档里的笔误,不知道作者想传哪个字符串,删掉这一句(只影响闲聊时的运功表现)。chidi 的同一个 NPC 早先就是这样处理的。
BIG5 的 0x5C 尾字节(扩展)(2026-10-19,KB 06 §7.266)
BIG5 里 功 等字的第二个字节正好是 0x5C(反斜杠),作者在它后面多写了一个 \,转 UTF-8 后这个多出来的 \ 跑到了收尾引号前面,把引号转义掉,字符串一直吃到文件后面。scripts/lpc_fix_big5_quote_backslash.py 原先只在引号后面跟 , ) ; } ] + 或行尾时才删;这次把“引号后面紧跟颜色宏再接字符串”(HIY"功\"HIR"』")也算进去,删掉多余的 \。u/d/daniel/npc/mastersu.lpc、daemon/class/sage/bullets/bullets.lpc 等 4 个文件,共 21 处。
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,scripts/toolkit_fix.sh)
本库在新驱动下重扫,机械修复:2 个未使用的局部变量,14 处浮点数赋给 int 的地方改成 to_int()。修后与 HEAD 逐个物件对比载入,没有退步。
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(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])) ...,不检查列表是否为空。本库有 1 个 NPC 文件写成 set("chat_msg", ({ }))(或 chat_msg_combat)并带正的 chat_chance(例如 /daemon/class/ronin/master.lpc),每次心跳有 chat_chance% 的概率 random(0) 为 0、msg[0] 报 Array index out of bounds(驱动捕获,心跳继续,但每次一条运行时错误),这个 NPC 在内存里就一直发生。在 random(100) < chance 前面加 sizeof(msg) &&(scripts/lpc_fix_chat_empty.py;if (random(100) > chance) return 0; 的写法是 !sizeof(msg) ||)。改动:/std/char/npc.lpc(共 1 个文件)。
巫师命令不带参数时对 0 调用 efun(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 与工作树对照,没有退步。
(: 函数 :) 指针指向对象自己没有定义的函数(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 处函数体被作者整段注释掉(指针没跟着删),是有意关掉的,没动。
(: 函数名 :) 指针指向本库没有的函数:只含这个指针的行加了注释(2026-10-22,KB 06 §7.355 补充)
inquiry / item_desc / chat_msg* 里的 (: 函数名 :) 指向本对象(含包含文件、继承的父类)没有函数体的函数:用到时(ask、look、NPC 闲聊)驱动抛 *Undefined lfun pointer called: 函数名,这一拍心跳的后半段被跳过。这些函数没有可以补回的来源(别的库同一路径的文件里没有,或者它还要用本库没有的东西),所以 1 个文件里 2 行只含这个指针的行("剃度": (: ask_for_join :), "出家": (: ask_for_join :),)加了注释,原文保留并写明原因,这一项当作没有这个话题 / 物品 / 闲聊条目(scripts/lpc_fix_dangling_lfun.py;#include 的文件在库里缺失的对象、指针不在整行上的写法没动)。 前提(function_exists())和加载检查(HEAD 与工作区对比,本库改动的 .lpc 文件)的结果见 KB 06 §7.355:没有一个回归。
汉字按字节切片的老写法没有换成按字符:改法名 "澄" + name[2..3]、辈分判断 name[0..1] == "澄" 等 1 处(2026-10-22,KB 06 §7.360)
原档按字节算字符串区间(一个汉字 2 字节):name[2..3] 是名字的第二个字,name[0..1] == "澄" 问名字是不是以「澄」开头。转成 UTF-8 之后,这个驱动的区间和 strlen() 都按字符算(strlen("澄观") 是 2,"澄观"[0..1] 是两个字),老算术原样留着、意思全变:拜入少林 / 武当 / 峨嵋等派的玩家被改成错误的法名(二字名 张三 变成一个字的 澄,更长的名字取到第三个字)。
本库 1 处(1 个文件):拼名字的赋值 1 处。例:name[0..1] → name[0..0](/daemon/class/bonze/master.lpc:99)。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,不要重跑。
血条 / 表情标题按字节算显示宽度(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(一格两个空格)。本库 2 处(1 个文件)。证明(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… 行。