info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
Century/adm-single 引擎家族的一支(`adm/single/master.lpc` + `adm/daemons/securityd.lpc`,与姊妹档案 `sje` 同源;名字相似的 `sjplgfjxb`/`sjplii` 其实是完全不同的 ES II 引擎家族,只是巧合共用"书剑"招牌,并非同源代码),以金庸小说《书剑恩仇录》为背景的重庆电信"书剑"泥潭。新角色从"武馆前院"起步,两侧设有练武场,北面通往武馆大厅——是这批"书剑"系泥潭共用的经典新手区布局;门派收录不限于原著:`ask shizhe about 拜师` 可以拜入武当、少林、星宿、华山、丐帮、神龙岛、昆仑、铁掌、古墓、慕容、桃花岛、天龙寺、大轮寺、嵩山、明教、峨嵋等十余个门派,横跨金庸绝大多数小说的门派体系,而非只围绕书剑恩仇录一部作品。"书剑天下礼物使者"NPC 是新手快速上手的捷径:`ask shizhe about newbie` 之后再报出想加入的门派名,即可一次性获得该门派整套武学(技能 201 级、识字 200 级、内力/精力上限拉满)与巨额初始经验,但每个角色只能使用一次;地图以书剑恩仇录的江湖足迹为骨架,城镇场景包括长安、兰州、沧州、杭州、黄河、恒山、雪山等地,巫师账号则有独立的按 id 授权的登录 IP 白名单机制。
English
A Chongqing Telecom "Book and Sword" MUD nominally based on Jin Yong's novel of the same name (published in English as The Book and the Sword), on the Century/adm-single engine (adm/single/master.lpc plus adm/daemons/securityd.lpc) shared with sibling sje elsewhere in this archive — despite similar naming, sjplgfjxb and sjplii are actually a wholly different (ES II) engine lineage, a coincidence rather than shared code. New characters start in the Martial-Arts-Hall Front Yard, and the sect roster reaches well beyond the source novel: `ask shizhe about 拜师` opens the door to Wudang, Shaolin, Xingxiu, Huashan, the Beggars' Sect, Divine Dragon Island, Kunlun, Iron Palm, the Tomb of the Living Dead, the Murong clan, Peach Blossom Island, Tianlong Temple, Dalun Temple, Songshan, Mingjiao, and Emei — spanning most of Jin Yong's whole sect universe rather than one book's. A "Book and Sword World" gift-bearing NPC lets a newcomer fast-track into any of these sects with a maxed skill/literacy set and a huge starting XP bonus, once per character, and the map follows the novel's own geography through Chang'an, Lanzhou, Cangzhou, Hangzhou, the Yellow River, Hengshan, and the snow mountains. Wizard accounts are gated by their own per-id login IP whitelist.
README
本次修复的关键 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() 覆盖驱动传入的 user 参数:导致每一次连线最开
头 logind.lpc 检查 IP 黑名单(BAN_D->is_banned(),此时玩家还
没输入 id)都会因为权限判定错误而报 *Read access denied.——已
排除 func=="load_object"/"include" 两种情况,不再覆盖。
3. 经典 §8.1 GBK 字节区间 is_chinese(),以及对应的姓名长度上
限没有减半(原来是字节数时代的 4-8,改成 2-4,并去掉按字节配对
的 i%2 窗口检查)。
4. §7.50 is_killing() 传对象而非字符串:这次不在
accept_kill() 里,而是 5 个 NPC 揭穿身份后开打的对话档案里(
d/city/npc/{guidao,guidao1,guidao2,xiaosong}.lpc、
d/foshan/npc/wanglaohan.lpc、d/shaolin/npc/xiaosong.lpc),
这些是直接呼叫(非 ->),会让 preload 阶段生成 NPC 时整个编译
失败。约 20 处 kungfu/skill/*.lpc 里也有同样写法的
target->is_killing(me),但那些是 -> 呼叫,不会导致编译失败,
只是运行时判断不准(内容/平衡性问题),按惯例本次不追加修改。
深度功能测试(§10.7)修复的严重 bug
注册流程本身完全正常(欢迎信息、VIP 提示、新闻系统提示都正确显
示,角色也正确落地进游戏世界),但落地之后任何一个玩家指令,包
括最基本的 look,都会返回"什么?"——游戏事实上完全无法操作。
这个症状被埋在一长串正常的欢迎讯息后面,很容易被误认为是正常返回
而忽略过去。
根源是 AGENTS.md §8.3b 已经记录过的经典 bug:adm/daemons/
commandd.lpc 的 rehash() 用 sscanf(cmds[i] + "$", "%s.c$",
cmds[i]) 从目录列表里筛出指令档案,但转档后所有档案都已经改名成
.lpc 后缀,这个模式永远匹配不到任何东西,指令表永远是空的。这份
档案之前在全库排查 §8.3a(private nomask command_hook)时就已经
被列为"已修复"——command_hook() 本身确实没问题——但 §8.3a 和
§8.3b 是两个完全独立的 bug,即使前者修好了,command_hook() 内部
呼叫的 find_command() 因为指令表是空的仍然对任何动词都返回 0,
一样导致每个指令都落空。已把 "%s.c$" 改成 "%s.lpc$" 修复。
修复前 boot.log 里还反复出现 adm/daemons/baoshid.lpc(随机放置
宝石/NPC 的精灵)的 eval 开销超限报错(一次会话 15 次),修好指令
表之后这个报错也完全消失了——很可能是空指令表导致每次按键都要
fallback 去尝试表情/频道系统的探测逻辑,间接给这个不相关的精灵造
成了额外编译压力。
管理员账号 / Admin account
- ID:
fluffos - 密码 / Password: 注册时自设(至少 5 位,必须同时包含数字和英 文大写字母)
- 权限 / Level:
(admin),通过/adm/etc/wizlist授予。 - 这份档案还有第二层限制:
adm/daemons/sited.lpc里硬编码了一个 按巫师 id 生效的登录 IP 白名单(valid_login),巫师状态的账号 如果不在这份表里,无论从哪个地址登录都会被拒绝并提示"巫师必须有 地址限制"。已经在这份表里加了"fluffos": ({ "127.", "0." }), 允许本地/WASM 测试连线。正式对外开服后如果要开放巫师从其它地址 登录,需要用巫师指令追加更多前缀。
警告:对外公开架设前请务必修改此密码。
本地运行
cd libs/sjecl
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40139。
NOTES · 移植与修复记录
WASM 修复摘要(迁移自 meta.json 的 group_note)
Century/adm-single 血统(adm/single/master.lpc + adm/daemons/securityd.lpc),书剑代码库。WASM 修复:(1)§7.60 master.lpc log_error()→CHANNEL_D->do_channel() 编译期崩溃,用 find_object(CHANNEL_D) 守卫;standard_trace()(运行时的 error_handler 路径,不只是编译警告)里也有同样的模式,同样地守卫。(2)§7.59 securityd.lpc 的 valid_read() 无条件用 this_player() 覆盖驱动提供的 user 参数,拒绝了 load_object/include 触发的读取(比如 BAN_D->is_banned() 在每一次连线时都报"Read access denied"崩溃,因为 logind.lpc 做的第一件事就是拿连线 IP 去核对封禁名单,这时候连玩家 id 都还没设置)——已把 func=="load_object"/"include" 排除在覆盖之外,匹配已记载的修法。(3)§8.1 没减半的 GBK 字节区间 is_chinese() 加上对应没减半的长度界限 check_legal_name()(原为 4/8 加 i%2 字节窗口,改成逐码点的 2/4)。(4)§7.50 accept_kill 类的 is_killing(who) 物件/字符串不匹配,但这次不在 accept_kill() 里——而是在 5 个独立的 NPC"先揭穿身份再开打"对话档案里(d/city/npc/{guidao,guidao1,guidao2,xiaosong}.lpc、d/foshan/npc/wanglaohan.lpc、d/shaolin/npc/xiaosong.lpc),这些会静默破坏预载阶段 NPC 生成的编译;同样的 bug 也出现在约 20 个 kungfu/skill/*.lpc 档案里,写法是 target->is_killing(me)/ob->is_killing(me) 这种箭头呼叫,不会导致编译硬失败(只有 NPC 档案里那种直接/裸呼叫才会),按既定的"内容 bug 不逐一穷尽修复"惯例保持原样——只修了会阻断的那几处。另外播种了管理员:fluffos (admin) 加入(此前几乎空白的)adm/etc/wizlist,外加在 adm/daemons/sited.lpc 硬编码的 valid_login 映射(127./0. 前缀)里加了一条——这条血统在登录时独立强制执行按巫师 id 的 IP 白名单(sited.lpc 的 is_valid()),所以一个刚播种进 wizlist 的管理员 id 如果不同时播种这第二张表,就会永久无法从任何地址登录("巫师必须有地址限制")——和 AGENTS.md §1.5"既要播种阶级也要播种真正的权限数据"是同一类问题,只是表不同。已验证:完整注册(id→确认→密码→确认→中文名字→天赋摇点→确认→电子邮件→性别)→进入游戏世界,权限正确显示 (admin)。update 指令的成功讯息因为一个持续的 VIP/欢迎讯息刷屏打乱了脚本化测试的发送时机而没能捕获到(不是功能性 bug——已直接核对源码确认 ADM_PATH 和 securityd 的 exclude_read 表都没有把 (admin) 排除在 update.lpc 所在的 cmds/app 或 cmds/imm 之外)。记录了一处不阻断、未修的内容 bug:/d/wudang/npc/th.lpc 的 create() 呼叫 carry_object() 时传入的参数求值结果是 0(缺失的道具档案),导致 baoshid.lpc 预载 NPC 放置阶段出现一个被捕获的"Bad argument 1 to EFUN call_other()"——不阻断开机或任何核心功能。LPC 格式化工具对全部 10260 个档案运行;还原了 2 个通过"去空格后比对旧档案"扫描(覆盖 191 个格式化工具触碰过的档案)确认有 CJK 重新加空格损坏的档案(clone/npc/tdh/idea.h、clone/obj/job/func.lpc);直接逐一比对了全部 3 个 ASCII 地图档案(d/lanzhou、d/shaolin 两份)——全部干净,只是排版调整。格式化后重新验证过开机和注册都干净。
深度功能测试(§10.7,2026-08-05):一个让整个游戏完全无法操作的严重 bug
之前 WASM 阶段的"注册→look/score/quit 都干净"结论具有严重误导性——
这次深挖发现,注册流程本身确实一路畅通(欢迎信息、VIP 提示、新闻系
统提示全部正常显示),角色也正确落地进游戏世界,但落地之后任何一
个玩家指令,包括最基本的 look,都会返回"什么?",游戏事实上完
全无法操作,只是这个症状被埋在一长串正常的欢迎讯息后面,不细看很容
易被误认为是正常的静默返回。
根源是 AGENTS.md §8.3b 已经记录过的经典 bug——adm/daemons/
commandd.lpc 的 rehash() 用 sscanf(cmds[i] + "$", "%s.c$",
cmds[i]) 从目录列表里筛出指令档案,但转档后所有档案都已经改名成
.lpc 后缀,这个模式永远匹配不到任何东西,导致 search/
user_cmds 两张指令表永远是空的,find_command() 对任何动词都返
回 0。这份档案早在之前一轮全库排查 §8.3a(private nomask
command_hook)问题时就已经被列入"已修复"名单——command_hook()
本身确实是干净的 nomask,没有 private——但 §8.3a 和 §8.3b 是
两个完全独立的 bug,可以同时存在也可以只存在一个,这份档案就是
"§8.3a 已修好,但 §8.3b 从未被检查过"的典型案例:即使 command_hook()
被正确呼叫了,它内部呼叫的 find_command() 因为指令表是空的,永远
返回 0,一样导致每个指令都落到最后的"什么?"兜底分支。已把
"%s.c$" 改成 "%s.lpc$",恢复正常。
一个有趣的连带线索:修复前的 boot.log 里反复出现
adm/daemons/baoshid.lpc(随机放置宝石/NPC 的精灵)的
choose_baosi()/random_place() 触发 *Too long evaluation.
Execution aborted.(eval 开销超限)报错,一次会话里出现了 15 次。
一开始怀疑是 random_place() 自己"最多重试 50 次"的循环里连续编
译多个从未加载过的 NPC 档案导致开销爆表的独立 bug;但在修好指令表
之后,同样的操作序列里这个报错完全消失,一次都没有再出现——很
可能是空指令表导致每一次玩家按键都要 fallback 去尝试
EMOTE_D->do_emote()/CHANNEL_D->do_channel()(这两个精灵自己的
探测逻辑触发了比正常情况多得多的物件编译),间接给 baoshid.lpc
造成了额外压力,让它更容易撞上 eval 上限。修好指令表这一个根因就
顺带解决了两个看起来毫不相干的症状——已在 AGENTS.md §8.3b 补充记
录了这个连带现象,提醒以后遇到类似"看起来独立的 eval 超限报错"时
先检查指令表是否健康。
§7.86(留言板post崩溃)不适用:全库只有 1 处replace_program(BULLETIN_BOARD)匹配(adm/daemons/ huanjing2003/temp/huanjing1_pro_b.lpc),核实过这份档案本身从未 被任何地方引用(虽然它所在的"幻境"迷你游戏区域本身通过d/city/guangchang.lpc是可达的,但这块留言板对象从未被放置/实 例化),属于死代码,未做改动。- §8.9 不适用:
enter_world()的食物/饮水初始化user->set("food", 200)/user->set("water", 200)是无条件的固 定值,没有年龄判断包装。 - 战斗/死亡/复活/新手礼物系统测试:在北大街和"少林刀僧 行者"打 了一场,角色多次尝试撤退都被拦截,最终力竭阵亡;死亡流程走的是 这份档案自己独立实现的鬼门关→孟婆亭→孟婆汤→轮回投胎,全程未受打 扰,正确落地在文档记载的新手起点"武馆前院",
score确认死亡计数 +1、角色状态正常恢复。落地后"书剑天下礼物使者"NPC 也正常响应ask shizhe about newbie(废除武功+赠送一周贵宾),符合 README 记载的机制;ask shizhe about 拜师这一步没能成功验证——中文参 数在这次会话里传输时疑似被截断(拜师→�师),看起来像是本次 测试用的 tmux 输入管线的编码问题,不是确认过的 mudlib bug,未深 究,留给后续测试。 - 本次没有测试:商店、门派拜师的完整效果(原因见上)。
§7.86 跨库扫描修复(留言板 post 崩溃)
§7.86复核:唯一 1 处命中确认为死代码,非误漏:本档案早前的深度测试已经检查过全库唯一一处BULLETIN_BOARD命中(adm/daemons/huanjing2003/temp/huanjing1_pro_b.lpc),并核实过这份档案从未被引用/实例化,属于死代码,当时判断为§7.86 不适用、故意未改动。本次是跨库机械扫描,判定规则不区分"死代码/活代码",顺手把这处也一并做了同样的无害删除(保留inherit),不影响此前的死代码结论,仅记录在案。
§7.100 sub-threshold instance (2026-08-20)
Found during the §7.100 tail-sweep (below the original 166-lib survey's
=100-occurrence threshold, never checked). Same lineage/shape as
sibling libs bxsj/bxsj1: 24 live replace_program(ROOM);
occurrences deleted (d/wanshou/*.lpc, data/group/groom/*.lpc,
d/cangzhou/dangpu.lpc). No roommaker.lpc factory-bug variant.
Verified via a clean native driver boot (zero new debug.log errors,
port listening, killed by exact PID after ~8s).
``§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/death2.h 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.
Note: the touched header here (death2.h) turned out to be dead/unreferenced code in this lib -- the live NPCs (yanluo.lpc/mengpo.lpc/pusa.lpc) actually #include a sibling death.h, which was already guarded from an earlier fix. This edit is a harmless no-op; no live vulnerability existed in this lib for this file.
§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).
Shop + 拜师 (2026-09-04 librarian loop)
The 2026-08-05 deep test left shop and 门派拜师 untested (ask shizhe
about 拜师 was garbled by the tmux/telnet path). This pass used
mudclient.py (raw socket) on port 40139.
First send is the BIG5 gate (Are you using BIG5 font(Y/N)?) — send
n. Email must have ≥2 characters before @ ([email protected]; [email protected]
is rejected). New characters start with no money objects.
拜师 (mortal sjeqinb / 秦风测 / Mud@2026): landed 武馆前院,
answer n to skip 狄云's tutorial. ask shizhe about newbie wiped
skills and granted the VIP newbie pack. Immediate follow-up ask
shizhe about 拜师 hit the speech-busy gate (您先歇口气再说话吧);
that inquiry is help text only. ask shizhe about 华山 recruited via
recruit_apprentice (create_family("华山派", 99, "弟子")) and
moved the character to /d/huashan/zhengqi (正气堂, 岳不群/宁中则
present). score: 头衔 华山派第一百代弟子, 师承【华山派】【书剑礼物使者】.
Unclean first quit; reconnect (n / sjeqinb / Mud@2026) printed
重新连线完毕 and the same 华山 title/family — persist confirmed.
Shop (seeded admin fluffos / Mud@2026, (admin)): newbies have
no starting cash, so this was a paid buy with wizard-cloned currency
(not a programming workaround — call me->add_money is ACL-denied on
this lib). clone /clone/money/gold, goto /d/xiangyang/shaobingpu,
list showed 烧饼 二两白银又八十文铜钱. buy 烧饼 from liu misses
(NPC do_buy wants the item id, not from <npc>). buy bing
succeeded: 你以二两白银又八十文铜钱的价格从烧饼刘那里买下了一个烧饼。
and i showed 一个烧饼(Shao bing) plus change. No LPC change.