info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
西游记之新纪元(xiyouji.org,自 1998 年前后运行至今的经典老牌 MUD)取材吴承恩原著神话世界观,新角色从长安城南城客栈出发,此后游历天庭、地府、花果山等经典场景;字节级比对显示,这并非本合集里 `xyj2000` 的普通姊妹档案,而实际是同一份档案分两次分别归档:约 8700 个文件里超过 99% 逐字节相同,剩余差异大多只是行末空格或这份拷贝独有的后期 bugfix 注释;注册前有一道真实的年龄声明("您是否是中小学学生或年龄更小?"),回答是会被系统直接请出游戏,是与 `xyj2000` 共有的原始设计,不是 bug。
English
Journey to the West: New Era (xiyouji.org), a classic old-school MUD running since ~1998, set in Wu Cheng'en's mythological world -- new characters start at a South City Inn in Chang'an before roaming the Heavenly Court, the underworld, and Flower-Fruit Mountain. A file-level check this pass found this is not merely a sibling of this collection's xyj2000 but effectively the exact same archive packaged and onboarded separately: over 99% of its roughly 8,700 files are byte-identical, and nearly all remaining differences are either a single stray space or later bugfix-stage comments this copy happens to carry that the other doesn't. Shares the same real age-declaration registration gate (rejects players who say they're primary/secondary-school students) as xyj2000.
README
内容亮点
- 以《西游记》神话世界为背景的取经冒险 MUD,玩家扮演一名踏上西天取经 之路的江湖人物,起点是长安城的南城客栈。
- 可习得各类西游神话相关的武学、法术,游历天庭、地府、花果山等经典 场景——是国内最早一批以《西游记》为题材的经典 MUD 之一。
- 注册前有一道真实的年龄提示("您是否是中小学学生或年龄更小?"), 回答 yes 会被系统直接请出游戏,是这款游戏自带的一项原始设计。
在线试玩
https://mudlibs.fluffos.info/xyj2000f/
管理员账号 / Admin account
- id:
fluffos - 密码 / password:
Mud@2026 - 中文名 / display name: 浮浮
- 权限 / level:
(admin)—— 最高级别,通过/adm/etc/wizlist中的fluffos (admin)行授权(score 中会显示【巫师】称号)。
警告:公开架站前请务必修改此默认密码。
本地运行
cd libs/xyj2000f
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40012。
NOTES · 移植与修复记录
fluffos(西游记2000).tar.gz → xyj2000f
- Archive:
archives/fluffos(西游记2000).tar.gz(59MB — a full FluffOS driver source checkout,.gitincluded, bundling the mudlib as a NESTEDmudlib/world.tar.gzinside it (extracted separately). The mudlib itself is "西游记"/"Xi You Ji"/"Journey to the West",xiyouji.org, up since ~1998 per in-file credits (mon@xiyouji). - Mudlib root:
world/(after extracting the nested tarball). Config atworld/config.xyj. - Port: 40012.
Status: DONE — boots clean, playable over telnet
Full flow confirmed: ASCII banner, GB/BIG5 prompt (send "gb"), site
credits, age-appropriateness question (a real, intentional gameplay/
content gate, not a bug). Note the BIG5 hint line in the encoding-select
banner itself displays as mojibake (﹁村癘舧眤...) — likely a genuine
BIG5-encoded substring embedded in an otherwise-GBK file (same shape as
ds386's Latin-1-in-GBK-file issue) — cosmetic only, not fixed.
What was fixed
1. Encoding: routine GB18030→UTF-8 pass, 766 .c" refs auto-fixed, 72
angle-bracket .c>→.lpc> refs, and 89 local angle-bracket
#include <x.lpc> → "x.lpc" conversions handled automatically by
convert_lib.sh's newly-generalized fix (first real large-scale test
of that generalization from lib #13's manual fix — worked cleanly,
no manual follow-up needed for this pattern at all this time).
.c→.lpc: 5637 files. static→nosave: 47 files.
2. adm/daemons/convertd.lpc (a charset-conversion daemon, containing a
large Greek-alphabet lookup table as string literals) had 5 lines
shaped like "α\", — a stray trailing backslash right before the
closing quote, which escapes it instead of closing the string,
turning the rest of the file into one runaway unterminated string
literal (Illegal character/syntax error cascade). Pre-existing
data typo, not something our pipeline introduced. Fixed with a
targeted sed -E 's/\\"(,)?$/"\1/' on the whole file (removes a
trailing \" → ", or \", → ",, at end of line only, so it can't
touch a legitimately-escaped quote appearing mid-string elsewhere).
Known remaining issues (documented, not fixed)
- 66 lpcc-sweep failures (of 5637, 98.8% pass) — not triaged given the small count and time budget; boot/login path unaffected.
How to run
cd libs/xyj2000f
~/src/fluffos/build-debug/src/driver config.fluffos
python3 ../../scripts/mudclient.py 127.0.0.1 40012 --timeout 10 --send "gb" --send "" --send "quit"Post-hoc fix: UTF8-native is_chinese/registration (AGENTS.md §15h)
Applied in a later batch pass across the whole project: is_chinese/is_chinese2
in the shared chinese.lpc simul_efun fragment used GBK byte-range checks that
silently never match real Chinese text once strings are UTF-8 (this driver's
str[i] returns a Unicode codepoint, not a GBK byte). This broke character
registration specifically -- any real Chinese name was rejected. Fixed the
range check to test the CJK Unicode block instead, and halved the
GBK-byte-calibrated length bounds in check_legal_name to match. See
AGENTS.md §15h for the full writeup; confirmed via a real interactive
registration test (Chinese surname + given name reaching the next prompt).
Re-verification pass: driver rebuild + LPC formatter + WASM build
- Formatter:
format-corpus.mjsover all 5637.lpcfiles; 5544 reformatted, 91 unchanged, only 2 refused (self-checkerrors) — very clean corpus. - Native retest against rebuilt driver: clean, zero fixes needed. Full registration flow (gb encoding → age-gate
no→newkeyword → English id → real Chinese name → password → email → gender → gift allocation9/y) verified end-to-end reaching 南城客栈, withlook/score/quitall producing correct output, zero debug.log errors. - WASM test — 1 regression found + fixed (WASM-specific, does not reproduce natively):
adm/daemons/logind.lpc'sencoding()callback runs a one-time "mirror site verification" gate (!find_object(DNS_MASTER) || !"/adm/daemons/band"->check_ip_(...)) after every player selects gb/big5. This lib'sadm/etc/preloadDOES includedns_master(unlike libs where §15p's DNS-preload-exclusion policy applies) and it preloads fine natively (real sockets work), sofind_object(DNS_MASTER)is truthy there and the gate passes cleanly — but under WASM (no sockets package)dns_masterfails to compile at preload,find_object(DNS_MASTER)is always0, and the code unconditionally calledDNS_MASTER->get_host_name(...)even inside its own "absent" branch (building the shutdown log message) — which itself crashed with*No program in objectbefore ever reaching the intendedshutdown(1), leaving the connection stuck in a broken state (every subsequent input treated as an unrecognized command, no way to ever complete the encoding step). This is the exact AGENTS.md §15ai pattern (xiyouji2003's finding, same lineage) applied to a lib that hadn't needed it before because it never excludesdns_masterfrom preload. Fixed identically: guard withfind_object(DNS_MASTER) && ...so "daemon absent" means "skip the gate" (allow login) rather than "gate failed" (attempt, and crash trying, to shut down). This is a no-op change natively (dns_master is always present there) and only changes behavior under WASM. Re-verified clean both ways: native registration still reaches 南城客栈 with zero errors (real name 孙悟空), and the full registration flow (incl. the age-gate andnewkeyword quirks, real name 猪八戒) now completes under WASM too, reaching 南城客栈 withlook/quitboth working — full WASM playthrough, not just boot. Not affected by the documentedquery_ip_number()WASM limitation (this lib's gate isfind_object-based, not IP-format-based).
WASM-enablement pass (loopback / admin seeding)
- Loopback ban bypass (§1.3b):
adm/daemons/band.lpc— new private helperis_loopback_site()(~line 153), short-circuitsis_banned()(~line 161),create_char_banned()(~line 179), andis_strict_banned()(~line 194) to return 0 (not banned) for127.0.0.1/::1/localhost/127.-prefix sites. - IP-format kick (§1.3a, folded into the loopback patch):
adm/daemons/logind.lpcencoding()(~line 180) — the "No IP" / "Non_number" destruct-on-connect checks now only run for genuinely remoteip_numbervalues; loopback skips straight through. - Anti-flood / multi-login cap (§1.3e):
adm/daemons/logind.lpcget_id()(~line 348) — theMAX_LOGINper-IP multi-login-cap block is now skipped entirely whenquery_ip_number(ob)is loopback. - Wizard site whitelist (§1.3b):
adm/daemons/securityd.lpcmatch_wiz_site1()(~line 116) — loopback sites always pass (return 1) before the original whitelist-line logic runs. - Uptime gate: none found (
logind.lpchas nouptime()connection gate). - Fail-closed retrofit (2026-07-24 security correction): all four gates above originally also treated an empty/non-string IP as loopback (defensive fallback for the then-broken
query_ip_number()). Since the driver's IP-reporting bug is now fixed upstream (WASM reports a clean127.0.0.1like native), that fallback was removed — loopback is now strictlystringp(ip) && (ip=="127.0.0.1" || ip=="::1" || ip[0..3]=="127."); anything unparseable/empty is treated as untrusted/remote and goes through the original gate logic, same as before this pass. Retested: fluffos login +look/quitstill clean over loopback. - Admin account (§1.5):
fluffos/Mud@2026, display 浮浮, status(admin)viafluffos (admin)appended to/adm/etc/wizlist(read bysecurityd.lpc). Registered through the real flow. Verified re-login +update /adm/daemons/band→ recompiled successfully as fluffos, score shows 【巫师】title. - Retest: fresh normal registration (孙悟空-style real Chinese name) reaches 南城客栈 with working
look/score/quit; test char saves removed; no new debug.log errors. - Save files to force-add (untracked, NOT gitignored):
libs/xyj2000f/work/data/login/f/fluffos.o,libs/xyj2000f/work/data/user/f/fluffos.o.
深度功能测试 / Deep functional test (2026-08-06)
第一次完整游玩测试(原生驱动 build,ASAN/UBSAN debug 构建)。测试角
色 id xyjtestb,中文名 沙悟净。本轮 WASM 未重新验证:emsdk 工具链
下载硬编码指向 storage.googleapis.com,本次会话的出口代理策略性拒
绝该域名(403,已用 curl $HTTPS_PROXY/__agentproxy/status 确认是策
略拒绝而非临时故障),本地无法构建 WASM 驱动。
发现并修复:maximum evaluation cost 过低,每一次新角色注册都会在 make_body() 里静默中止(AGENTS.md §7.90 新的、更严重的实例)
- 症状:真实游玩(而非仅注册后立刻检查
look)触发。用第一个测 试账号(xyjtest/沙悟净)走完性别选择前的所有步骤(英文名 → 中文 名 → 密码 → 确认密码 → 邮箱)后,连线在邮箱提示之后卡住——没有任何 玩家可见的错误信息,看起来就像连接挂起了。debug.log里同一时刻有 一条完整的运行时错误:Eval interrupted: object adm/obj/simul_efun cost limit reached, limit: 400000 usec.,栈追踪经过logind.lpc的get_email()→make_body()→master.lpc/simul_efun.lpc的author_file(),触发点是/std/char.lpc(玩家 身体类)第一次编译时连带加载feature/edit、feature/finance等 继承链。 - 根因:
config.fluffos里maximum evaluation cost : 400000—— 比 AGENTS.md §7.90 记录的"这个项目最常见的 700000 模板默认值"还要 低,而这份档案第一次编译整条std/char.lpc继承链的开销超过了这个 预算。和 §7.90 原案例(只在某些从未访问过的房间移动时零星触发)不 同,这里是每一次新角色注册都会 100% 复现——因为make_body()是注册流程的必经步骤,第一次编译std/char.lpc的开销不会因为运气 好而躲过去。 - 修复:把
maximum evaluation cost从400000提高到5000000(AGENTS.md §7.90 记录的同一个已在本项目 30+ 份档案验证过 安全的数值)。 - 验证:修复前用第一个测试账号复现了这次静默中止(
debug.log里 完整的cost limit reached追踪,连接停在邮箱提示后不再有任何响 应);修复后重启驱动,用第二个测试账号(xyjtestb/沙悟净)完整走 完注册全部步骤(含性别选择、天赋接受确认),顺利进入南城客栈,debug.log全程无cost limit reached或任何其它运行时错误。
发现并修复:注册流程里遗留的 printf("%O", ob) 调试输出(AGENTS.md §7.34 已知模式的又一实例)
- 症状:玩家输入完中文名之后,屏幕上多出一行裸露的对象内部路 径,如
/obj/login#0,夹在"您的中文名字:"和"请设定您的密码:" 两行正常提示之间。 - 根因:
adm/daemons/logind.lpc:646(中文名确认成功分支)有一行 未加注释的printf("%O\n", ob);,是原作者遗留的调试脚手架(该文件 开头注明cracked by vikee 2/09/2002),archive 里原样就是这样。 该文件里没有找到第二条平行路径(比如接受系统随机名字的分支)里的 重复实例。AGENTS.md §7.34 已把这个模式记录为登录/注册流程的常见通 病。 - 修复:删除这一行,与 §7.34 记录的既定修法一致。
- 验证:修复前用第一个测试账号亲眼看到
/obj/login#0出现;修复 后用第二个测试账号完整走过一次注册(英文名→中文名→密码→确认密码 →邮箱→性别→天赋),确认这一行不再出现。§9格式化自检通过。
测试内容与结果
- 注册:GB 编码 → 是否中小学生(回答 no)→
new注册 → 英文名 → 中文名(沙悟净)→ 密码(含大小写字母/数字)→ 确认密码 → 邮箱 → 性 别(m)→ 天赋接受(体格/根骨/悟性/灵性四项,可重选或直接接受), 全程顺利进入南城客栈。 - 战斗:
朱雀大街的疥顶小僧(d/city/npc/jieding.lpc,attitude: peaceful,物理属性较弱但技能等级 50-79)——set wimpy 70后fight seng触发真实对战,在气血降到 70%(140/200)时正确 自动逃跑("看来该找机会逃跑了..."),角色被传送到相邻的十字街 头,无崩溃。d/obj/misc/muren.lpc(一个理论上更适合当陪练目标的 木人,accept_fight()里没有攻击者属性镜像,是固定弱属性combat_exp 50000)在这份档案的d/目录树里没有找到任何房间引用 它——很可能是这份快照里未被实际放置使用的遗留内容,未深究。 - 持久化:两种方式都验证了——(a) 驱动整体重启(比普通 quit+ relogin 更强的持久化测试)后用第一个测试账号密码重新登录,正确恢 复到南城客栈、
(player)权限、装备状态;(b) 真实quit(丢弃不值 钱的粗布衣,符合已知的丢弃机制)后立即重新登录,同样正确恢复—— 但两次都回到了南城客栈这个固定入口,而不是quit时所在的十字街 头。查证help newbie第一条明确写着"进入西游记,你会最先出现在 长安城的南城客栈",没有措辞限定"仅第一次",读作这条游戏本身固定 的登录起点设计(不是位置持久化 bug),如实记录而非默认判定。 - 管理员账号:
fluffos/Mud@2026登录,update /adm/daemons/logind热更新成功,确认写 ACL 正常。 - 门派/拜师:未覆盖——
朱雀大街上偶遇的门派弟子 NPC(五庄 观第三代弟子 张果老)是过路巡逻角色,ask ... about 拜师时人已经 离开房间;help menpai描述了八大门派的风格但没有给出具体的拜师 地点/NPC 名字,需要进一步探索地图才能找到固定的拜师入口。经济/商 店同样未覆盖。均如实标注为本轮未测,而非默认"和同引擎家族其它档 案一样所以没问题"。
WASM 修复摘要(迁移自 meta.json 的 group_note)
西游记 2000 的一份快照。
§7.86 跨库扫描修复(留言板 post 崩溃)
BBS_BOARD、BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 28 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
§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, same lineage as sibling xyj2000). 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 40012.
§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.
Round-four gap closure: sect apprenticeship + shop/economy (2026-08-24)
Closes the two items the 2026-08-06 §10.7 pass left explicitly untested (see that section above).
Sect apprenticeship
No stationary, map-placed sect-master NPC exists in this archive's
current room layout — d/nanhai/npc/master.lpc (玄智和尚, a bonze
master of 山烟寺, create_family("山烟寺", 26, "住持")) is a fully
functional recruiter NPC (attempt_apprentice/do_recruit/
recruit_apprentice), but a corpus-wide grep found zero room files
anywhere under d/ that load it — same "written but never placed"
shape as d/obj/misc/muren.lpc noted in the original pass. Used the
admin account's clone wizard command to place it in 南城客栈 (the
same technique implicitly endorsed by this round's task framing) as
the practical way to reach it.
Found and fixed a real instance of AGENTS.md §7.117 (bai.lpc/
apprentice.lpc's "did you betray your old sect" check missing an
existence guard for first-time applicants): cmds/std/apprentice.lpc
line 59 had the exact dominant vulnerable form,
if ((string)me->query("family/family_name") != (string)ob->query("family/family_name")),
with no guard on whether the applicant (me) already has a family.
Its sibling cmds/std/recruit.lpc already carries the fix on its own
matching branch (line 64, if ((ob->query("family")) && ...), with
the tell-tale // follow modified by elon 09-10-95 to fix a bug in 1st
time recruit comment) — this lib was not part of the large corpus-wide
§7.117 sweep (not matched by that session's grep), so it's a new,
previously-uncaught instance of the same one-file-of-the-pair-missed
gap already seen on sjshv150. Fixed identically to the documented
remedy: if (me->query("family") && (string)me->query("family/family_name")
!= (string)ob->query("family/family_name")).
- Confirmed via
lpcc --batch: file compiles clean (not among this lib's pre-existing 64 unrelated batch failures). - Confirmed live end-to-end via a full normal playthrough: registered a fresh character,
ask master about 出家→kneel(剃度, renamed,classset tobonze) →apprentice master— completes via the NPC's ownattempt_apprentice/recruit.lpcpath (already-guarded sibling branch) andscorecorrectly showed山烟寺第二十七代弟子 <name>with你的师父是玄智和尚. - Separately, specifically exercised the buggy
apprentice.lpcbranch itself (which the natural NPC-driven flow above does NOT reach, since that NPC always recruits *after* the player callsapprenticefirst): registered a second fresh, family-less character, used the admin account'scall master->command("recruit <id>")to force the master to recruit first (bypassingattempt_apprentice's own class/gender gate, which is irrelevant to the bug under test), then had the applicant confirm withapprentice master. Before the fix this unconditionally took the "betrayed your old sect" branch (wrongly, since a first-timer has no family to betray) and would have zeroed score / set thebetrayerflag; after the fix it correctly took the normal "拜师" branch (你决定拜玄智和 尚为师。) with no betrayer flag, andscoreafterward showed clean membership (山烟寺第二十七代弟子 <name>, no score reset).
Shop / economy
Found a real, placed vendor NPC: d/city/bookstore.lpc loads
d/city/npc/bookseller.lpc (孔方兄, inherit F_VENDOR_SALE), selling
several books via vendor_goods. Tested with the admin account
(cloned the vendor and some /obj/money/silver into the same room,
set_amount() via call to fund the purchase):
buy san from kongfang(三字经, value 1000 = 10两银子): had exactly 10 两银子, spent all of it, received the book, zero coin objects left over — correct exact-payment math.buy xyjbook from kongfang(《西游记》, value 100 = 1两银子): had 15 两银子 (1500), paid 100, correctly received 14两银子 in change (pay_money()'s gold/silver/coin decomposition of the 1400 remainder) plus the book — correct change-making math, not just exact-payment.- No
debug.logerrors from either purchase.
No programming bugs found in the shop/vendor path itself
(feature/vendor.lpc/vendor_sale.lpc/cmds/std/buy.lpc) — the
type-mismatch-looking mixed buy_object(object who, object item)
override in bookseller.lpc (declared parameter type object but
always actually passed a string item id from cmds/std/buy.lpc) is
functionally harmless in this driver (no runtime type enforcement on
dynamically-dispatched call args), so not treated as a bug per the
scope rule (no crash, no error signature).
Cleanup
Removed the two round-four test character saves (xyjtc/xyjte,
untracked .o files under work/data/{login,user}/x/) and destroyed
all admin-side clones (the two placed-for-testing NPCs, leftover
money/book clones) before stopping the driver. xyjtestb (from the
original 2026-08-06 pass, already tracked in git) was left untouched.
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.