Myth of the Three Realms (Baoji Station)

✅ 可玩

三界神话『宝鸡站』

sjsh

🔑 fluffos / Mud@2026 更新 728ae75 2026-09-01 源码 下载 ZIP

▶ 开始游玩 · Play Now

"三界神话"系列(San Jie Shen Hua)宝鸡站版本,是五个同系档案中的第一个、也是原始版本(另外四个:`sjshv150`/`sjshv2578bb`/`sjshwzb`/`sjshwzjqb`,以及借用同一套地名风格但代码库不同的 `xyj20032`),武侠门派场景与"三十三天""蟠桃园""蓬莱""天宫"等神话地名并存。注册流程有一处容易踩坑的细节:全新账号必须先输入字面上的 `new`,再输入想要的英文 id,然后直接进入中文名字,中间没有常见的 y/n 确认步骤;紧接着密码/邮箱/个人主页/ICQ/性别之后,还有一段独立的"天赋分配"小游戏(体格/根骨/悟性/灵性四项数值可调)才真正落地新手村〖南城客栈〗(长安城)。`d/job` 提供打工玩法,配合 `d/club`(社团/帮会)、`d/zj` 等场景,是这个神话世界观下相对丰富的辅助玩法;一个后台"天地劫"精灵还会定期向全服广播魔教攻陷各大门派(蜀山剑派、五庄观、大雪山、南海普陀山等)的滚动剧情。死亡/复活流程完整:死后由判官 `崔珏` 在〖阴阳界〗主持"翻生死簿"仪式,复活地点是荒郊小店,同一间小店的"生死之间留言板"保留了 27 条 2001 年前后的真实历史留言。招牌横幅、天赋分配欢迎语("欢迎光临西游记!")、`help newbie` 标题("【仙侣情缘】之新手指南")三处各自写着不同的游戏名,是多次换皮转手留下的历史痕迹,不影响实际玩法。

English

The original Baoji-station build of the "Myth of the Three Realms" (San Jie Shen Hua) series — the first of five related codebases in this archive (the other four being sjshv150, sjshv2578bb, sjshwzb, and sjshwzjqb) — mixing ordinary wuxia sect scenes with mythological place names like the Thirty-Three Heavens, the Garden of Immortal Peaches, Penglai, and the Heavenly Palace. Registration has a quirk worth knowing: a brand-new account must type the literal word `new` before its chosen English id, then goes straight to picking a Chinese name with no y/n confirmation step, followed by a standalone stat-allocation minigame (physique/bone-structure/comprehension/spirituality) before landing at the South-City Inn newbie village in Chang'an. Day-labor jobs (`d/job`) combine with a clan/guild system (`d/club`) and other zones (`d/zj`) for a relatively rich set of side activities, and a background "Apocalypse of the Three Realms" daemon periodically broadcasts server-wide flavor announcements of the Demonic Cult overrunning major sects (Shushan Sword Sect, Wuzhuang Temple, the Great Snow Mountain, Nanhai Putuo Mountain). Its banner, its stat-allocation welcome text ("Welcome to Journey to the West!"), and its `help newbie` title ("A Beginner's Guide to Immortal Lovers' Fate") each name a different game — harmless scars from being re-skinned and passed along multiple times. Death and revival are fully implemented: Judge Cui Jue presides over a "reviewing the book of life and death" ritual in the Yin-Yang Realm, reviving players at a wayside shop whose "Between Life and Death" board still preserves 27 genuine player messages from around 2001.

README

本次修复的关键 bug

