Immortal Companions' Bond: Zhejiang University Edition

✅ 可玩

仙侣情缘·浙大版

xlqyzdb

🔑 fluffos / Mud@2026 更新 67dd0d2 2026-09-01 源码 下载 ZIP

▶ 开始游玩 · Play Now

仙侣情缘·浙大版由 bugbug 与 alading 在"缥缈水云间"站点基础上二次开发,是一个西游记题材的仙侠世界:从长安城的南城客栈开始冒险,逐步领悟"道行境界""武学境界""法力修为"等修行体系,整体基调偏神话/仙侠而非纯武侠,人物成长围绕"西天取经""大闹天宫"等西游记经典桥段展开,注册流程还有一道"是否为中小学生"的年龄限制问答(回答 no 才能继续),新角色可分配体格/根骨/悟性/灵性四项天赋属性,或直接接受系统默认值;经逐字节比对确认,本档案与本项目另收录的"仙侣情缘(早期测试版)"(xlqy_early)、"新仙侣情缘之飘渺纪元"(xlqy_new2007)公共路径下 84%-86% 的文件完全相同,其实是同一位作者笔下同一个世界观的三个开发快照,而非仅仅共享引擎;与更早的 2001 年"知秋站"存档(xianlvqiyuan)则是更远的同源近亲,文件层面的重合度只有约 10%,与那份存档自身更贴近原始代码的情况相符。

English

Developed by bugbug and alading as a second-generation build on top of the "Misty Waters" server, this is a Journey-to-the-West-themed immortal/mortal romance world: adventures begin at a Chang'an south-city inn, and characters progress through named cultivation stages (Taoist attainment, martial-arts mastery, magical-power cultivation) toward a scripture-seeking, havoc-in-heaven-style arc, with an age-restriction question gating registration. A byte-level file comparison found this shares 84-86% of its common-path files with this collection's "Immortal Companions' Bond (Early Test Build)" (xlqy_early) and "New Immortal Companions' Bond: Age of Mist" (xlqy_new2007) — genuinely the same authored game world across three development snapshots, not just a shared engine. It is a more distant relative of the 2001 "Zhiqiu-station" snapshot also archived here (xianlvqiyuan) — same overall setting and engine lineage, but only about 10% file-level overlap, consistent with that archive's own earlier, closer-to-original codebase.

README

内容亮点

在线试玩

https://mudlibs.fluffos.info/xlqyzdb/

管理员账号 / Admin account

警告:这是一个公开的默认密码,仅供本地/浏览器试玩。正式对外开服前
请务必修改此密码。

本地运行

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

游戏端口:40033

NOTES · 移植与修复记录

xlqyzdb — 仙侣情缘 "浙大版" (ZJU fork)

Archive: 仙侣情缘浙大版.rar. Port: 40033. Status: done (boots clean with a trimmed preload list, full registration flow verified end-to-end including a real Chinese name).

What this is

