Xi'an Jiaotong University Journey to the West (Huanle Yuan)

✅ 可玩

西安交大西游记

xajdxyj

🔑 fluffos / Mud@2026 更新 4391f58 2026-09-04 源码 下载 ZIP

▶ 开始游玩 · Play Now

"神话世界·西游记"4.50 版,游戏内品牌为《欢乐园》("A Journey to the West",大学生版),西安交大兵马俑 BBS 附属的 MUD,严格取材《西游记》神话世界观:地图有高老庄(含卧室、后院等场景)、三十三天、蟠桃园、蓬莱、南海、天宫等取经沿途的经典地标,都是真正可探索的场景(和本项目"三界神话"系家族地名风格相似是巧合,代码库并不相同)。校园 BBS 背景的"大学生版"定位体现在专属的 `task`/`renwu` 任务目录,以及呼应《欢乐园》品牌的 `d/happy` 场景;开机后还有 180 秒的连线保护期,须在第一行送出字面量绕过短语 `let me join ok` 才能连线,是这份档案特有的防灌水/开机稳定期设计。

English

Version 4.50 of "Mythical World: Journey to the West," branded in-game as Huanle Yuan ("Garden of Joy," subtitled "A Journey to the West, College Edition") — a MUD affiliated with the Terracotta Warriors BBS at Xi'an Jiaotong University. True to its title, the map follows Journey to the West mythology closely: Gao Village (with its own bedroom and backyard scenes), the Thirty-Three Heavens, the Garden of Immortal Peaches, Penglai, the South Sea, and the Heavenly Palace are all real, explorable waypoints along the pilgrimage route (a coincidental naming overlap with this collection's separate "Three Realms Mythology" family, not a shared codebase). The campus-BBS "college edition" framing carries through to a dedicated task/quest directory and a matching in-game "Happy Garden" scene. A distinctive quirk: the server enforces a 180-second post-boot connection-protection window, requiring the literal bypass phrase "let me join ok" as the first line sent to connect during that window — deliberate anti-flood/startup-stability design, not a bug. The death/revival cycle is driven by judge-NPC dialogue rather than room exits, and combat and message-board posting both round out the game.

README

内容亮点

本次修复的关键 bug

1. 损坏的 convertd.lpc 字节:和 sjsh 系家族完全相同的损坏 模式(45 处,同样的 "Illegal character 0xce/0xb2/0xee/0x96/ 0xa3" 特征,出现在第 250 行附近)。用同样的字节级 Python 脚本修 复。 2. 真正的语法错误adm/daemons/logind.lpcbanned_name 数组字面量里,有一行开头多了一个逗号(上一行结尾的逗号后面紧跟 ,"欢乐园"...),产生了一个空数组元素——LPC 不像 JS 那样容忍 这种写法,直接编译失败,并连锁引发文件后面 5 个额外的解析错误 (包括"Undefined variable mud_list",因为解析器再也没能恢复正 常)。已去掉多余的逗号。 3. 经典 §8.1 GBK 字节配对 bugadm/simul_efun/chinese.lpcis_chinese()adm/daemons/logind.lpccheck_legal_name() 都假设每个中文字占两个字节(strlen%2 奇 偶门槛,以及用一个"魔法参考字符串"做原始字节区间比对),在这个 驱动按 UTF8 码点索引字符串的情况下,奇数字数的合法中文名字会被 误判为不合法。两处都已改成逐码点的 0x4e00-0x9fff 区间检查, check_legal_name 的长度上限也从字节数 2-12 改成字符数 1-6。 4. §7.90:maximum evaluation cost 700000 太低,驱动 PRELOAD 阶段 (囚室房间 NPC 的冷编译开销)就直接触发不可 catch() 的 eval-cost 报错config.fluffos 已把该项调到 5000000。 5. §7.12:adm/simul_efun/message.lpcshout()this_player() 直接传给 message() 的排除参数,在游戏内整点 报时(无玩家上下文的 call_out)触发时炸出 Bad argument 4 to EFUN message()——已按同文件 tell_room() 已有 写法改成 this_player() || ({})。 6. §7.34 debug 遗留输出logind.lpc::get_name() 里取名成功后有 一行裸的 printf("%O\n", ob),把登录对象内部路径直接打在提示语 之间——已删除。

