info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
西游记("Journey to the West",又名"西游记之新纪元",xiyouji.org 站,自 1998 年前后运行至今)是一套取材吴承恩原著神话世界观的老牌 MudOS mudlib,三十三天、蟠桃园、蓬莱、天宫等都是实际建成的场景;注册流程带有"不欢迎中小学生玩家"的年龄声明问答,属于原始设计的一部分,不是 bug;死亡后会经崔判官地府审判、最终转生落脚在乡间客栈。字节级比对显示,本合集另一份档案 `xyj2000f` 实际上是同一份档案在不同修复阶段的两次分别归档(约 8700 个文件里超过 99% 逐字节相同,其余多是行末空格或后期 bugfix 注释的差异),而不只是同名的姊妹档案;更意外的是,这份档案的地图/NPC 文件布局还以约 99.5% 的路径重合度被 `xyj2006n`(AKAI/Tomud 引擎)和 `xyj20032`(与之完全无关的"三界神话"引擎)两份档案原样沿用——例如小和尚 NPC 疥顶小僧、泾水桥场景在四份档案里都逐字相同——使这份档案的地图成为本合集里已知最"干净"的西游记原始地图母本,被至少四款不同招牌的游戏拿来作为地基,这层关系此前从未被这四份档案中的任何一份记录过。
English
Journey to the West, also known as "Journey to the West: New Era" (xiyouji.org, running since ~1998), a venerable old-school MudOS mudlib set in the mythological world of Wu Cheng'en's classic novel -- the Thirty-Three Heavens, the Peach Garden, Penglai, and the Heavenly Palace are all real, built zones. Its registration flow includes an age-declaration prompt stating it "does not welcome primary/secondary school players" — an original design choice, not a bug. A file-level check this pass found this collection's own xyj2000f is effectively the same archive at a different fix-stage (over 99% of its ~8,700 files byte-identical, the rest cosmetic or later bugfix-comment differences) -- not just a similarly-named sibling. More surprisingly, this lib's map/NPC file layout is also reused almost path-for-path (about 99.5% of files, though only a minority remain byte-identical content) in sibling archives xyj2006n and xyj20032, even though those two run entirely different core engines (AKAI/Tomud and the unrelated "Three Realms" codebase respectively) -- e.g. the young monk NPC 疥顶小僧 and the bridge scene 泾水桥 are word-for-word identical across all of them. That makes this lib's map the closest surviving "clean" copy of an original Xiyouji archive reused as a foundation across at least four titles in this collection. A live combat test against 疥顶小僧 on Vermillion Bird Street ran the standard death/revival flow through Judge Cui Jue's court to a countryside inn.
README
内容亮点
- 以吴承恩原著《西游记》为背景,三十三天、蟠桃园、蓬莱、天宫等地 标齐全,和"三界神话"系家族地名风格相似但代码库完全独立。
- 这是
scripts/scan_known_bugs.py(本次会话新写的只读静态扫描工 具)第一次派上用场的档案:常规的"注册+look+score+quit"通关测试 完全没有踩到的 5 个潜伏 bug(详见下方"补充修复"),都是靠这个新 工具事后扫描才挖出来的——包括一本特定藏经阁书籍的坏 include 路 径、一个 NPC 档案里漏转的原始 GBK 字节。 - 注册流程里连锁出现过一个特别隐蔽的 bug:
preload文件里一个互 联互通精灵的加载行被人为注释掉(大概率是有人在真正的 socket 兼 容性问题修好之前,为了让整个 mudlib 能开机而留下的权宜之计), 导致每一次连线选完编码后都会立即被动断线——根源在于开机预载状 态,而不是编码选择本身(详见下方 bug 修复第 3 条)。 - 深度功能测试(§10.7)发现全档案 28 块留言板的
post指令必然崩 溃(AGENTS.md §7.86)——这是这个 bug 第三次在互不相关的代码库家 族里被独立发现(此前只在"天涯"和"hy/海洋"两个家族见过),而且这 次还发现了一个新变体:3 份留言板用的是另一个同样有问题的基类BBS_BOARD,不只是常见的BULLETIN_BOARD。
本次修复的关键 bug
1. adm/daemons/convertd.lpc 的字节级损坏:GB/BIG5 转码用的
inittable 初始数组,本应是每个元素一个符号的字符串数组,实际
存档内容里大量元素被压成了一整行、符号之间互相粘连(和这批档案
里 sjsh 系列同款的转档损坏),已用标准修复脚本按码位重新切
分(约 45 行受影响)。
2. adm/daemons/network/dns_master.lpc 的 §7.52 socket 依赖:
这是一个真正的多用途互联互通精灵(query_mud_name()/muds
mapping 等被 mudlist_a/mudlist_q/gtell/gchannel/rwho
等约 28 个其他服务文件呼叫),按 AGENTS.md §7.52 对多用途精灵的
例外处理,没有整个文件停用:只把两个真正碰 socket 的入口函
式掏空(startup_udp() 现在直接 return 0,正好符合它原本失败
时的返回惯例,create() 里 if (startup_udp())
init_database(); 不需要改动就会自然跳过依赖 socket 的资料库初
始化;send_udp() 变成空函式),并清掉了 send_shutdown() 里一
处残留的 socket_close() 呼叫。
3. 由上一条引出的连锁 bug——adm/etc/preload 里 dns_master 被
注释掉了:这份档案的 preload 文件里 #/adm/daemons/
network/dns_master 这一行被人为注释掉(原始压缩包里是启用状态;
多半是有人在 socket 兼容性 bug 修好之前,为了让整个 mudlib 能开
机而临时关掉的权宜之计),导致 DNS_MASTER 精灵永远不会在开机
时被预加载。adm/daemons/logind.lpc 的 encoding() 在选完编码
后第一次连线会检查 if(!find_object(DNS_MASTER) || ...)
shutdown(1);——find_object() 只对已加载的对象返回真,精灵没
被预加载所以这个检查恒真,每一次连线都会在选完编码后立刻触发
shutdown(1),导致注册流程在编码选择之后彻底卡死(所有后续
输入都显示"什么?")。这不是编码选择本身的 bug,是 shutdown(1)
调用之后连线对象再也没有注册任何 input_to(),所以后续输入落
进了没有挂钩的通用解析器。既然 §7.52 的编译修复已经让
dns_master.lpc 能正常载入,把 preload 里那一行重新启用即可,
不需要改动 logind.lpc 本身的检查逻辑。
深度功能测试(§10.7)修复的 bug
- printf 调试残留:
adm/daemons/logind.lpc的get_name()里 有一处printf("%O\n", ob),每次注册都会把整个玩家对象的原始引 用打印到连线画面上(和"天涯"/"hy"两个家族里已经修过的同款泄漏一 样)。已删除。 - 留言板
post崩溃(AGENTS.md §7.86):全档案 28 份留言板文 件都同时inherit一个留言板基类又多余地对自己replace_program()成同一个类,导致post指令必然崩溃。其中 25 份用的是常见的BULLETIN_BOARD,另外 3 份(xyj_b.lpc、menpai_bbs.lpc、query_bbs.lpc)用的是一个此前没见过的变体基 类BBS_BOARD(/std/bbsboard.lpc),同样的致命形状。已删除全 部 28 处多余调用,live 验证过两块不同的留言板post都能正常保 存。这是 §7.86 第三次在互不相关的代码库家族里被独立发现,已更新 AGENTS.md 记录这个跨家族确认。 - §8.9 确认不适用:这份代码库的食物/饮水初始化本来就是无条件 执行,没有任何年龄判断包装,不存在错对象年龄检查的问题。
- 死亡/复活流程确认正常:
d/death/npc/pang.lpc("朱笔判官 崔 珏")的判定守卫是标准的if (!ob || !present(ob)) return;(AGENTS.md §7.68,现已收窄到仅bmxkx2001适用),未做任何改 动;确认其init()没有hy2000那次发现的wizardp()排除判 断,管理员测试账号可以正常走完整个复活流程——现场验证五阶段对话 全部播放、reincarnate()正确执行、送到"荒郊小店"复活,系统本 身没有问题。
补充修复(静态扫描工具发现的潜伏 bug)
本次会话新写了 scripts/scan_known_bugs.py(一个只读静态扫描工具),
对已经标记 playable 的档案回头扫了一遍,抓到 5 个原本的"注册+look+
score+quit"通关测试没有踩到的潜伏/延迟加载 bug:
d/obj/books-nonskill/book-qujing.lpc的绝对路径#include </d/qujing/obstacle.h>(读这本书才会触发,改成双引号)。logind.lpc的check_legal_name()有标准 §8.1 的i%2奇偶校验 +[i..<0]后缀切片写法——这份档案因为is_chinese()恰好只检查 第一个字符所以没有实际造成拒绝,但还是按标准逐码点写法修正,保持 一致性和稳健性。master.lpc的valid_read()/valid_write()原样转呼叫SECURITY_D,没有user == this_object()的短路判断——这正是让 同宗档案xyj20032每一次新角色注册都在选完性别后静默卡死的那个 bug(详见该档案的 README),这里虽然没有被实际触发,还是提前补上 同样的防御。- 3 处
is_killing(me)传对象给宣告成is_killing(string id)的函 式(§7.50,daemon/class/dragon/dragonforce/roar.lpc、daemon/ class/yaomo/kusong/huomo/fire.lpc、cmds/std/surrender.lpc)。 d/sky/npc/zz-tianwang.lpc还是原始 GBK 字节,最早那一轮批量转 码没有转到,用iconv -c -f GB18030 -t UTF-8补转。
另外还发现并修复了 adm/simul_efun/message.lpc 的 tell_room()
把未设定的 exclude 直接传给 message() 第 4 个参数的 §7.12 bug
(exclude || ({}))。
管理员账号 / Admin account
- ID:
fluffos - 密码 / Password:
Mud@2026(2026-09-03 重新注册;上一轮 2026-08-05 的存档已不在work/data/login/f/) - 权限 / Level:
(admin),通过/adm/etc/wizlist授予(adm/daemons/securityd.lpc真的会在开机时读取WIZLIST),登录 后自动显示"目前权限:(admin)"确认生效。
警告:对外公开架设前请务必修改此密码。
注册流程提示(供后续测试参考)
编码选择(gb/big5)之后会先问"您是否是中小学学生或年龄更小?
(yes/no)",答 no 才能继续到英文 ID 提示;新玩家在英文 ID 提示处
键入 new 才会进入取中文名字/设密码/设 email/选性别的完整创角流
程;创角最后一步是天赋点数分配菜单,键入 9 接受默认值后还会有一
次 [y/n] 二次确认。
本地运行
cd libs/xyj2000
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40155。
NOTES · 移植与修复记录
WASM 修复摘要(迁移自 meta.json 的 group_note)
西游记2000(Journey to the West 2000),一个以西游记为题材的经典 MudOS 时代 mudlib,注册流程里带有一个"是不是在校学生"的年龄门槛问题,这是原始设计,不是 bug。WASM 修复了 3 个连环 bug:(1)adm/daemons/convertd.lpc 的 GB/BIG5 转换表(inittable)带有和 sjsh 系档案上同样的字节级损坏模式(多个符号挤在一起,而不是每个数组元素一个)——用标准的逐码点重新拆分脚本修复(影响约 45 行)。(2)adm/daemons/network/dns_master.lpc,一个真正的多用途 intermud 精灵(query_mud_name()/muds 映射被约 28 个其它服务档案呼叫:mudlist_a/mudlist_q/gtell/gchannel/rwho 等)——按照 AGENTS.md §7.52 对多用途精灵的例外处理,没有整个档案掏空,只掏空了两个碰 socket 的入口点(startup_udp() 现在回传 0,符合它自己既有的失败回传惯例,让 create() 里的 'if (startup_udp()) init_database();' 自然跳过依赖 socket 的初始化;send_udp() 变成 no-op),另外删掉了 send_shutdown() 里遗留的一处 socket_close()。(3)由此暴露出的一个连锁 bug:adm/etc/preload 里 dns_master 那一行被注释掉了(原始压缩包里是启用的;很可能是有人在 socket 兼容修复出现之前,为了让 mudlib 至少能启动而做的临时变通),导致 DNS_MASTER 从未被预加载。adm/daemons/logind.lpc 的 encoding() 在选完编码后紧接着检查 'if(!find_object(DNS_MASTER) || ...) shutdown(1);'——find_object() 只对已经加载的物件回传真,精灵既然从未被预加载,这个检查就永远为真,导致每一次连线在 GB/BIG5 选择完之后立刻触发 shutdown(1),之后再也没有注册 input_to()(后续所有输入都显示为无法识别的指令)。已通过读 logind.lpc 的 encoding() 并确认预加载那一行被禁用(而原始压缩包里是启用的)根源定位;修复方式是重新启用那一行预加载,因为 dns_master.lpc 现在已经能在 WASM 下干净编译运行——logind.lpc 自己的检查逻辑不需要改动。管理员账号播种:fluffos (admin) 加入 adm/etc/wizlist(securityd.lpc 真的会在开机时读取 WIZLIST)。注册流程在一次连续的 WASM 客户端会话里完整验证过:GB/BIG5 选择→否(年龄门槛)→new→英文 id→中文名字→密码+确认→电子邮件→性别(m/f)→属性分配菜单(9 接受默认值,y 确认)→带着完整角色属性表进入游戏世界;look/score/quit 都正常。管理员权限已直接通过登录时的"目前权限:(admin)"确认。LPC 格式化工具对全部 5637 个档案运行(写入 5542 个,4 个因为转档之前就存在的未结束字符串/文本块内容 bug 被拒绝格式化,和格式化工具本身无关,91 个未改动)。改动档案格式化前的原始内容里没有 :: 父类呼叫拆分命中,没有 CJK 重新加空格命中,没有 case 标签带尾随注释的候选。格式化后用同样的完整注册流程重新验证过——干净,管理员权限依然是 (admin)。追加修复:scripts/scan_known_bugs.py(本次会话新写的静态扫描工具)事后标记出 5 个懒加载/潜伏的 bug,是原本只测注册流程的冒烟测试从未触发过的:d/obj/books-nonskill/book-qujing.lpc 的绝对路径写法 #include </d/qujing/obstacle.h>(永远解析不了,已改成引号写法);logind.lpc 的 check_legal_name() 带有标准 §8.1 的 i%2 奇偶门槛加 [i..<0] 后缀切片写法(这里之所以无害,只是因为这份档案的 is_chinese() 恰好只检查 str[0],但为了统一/稳健起见还是改成了标准的逐码点写法);master.lpc 的 valid_read()/valid_write() 直接转发给 SECURITY_D,没有 'user == this_object()' 保护(和手足档案 xyj20032 上曾经静默弄坏每一次新角色物件编译的那个潜伏风险一模一样——这里没有观察到实际触发,属于主动预防性修复);3 个档案用物件呼叫了 is_killing(me),而 feature/attack.lpc 声明的是 is_killing(string id)(§7.50,daemon/class/dragon/dragonforce/roar.lpc、daemon/class/yaomo/kusong/huomo/fire.lpc、cmds/std/surrender.lpc);d/sky/npc/zz-tianwang.lpc 还残留有原始 GBK 字节,是原来批量 GBK→UTF8 转换那一遍漏掉的(已用 iconv 转换)。另外还发现并修复了 adm/simul_efun/message.lpc 的 tell_room() 把一个未设置的 exclude 直接转发进 message() 第 4 个参数的问题(§7.12,改成 exclude || ({}))。修复后重新验证干净。
深度功能测试(§10.7,2026-08-05)
- printf 调试残留:
adm/daemons/logind.lpc的get_name()里有 一处活跃的printf("%O\n", ob)(和"天涯"/"hy"两个家族里已经修过 的同款泄漏一样),每次注册都会把整个玩家对象的原始引用打印到连 线画面上。已删除。 - §8.9 不适用:
enter_world()的user->set("food", user->max_food_capacity())等语句是无条件执行的,本来就没有用ob(登录对象)做任何年龄判断包装,所以不存在错对象年龄检查的 问题——这份代码库的写法本身就是对的。 - 留言板
post崩溃 bug(AGENTS.md §7.86,第三个不相关家族确 认,且发现了一个新变体):这个 bug 之前在"天涯"(tybxjh/xhcii/zxty)和"hy/海洋"(hy2000/hy2002)两个互不相关的家 族里都见过,这次在完全独立的西游记代码库里又发现了——而且这次除 了常见的inherit BULLETIN_BOARD+ 多余replace_program (BULLETIN_BOARD)(25 处)之外,还发现了一个新变体:3 份档案 (xyj_b.lpc、menpai_bbs.lpc、query_bbs.lpc)用的是另一个同 样有问题的留言板基类BBS_BOARD(/std/bbsboard.lpc),同样的inherit+ 多余replace_program写法,同样的ob->edit((: done_postnews, ... :))闭包创建崩溃机制——已确认bbsboard.lpc自己的do_postnews()和bboard.lpc的do_post()是同一个致命形状。共删除 28 处多余的replace_program调用(25 处BULLETIN_BOARD+ 3 处BBS_BOARD)。live 验证过两 块不同的留言板(南城客栈留言板、生死之间留言板)post都能正常 打开编辑器并保存成功。这已经是 §7.86 第三次在互不相关的代码库家 族里被独立确认,基本可以认定是这一代 ES2 衍生代码库的通用陷阱, 已更新 AGENTS.md §7.86 记录这个跨家族确认和新的BBS_BOARD变体。 - 战斗/死亡/复活测试:在朱雀大街和"疥顶小僧"(一个使用"重重叠 叠"分身幻术的强力和尚 NPC)打了一场,很快落败身亡,落到"阴阳 界","朱笔判官 崔珏"在场(这份档案自己对§7.68 死亡守卫 NPC 的称 呼和造型,判定守卫写法和"天涯"/"hy"家族的"白无常"完全一样:
if (!ob || !present(ob)) return;,位于d/death/npc/pang.lpc)。 先检查了pang.lpc的init()有没有 hy2000 那次发现的wizardp()排除判断——确认没有,管理员测试账号不会被排除在自 动复活流程之外,于是直接原地等待,没有对判定守卫做任何改动(按 AGENTS.md §7.68 现在收窄后的纪律)。复活流程完整播放了全部五个阶 段对话,reincarnate()正确执行,最终送到"荒郊小店",角色状态 (气血/精神部分恢复、食物/饮水满格)符合预期——复活系统本身完全 正常,不需要任何修复。 - 本次没有测试:拜师/门派、商店(时间主要花在追查/验证留言板 bug 的完整范围上,留给后续深挖)。
§7.100 sweep (2026-08-19)
Fixed the corpus-wide inherit ROOM; ... replace_program(ROOM); redundant-replace bug (AGENTS.md §7.100). 142 live occurrences deleted: 141 via scripted sweep (fix_710_room.py), plus 1 hand-fixed roommaker-tool template (obj/roommaker.lpc, simple string-builder variant). 1 already-commented-out instance 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 40155.
§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.
深度功能测试(2026-09-03,第三轮)
新角度:2026-08-05 第一轮明确跳过的商店 / 拜师,加上 quit+reconnect。
死亡/复活与留言板 post 不再复测。
- 管理员存档缺失:
adm/etc/wizlist仍有fluffos (admin),但work/data/login/f/fluffos.o不在树上(2026-08-05 那次注册的存档 已丢)。按 README 流程重新注册:gb→no→new→fluffos→ 中文名「浮浮」→Mud@2026×2 → email →m;MOTD 是start_more分页,先q再走/d/wiz/init天赋菜单(9接受默认、y确认)。 登录显示「目前权限:(admin)」,落地南城客栈。 - 商店:客栈
店小二(/d/city/npc/xiaoer,房间本身就加载)list列出水晶球/炸鸡腿/挑战金牌/狗肉/花生/下棋指南/桂花酒袋;clone /obj/money/gold后buy jitui from xiaoer成功(「你向店 小二买下一根炸鸡腿」),黄金按价找零成九十九两银子 + 二十文钱。d/city/npc/tiejiang.lpc(周铁匠)源码在、但没有任何长安房间objects引用它——内容未接线,不是运行期 bug,本轮用已摆好的店 小二做商店验证。 - 拜师:
goto /d/qujing/wuzhuang/wangxian,bai lan(蓝采和) 当场收徒:「好,那我就勉为其难吧。」→ 五庄观第四代弟子,师承蓝采 和。save+quit后重连,score仍是「五庄观第四代弟子 / 你的 师父是蓝采和」,银钱仍在;炸鸡腿未随存档回来(食物物件常见no_save/quit 丢弃,金钱与门派都持久,不按编程 bug 记)。 - 日志:本轮 live
debug.log是libs/xyj2000/log/debug.log(cd libs/xyj2000 && driver在 chdir 前进打开)。无error:/Too deep recursion/No program。work/log/log只有编译警告 (#pragma、未用局部变量、vendor_sale.lpc:504的remove()参数个数与父类不一致)——商店买卖已现场成功,该警告不是本轮新 引入的运行期故障。 - 结论:商店 / 有机拜师 / 重连均通过,本轮无新编程 bug 可修。