Book and Sword II

✅ 可玩

书剑贰

sje

🔑 fluffos(密码见 README) 更新 e55ab17 2026-09-04 源码 下载 ZIP

▶ 开始游玩 · Play Now

湖北电信的"书剑贰"泥潭,Ver2.1,和姊妹档案 `sjecl`(书剑恩仇录)同属 Century/adm-single 引擎家族(`adm/single/master.lpc` + `adm/daemons/securityd.lpc`)。新角色同样从"武馆前院"起步,东西两侧是练武场,与 `sjecl` 共享同一套新手区蓝本,但新手照顾机制不同:"武馆门卫"NPC(`ask menwei about 礼物`)提供一次性新手礼包,`ask menwei about 传功`则可以当面讨要内力灌顶,最多三次、每次增加 100 点内力上限;武馆内还有一位会主动使出"天魔掌""天魔剑""天魔刀""天魔功"等技能的"护法使者"NPC,是这份档案自己特有的一位人物。死亡后的审判场景也与 `sjecl` 不同:走的是阎罗大殿、黑白无常与秦广王审判的路线,而不是孟婆汤桥段。巫师账号有比 `sjecl` 更严格的本地登录限制:`adm/daemons/sited.lpc` 规定只有已经具备巫师权限的 id 才能从 `127.0.0.1`/`localhost` 连线,其余一律拒绝。

English

A Hubei Telecom "Book and Sword II" MUD, version 2.1, on the same Century/adm-single engine (adm/single/master.lpc plus adm/daemons/securityd.lpc) as sibling sjecl (The Book and the Sword) elsewhere in this archive, and sharing its classic newbie layout: new characters start in the Martial-Arts-Hall Front Yard, flanked by practice grounds. Newcomer care differs from sjecl's version, though — a "gatekeeper" NPC hands out a one-time starter package on request, and separately grants up to three rounds of internal-power infusion (+100 max internal power each) just for asking. The hall also houses a "guardian envoy" NPC unique to this archive, who proactively unleashes named skills like Heavenly Demon Palm/Sword/Blade/Art in combat, and wizard accounts face a stricter local-login IP policy than sjecl's (only already-wizard accounts may connect from 127.0.0.1/localhost). It has 38 player-run bulletin boards archive-wide, including one that guilds can create for themselves via the guild board-creation command.

README

本次修复的关键 bug

大部分 bug 和 sjecl 完全一样(同宗代码),修法也相同:

1. §7.60 master.lpclog_error()/standard_trace()CHANNEL_D 尚未加载时呼叫它,触发编译期递归加载崩溃——两处都补 上 find_object(CHANNEL_D) 判断。 2. §7.59 securityd.lpcvalid_read() 无条件用 this_player() 覆盖驱动传入的 user 参数——已排除 func=="load_object"/"include" 两种情况。 3. 经典 §8.1 GBK 字节区间 is_chinese() + 对应姓名长度上限没有 减半的 check_legal_name()。 4. §7.50 is_killing(who) 传对象而非字符串——同样的 5 个 NPC 揭穿身份对话档案(sjecl 有 6 个,这份档案少了少林那份)。

这份档案独有的新 bug:

5. adm/daemons/backupd.lpc 用了未定义的 BACKUP_DIR 常量,而 且这个精灵在 preload 列表里,直接导致启动失败。硬盘上已经有一个 /backup 目录(libs/sje/work/backup/),确认了原意——在 globals.h 里补上 #define BACKUP_DIR "/backup/"。 6. adm/daemons/sited.lpcis_valid()sjecl 那份更严 格:任何非巫师身份的 id 只要从 127.0.0.1/localhost 连线, 一律直接拒绝(只有 wiz_level(id) 为真的账号才能用本地连线)。 这是这条支线自己的安全策略,不是 bug——本次没有修改它,而是先把 fluffos 写入 wizlist 再直接用它注册,一次性同时验证了注册流程 和管理员权限。

深度功能测试(§10.7)修复的 bug

sibling-check 自 sjecl(同一血统,已完成深挖):

管理员账号 / Admin account

警告:对外公开架设前请务必修改此密码。

本地运行

cd libs/sje
~/src/fluffos/build-debug/src/driver config.fluffos

