Extraordinary Sunset MUD

✅ 可玩

非凡夕阳MUD

ffxymud

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

▶ 开始游玩 · Play Now

非凡夕阳MUD,登入横幅另称"非凡公子·新月传奇录II",是"天涯"世系金庸武侠 MUD 家族的一支,与 `tybxjh`/`xhcii`/`zxty`/`jhfy2`/`xysylmhb`/`xyzxiiylzymh`(以及局部改写过的 `wlhd`)共享同一套金庸门派/地图世界观——华山、武当、峨嵋、丐帮、明教、天龙寺等门派场景齐全,"世界之巅"跳崖场景等内容逐字节一致。本档案独有客户端版本校验握手(连线后第一行须为固定字符串),是这一支"天涯"分支里独有的接入门槛,其余手足档案都没有这个要求。

English

Part of the "Roaming Swordsman" engine lineage, though its own login banner instead reads "Extraordinary Young Master: Legend of the New Moon II." Despite the differing names, its map and key scenes are byte-identical to several sibling archives in this project, confirming it belongs to a shared family built on Jin Yong's sect and map world (Huashan, Wudang, Emei, Beggars' Sect, Ming Cult, Tianlong Temple, and more). Uniquely requires clients to send an exact version-handshake string before login proceeds.

README

内容亮点

注册流程(客户端版本校验)

连线后驱动会先要求输入一个固定的客户端版本字符串 shikongyouxia3.0(明文比对,不是加密握手),验证通过后才继续 问英文名字。之后是标准流程:英文名字(键入 new 会被要求重新输 入——本档案是直接检测重名后自动进入新建流程,不需要额外键入 new)→ 确认建立(y/n)→ 中文名字(回车可用系统随机名)→ 密码 (≥5 字元)→ 确认密码 → 天赋选择(0 随机 / 1-4 指定单项数值)→ 接受天赋(y/n)→ 电子邮件地址 → 性别(m/f)。

本次修复的关键 bug

管理员账号 / Admin account

管理员名单存储在纯文本文件 adm/etc/wizlist 里;账号本身通过正常 注册流程创建。

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

本地运行

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

游戏端口:40142

NOTES · 移植与修复记录

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

时空游侠录(Roaming Swordsman)血统代码库,登入横幅为"非凡公子/新月传奇录II"。没有发现 mudlib bug——WASM 下开箱即用,全程零修复。唯一的门槛:get_id() 要求 id 提示后的第一个输入必须是字面字符串 "shikongyouxia3.0"(固定魔术字符串校验,不是加密),才会进入真正询问英文 id 的环节——不先发这个,每次注册尝试都会看到"你的客户端版本有问题"并断线。排版格式化工具的第三类盲点检查(CJK 重新加空格)在约 25 个"误报"里抓到了两处真正的损坏(d/huashan/map.lpc,一张 ASCII 地图,形态和 cctx 的一样;d/player/fyue_room.lpc,一段房间描述,格式化工具的分词器在这里失去同步)——两者都直接还原,没有手工修补。另外记录但没有修复:d/city/sj.lpc(一个无法到达、没有任何房间连向它的孤立房间)通篇有转档之前就存在的缺引号问题(该用字符串字面量的地方写成了裸词中文,比如 add_action(do_out, out) 应为 add_action("do_out", "out"))——如果真被加载会编译失败,但因为没有任何路径能到达这个房间,从不影响注册/游戏/退出流程。管理员账号(fluffos/Mud@2026)通过真实注册流程 + adm/etc/wizlist 播种。

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

深度功能测试 / Deep functional test (AGENTS.md §10.7)

原生驱动(~/src/fluffos/build/src/driver,linux-debug + ASAN/UBSAN)从档案根目录启动,端口 40142 正常监听。WASM 验证跳过(emsdk 被代理拒绝,与其他档案一致,未在 WASM 下复测)。