详见 NOTES.md"深度功能测试(§10.7)"一节:完整验证过移动、留言板 postkill 战斗、死亡/复活全流程(NPC 判官对话驱动,非 exits 出口驱动,§7.101 不适用;§7.68 的重试式修复因未满足前提条件也未套 用)、quit 后重连存档恢复。

已记录但不是 bug:连线开机保护期

logind.lpcencoding() 有一个 180 秒的连线保护期——开机后 CONTROL_CENTER->query("MUD_SRART_TIME")(原始代码就拼错了 "START")在 3 分钟内会拒绝所有正常连线,除非第一行送出的是字 面量绕过短语 let me join ok。这是有意的防灌水/开机稳定期设计, 不是缺陷——测试脚本第一行必须先送这个短语。

管理员账号 / Admin account

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

本地运行

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

连线时第一行请先输入 let me join ok 绕过开机保护期。游戏端口: 40179

NOTES · 移植与修复记录

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

神话世界·西游记 4.50 版,游戏内品牌《欢乐园》("A Journey to the West"),西安交大 BBS 附属 MUD。WASM 修复:(1)熟悉的 convertd.lpc 字节级损坏(45 行,和 sjsh 家族里见过的相同 0xce/0xb2/0xee/0x96/0xa3 非法字符签名,第 250 行附近)——用标准的字节级 Python 脚本修复。(2)adm/daemons/logind.lpc 的 banned_name 数组字面量里一个真正的语法错误:一行开头多了一个逗号(',"欢乐园",...'紧跟在上一行结尾的逗号后面),产生了一个不合法的空数组元素,连锁引发文件后面 5 个额外的解析错误(包括后面的"Undefined variable mud_list",因为解析器再也没能恢复正常)——已去掉这个多余的逗号。(3)adm/simul_efun/chinese.lpc 和 adm/daemons/logind.lpc 里 §8.1 类的 is_chinese()/check_legal_name() GBK 字节配对 bug:两处都假设每个中文字占 2 字节 GBK 编码(strlen%2 奇偶门槛,对照一个魔术参考字符串做原始字节区间检查),在这个驱动按码点索引的情况下会拒绝合法的奇数字数 UTF8 中文名字——已把两处都改成逐码点的 0x4e00-0x9fff 区间检查,并把 check_legal_name 的长度界限从字节数(2-12)改成字符数(1-6)。另外记录(不是 bug):logind.lpc 的 encoding() 里有一个开机后 180 秒的连线闸门——CONTROL_CENTER->query('MUD_SRART_TIME')(原文如此,原始源码里的拼写错误)会在驱动启动后的 3 分钟内拒绝所有登录,除非发送的第一行是字面的绕过短语'let me join ok';这是有意的防灌水/开机稳定设计,不是缺陷。管理员账号播种:fluffos (admin) 加入 adm/etc/wizlist(原档案完全是空的;wiz_levels 顶层是 (admin),SECURITY_D 正确指向 /adm/daemons/securityd,没有诱饵副本)。注册流程在一次连续的 WASM 客户端会话里完整验证过:'let me join ok' 绕过→英文 id→y/n 创建确认→中文名字→单一密码+确认(不是双密码机制)→性别→电子邮件(可选,留空也行)→天赋重掷循环(y 接受)→带着完整角色属性表进入游戏世界,全程没有任何意外错误。管理员权限已直接通过 wizlist 指令输出"目前权限:(admin)"确认。LPC 格式化工具对全部 7338 个档案运行(写入 7272 个,3 个报错,63 个未改动)。没有 :: 父类呼叫拆分命中,没有 CJK 重新加空格命中,没有 case 标签带尾随注释的候选。两个 map.lpc 档案确认内容完全相同(只是空白差异)。格式化后用同样的完整注册流程(包括'let me join ok'绕过)重新验证过——干净,管理员权限依然是 (admin)。

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

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

