info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
西游记 ES II mudlib 家族的 4.50 版,地图风格和同门 `xyj42`/`xyj2000` 相近(三十三天、蟠桃园、蓬莱等神话地标齐备),但玩法细节各有不同:注册没有"new"关键字,只要输入的英文 ID 还没有存档,就会直接问"使用 X 这个名字将会创造一个新的人物,您确定吗(y/n)?";天赋点数不是选栏位调整,而是不断重掷的循环("您同意这组天赋数值吗?[n|y|q]",`n` 重掷、`y` 接受);新角色从南城客栈起步,若直接找灵台方寸山掌门拜师会因经验值/道行不足被拒,改找门下低阶弟子才能顺利入门;死亡后会被打入阴阳界,由崔判官核对生死簿,复活时带有真实的属性惩罚。
English
Journey to the West, from the same ES II mudlib family as this collection's xyj42/xyj2000, sharing their Thirty-Three Heavens / Peach Garden / Penglai map style and Lingtai Fangcun Mountain sect. Registration here has no explicit "new character" keyword -- entering an English ID that isn't already on file goes straight to "Creating a new character named X, are you sure? (y/n)" -- and talent points aren't assigned by adjusting slots but through a reroll loop ("Do you accept these talent values? [n|y|q]"). New characters land at the South City Inn; apprenticing to the sect's senior master correctly fails for outsiders (a real in-code experience-and-Taoism-points gate) but succeeds under a junior disciple, and death sends characters through the underworld (阴阳界), where Judge Cui reviews the register of life and death and revives them with a real stat penalty.
README
内容亮点
- 又一个独立的西游记 ES II 家族实现,地图和
xyj42/xyj2000风格 相近(三十三天、蟠桃园、蓬莱等),但天赋数值的获取方式不同:不 是选栏位调整,而是不断重掷的循环(n重掷、y接受)。 - 注册没有
new关键字,任何还没有存档的英文 id 直接会被问"确定 创建新角色吗",比大多数档案的流程更简洁。 - 本次修复全部由本次会话新写的自动化工具(
lib_bulk_fix.py/scan_known_bugs.py)在第一次开机测试之前就提前找出并修好,包 括提前防御了在同宗档案xyj20032上导致注册静默卡死的master.lpc短路判断缺失问题(这里虽未被实际触发)。
本次修复的关键 bug
本次借助会话新写的 scripts/lib_bulk_fix.py/scripts/
scan_known_bugs.py 在第一次开机测试前就先抓出来:
1. 经典 §8.1 GBK 字节区间 is_chinese()(str[0] > 160 &&
str[0] < 255)改成逐码点检查(0x4e00–0x9fff);配套的
check_legal_name() 的 i%2 奇偶校验/[i..<0] 后缀切片也改成
逐码点的 name[i..i]。
2. master.lpc 的 valid_read()/valid_write() 原样转呼叫
SECURITY_D,没有 user == this_object() 短路判断——这正是
在同宗档案 xyj20032 上让每一次新角色注册都静默卡死的那个
bug(详见该档案 README),这里虽然没有被实际触发,还是提前补
上防御。
3. 14 处 is_killing(me)/is_killing(who) 传对象给宣告成
is_killing(string id) 的函式(§7.50),分布在各门派内功daemon
和 cmds/std/surrender.lpc。
httpd.lpc/dns_master.lpc 本来就已经在 adm/etc/preload 里被注
释掉(原始档案自带的状态),休眠状态,没有开机崩溃风险,未作处理。
管理员账号 / Admin account
- ID:
fluffos - 密码 / Password: 注册时自设
- 权限 / Level:
(admin),通过/adm/etc/wizlist授予,登录 后自动显示"目前权限:(admin)"确认生效。
警告:对外公开架设前请务必修改此密码。
注册流程提示(供后续测试参考)
gb → 英文 ID(没有 "new" 关键字,直接输入想要的 ID)→ y(确认
创建新角色)→ 中文名字 → 密码 + 确认 → 电子邮件 → 性别(m/f)→
y(接受当前这组天赋数值,或 n 重掷)→ 进入游戏前会先显示一段
分页 MOTD,q 可以直接跳过。
本地运行
cd libs/xyj451
~/src/fluffos/build-debug/src/driver config.fluffos游戏端口:40112。
NOTES · 移植与修复记录
WASM 修复摘要(迁移自 meta.json 的 group_note)
西游记451(ES II mudlib 家族,v4.50)。注册流程没有 'new' 关键字:任何还没存档的英文 id 都会直接落到"是否使用 X 作为新角色,确认?(y/n)"。天赋分配是一个重投循环("接受这组属性吗?[n|y|q]",n 重投,y 接受,q 大概是放弃),和 三界/xyj2000 家族那种字段选择菜单不一样。WASM 修复靠 scripts/lib_bulk_fix.py + scripts/scan_known_bugs.py(两个都是本次会话新写的工具)在第一次启动测试之前就主动抓出来:(1)标准的 §8.1 GBK 字节区间 is_chinese()(str[0] > 160 && str[0] < 255)重写成逐码点 0x4e00-0x9fff 检查,配套的 check_legal_name() 的 i%2 奇偶门槛/[i..<0] 后缀切片也改成了逐码点的 name[i..i];(2)master.lpc 的 valid_read()/valid_write() 直接转发给 SECURITY_D,没有 'user == this_object()' 保护——和 xyj20032 上曾经静默弄坏每一次注册的那个潜伏风险一模一样,这里没有观察到实际触发,属于主动预防性修复;(3)14 处 is_killing(me)/is_killing(who) 呼叫点,分布在 kungfu 技能的 daemon/class/*.lpc 档案和 cmds/std/surrender.lpc 里,用物件当参数传,而 feature/attack.lpc 对应声明的是 is_killing(string id)(§7.50)。httpd.lpc/dns_master.lpc 本来就已经在 adm/etc/preload 里被注释掉(休眠状态,原来就是这样)——保持原样,没有编译崩溃风险。管理员账号播种:fluffos (admin) 加入 adm/etc/wizlist。注册流程在一次连续的 WASM 客户端会话里完整验证过:GB 选择→英文 id→y(确认新角色)→中文名字→密码+确认→电子邮件→性别(m/f)→y(接受重投的属性)→q(跳过分页的每日消息)→在南城客栈进入游戏世界,look/score 都干净。管理员权限已通过"目前权限:(admin)"确认。LPC 格式化工具对全部 9322 个档案运行(写入 9297 个,9 个转档之前就存在的未结束字符串/文本块内容错误未做格式化,16 个未改动)。没有 :: 父类呼叫拆分命中,没有 CJK 重新加空格命中;唯一一处 case 标签带尾随注释的命中(ftpd.lpc)经 diff 复核干净。格式化后重新验证干净,管理员权限依然是 (admin)。
§7.86 跨库扫描修复(留言板 post 崩溃)
BBS_BOARD、BULLETIN_BOARDinherit+ 多余replace_program()致命形状(AGENTS.md §7.86,post命令崩溃):全档案 41 处命中,已删除多余的replace_program(...)调用(保留inherit),逐文件保留原有行尾格式(CRLF/LF 按文件原样)。本次为跨库 §7.86 扫描修复(触发原因:该 bug 已在 6+ 个互不相关的血统家族独立确认,属于近乎普遍的拷贝粘贴模式),仅做编译检查(驱动干净启动、端口正常监听),未做完整 §10.7 深度游玩测试。
§7.100 跨库扫描修复(ROOM 基类同款 replace_program() 致命形状)
- 同款
inherit ROOM; ... replace_program(ROOM);冗余自替换(AGENTS.md §7.100):work/下 1,155 处存活匹配。脚本删除了 1,153 处标准独立行; 另外 2 份房间生成工具(obj/roommaker.lpc、d/obj/clone/misc/roommaker.lpc,字符串拼接变体,str += "... replace_program(ROOM);..."写死在生成模板里)手动修复。data/下额外 核查过,无命中。验证:真实 debug 驱动干净编译启动、端口正常监听,debug.log无新增 "cannot replace"/错误行。
§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.
Deep functional test (round two, 2026-08-27)
First full §10.7 round-two live playthrough of this lib (previous work
was WASM-onboarding + mechanical corpus sweeps only, never a continuous
played session). Booted ~/src/fluffos/build-debug/src/driver
config.fluffos natively on port 40112 and drove it with raw Python
socket scripts (a persistent named-pipe/background-reader bridge to
allow interleaving two simultaneous connections — an admin session and
a test-character session — in the same room for shop/money setup).
Admin account. adm/etc/wizlist already listed both winding
(admin) and fluffos (admin), but neither had a corresponding save
file — fluffos had to be registered live (English id fluffos,
Chinese name 管理员, password Mud@2026 — this project's standard
password IS accepted). Confirmed (admin) privilege via the login
banner and via live call/clone/goto/summon admin commands.
Bugs found and fixed (3, all genuine programming bugs — missing guards / pre-existing data corruption causing real crashes, not content/design):
1. adm/simul_efun/file.lpc's log_file() writes into /log/nosave/
with no assure_file() guard (AGENTS.md §7.11, same shape as the
xajhxo/xyj2006n instances — this lib is the same xyj4.5
lineage as xyj2006n, byte-for-byte identical bug). Reproduced live:
admin call wukongce->add("money",5000) (used to fund a shop-buy
test) threw an uncaught *Wrong permissions for opening file
/log/nosave/CALL_PLAYER for append. "No such file or directory".
Since cmds/usr/suicide.lpc's real account-deletion path calls the
identical log_file("nosave/SUICIDE", ...) line before any deletion
logic, every real suicide -f would hit the same crash. Fixed by
forward-declaring and calling the file's own already-present
assure_file() helper inside log_file():
``lpc
void assure_file(string file);
void log_file(string file, string text) {
assure_file(LOG_DIR + file);
write_file(LOG_DIR + file, text);
}
`
Verified live: the same call completed cleanly post-fix and
/log/nosave/CALL_PLAYER` was created with the expected audit line.
2. adm/obj/master.lpc's log_error() mislabels plain compile
WARNINGS as "编译时段错误:" (compile-stage ERROR) for wizards
(AGENTS.md §7.103, narrower variant — this lib's log_error()
already gates the raw dump behind wizardp(this_player(1)), so
ordinary players only ever see the generic default error message,
not the raw warning text; only wizards were affected). Reproduced
live: logging in as the seeded admin and running look/score/i
each dumped 编译时段错误:/cmds/std/look.lpc:363:1: warning: Unused
local variable 'limb_status'-style text as those command files got
lazily compiled for the first time this boot. Fixed by adding the
standard strsrch(message, "warning:") == -1 guard inside the
existing wizardp(...) branch only (non-wizard branch untouched):
``lpc
if (wizardp(this_player(1))) {
if (strsrch(message, "warning:") == -1)
efun::write("编译时段错误:" + message + "\n");
} else
efun::write(get_config(__DEFAULT_ERROR_MESSAGE__) + "\n");
`
Since master.lpc only compiles once at boot, this required a full
driver restart to verify — confirmed live post-restart: the same
admin register→look→score→i sequence produced zero warning dumps,
while warnings still land in debug.log` for wizards to find later.
3. Pre-existing embedded-CRLF corruption in two NPC .o save files
(data/npc/boss/xiaoxiao.o, data/npc/meng-zhu.o) — confirmed
byte-identical in the pristine raw/ archive, so this predates this
project's conversion, not introduced by it. Each file's "long"
description string contains a real \r\n pair (this driver's
save_svalue()/restore_object_from_buff() expects an embedded
in-string newline to be written as a bare \r, with real \r\n
reserved for top-level statement separators — see AGENTS.md's .o
save-file section, third confirmed instance after fy2/fy2qh).
Reproduced live: admin goto /d/city/bingqipu and goto
/d/suburb/xkx/taishan/fengchan (forcing each room to reset and
restore its NPC) threw uncaught 执行时段错误:*restore_object():
Illegal file format - 1 in debug.log, leaving the room
un-populated. Also independently reachable through ordinary NPC
wandering (张果老 walking into 萧萧's room triggered the same crash
without any admin involvement). Fixed with a quote-aware scan:
collapse \r\n → \r only when inside an (unescaped) " string
literal, leaving every top-level \r\n separator untouched. Verified
live post-fix and post-restart: both rooms now populate their NPC
with no restore error. Note: data/npc/boss/ is covered by this
project's own .gitignore (treated as ephemeral boss-respawn churn),
so xiaoxiao.o's fix is local-only/untracked; meng-zhu.o sits one
directory up and so is tracked and part of this commit.
Full playthrough exercised (test character wukongce/测试悟空,
female): GB encoding select → new-character confirm (y) → Chinese
name → password/confirm → email → gender (f) → talent reroll (tried
n once, then y) → entered the game at 南城客栈, clean look/
score/i at every step. Moved west/east between 朱雀大街 and the
inn, re-checked score after the move (clean). Shop economy: list at
店小二, buy huasheng from xiao er after being funded via
/adm/auxil/moneyd->pay_player() (the correct way to grant test money
— physical coin/silver/gold objects, not a raw add("money",N")
property, which does nothing for can_afford()/buy), confirmed
inventory and change correctly. Message board: post/read/discard
at 南城客栈留言板 — clean, no crash (re-confirms the already-fixed
§7.86 post bug holds), pagination help menpai's multi-page text
tested with ENTER/b(back)/q(quit) — all worked correctly, no §7.153
gap. Sect join: apprentice puti (a high-tier inner-circle master)
correctly rejected an outsider per its own in-file combat_exp/
query_skill requirements (content, not a bug); apprentice yun qing
(a lower-tier disciple, matching the newbie doc's own advice to start
with a low-ranking member) succeeded, "恭喜您成为方寸山三星洞的第四代
弟子。" learn unarmed from yun qing succeeded, skill progressed to
"初学乍练 1/0". Safe-sparring: fight <npc> is this lib's actual
point-to-point spar mechanism ("这种形式的战斗纯粹是点到为止...不会真
的受伤") — the test NPC (扫地小童) declined the fight invitation, so
per the game's own documented fallback ("如果对方不愿意接受你的挑战,
你仍然可以迳行用 kill 指令开始战斗") used kill instead, which was
accepted and produced a real fight with escalating wound messages.
Combat/death/respawn: lost the fight, died, sent to 阴阳界
(the underworld) with 崔判官, who reviewed the "生死簿" and resurrected
the character automatically after a short wait, respawning at 荒郊小店
with sect membership preserved but inventory dropped and 潜能 reduced
from 99 to 50 (a death penalty — consistent, intentional design, not a
bug). Quit/reconnect: did two full quit→wait→reconnect cycles; both
times state persisted correctly (sect, HP, stats), no leaked duplicate
user/login objects (ulist showed exactly one connection),
tell/find_player worked correctly both before and after reconnect
(§7.131/§7.152 clean).
Cross-cutting pattern sweep (all the standing AGENTS.md patterns
listed in the round-two task brief, checked via targeted grep against
work/): §4.3 (no log_file() call site has "static" in its literal
path argument — the one "static/SUICIDE" hit is in cmds/usr/sui1.old,
a dead .old-extension file the driver never compiles and nothing
references), §4.4 (data/chinese.o and other dict-like files are
valid UTF-8, no BIG5-as-GBK residue found), §7.80/§7.118 (no
[0..<1]/off-by-one filename-slice hits), §7.86/§7.100 (already swept
corpus-wide; live-reconfirmed via a clean post on the inn's board),
§7.112 (no unguarded NPC/reconnect init() re-entry issue found),
§7.121 (no bare float literal assigned to an int-typed field/param),
§7.122/§7.150 (obj/login.lpc destructs itself correctly on
time_out()/successful login, no heartbeat leak), §7.123 (no
column-zero bare IDENT = (\[|\{... file-scope statement), §7.129
(no tell_room()/message() wrapper forwarding a bare unset varargs
arg), §7.130/§7.133 (obj/login.lpc's net_dead()/obj/user.lpc's
reconnect() both correctly defined, no unconditional post-non-
interactive liveness check), §7.131/§7.152 (find_living/find_player
work correctly, confirmed live via cross-connection tell both before
and after reconnect; feature/command.lpc's enable_player() calls
set_living_name() once on the same persistent object, which survives
netdead/reconnect since the object itself is never destroyed), §7.132
(no map()-over-mapping wrong-arg-bound callback found), §7.134 (no
field defaulting to 0 instead of ({}) found in this pass's scope),
§7.139 (no interactive-catch-tell config flag issue observed), §7.141
(no replace_program() fold found in a room create()), §7.142 (no
virtual-object-masks-broken-exit shape found), §7.143 (this lib's own
vendor NPC flow uses a plain list/buy_object()/complete_trade()
pattern, not force_me()/add_action() — no swallowed-response shape
found), §7.144 (no one-shot setup guard defeating per-instance setup
found), §7.145 (no broken pre-check wired as a direct verb override
found), §7.146 (no broken /-for-/* comment found — false-positive
grep hits were all ASCII-art map lines/format strings, verified by
reading each), §7.147 (no unchecked function-returns-0-on-missing-
content found in this pass's scope), §7.148 (no parameter named a
reserved keyword like nosave), §7.149 (securityd.lpc's wizlist
loader is a plain read-and-parse, no first-admin auto-promote
bootstrap to check), §7.151 (the one keys(ind_list)[j] hit in
d/qujing/wudidong/npc/yanshu.lpc correctly indexes the keys array by
the loop variable — not the buggy shape), §7.153 (help menpai's
pagination b/q/ENTER all handled correctly, tested live).
Cleaned up: removed the throwaway wukongce test-character save files
(data/{user,login}/w/wukongce.o) and the empty test board save
(data/board/nancheng_b.o, regenerates on first real post) before
committing. Kept the seeded fluffos admin account.
AGENTS.md §7.19 fix: enable_player() reentrancy from init()
feature/command.lpc's enable_player() (wrapper around
enable_commands()) was reachable from an NPC's init() (via the
shared std/char.lpc setup() chain), and enable_commands() is only
safe to call from create() -- calling it again on an object already
living() makes the driver re-invoke that same object's init() as a
side effect, which recursed back into enable_player() on the same call
stack until "Too deep recursion" aborted the boot on a room's first-ever
visit. Fixed with a true reentrancy flag (in_enable_player_now, set at
entry, cleared before every return), NOT a living()-gated guard --
disable_player() in the same file legitimately re-calls
enable_commands() while already living() (sleep/wakeup via
cmds/std/sleep.lpc, revive via feature/damage.lpc), which a
living() guard would silently break. Verified via lpcc --batch
single-file compile check (PASS). Part of the corpus-wide §7.19 sweep
(Batch C).