1. adm/daemons/convertd.lpc 的希腊字母/GBK 转换表里有损坏的原始 字节:44 行的结尾紧跟着一段非 UTF8 的乱码字节,恰好最后一个字 节是 0x5C(反斜杠),把本该结束字符串的引号给转义掉了,导致字 符串没有正常结束,编译直接失败。已用逐字节脚本只去掉每行末尾那 段"反斜杠+乱码",表里其它非阻塞性的乱码内容(纯内容质量问题,不 影响启动)保持原样未动。 2. §7.41 类损坏的存档数据adm/daemons/emoted.lpccreate() 对自己损坏的存档做了未加保护的 restore(),preload 时未捕获抛出——已包一层 catch(),并显式补上 emote=([]) 兜 底。(那条被捕获的错误依然会通过这份档案自己的 error_handler() 打印出一长串日志,这是已经在 sjplgfjxb 上确认过的正常/无害行 为,不代表还有问题。) 3. (§10.7 深度测试新发现,AGENTS.md §7.97)include/net/config.hLISTNODES 宏缺失续行反斜杠,间接导致死亡流程永久卡死:宏 第一行没有以 \ 续行,让后面几行的映射内容原样泄漏成源码,把 dns_master.lpc 编译坏了;玩家死亡时的系统频道广播会尝试调用它, 未捕获的异常直接打断 die(),导致角色卡在无限重复的"你死了"里, 永远到不了鬼门关。已给宏补上反斜杠,死亡→复活全流程重新验证通过。 4. (§7.34 debug 残留,已清) adm/daemons/logind.lpcget_name() 里一行裸 printf("%O\n", ob),会把内部对象路径打印 在中文名字确认和密码提示之间——已删除。 5. (§7.11 缺失目录防护,已补) adm/simul_efun/file.lpclog_file() 没有调用同文件里现成的 assure_file(),几个日志路径 (如崩溃处理器用的 /log/nosave/)在这份档案里从未随仓库分发—— 已在写入前补上 assure_file() 调用。

注册流程(备忘,不是 bug)

一个全新的 id 需要先输入字面上的 new,再输入想要的英文 id,然后 直接是中文名字——confirm_id() 是硬编码 "Yes" 自动调用的,中间没 有 y/n 确认这一步。

管理员账号 / Admin account

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

本地运行

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

游戏端口:40141

NOTES · 移植与修复记录

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

三界神话『宝鸡站』,5 档案家族集群(sjsh/sjshv150/sjshv2578bb/sjshwzb/sjshwzjqb)的第一个。WASM 修复:(1)adm/daemons/convertd.lpc 希腊字母/GBK 转换映射表里损坏的原始字节——44 行在闭合引号前紧跟着一段杂散的非 UTF8 字节序列,最后一个字节恰好是 0x5C(反斜杠),转义掉了闭合引号,让字符串字面量没有正常结束,导致编译失败;已用字节级脚本只去掉每一行末尾那段有问题的反斜杠+乱码(同一张表里其它非阻断编译的乱码内容保持原样——纯粹是内容质量层面的噪音,不是开机阻碍,按既定惯例不在本次范围内)。(2)§7.41 类损坏的存档数据:adm/daemons/emoted.lpc 的 create() 对自己损坏的存档档案做了未加保护的 restore();已包一层 catch(restore()),并显式补上 emote=([]) 兜底(被捕获的错误依然会通过这份 mudlib 自己的 error_handler() 详细记录,确认无害/预期之中,和 sjplgfjxb 已记载的模式一致)。注册流程备忘(不是 bug,是为了测试/以后参考):一个真正的新 id 必须先字面输入"new",然后才是想要的 id,然后是中文名字——confirm_id() 是硬编码"Yes"自动呼叫的,中间没有 y/n 确认步骤。管理员账号播种:fluffos (admin) 加入 adm/etc/wizlist。已验证:完整注册(new→id→名字→密码→确认→电子邮件→个人主页→ICQ→性别)→look/score/quit 全部干净,管理员账号权限正确显示;trusted_write['/']/exclude_write 也已直接核对源码确认授予 (admin) 不受限的写入权限。LPC 格式化工具对全部 9350 个档案运行;还原了 2 个通过"去空格后比对旧档案"扫描(覆盖 79 个格式化工具触碰过的档案)确认有 CJK 重新加空格损坏的档案;另外直接逐一比对了唯一一个近似 ASCII 地图的档案——干净,只是排版调整。格式化后重新验证过,干净。

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

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

之前两轮都只做过编译检查/浅层注册测试,没有真正玩过。这次用原生驱动(build-debug/src/driver)通过 scripts/tmux_mud.shscripts/mudclient.py 走完整轮:GB/BIG5 编码选择 → 「是否中小学生」拦截 → new → 英文 id(3-8 位纯字母,测试用 testqx/qxtwo/qxthree)→ 中文名字 → 密码(至少 5 位)→ 确认密码 → 电子邮件(须含 @.,长度 ≥9)→ 个人主页(可空)→ ICQ(可空)→ 性别 m/f → 天赋分配小游戏(d/wiz/init.lpc,房间 〖天赋房〗,选 9 接受默认再 y 确认)→ 落地 〖南城客栈〗(长安城,/d/city/kezhan)。README 记载的注册坑点(new 硬编码流程、confirm_id() 无 y/n 确认)依然准确无需更正。look/score/quit/男女两种性别分支都验证过,quit 干净退出无崩溃。