游戏端口:40146

NOTES · 移植与修复记录

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

和 sjecl 同一个 Century/adm-single 血统(adm/single/master.lpc + adm/daemons/securityd.lpc)——大部分 bug 相同,用了相同的修法:(1)§7.60 master.lpc log_error()/standard_trace()→CHANNEL_D 编译期崩溃,两处都用 find_object(CHANNEL_D) 守卫。(2)§7.59 securityd.lpc 的 valid_read() 无条件用 this_player() 覆盖,拒绝了 load_object/include 触发的读取,已把这两种情形排除在覆盖之外。(3)§8.1 没减半的 GBK 字节区间 is_chinese() 加上对应没减半的 check_legal_name() 长度界限(原为 4/8 加 i%2 字节窗口→改成逐码点的 2/4)。(4)§7.50 is_killing(who) 物件/字符串不匹配,出现在和 sjecl 相同的 5 个 NPC 对话档案里(少了 sjecl 里少林那份,这份快照没有)。这份档案独有的新发现:adm/daemons/backupd.lpc 用了未定义的 BACKUP_DIR 常量(会被预载,导致开机失败)——已在 globals.h 里加上 #define BACKUP_DIR "/backup/"(硬盘上已经有一个 /backup 目录,确认了原意)。另外:adm/daemons/sited.lpc 的 is_valid() 比 sjecl 的规则更严格/相反——任何非巫师身份的 id 从 127.0.0.1/localhost 连线一律无条件拒绝(只有 wiz_level(id) 为真的账号才能用回环地址),所以 WASM 测试(永远是 127.0.0.1)只能注册巫师身份的 id——已改用先播种 wizlist 再直接以 fluffos 身份注册(一次性验证管理员和注册流程)来绕过,而不是当作需要修的问题(这是这条血统真实的安全策略选择,不是 bug)。管理员账号播种:fluffos (admin) 加入 adm/etc/wizlist(这里不需要像 sjecl 那样再加第二条 sited 表条目,因为 127.0.0.1 规则本来就给巫师身份账号开放了完整的回环访问权限)。已验证:完整注册→进入游戏世界,权限正确显示 (admin)。记录了一处不阻断、未修的内容 bug:adm/daemons/questd.lpc 的 spread_quest()/init_dynamic_quest() 在预载的动态任务放置阶段对一个为 0 的物件呼叫 environment()(被捕获,不阻断开机)。LPC 格式化工具对全部 8527 个档案运行;还原了 1 个通过"去空格后比对旧档案"扫描(覆盖 85 个格式化工具触碰过的档案)确认有 CJK 重新加空格损坏的档案(d/quanzhen/hudi5.lpc);直接逐一比对了两个 ASCII 地图档案(d/shaolin 下两份)——干净,只是排版调整。格式化后重新验证过,干净。

深度功能测试(§10.7,2026-08-05)

这份档案和刚深挖过的手足档案 sjecl 同一血统,先检查了 sjecl 自 己的 §10.7 记录(§8.3b 指令表死档、§7.86 留言板 post 崩溃、 §7.5 file_size ACL 误判等),逐项核实:

§7.100 房间基类 replace_program() 扫尾修复(2026-08-19)

