Journey to the West 2000

✅ 可玩

西游记2000

xyj2000

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

▶ 开始游玩 · Play Now

西游记("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

内容亮点

本次修复的关键 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/preloaddns_master 被 注释掉了:这份档案的 preload 文件里 #/adm/daemons/ network/dns_master 这一行被人为注释掉(原始压缩包里是启用状态; 多半是有人在 socket 兼容性 bug 修好之前,为了让整个 mudlib 能开 机而临时关掉的权宜之计),导致 DNS_MASTER 精灵永远不会在开机 时被预加载。adm/daemons/logind.lpcencoding() 在选完编码 后第一次连线会检查 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

补充修复(静态扫描工具发现的潜伏 bug)

本次会话新写了 scripts/scan_known_bugs.py(一个只读静态扫描工具), 对已经标记 playable 的档案回头扫了一遍,抓到 5 个原本的"注册+look+ score+quit"通关测试没有踩到的潜伏/延迟加载 bug:

另外还发现并修复了 adm/simul_efun/message.lpctell_room() 把未设定的 exclude 直接传给 message() 第 4 个参数的 §7.12 bug (exclude || ({}))。

管理员账号 / Admin account

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

注册流程提示(供后续测试参考)

编码选择(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)

§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 不再复测。