A modified fork of "仙侣情缘"/XLQY by bugbug & alading at 缥缈水云间 (ZJU — Zhejiang University), circa 2003-4-5, based on a branch closer to xlqy_new2007 (archive #26, chinese.c byte-identical) than to xianlvqiyuan's (archive #38) 2001 original — though logind.c/ master.c/securityd.c/convertd.c all differ from both, so this needed its own diagnostic pass. adm/obj/{master,simul_efun} layout. Archive required nested extraction (.rarxlqy.tar.gztar xzf). ~9,206 raw files, 7,952 after .c.lpc rename.

Fixes applied

1. Standard §15h: is_chinese() single-char CJK rewrite; check_legal_name() bound < 2 || > 12< 1 || > 6, removed the i%2==0 gate (name[i..<0] slice was already clean). 2. §15o insurance: added get_include_path() to master.lpc. 3. §8h recurrence: convertd.lpc's Greek-table stray-backslash typo, 45 occurrences, fixed with the standard sed pattern (no CRLF present). 4. §15g/k-adjacent hardening: adm/simul_efun/file.lpc's cat() hardened against a missing file (same fragile write(read_file(...)) pattern as xianlvqiyuan, though the BANNER file itself was already correctly lowercase in this archive — applied as insurance regardless). 5. A long boot-hang investigation, resolved pragmatically per explicit user direction ("DNS / inet daemon should be generally disabled" / "It's not our target fix this round"): the full boot (with the complete original adm/etc/preload, 24 entries) took an extremely long time — observed via instrumented waits showing the driver spending the overwhelming majority of wall-clock time blocked (e.g. 13 seconds of accumulated CPU time over 9+ minutes of wall-clock), consistent with one or more blocking/slowly-timing-out network calls scattered through preload and early object loading. Initially suspected and partially fixed adm/daemons/network/dns_master (an intermud DNS/mudlist daemon whose create() calls resolve() + socket_create()/socket_bind() to bootstrap a cross-mud database from a hardcoded remote server) — commented out its networking calls and removed it from preload. This did NOT fully resolve the slowness on its own (a full-preload retry still took many minutes), and rather than continue open-ended bisection across the other ~20 preload daemons, applied the pragmatic fix directed by the user: trimmed adm/etc/preload down to only the entries needed for the registration flow itself (securityd, band, virtuald, logind, cmd_d, chinesed, convertd) and excluded the rest (emoted, aliasd, fingerd, channeld, monitord, natured, weapond, rankd, combatd, miscd, spelld, storyd, choosed, dns_master, locationd, feizeid, auto_cleard — combat/quest/ location/social systems, not needed to test registration). With this trimmed list the driver booted in well under 15 seconds. This is a deliberate scope reduction for testing purposes, not a full restoration of the original mudlib's feature set — if this lib is ever run for real play rather than registration-flow verification, the excluded daemons should be re-added one at a time and checked individually for their own preload-time cost/blocking behavior.

New standing policy (see AGENTS.md)

Per explicit user direction, proactively check every future lib's adm/etc/preload for a DNS/intermud/network daemon and exclude it BEFORE the first boot attempt, rather than waiting to discover a boot hang first.

Interactive test result — full registration flow

With the trimmed preload list, verified the complete registration path in one continuous connection (same flow shape as xianlvqiyuan): gbno (not a student) → newxlqzda (English name) → 秦风 (real Chinese name) → accepted, proceeds to "请设定您的密码:".

lpcc sweep

7,952 files, 7,824 pass / 128 fail (98.4%). Failure tail is the usual shape (missing globals/inherits, a handful of syntax typos) — not triaged individually per AGENTS.md §6b/§13. Memory stayed healthy throughout (~14-15GB free).

Re-verification pass (2026-07-23)

Extended the interactive test past the password prompt through full registration (id xlqzdgz, name 秦江), gift confirmation, look, score, and quit — all completed correctly with real content (entered the game world at 南城客栈/"South City Inn" with real NPCs, score rendered the full character sheet, quit showed the real flavor-text quit sequence).

Investigated but not fixed — the "default error message" anomaly is real, reproducible, host-load-dependent, and non-blocking. The transient 你发现事情不大对了,但是又说不上来。(config's default error message) noted as a one-off, non-reproduced anomaly in the sibling lib xianlvqiyuan's NOTES.md reproduces HERE consistently but with a highly variable count per connection (observed 0, 3, and up to 18+ occurrences across repeated identical test runs against the same unmodified code). Instrumented adm/daemons/logind.lpc's enter_world() with temporary write() markers (removed after diagnosis, per the project's established §8c/§15d technique): confirmed the errors are scattered across several *different*, functionally unrelated calls in the same stretch of code (new MAILBOX_OB creation, "/adm/daemons/ipd"->seek_ip_address(), CHANNEL_D->do_channel(), UPDATE_D->check_user()) rather than one single repeated bad call — inconsistent with a single deterministic bug in any one of them. Also confirmed via a temporary instrumented master.lpc's error_handler() (writing standard_trace()'s full text to a scratch file, removed after diagnosis) that the underlying real error text never gets captured — not in debug.log (checked extensively, zero matches even with markers correlating the exact moment), not in the instrumented scratch file either (it was never even created, meaning error_handler()'s own write_file() produced no output during the very runs where the friendly message WAS shown to the player — inconsistent with a straightforward reproducible LPC-level error). Combined with the count varying run-to-run against byte-identical code, this points to a host-load/timing-sensitive artifact (this sweep runs many sibling agents' driver processes concurrently on a shared, resource-constrained host) rather than a deterministic mudlib bug — plausibly related to eval_cost/timer edge cases under CPU contention (see AGENTS.md's set_eval() gotcha for a similar class of timing-sensitive symptom). Not chased further: it never prevented registration, look, score, or quit from completing correctly in any of the ~6 test runs performed (worst case, extra noise the player has to scroll past), and matches the sibling lib's own precedent of being noted-but-not-investigated. Flagging here in more detail than the original one-line note in case a future pass sees it escalate to something that actually blocks a flow — this pass didn't observe that.

Driver rebuild / formatter / WASM pass (2026-07-23)

WASM-enablement pass (2026-07-24)

Standard four-change pass (AGENTS.md §1.3b/§1.3e/§1.5). Gates patched:

1. Loopback always allowedadm/daemons/band.lpc: added a shared is_local_site(site) helper (true for 127.0.0.1, leading 127., empty/non-string, or malformed non-dotted-quad IPs = WASM garbage) and short-circuited is_banned(), is_strict_banned(), and create_char_banned() with it (all three take an IP string; covers the two is_strict_banned login gates in logind.lpc logon()/ encoding() and the create_char_banned/is_banned guest-jail check in enter_world()). 2. IP-format checks bypassed for loopbackadm/daemons/logind.lpc encoding() (~line 190): the !ip_name destruct and the every-char-must-be-digit-or-dot loop over query_ip_number() (both would kill any WASM connection with a garbage IP) now only run when band->is_local_site() is false. 3. Anti-flood throttles exempt loopbacklogind.lpc logon(): (a) the 5-second same-IP reconnect throttle (last_ip + time()+5) and (b) the logon_cnt > 10 per-IP concurrent-connection cap now skip local connections (new is_local flag); (c) the MAX_LOGIN (=5) per-IP multi-login cap in get_id() (~line 360) skips local connections too. 4. Uptime startup gate — none exists (the uptime() use in encoding() only ages out the newid registration-in-progress temp map every 300s); nothing to bypass. The per-id newid in-memory throttle ("已经有人在注册这个id了", cleared every 5 min / on restart) was left as-is — it is per-id not per-IP and only affects re-registering the same id twice within 5 minutes. 5. Admin account seeded — id fluffos, pw Mud@2026, name 浮浮, registered via the real flow (gbnonew → id → Chinese name → password x2 → email → gender m, then the 西游记-style gift screen at first world entry: 9 accept → y confirm). Granted (admin) via adm/etc/wizlist. Verified: banner shows (admin), wizard update /adm/daemons/band recompiles successfully, character finalized (no_gift flag cleared, lands in 南城客栈).

Save files (untracked, NOT gitignored — orchestrator must git add):

Retest: fresh normal registration (id qinfzd, 秦风) works end-to-end (gift screen → world → score renders → quit clean); fluffos login + wizard command works; debug.log has no new errors. One pre-existing, unrelated error surfaced once in a transcript: lazy-loading adm/daemons/emoted hits restore_object(): Illegal mapping format on the archive's own corrupt /data/emoted seed save (§7.7 class; emoted is excluded from the trimmed preload) — cosmetic, command still dispatched, not introduced by this pass. Test char saves removed. Note: id length limit is 3-8 chars (fluffos = 7 fits).

Fail-closed loopback retrofit (2026-07-24)

Security correction, applied retroactively. adm/daemons/band.lpc's shared is_local_site() helper (item 1 above) originally treated an empty/non-string/malformed IP as "local" (fail-open) — a stopgap for a since-fixed WASM driver bug. Tightened to fail-closed:

int is_local_site(string site) {
  if (!stringp(site)) return 0;
  if (site == "127.0.0.1" || site == "::1") return 1;
  if (strlen(site) >= 4 && site[0..3] == "127.") return 1;
  return 0;
}

An unparseable/empty IP now falls through to the normal (non-exempt) path everywhere this helper is consulted — is_banned(), is_strict_banned(), create_char_banned() in band.lpc, and every logind.lpc call site that gates on it (the IP-format check in encoding(), the anti-flood throttles in logon()/get_id()). No other call site needed a separate edit since they all delegate to this one helper. Retested: fresh registration (id xlqztwo, name 秦岭峰) still completes end-to-end via loopback (gift accept → world → look/score render correctly → quit); fluffos login + update /adm/daemons/band still succeeds (重新编译 /adm/daemons/band.lpc:成功!). Zero new runtime errors. Test char saves (xlqzgat/xlqzone/xlqztwo) removed after verification.

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

浙大分支的 XLQY。状态已从过时的 limited 修正——这份档案自己的 README 和 group_note 里从未记录过任何缺陷说明,本轮重新测试也没有发现:XLQY 家族里 xianlvqiyuan 的手足档案。管理员登录干净正常:GB/BIG5 选择→未成年人门槛(否)→id+密码→'您的系统权限目前是:(admin)'。

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

深度功能测试 / Deep functional test (2026-08-07)

第一次完整游玩测试(原生驱动 build)。测试角色 id zdbtest,中文名 云梦仙。本轮 WASM 未重新验证:emsdk 工具链下载硬编码指向 storage.googleapis.com,本次会话的出口代理策略性拒绝该域名(403,用 curl $HTTPS_PROXY/__agentproxy/status 确认是策略拒绝而非临时故障), 本地无法构建 WASM 驱动。

发现并修复:注册流程遗留的 printf("%O", ob) 调试输出(AGENTS.md §7.34 已知模式的又一实例)

adm/daemons/logind.lpc 中文名确认成功分支有一行未加注释的 printf("%O\n", ob);(紧接在 ob->set("name", arg); 之前)。同一档案 里还存在 loggind.lpclogin.lpc 两份带同样调试输出的旧文件,但通过 include/globals.h#define LOGIN_D "/adm/daemons/logind" 确认它们 未被任何地方引用,是死文件,未做改动。按 §7.34 既定修法直接删除 logind.lpc 里的这一行。修复前注册第一个测试账号时确认能看到裸露的 对象路径夹在提示语之间;修复后(本轮实际注册)未再出现。§9 格式化 自检通过({"total":1,...,"unchanged":1,"errors":0})。

发现并修复:emoted 守护进程未捕获的 restore() 抛错,导致该守护进程永久残废(AGENTS.md §7.87 已知模式的又一实例,触发原因不同)

`` 执行时段错误:*restore_object(): Illegal mapping format while restoring emote. 程序:/feature/save.lpc 第 18 行 物件:/adm/daemons/emoted 呼叫来自:/feature/command.lpc 的 command_hook() 第 85 行,... 呼叫来自:/adm/daemons/emoted.lpc 的 create() 第 48 行,... ``

``lpc void create() { if (!restore() && !mapp(emote)) emote = ([]); } `restore() 抛出(而不是返回假值)时,if 语句的 fallback 赋值永远不会执行,emote 全局变量从此在整个进程生命周期里都是 0。 但这次的触发原因和 §7.87 原始实例不同:那次是 maximum read file size(300000)小于存档实际大小(328298 字节);这里 data/emoted.o 只有 262239 字节,本档案的 maximum read file size 同样是 300000,明显没超限——debug.log 报的是Illegal mapping format, 说明这份存档本身的映射语法就是驱动的 restore_object() 解析不了的 真实损坏数据(AGENTS.md §7.7 记录的"损坏存档可以合法地导致 restore() 失败"的另一种损坏形状,不是资源限制)。因为 create() 只在进程内第一次引用该对象时跑一次,且这次触发点是玩家 一次普通的移动指令(不是显式的 emote 指令),debug.log 里也确实 只留下这一条记录,此后同一进程内的 emote 相关调用(smile 等)不 会再重复报错,但 emote 会一直是 0,任何后续 do_emote() 路径 (包括 NPC 自身触发的社交行为)本应像 §7.87 里 xyj20032 那样以 *Value being indexed is zero.` 崩溃——本轮只是恰好没有再触碰到那条 代码路径就先被这次会话中断了,但代码本身的脆弱性和 §7.87 完全一致, 按同样标准视为需要修复的程序 bug。

``lpc void create() { catch(restore()); if (!mapp(emote)) emote = ([]); } ``

测试内容与结果

§7.100 sweep (2026-08-19)

Fixed the corpus-wide inherit ROOM; ... replace_program(ROOM); redundant-replace bug (AGENTS.md §7.100). 231 live occurrences deleted: 230 via scripted sweep (fix_710_room.py), plus 1 hand-fixed roommaker-tool template (obj/roommaker.lpc, simple string-builder variant). 3 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 40033.

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

深度功能测试补测:真实 PVE 战斗 + 商店买卖(2026-08-24)

补齐上一轮标记为"未覆盖"的两项(真实野外战斗、buy 商店购买), 使用管理员账号 fluffos

真实 PVE 战斗

从起始房间〖南城客栈〗沿 west/south/south/south/west/northwest/ west/south 到〖民居〗(d/city/minju3.lpc),kill rat 攻击非陪练 性质的〖大老鼠(Rat)〗。战斗完整回合制展开(拳法/擒拿/腿法攻防轮流 描述、命中/闪避判定、体力状态提示逐级下降),管理员角色自身未受 训练、命中率极低,最终被大老鼠击杀——你死了 → 传送到〖阴阳界〗 死亡之门(d/death/gate.lpc)→ 判官崔珏对话 → 自动"命不该死"判定 → 复活并传送到〖荒郊小店〗,随身携带的不值钱物品掉落一件(预期行 为)。整个死亡→复活流程正确完成,未见任何崩溃或卡死;debug.log 干净(唯一相关记录是下方 emoted 的已知一次性提示,见下)。结论: 真实 PVE 战斗机制(含死亡与复活)功能正常,无程序 bug。

商店买卖(buy/sell,董记当铺 d/city/dangpu.lpc

路径:起始房间 westwest(朱雀大街 → 董记当铺)。用 clone 指令(管理员权限)复制一把〖铜锤〗(value 500)验证 sell/buy 的金额与库存计算:

结论:sell/buy 双向金额与库存计算均正确,无程序 bug。

发现并修复的真实程序 bug:log_file() 未加 assure_file() 保护(AGENTS.md §7.11 已知模式的又一实例)

`` 执行时段错误:*Wrong permissions for opening file /log/nosave/CLONE for append. "No such file or directory" 程序:/adm/obj/simul_efun.lpc 第 17 行 呼叫来自:/cmds/imm/clone.lpc 的 main() 第 56 行 呼叫来自:/adm/obj/simul_efun.lpc 的 log_file() 第 17 行 ``

```lpc void assure_file(string file);

void log_file(string file, string text) { assure_file(LOG_DIR + file); write_file(LOG_DIR + file, text); } `` (文件:adm/simul_efun/file.lpc`。)

已知的一次性无害提示(非新 bug,确认此前修复仍然有效)

south/list 等指令在每个驱动进程生命周期内第一次触发 /adm/daemons/emoted 惰性加载时,仍然出现"错误讯息被拦截:"开头的 restore_object(): Illegal mapping format 提示(本轮死亡战斗与商 店测试各触发一次,分属两个不同的驱动进程)。这与 2026-08-07 深度 功能测试记录的 §7.87 修复(emoted.lpccatch(restore()))预 期行为完全一致——该提示不写入 debug.log(仅出现在玩家屏幕上,与 此前记录的"底层真实错误文本从未被捕获"现象一致),且未阻断任何后 续指令。确认修复仍然有效,非新问题。

AGENTS.md §7.19 sweep (2026-09-01): enable_player() reentrancy from init()

Same corpus-wide bug class as mhxy/wuhanzhan (AGENTS.md §7.19): this lib's feature/command.lpc enable_player() wrapper (around the raw enable_commands() efun) is reachable from an NPC's init() via a redundant create()-then-init()-calls-setup() (or reset_me() calling setup()) chain -- confirmed via a body-aware static scan of every init() in this lib: 43 NPC/item files call setup() directly or via reset_me() from init() (e.g. d/zhangmen.lpc, d/nanhai/npc/zhangmen.lpc, d/xueshan/npc/zhangmen.lpc -- the same zhangmen.lpc family already documented as the original live-reproduced crash on mhxy), after create() already called setup() once (which already made the object living()). Calling enable_commands() a second time on an already-living() object makes the driver re-invoke that object's own init() as a side effect, which re-enters this same chain while the original call is still on the stack -- genuine reentrancy, crashing with "Too deep recursion" (most likely on an NPC's first-ever preload/compile).

feature/damage.lpc's revive() and cmds/std/sleep.lpc's wakeup() both call enable_player() again while the object is still living() (kept alive across the disabled interval by disable_player()'s own internal re-enable_commands()). This confirms a bare if (living(this_object())) return; guard would be the WRONG fix -- used the same true reentrancy-flag fix as mhxy instead: a nosave private int in_enable_player_now; set for the duration of the wrapper's body, guarding only genuine same-call-stack reentrancy while leaving every legitimate re-enable (revive/wakeup/disguise) unaffected. enable_player() had a single fall-through exit (no early returns), so one guard-at-top + one clear-at-bottom pair was sufficient. Verified via a single-file lpcc --batch compile check (PASS) -- not individually live-boot-tested.