ROOM 宏(/inherit/room/room)在本档案 2,004 处房间文件的 create() 里紧跟 inherit ROOM; 之后又多余调用了一次 replace_program(ROOM);——AGENTS.md §7.100 记录的同一个休眠 bug。 本档案就是 §7.100 条目本身引用的"sje-precedent"(bug 长在工厂 里,不只是出货内容里):不只 clone/misc/roommaker.lpc 一份房间 建造工具,帮派管理指令集 cmds/minister/*.lpcacceptallyresignbanishdeclarelinkroommkroom(2 处)、 addnpc(2 处)、add_actionadd_descset_longset_short)、cmds/king/build_bank.lpc(2 处)、cmds/adm/ ra.lpc 也全部把这个 bug 烤进了各自的房间字符串拼接模板里——一共 14 个文件、18 处字符串拼接变体。用 fix_710_room.py 扫过 work/,删除 1,976 处标准形状;work/data/group/groom/ 下 10 个 真实 .lpc 房间源文件(帮派废墟场景,inherit ROOM; 独立标准形 状,被脚本默认的 data/ 排除规则跳过)另外单独用同一脚本对该子 目录扫过,删除 10 处;上述 18 处字符串拼接变体用一个小 Python 脚 本(二进制模式,逐文件计数校验)批量替换成 str += "\n\tsetup();\n}\n";。1976+10+18=2004,与survey记录的 存活总数完全吻合;git diff --stat 显示 1998 个文件净删 2004 行、增 18 行,同样吻合。work/data/group/npcwork/data/group/ obj 下确认没有真实命中。

驱动干净启动(零新增编译错误、端口 40146 正常监听、debug.log 无任何"cannot replace"/"cannot bind"行)。本档案此前从未被真实注 册过(只有 wizlist 里的 fluffos (admin) 条目,没有存档)——用完 整注册流程(UTF-8 编码:BIG5 询问答 N、id、密码 Mud2026A、中文 名"秦风"、天赋 0 随机+接受、邮箱、性别 m)创建,落地"门廊", look/score/quit 均正常,全程 debug.log 保持干净。新建的 管理员存档(data/{login,user}/f/fluffos.o)已提交,补全此前 NOTES.md 记录过、但从未真正落地的管理员播种。驱动按精确 PID 结 束。

``§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 d/death/npc/yanluo.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): 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.

AGENTS.md §7.19 fix: enable_player() reentrancy from init()

feature/command.lpc's enable_player() (wrapper around enable_commands()) was reachable from an NPC's init(): the shared inherit/char/char.lpc setup() (called from every character's create()) itself calls enable_player(), and d/gb/npc/xixia-wushi.lpc redundantly calls setup() again from inside its own init() (on top of the setup() its create() already made) -- same shape as the originally-documented mhxy zhangmen.lpc case. enable_commands() is only safe to call from create(): calling it again on an object already living() makes the driver re-invoke that same object's init() as a side effect, which recurses back into enable_player() on the same call stack until "Too deep recursion" aborts the boot on a room's first-ever visit. Fixed with a true reentrancy flag (in_enable_player_now), NOT a living() guard (which would break legitimate re-enables from revive() in feature/damage.lpc and wakeup()/wakeup2() in cmds/std/sleep.lpc, both confirmed to re-invoke enable_player() on this lib while the object is still living()). Verified via lpcc --batch single-file compile check (PASS). Part of the corpus-wide §7.19 sweep (Batch E).

深度功能测试第三轮 / §10.7 round three (2026-09-03)

新角度:2026-08-05 那一轮测过指令表、留言板 post、武馆切磋、北大街 死斗和鬼门关→阎罗审判复活。当时写明没测的是门派拜师和商店。本轮只 补这两条,不重打死亡。管理员仍是 fluffos / Mud2026A(中文名秦风, UTF-8:BIG5 询问答 N)。端口 40146。回环只放行巫师,本号是 admin。

实测过程

发现并修复的 PROGRAMMING bug

log_file()assure_file()(AGENTS.md §7.11,和 ldtx 同一 Century/adm-single 形状)adm/simul_efun/file.lpclog_file() 是裸 write_file(LOG_DIR + file),同文件里已经有 assure_file() 却没用。log/nosave/ 被 gitignore,全新/干净树上不 存在。巫师 clone 在物件已经复制成功之后去写 log_file("nosave/CLONE_OBJ"),抛

*Wrong permissions for opening file /log/nosave/CLONE_OBJ for append.
"No such file or directory"

栈在 file.lpc / simul_efun.lpc 第 8 行。clone 本身成功,但每 次都对巫师刷执行时段错误。securityd 晋升日志和 valid_write 拒绝 日志(log_file("FILES"))是同一条路。

修复:前向声明 + assure_file(LOG_DIR + file),并加 nosave int in_log_file 重入守卫(securityd 拒绝写入也会 log_file("FILES"),避免再走一遍 file_size/mkdir;同 fqyy2 2026-09-03)。CRLF 按二进制保留。复测:clone 零条权限错误, work/log/nosave/CLONE_OBJ 被自动建出并写下审计行。冷启动无 “Too deep recursion”。

本轮修改的文件