深度功能测试(§10.7 round-four,2026-08-20)—— 补齐上一轮标记的「未覆盖」项

build-debug 驱动配 config.fluffos(端口 40141)单独起了一份 sjsh 专属进程,写了双 socket(admin + 测试角色 qxfour)的 Python 脚本互相配合走查,而不是用 tmux_mud.sh。新建的测试角色 qxfour(密码 TestPass123)走完整注册流程后落地〖南城客栈〗,测试全程用管理员 fluffos 账号 goto/summon/clone/call/give 配合搭桥(给钱、临时调高 combat_exp 以跳过拜师门槛),不涉及修改任何代码逻辑。测试结束后已用 git restore 清掉 fluffos.o/avguser/maxonline 的会话统计噪音,并删除了 qxfour 的测试存档(未跟踪文件,直接 rm),仓库无残留改动。

1. 商店 buy——完整实测通过,价格扣除和收货都正确:一开始用 buy jitui from xiaoer 失败,报错"你要跟谁买东西?"——排查后发现这不是 bug:〖荒郊小店〗店小二(d/ourhome/npc/xiaoer.lpc)的 set_name() 实际 id 列表是 ({"xiao er", "xiao", "waiter"}),并不包含"xiaoer"("xiaoer"只出现在语义完全不同的 shop_id 属性里,那是给"买下整个店铺"的新店主重新分配称呼用的,不是拿来给玩家 present() 匹配的);而长安城〖南城客栈〗那位同名店小二(d/city/npc/xiaoer.lpc)的 id 列表里则确实有"xiaoer"——两份几乎一样的 NPC 代码之间的别名列表不一致,属于内容层面的差异,不是错误信号,未作改动。改用 buy jitui from xiao 后一次成功:你向店小二买下一根炸鸡腿。,管理员用 clone /obj/money/coin + call coin->set_amount(300) + give coin to qxfour 给了 300 文钱,购买后 i 显示余额精确变成"二两银子(Silver)、二十文钱(Coin)"(300−80=220,找零拆分成银子+铜钱也完全正确),背包里多了"炸鸡腿(Jitui)"。全程 debug.log 干净无新增报错。

2. 门派拜师(bai/apprentice)与正式技能学习(xue/learn)——完整实测通过:找了个明确不需要任何门槛就欢迎新人的开山祖师——将军府〖正厅〗(d/jjf/keting.lpc)的秦琼(d/jjf/npc/qinqiong.lpccombat_exp>=100000 即可)。管理员 goto+summon 把测试角色接到该房间,再用 call qxfour->set("combat_exp", 200000) 跳过战力累积(纯粹为了压缩测试时间,不改代码)。bai qin 一次成功:秦琼说道:很好,时下正是用人之际,小兄弟多加努力,他日必定有成。秦琼决定收你为弟子。恭喜您成为将军府的第三代弟子。score 里"师承"字段正确显示"将军府秦琼",称号变成"将军府第三代弟子"。随后 xue spear from qin for 5 一次成功:你向秦琼请教有关「基本枪法」的疑问。你听了秦琼的指导,似乎有些心得。skills 指令正确显示新学会的"基本枪法 (spear) - 初学乍练 1/0"。全程无崩溃,debug.log 干净。(注:地图上大多数"第二代"及以上的门派 NPC 的 attempt_apprentice() 都设了很高的 combat_exp/daoxing 门槛甚至要求已在同门——这是常规的师门晋升设计,不是 bug;此外发现 d/city/npc/shubao.lpc(另一处"秦琼")虽然写了完整的 attempt_apprentice(),但全档案 grep 不到任何房间引用它——是真正的死内容,与本轮实测通过的 d/jjf/npc/qinqiong.lpc 是两份独立的秦琼代码。)

3. (低优先级,本轮时间充裕补测完成)邮件系统——存在且能用,不是缺失功能:〖南城客栈〗千里眼(d/ourhome/npc/bigeye.lpc)的 inquiry 表里挂着"mail"/"发信"/"收信"等关键词,要用 ask <目标> about mail(目标同样要用 id 列表里的词,如"bigeye",而不是显示名里带空格的"qianli yan")触发;ask bigeye about mail 一次成功,千里眼把一个"秋雁的信箱(Mailbox)"塞进了玩家背包,随后 mail 指令(在拥有信箱后才会通过 obj/mailbox.lpcadd_action 注册)也能正常呼出"你要寄信给谁?"提示——邮件系统是通过"先找千里眼要信箱"这一步触发的,不是常驻指令,之前几轮没找到只是没跟 NPC 对话过,不是代码缺陷。