血统确认adm/obj/master.lpc 的档头注释("for ES II mudlib / original from Lil / rewritten by Annihilator (11/07/94)")与 AGENTS.md §11 的 ES II / 东方故事巨型家族(尤其是 kxkj/kxkj1/kxkjii2 一支)完全一致, cmds/std/go.lpc 的移动派发逻辑也是同一形状(先查房间自己的 exits 映射键是否存在,存在才会调用 valid_leave())——因此在深挖前 先按 §7.101(kxkjii2 发现的死亡出口 bug)和 §7.12(该家族常见的 message() 参数 bug)专门核对过这两类,见下文。此前的 WASM 阶段已经 按 §8.1 修复过 is_chinese()/check_legal_name() 的 GBK 字节配对问题、 按 §7.86 修复过全部 44 处留言板 replace_program() 崩溃——本轮复测两者 均确认线上正常,未回归。

本轮修复的 bug

1. **§7.90 变体:maximum evaluation cost 默认值 700000 在 PRELOAD 阶段 就直接把驱动"防不住"(*Can't catch eval cost too big error.)**: config.fluffos 沿用了本项目最常见的 700000 默认值,但 adm/obj/ master.lpcpreload("/d/wiz/qiushi")——囚室房间,create() 里 通过 setup() 生成 NPC d/wiz/npc/yuzu.lpc——连续 3 次触发 Eval interrupted: ... cost limit reached, limit: 700000 usec. 重试后,第 4 次直接变成不可 catch()*Can't catch eval cost too big error.,在驱动刚启动、还没有任何玩家连线的阶段就写入 debug 日志。这是 NPC 首次冷编译(/std/char 及其整条 inherit 链)的一次性开销撞上偏低上限,与 AGENTS.md §7.90 已归档的 xyj2000f/xiyouji450/xlqy_early 等实例是同一形状。修复: config.fluffosmaximum evaluation cost 从 700000 提到 5000000(本项目已有 30+ 个库使用的常规补救值)。现场验证:修复前 每次重启驱动都在 preload 阶段稳定复现上述 3+1 次报错;修复后连续 两次全新驱动重启,d/wiz/qiushi 的 preload 均无任何 eval-cost 报错, 随后完整的注册、移动、战斗、死亡/复活全流程(见下文)也没有再触发 过 cost limit reached。 2. §7.12 变体:adm/simul_efun/message.lpcshout()this_player() 直接当 message() 第 4 个排除参数,在非玩家上下文 (游戏内报时的 heart_beat/call_out)调用时炸出 Bad argument 4 to EFUN message():同文件里的 tell_room() 早就 有 exclude || ({}) 的防御写法,但 shout() 漏了同样的保护。 adm/daemons/timed.lpc::bj_hour_event() 每到欢乐纪元的整点就调用 shout() 广播"现在是北京时间...点整",此时没有 this_player() (返回 int(0)),message() 的类型检查直接报错——这个游戏内 时钟推进远快于真实时间(本轮会话不到 5 分钟真实时间,游戏内时辰 就从丑时跳到卯时,跨过了至少一次整点广播),所以这不是要等很久才 触发一次的边角情况,而是几乎每次整点都会命中的常规错误。现场验证: 修复前的第一次驱动启动,日志里在 Initializations complete. 之后几秒内就出现了这条 Bad argument 4 to EFUN message() 报错 (伴随 previous_object(1): /adm/daemons/timed,确认是 bj_hour_event() 触发);修复(this_player() || ({}))后重启, 同一会话跑满至少一次整点广播窗口(游戏内从丑时到卯时), debug.log/stdout 都没有再出现这条报错。修复方式与该文件里 tell_room() 已有的写法完全对称。 3. §7.34 debug 遗留输出adm/daemons/logind.lpc::get_name(), 中文取名成功后、设定密码提示之前,有一行裸的 printf("%O\n", ob), 会把登录对象的内部路径(/clone/user/login#N 之类)直接打在取名 和密码提示之间——已删除,属于本项目在多个血统里反复见过的原作者 调试遗留输出,不影响任何逻辑,纯粹的信息泄露/观感问题。现场验证: 修复前后各走了一遍完整注册流程,修复后取名成功到密码提示之间干净 无多余输出。

§7.68 / §7.101 是否适用于本库的死亡系统:都不适用,理由分别记录:

其余检查过、确认没问题的项目:§7.5/§7.98(adm/daemons/ securityd.lpctrusted_read/trusted_write 目录前缀式 ACL, valid_read() 对未设置 euid 的情况是放行而非拒绝,即使是这样也没有 在 file_size/stat 上出现过 §7.5 那种"目录 ACL 挡住编译期检查"的假 阴性)、§8.13(没有独立的"WIZ 密码"二次登录闸门)、§8.14(is_banned() 统一传入 query_ip_number(),没有 query_ip_name/query_ip_number 混用)。留言板 post/look board 现场复测正常(§7.86 修复未回归)。 二次登录(quit 后重连)确认存档正确保存并恢复:管理员权限、留言板 帖子均在重连后完整可见。

已记录但保持不修的观察项(非 bug,符合 §1.3e 精神)adm/daemons/logind.lpc::encoding() 里"限制多重登录"的节流逻辑用 query_ip_name(usr[i]) == ip_number 比较(一边是反向 DNS 主机名,一边 是点分十进制 IP),实际上永远不相等,导致这个节流从未真正生效——但 既然本项目对这类 circa-2000 的连线节流机制本来就是"直接绕过,不用 修对"的既定政策(§1.3e),这个 bug 的净效果恰好就是"节流形同虚设", 不需要改成"修好之后又要专门加绕过",故按现状记录、不改代码。

环境说明:本轮测试中途,运行环境所在的底层主机发生过一次更换 (CPU 型号变成不支持 AVX 的 Xeon L5520),导致 build-debug/ 下用 -march=native 编译出的驱动二进制在新主机上无声崩溃 (SIGILL,退出码 132,stdout/debug.log 均无任何输出)——这不是这个 库本身的问题,其它任何库都会同样炸;已用 -DMARCH_NATIVE=OFF 重新配置并完整重建 build-debug(详见 AGENTS.md §12 新增第 5 条), 重建后本库和另一个对照库都能正常启动,本轮后续的全部现场测试都是在 重建后的驱动上完成的。

运行环境限制:本次会话的 sandbox 里没有 node(仅 ~/.vscode-server 下捆绑的 v16.13.2 可临时借用,已用它跑过一次 format-corpus.mjs,两处改动均"unchanged",说明本身格式已合规), scripts/wasm_client.js 这条 WASM 备用路径本次未启用,全程用原生 驱动 + scripts/tmux_mud.sh 完成注册、移动、留言板、战斗和完整死亡/ 复活流程的现场验证。

§7.100 sweep (2026-08-19)

Fixed the corpus-wide inherit ROOM; ... replace_program(ROOM); redundant-replace bug (AGENTS.md §7.100). 192 live occurrences deleted: 189 via scripted sweep (fix_710_room.py), plus 3 hand-fixed roommaker-tool templates across 3 separate tool copies (obj/roommaker.lpc, d/obj/clone/misc/roommaker.lpc, u/fof/roommaker.lpc — all simple string-builder variant). 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 40179. Boot auto-created empty data/{board,ip_usage,usage}/ directories (normal daemon first-boot behavior); left untracked/untouched.

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

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

新角度:2026-08-09 第一轮做了战斗 / 死亡 / 留言板,没测商店和拜师。