"天涯"血统关系确认:本档案地图/门派内容与 tybxjh/xhcii/zxty/jhfy2/xysylmhb/xyzxiiylzymh 逐字节一致(README 已记录),AGENTS.md §7.68/§7.86/§8.9 的既有条目也已把 ffxymud 列为这条"天涯"血统的确认成员之一。这不是本轮任务描述里猜测的"夕阳再现/XYZX"(xyzxfy2/ylfyxa3/xajhzcjh 那条,§7.11 反复确认的血统)——AGENTS.md §11 的血统地图把"夕阳再现 family"(xyzxfk/jhfy/xajhzcjh 等)和"XYZX/炎龙封印 branch"(xyzx3/ylfyxa3/longyunmeng)列为两条独立血统,都不含 ffxymudffxymud 真正所属的"天涯"家族在 §11 里尚未有专门条目,但在 §7.68/§7.85/§7.86/§8.9 的正文叙述里反复被引用为独立于两者的第三条血统。误报的原因很好理解:三个家族名字/主题(夕阳、天涯、XYZX)字面上都容易联想到一起,但 master.lpc/logind.lpc 的具体实现互不相同。

发现并修复的 PROGRAMMING bug

1. log_file() 缺少 assure_file() 目录保护(AGENTS.md §7.11 的又一确认实例,"天涯"家族第 N 次独立确认同一形状)adm/simul_efun/file.lpclog_file() 直接 write_file(LOG_DIR + file, text),全新检出 work/log/ 下没有任何子目录。adm/daemons/logind.lpcget_gender()新角色注册流程的最后一步)紧跟着 enter_world() 之后调用 log_file("login/newid.log", ...)login/ 子目录不存在时抛出未捕获的 *Wrong permissions for opening file /log/login/newid.log for append。已确认这不会中断注册本身(enter_world() 已经先执行),但会在每一个全新角色的登录时刻在 debug.log 里留下一条未捕获运行期错误。修复:补上 assure_file(LOG_DIR + file);;因为同档案内 assure_file() 定义在 log_file() 之后,额外加了一行前向声明(AGENTS.md §6.5)。已用真实注册复现(修复前,见下方"实测过程")并复测通过。 2. config.fluffosmaximum evaluation cost 过低,导致注册流程 get_email()→make_body() 首次冷编译 /clone/user/user 整条继承链时中止(AGENTS.md §7.90 的 xyj2000f/xiyouji450 变体:make_body() 而非普通移动触发):本档案沿用项目模板默认值 700000,但 /clone/user/user 首次加载要拉入 inherit/char/char.lpc 及其近十个 feature/*.lpc 全部编译,加上 securityd.lpc 的 ACL 检查,实测每一次全新注册(新驱动进程、从未编译过这条继承链时)都在 get_email() 里以 *Too long evaluation. Execution aborted. 中止——连线对此没有任何提示,直接停在邮箱提示之后不再响应。修复:仿照项目里 30+ 档案的既有做法,将 maximum evaluation cost 提到 5000000。已用两次独立的全新驱动进程复现(修复前必然触发)并复测通过(修复后零次触发,含完整注册+战斗+死亡+退出+重连的整场会话)。 3. adm/daemons/logind.lpcget_resp()/get_name() 各有一处 printf("%O\n", ob) 调试输出泄漏给玩家(AGENTS.md §7.34 的又一确认实例,"天涯"家族里 jhfy2 的第九个 §8.9 实例报告里提到的"双路径 printf 变体"这次落在了取中文名字这一步,而不是 §8.9 那一行):玩家刚输入完中文名字,屏幕上会直接看到形如 /clone/user/login#4 的原始物件路径。已确认删除两处 printf 调用不影响任何后续逻辑(ob->set("name", ...) 紧跟在后面,未使用 printf 的返回值)。已用真实注册复现(修复前,见"实测过程")并复测通过。 4. 新角色食物/饮水初始化门槛读了错误的物件(AGENTS.md §8.9 的第十个确认实例,"天涯"家族第二例——jhfy2 是第九例)enter_world()if (ob->query("age") == 14)ob 是登录用的临时连线物件,全档案没有任何地方对这个类别 set("age", ...);真正的角色物件 user 才会在 setup() 里被设成 14 岁(race/human.lpcsetup_human())。门槛永远为假,新角色的 food/water 从未被 max_food_capacity()/max_water_capacity() 初始化过。有意思的一点:和 jhfy2 一样(笔记里已记录"某些默认值另外把 food/water 填满了"),本档案的新角色在 score看起来食物/饮水是满的——这是 race/human.lpcsetup_human() 里另一段独立逻辑(if (ob->query("mud_age") < 100) { if (!ob->query("water")) ob->set("water", ...); ... })顺带把新角色的 food/water 也填满了,掩盖了这个 bug 本该造成的"新角色一进游戏就挨饿"的直接症状——但 bug 本身(enter_world() 里那处门槛永远为假)依然是真实存在、需要修的逻辑错误:一旦这两个初始值被吃掉/清零(例如死亡+复活,或者未来任何一次修改动了 race/human.lpc 那段兜底逻辑),角色将永远无法再通过任何正常游戏内机制重新把食物/饮水填满,因为该走的那条初始化路径永远不会被触发。修复:ob->query("age")user->query("age")。已用真实注册(score 复测确认食物/饮水两个条都能正确显示部分消耗后的中间值,证明修复后的门槛函数本身被正确执行且没有引入新的满/空判断错误)验证。 5. tribar_graph() 的字节切片假设 UTF-8 字符仍是 2 字节宽(AGENTS.md §7.85 的又一确认实例,"天涯"家族原始实例)cmds/usr/score.lpc/cmds/usr/sc.lpc(玩家 score/sc 指令,逐字节相同的两份拷贝)和 cmds/adm/score2.lpc(管理员 score2 指令,25 格变体)三处 tribar_graph() 都残留 GBK 时代"每格 2 字节"的 *2 乘数,转成 UTF-8(每格 3 字节)后:val=0bar_string[0..-1] 按本驱动的负索引惯例返回整个字串(空条显示成满条),且任何超过约 50% 的比例都会因切片越界被钳制成满条。这个 bug 和上面第 4 条互相掩盖:即使 §8.9 没被修,新角色食物/饮水条也会因为这个 bug 显示成满的;即使 §8.9 修复正确,食物/饮水条在真正接近满值之前也无法用来判断真实剩余量。修复:改写成不做字节假设的整数区间计算(filled/shown 两段直接按目标宽度线性换算,参考 AGENTS.md §7.85 记录的标准写法)。已用真实 score/sc/score2 三个指令复测:满值、约 94%(15/16、24/25 格)等中间值均正确显示部分消耗后的条形,未再出现"空/满不分"或过早封顶的现象。cmds/usr/skills2.lpc 也有一份同名函数,但实现方式完全不同(逐格 for 循环,未复用 bar_string/*2 那套算法),未发现同类问题,未改动。 6. cmds/adm/setskill.c.org 丢失了正常的 .lpc 扩展名,管理员技能捷径指令实际不存在(AGENTS.md §7.94 的又一确认实例,此前已在 xyzx3/dtxywzxzb 独立确认):唯一候选文件(没有互相竞争的草稿版本,不涉及内容取舍),直接复制恢复为 cmds/adm/setskill.lpc。恢复后同时暴露了该文件自身的一处独立 copy-paste bug(同样是 xyzx3/dtxywzxzb 已确认过的形状):非 all 的单一技能删除分支(level==0)里 write() 用的是只在 all 分支才赋值的 skills[i]setskill <目标> <单个技能> 0 必定触发 *Value being indexed is zero 崩溃。已在同一次修复里改成引用真正的局部变量 skill。已用真实指令复现(对 官兵 NPC 先 setskill bing unarmed 0,见"实测过程")并复测通过,setskill <目标> unarmed <正整数> 的正常设置分支和 all 分支均未受影响。

实测过程(同一连续会话)

1. 读 README 记录的注册流程(登入横幅另称"非凡公子/新月传奇录II"),确认唯一门槛是连线后第一行必须原样输入字面量 shikongyouxia3.0(伪装成"请输入您的英文名字"提示,实际是 get_id() 的固定字串比对),验证通过后才真正问英文 id。 2. 全新注册真实中文名角色"夕阳侠客"(id xiyangke),走完整流程:shikongyouxia3.0 → 英文名 → 确认建立 → 中文名 → 密码(≥5 字元)→ 确认密码 → 天赋(0 随机)→ 接受天赋 → 邮箱 → 性别。修复前在 get_email()make_body() 首次冷编译时以 *Too long evaluation 中止(§7.90,见上);修复并重启驱动后完整走完,MOTD、权限、上次登录时间正常显示,look/score/i 均正常。中文名提示后修复前会看到裸露的 /clone/user/login#N 调试输出(§7.34,见上)。 3. 战斗机制:cmds/std/fight.lpccan_speak 判定安全切磋 vs 直接开打(obj->query("can_speak") 为真 → 走 accept_fight()/fight_ob();否则走 kill_ob()),与本次会话在其他"天涯"血统档案里反复确认的变体一致。对新手客栈"店小二"(可讲话 NPC)使用 fight,正确触发协商对话,NPC 因未实现/拒绝切磋而回绝——机制本身(分支、notify_fail、无崩溃)验证通过。对北门"官兵"NPC 使用 fight 意外导致测试角色真正死亡——深入代码后确认 d/city/npc/bing.lpcaccept_fight()有意设计成"接受挑战但立刻反手用 kill_ob() 真打并给挑战者加上 500 回合通缉标记"的陷阱型 NPC(城市守卫,属于既有游戏内容设计,非程序 bug,已记录在下方"既有设计"一节)。改用 d/city/npc/mu-ren.lpc(练功木人,no_dieaccept_fight() 会把自己的技能/属性同步成挑战者本人的等级)复测:连续多回合仅造成个位数伤害、<气> 条缓慢下降但远未见底、期间多次"你的「基本拳脚」进步了",证实安全切磋机制在真正的安全 NPC 上工作正常。 4. 技能/门派获取,两条路径都验证: - 正常游戏内路径:管理员 goto /d/baituo/liangong,对无条件接受的"李教头"(d/baituo/npc/li.lpcattempt_apprentice() 无门槛)使用 apprentice li,成功拜师;score 确认头衔变为"白驼山派第三代弟子"、师父字段变为"李教头"。 - 管理员捷径:恢复出的 cmds/adm/setskill.lpcsetskill fluffos unarmed 50/dodge 40 成功设置(过程中发现并修复了上面第 6 个 bug 里的单技能删除崩溃,用 setskill bing unarmed 0 复现/复测)。cmds/wiz/cmds/adm/cmds/arch/(本档案没有 cmds/arch/ 目录)范围内没有找到专门的"直接授予门派"指令;最接近的捷径是通用的 force <NPC> to recruit <玩家id>(借用 apprentice/recruit 同一套底层逻辑,而不是绕过它),如实记录未发现专门的门派授予指令。 5. quit 宽限期:全档案 grep quit_time 只在 logind.lpc 的重连门槛里被读取,从未被任何地方 set() 过(永远读到 0,门槛恒假,属于死代码而非会阻塞操作的 bug,记录在下方"既有设计"一节),未发现"新号退出即删号"逻辑,cmds/usr/quit.lpc 本身没有基于账号年龄的二次确认。因此对已复活的 xiyangke 直接发送真实 quit 指令(未使用"掐断连线模拟"这条备用路径):非自动装载物品被正确丢弃、欢迎下次再来 正常显示、存档时间戳与退出时刻相符。 6. 等待真实时钟间隔(期间做静态代码审查、grep 血统关系、撰写本节笔记,实际间隔数分钟,驱动累计运行 30+ 分钟)后重新连线登录 xiyangkelook/score/i 确认角色位置、门派归属(未加入门派,符合预期)、死亡计数(1 次,符合修复前"官兵陷阱"那次真实死亡)、装备/背包状态全部正确持久化。 7. grep 全程 log/debug.log(本档案 log directory 配置解析到档案根目录下的 log/,而不是 work/log/——两处都检查过,本档案实际写入的是前者;work/log/logwork/log/login/* 是 mudlib 自己 log_file() 写的业务日志,不是驱动的 debug.log):修复后的整场会话(含 2 次驱动重启、多次注册、战斗、死亡、setskill、拜师、quit、真实间隔重连)里未再出现任何未捕获运行期错误。

发现但判定为既有设计、未改动的现象

已清理

深度功能测试(2026-08-12,round two,新驱动重测)

Re-tested against the freshly-rebuilt build-debug/src/driver (post corpus-wide quest_times/win_times %-operator fix + the Warning/warning driver-text revert).

发现并修复的 PROGRAMMING bug

1. log_error()adm/obj/master.lpc:140)完全没有严重度检查(AGENTS.md §7.34-class,与本轮此前 wdxtym 上修的形状完全相同——不是"算出 severity 但没真正拿来 gate",而是压根没有算)if (this_player(1)) efun::write("编译时段错误:" + message + "\n");所有诊断信息(包括最普通的编译警告)无条件转发给当前连线的 任何玩家,不区分是巫师还是普通玩家,也不区分警告还是错误。真实复现: 一次全新驱动进程下的全新注册(首次冷编译级联,触发大量 Unknown #pragma, ignored./Unused local variable 一类的纯警告), 新角色一进游戏前就先看到十几行原始编译诊断刷屏。修复:改成 if (this_player(1) && strsrch(message, "arning:") == -1) efun::write(...) (沿用本轮项目统一的大小写无关 "arning:" 判断惯例)。已用同一新 驱动进程下的第二次全新注册复测:debug.log 时间戳在整场会话前后 均未变化(无新错误),且屏幕上不再出现任何编译诊断刷屏。

Proactive checks(无需改动)

实测过程

新号 chenmufb/沐辰:shikongyouxia3.0 → 英文 id → 确认建立 → 中文名 (无 y/n 确认,直接进密码环节,与档案内 get_name() 非空分支的实际 实现一致)→ 密码(≥5 字元)→ 确认密码 → 天赋(0 随机)→ 接受 → 邮箱 → 性别,完整走通进入"北疆小镇"起始区域。修复前后各做一次全新注册; 修复前复现了上述编译诊断刷屏,修复后干净。log/debug.log 全程时间戳 未变化,确认无新增未捕获运行期错误。驱动最终按精确 PID kill,ps -p 确认已退出。

已清理

§7.100 房间基类 replace_program() 扫尾修复(2026-08-19)

ROOM 宏(/inherit/room/room)在本档案 2,087 处房间文件的 create() 里紧跟 inherit ROOM; 之后又多余调用了一次 replace_program(ROOM);——AGENTS.md §7.100 记录的同一个休眠 bug。 用 fix_710_room.py 扫过 work/,删除 2,085 处标准形状(净删 2,087 行,2 个文件各含 2 处);clone/misc/roommaker.lpcd/huanggon/obj/roommaker.lpc(两份独立的房间建造工具字符串拼接 模板)各剩 1 处 str += "...replace_program(ROOM);..." 变体,手工 改成 str += "\n\tsetup();\n}\n";。两份工具的 heredoc 模板本身已 经是干净的(没有这个 bug)。修复后 work/ 下 0 处存活残留,其余 均为转档之前已注释掉的 // 行,原样保留;work/data/ 下没有真实 .lpc 源码命中。git diff --stat 显示 2085 个文件净删 2085 行、 增 2 行,与脚本自报数字 + 2 处手工编辑吻合。

驱动干净启动(零新增编译错误、端口 40142 正常监听、debug.log 无任何"cannot replace"/"cannot bind"行)。管理员 fluffosshikongyouxia3.0 魔术字符串 + 密码 Mud@2026)实机登录成功, look/score/quit 均正常,全程 debug.log 保持干净。管理员存 档的 last_on 时间戳漂移已用 git checkout HEAD -- 还原,未提交。 驱动按精确 PID 结束。

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

深度功能测试(2026-09-04,shop)

Live pass, port 40142. Prior §10.7 already did 白驼山李教头 apprentice li; this round is shop only. First send is the magic string shikongyouxia3.0, then id fluffos / Mud@2026. Current admin score shows 普通百姓 / 目前还没有师傅 (the 白驼山 title from the earlier pass is not on this save). F_DEALER 丐帮 refuse would not apply.

Shop: goto /d/city/zuixianlou. clone /clone/money/gold then buy jitui succeeded (“你向店小二买下一根烤鸡腿”, inventory 鸡腿 + 99 两银子 + 20 铜板). list initially crashed after printing 烤鸡腿: shanzi.lpc calls init_shan(250, 2) while inherit SWORD only defines init_swordUndefined function init_shan then *No program in object. Fixed both copies (d/city/npc/obj/shanzi.lpc is the vendor path; d/city/npc/shanzi.lpc is a duplicate) to init_sword. After update, list shows the full board (鸡腿/扇子/酒袋/包子/养身丹) with no compile error.

No other code change.