4. (低优先级,本轮时间充裕补测完成)d/shushan/obj/muren.lpc 练功木人——纠正上一轮"全档案未被引用"的误判,实际是可达内容且实测能打:重新 grep 后发现 d/shushan/w-lianwu.lpc(西武场)和 d/shushan/e-lianwu.lpc(东武场)的 objects 表里各有 __DIR__ "obj/muren": 4(会展开成 /d/shushan/obj/muren)——上一轮的 grep 大概率只搜了字面量路径没搜到 __DIR__ 宏拼接后的引用。实测:管理员 goto+summon 把测试角色送进〖西武场〗,kill muren 失败(同款 id 别名坑:木人的 id 列表是 ({"mu ren","mu","wood man","wood"}),不含"muren",需要 kill mu),改用后一路真实打到死:accept_fight() 里第一次触发的 random(me->query("fight_times"))(此时 fight_times 还是初始值 0,即 random(0))没有报错也没有崩溃,说明这份 FluffOS 对 random(0) 的处理是安全的(返回 0),不是潜在 bug;一路格斗到"木人死了",score 正确记录"杀死敌人:1 名",全程 debug.log 无新增报错。与之相对,d/city/obj/muren.lpc 复核后确认在 d/city/ 目录下确实没有任何房间引用,是真正的死内容。

管理员账号(fluffos/Mud@2026)本次通过正常注册流程重新走了一遍——adm/etc/wizlist 里已有 fluffos (admin) 一行(更早的 WASM 阶段留下),但存档目录 data/login/f/data/user/f/ 下当时并没有真正落地的 fluffos.o(说明此前只播种了权限数据,账号从未真正注册过)。本次老老实实走完整个注册流程后立即显示"系统权限目前是:(admin)",update /d/city/kezhan.lpc 重编译成功,确认 write ACL 完全没问题;README 的既有记录基本准确,只是密码此前写的是"注册时自设",现已确认为标准 Mud@2026

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

#define ROOM "/std/room":删除 513 处多余的、独立成行的 replace_program(ROOM);(保留 inherit ROOM;),508 处脚本自动 删除;另有 3 份房间建造工具副本手动修正——obj/roommaker.lpcu/calvin/obj/roommaker.lpc 是标准的"两套模板"简单变体; u/koker/obj/roommaker.lpc 是同一血统家族已知的"3 处出现" room_code/str 变体(一处 room_code += 拼接 + do_saveroom() 里两条分支各一处),三处均手动删除。work/data 下未发现额外 .lpc 源文件(与手足 sjshv150 不同,本库没有那个变体)。修复后 全库仅剩 17 处历史遗留的 //-注释掉实例,均确认无害、未改动。已 用 build-debug 驱动干净启动验证(0 个新增编译错误,端口 40141 正常监听,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.

AGENTS.md §7.19: enable_player() reentrancy guard (2026-09-01)

Same corpus-wide bug class as mhxy/wuhanzhan: feature/command.lpc's enable_player() wraps enable_commands() and is unconditionally reachable from an NPC's init() via setup()/reset_me() (confirmed on this lib's own d/*/npc/zhangmen*.lpc-family NPCs, matching mhxy's originally-documented d/xueshan/npc/zhangmen.lpc pattern). Calling enable_commands() on an object that's already living() makes the driver re-invoke that object's init() as a side effect; since init() calls back into enable_player(), that is genuine same-call-stack reentrancy that repeats until "Too deep recursion" aborts a room's first-ever visit.

Fixed with a true reentrancy flag (nosave private int in_enable_player_now;), NOT a bare if (living(this_object())) return; guard — this lib's feature/damage.lpc revive() and cmds/std/sleep.lpc wakeup()/wakeup2() all legitimately re-invoke enable_player() while the object is still living() (that's how a fainted/asleep character gets commands back), so a living()-gated guard would silently break every one of those real re-enables. enable_player()'s single body has no early return statements, so the flag is set at entry and cleared once, before the function's fall-through end. Verified with a single-file lpcc compile check (exit 0, no errors) against feature/command.lpc.