Jianghu 2006

✅ 可玩

江湖2006

jh2006

🔑 fluffos / Mud2026Pass 更新 26ed1b3 2026-09-01 源码 下载 ZIP

▶ 开始游玩 · Play Now

游戏内标题为"江湖2005"(Annihilator/书剑 ES2 血统,与本项目中的 `sjplgfjxb`/`sjplii` 同宗,但与 Century/adm-single 家族的 `sjecl`/`sje` 是完全不同的谱系),金庸题材武侠 mudlib。地图除华山、武当、少林、峨嵋、崑仑、丐帮等常见门派场景外,还有独立的"苗疆"(`d/miaojiang/`)区域和"星宿"(`d/xingxiu/`,取自《天龙八部》星宿派)门派场景;在华山"正气堂"可以完整拜岳不群为师、成为"第十四代弟子"。这份档案自己的命名习惯也颇具特色:查看人物状态用的是 `stscore` 而非常见的 `score`,移动指令则是"方向-地点全名"式的复合字符串(如"西-小石路")而非单独的方向字。

English

The in-game title is Jianghu 2005 (Annihilator/Book-and-Sword ES2 lineage, sharing engine ancestry with this project's sjplgfjxb/sjplii but unrelated to the similarly-named Century-lineage sjecl/sje), a Jin-Yong-themed wuxia MUD. A Miaojiang (苗疆) region sits alongside the usual Wudang/Shaolin/Emei/Kunlun/Beggars'-Sect map, plus a Xingxiu Sect (星宿, the "Star-Reaping Cult" of Demi-Gods and Semi-Devils) directory; sect apprenticeship works end to end at Huashan's Hall of Uprightness, where NPC Yue Buqun accepts a player as his "14th-generation disciple." Two of this lib's own naming quirks: character status is checked with stscore rather than the usual score, and movement commands are compound direction-plus-destination strings (e.g. "西-小石路", "west–stone path") rather than a bare direction letter.

README

内容亮点

注册流程

连线后先回答 BIG5 字体问题(y/n,本次未发现编码问题)→ 英文 ID (3-8 个小写英文字母)→ 确认建立新角色(y/N)→ 密码(至少 5 位, 须同时包含数字和大写英文字母)→ 确认密码 → 中文名字(2-4 个汉字) → 天赋数值(输入 0-4 选择自订某一项,或输入 0 由系统随机产生,随 后需再输入 y 同意这组数值)→ 性别(m/f)→ 进入游戏世界。

注意:这份档案查看人物状态的指令不是常见的 score,而是 stscore

本次修复的关键 bug

管理员账号 / Admin account

管理员名单存储在纯文本文件 adm/etc/wizlist 里;账号本身通过正常 注册流程创建,已在游戏内确认 "您目前的权限是:(admin)" 显示正确 (会自动带到"巫师休息室")。

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

本地运行

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

游戏端口:40128

NOTES · 移植与修复记录

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

ES2/金庸血统 mudlib(Annihilator/书剑衍生),游戏内标题为"江湖2005/江湖2006"(书剑主题)。修复的 bug:(1)adm/daemons/securityd.lpc 的 valid_read() 里经典的 §7.59 this_player() 顶替 root 身份 bug——开机时立刻就会崩溃报"*Read access denied.",出现在 logind.lpc 的 gb_big5() 第一次惰性编译 BAN_D->is_banned() 的时候,英文名字提示根本还没出现;已仿照既有模式,把 func=="load_object"/"include" 排除在 this_player() 覆盖之外来修复;(2)adm/daemons/logind.lpc 里经典的 §8.1 GBK 字节区间 is_chinese() bug 加上 check_legal_name() 字节数没减半的长度界限(4/8 字符,i%2 门槛→2/4 字符);(3)adm/daemons/commandd.lpc 的 rehash() 里经典的 §8.3b 死指令表 sscanf——"%s.c$" 在改名成 .lpc 之后再也匹配不到任何东西,导致整个指令表变成空的;look/quit 碰巧还能用(是靠别处的 add_action 定义或者驱动内建),但包括查看状态用的指令(stscore,不是 score——这份档案不用"score"这个词)在内的其它所有指令都会落到"什么?",直到修好为止。管理员账号(fluffos/Mud2026Pass)通过真实注册流程 + adm/etc/wizlist 播种,游戏内"您目前的权限:(admin)"显示确认生效(自动带到巫师休息室)。完整的注册→look→stscore→quit 流程在排版格式化前后各验证过一次,用的是真实中文名字——注意这份档案的注册顺序是 id→y/N 确认→密码→确认密码→中文名字→天赋数值→性别,不是其它档案更常见的 id→确认→中文名字→密码那种顺序。

§10.7 深度功能测试(本次新增)

此前只做过注册→look→stscore→quit 的浅层验证。本次用管理员 fluffos 和一个全新普通角色 tester 实际走通了注册→look/i/stscore→武馆内移 动→沙袋练功的流程,并在源码层面逐一核对了 AGENTS.md 里已知的 bug 类别。

修复:d/death/gate.lpc 的 §7.68 复活软锁

run(object ob) 原代码 if (!ob || !present(ob)) return; 把"鬼魂对 象已经不存在了"和"鬼魂此刻只是暂时不在这个房间里"混为一谈,一旦判 定瞬间鬼魂碰巧不在场就永久放弃后续引导(送去 gateway/mpting/ pusadian),把鬼魂永久卡在鬼门关。按标准修法拆开:!ob 才是真正 放弃,!present 改为 1 秒后重试。已用管理员 update 指令热编译验 证无编译错误;受限于本次时间和城门 NPC(见下)没能实际找到活的怪物 触发真实死亡,未做端到端的活体复活验证——但这个修法和本项目里已经 反复验证过几十次的同一形状完全一致,可信度很高。

检查、确认不适用的已知 bug 类别

一个不是 bug 的测试方法论坑:连线后不主动打印任何东西 + tmux 中文乱码

logind.lpclogon()input_to("gb_big5", ob) 注册回调,自 己不 write() 任何东西——欢迎横幅和"您的英文名字"提示都是在 gb_big5()回调里才打印的,也就是说这份档案要求连线后先随便送一 行(哪怕是空行)触发第一次回调,才会看到任何东西。用裸 socket 或 tmux_mud.shread(不先 send)会一直卡着直到驱动自己的 30 秒 time_check() 超时断线("您花在连线进入手续的时间太久了"),一 度以为是驱动挂死,实际上只是没有先送空行。另外:本档案的出口键是 "方向-地点全名"这种复合字符串(如"西-小石路",不是单独的"西"或 "west")——用 tmux_mud.sh 送这类中文复合字符串偶尔会被 tmux send-keys -l 弄乱(已知限制,见 memory),改用 mudclient.py 直连 后确认输入完全正常、移动指令本身没有问题。

未能完成的部分(诚实记录,非"未发现")

新角色离开武馆的正常门槛是 combat_exp >= 3000 且完成馆内的拜别仪 式(d/wuguan/menlang.lpcvalid_leave()),本次时间关系没有练 到这个门槛;改用管理员账号 goto 到武馆大门外(/d/xiangyang/ damen)试图找一个可以安全测试战斗/死亡的目标,但该房间 objects 里唯一挂载的 npc/menwei(武馆门卫)在两次不同别名尝试下都返回"这 里没有这个人"——没有确认是编译失败还是别的原因,未深入排查。因此本 次没有做成活体的战斗/死亡/复活验证,gate.lpc 的修复只做到编译层 面确认,记录在案供未来 pass 补做。

更正(2026-08-05):§7.68 复活软锁"修复"已撤销

上面提到的"鬼魂离开/不在场时被永久放弃复活流程"曾被当作 AGENTS.md §7.68 记录的一类 bug 修复(把单次判定改成每 5 秒重试)。经用户指出并 重新审视:这更可能是有意的游戏设计,不是 bug——大多数这类档案里 鬼魂根本无法自行移动,所以"不在场"要么从未真正发生,要么是"离开去 在阴间游荡,想回来时再走回这个房间、流程会通过 init() 重新从头开始" 这种有意为之的宽松机制,而不是需要强制追上玩家的错误。强行重试还可能 引入新问题:如果鬼魂之后又走回这个房间,旧的重试和 init() 重新触发的 新一轮流程可能同时运行,导致对话重叠错乱。已把这处改动撤销,恢复成 原始的 if (!ob || !present(ob)) return; 单次判定写法(bmxkx2001 除外——那份档案里这确实是一个真实存在、经过实际复现验证的 bug:鬼魂 本身完全无法移动,是另一个不相关的 NPC 强行把鬼魂拖走导致的)。详见 AGENTS.md §7.68 顶部的撤销说明。

Deep functional test round two (2026-08-14)

Independently re-verified against current code rather than trusting the round-one writeup above. All 3 prior fixes confirmed still present. Found and fixed a real, 100%-reproducible §7.90-class eval-cost crash — this one hit enter_world() itself and, because it left the admin character with no environment, cascaded into a SECOND crash at quit.lpc's message() call, which looked like a new, unrelated bug at first.

New fix: §7.90-class eval-cost abort during enter_world() — root cause of a second, seemingly-unrelated quit.lpc crash

First admin login this pass never printed a room description at all — went straight from the password prompt to a bare > prompt, with update/stscore both still working (command dispatch was fine) but quit crashed: *Bad argument 3 to EFUN message() at cmds/usr/ quit.lpc:113 (message("system", ..., environment(me), me)). work/log/debug.log showed the actual root cause a few lines earlier: Eval interrupted: object clone/user/user#2 cost limit reached, limit: 700000 usec during enter_world() itself (adm/daemons/updated.lpc's create()cmds/arch/gift.lpc's create()restore()) — the abort happened before enter_world() ever move()d the character into 巫师休息室, leaving environment(me) permanently 0 for the rest of that session, which is exactly why the LATER quit crashed on an unrelated-looking line. Fixed with the established remedy: config.fluffos's maximum evaluation cost : 7000005000000. Verified: fresh reboot, first login landed cleanly in 巫师休息室 (the real starting room, not silently void), and the identical quit that crashed before now completes cleanly.

New fix: cmds/imm/update.lpc — the same present(environment(me)) crash class, a third path variant

This lib's wizard reload command lives at cmds/imm/update.lpc (not cmds/wiz/ or cmds/adm/, the two path variants already found and fixed on other libs this session) — same vulnerable shape, same fix. Finding this led to a corpus-wide check: 21 more libs share this exact path, fixed separately (see the dedicated corpus sweep: cmds/imm/update.lpc commit). AGENTS.md §7.106 updated to note all three known path variants.

New fix: adm/simul_efun/file.lpc's cat() had no null-guard

Standard proactive hardening; log_file() already correctly delegates to LOG_D->log_file(), which already calls assure_file().

Considered and rejected: adm/single/master.lpc's log_error() missing assure_file()

Unlike logd.lpc's log_file(), the real master file's log_error() writes directly with no assure_file() guard. Initially added one, then reverted: log_error() can fire during the simul_efun object's OWN compile (the function explicitly guards its file_owner() call with find_object(SIMUL_EFUN_OB) for exactly this reason), at which point assure_file() — itself a simul_efun — might not be callable yet, making the "fix" a plausible NEW crash rather than a safe hardening. The practical risk is low anyway (work/log/ already exists as a tracked directory, and the player-facing broadcast is already commented out, so no §7.10-class leak risk either) — left as-is rather than risk an unverified speculative change.

Re-verified: all 3 round-one fixes still hold

> 4` codepoint range (not the old byte-doubled 4/8 range).

Verification method

Booted native build-debug driver, admin login (fluffos/ Mud2026Pass) — this lib's login flow requires sending a blank line first to trigger the banner (logon() only registers an input_to callback, doesn't write() anything itself — a documented test- methodology note from round one, still holds). update /cmds/imm/ update and stscore as privileged-action/command-table checks. Two full rapid reconnects post-fix, both clean. Driver killed by exact PID after each reboot (three total: initial boot where the crash was found, one aborted reconnect during the config edit, clean reboot for verification); incidental fluffos.o save-timestamp churn reverted before commit.

Files modified this pass

§7.100 sub-threshold instance (2026-08-20)

Found during the §7.100 tail-sweep (below the original 166-lib survey's

=100-occurrence threshold, never checked). Same lineage/shape as

bxsj/sjecl/sjtx2/shujian2008/shujian3 (shared data/group/groom/*.lpc cluster), but a different room set: 15 live replace_program(ROOM); occurrences (data/group/groom/*.lpc, d/city/dayuan.lpc, d/mr/hc2.lpc/hc3.lpc/hc4.lpc, d/kunlun/lang2.lpc). No roommaker.lpc factory-bug variant. Verified via a clean native driver boot (zero new debug.log errors, port listening, killed by exact PID after ~8s).

Round four (2026-08-20): menwei mystery solved, first-ever live combat/death/resurrection, §7.78 confirmed, §7.112 found and fixed

Prior rounds never got past the combat_exp >= 3000 starting-hall gate and were blocked by an unexplained "这里没有这个人" (no such person here) when trying to interact with the one NPC (npc/menwei) at the hall's outer gate room (/d/xiangyang/damen). Root-caused and resolved this pass, then used the fixed NPC's room to reach a real live death/resurrection cycle for the first time in this lib's testing history.

Root cause of the menwei mystery: a real compile error, not a wrong alias

d/wuguan/npc/menwei.lpc:463 had a stray full-width Chinese period () placed OUTSIDE a string literal, right before an orphaned \n:

// BEFORE (byte-for-byte, confirmed via python3 repr()):
message_vision( sprintf(HIW "武馆门卫交给$N一块武馆令牌"。\n" NOR), me);

The lexer chokes on the individual UTF-8 bytes of the stray (Illegal character 0xe3/0x80/0x82), then chokes again on the bare \n outside a string, cascading into a second, unrelated-looking error at line 467 ($var illegal outside of function pointer) from the resulting string/quote misalignment. This is a genuine compile error that has blocked menwei.lpc from compiling at all, apparently since this file was last touched — confirmed live: a fresh goto /d/xiangyang/damen on an unpatched driver throws *No program in object '/d/wuguan/npc/menwei'! uncaught from inside d/xiangyang/damen.lpc's own create() (which new()s every entry in its objects mapping during setup()), which means the entire /d/xiangyang/damen room fails to load, not just the NPC — and this room is a normal, ungated city street any player can walk through on the way into or out of the wuguan district, not an admin-test-only path. Fixed by moving the closing quote to the correct position:

// AFTER:
message_vision( sprintf(HIW "武馆门卫交给$N一块武馆令牌。\n" NOR), me);

Live-verified: update /d/wuguan/npc/menwei recompiles clean, and a fresh goto (after also update-ing the room object, since the room's earlier crashed instance stayed resident in a half-initialized state until forced to reload — a red herring worth remembering: after a compile-error fix, force-reload the ROOM too, not just the fixed file, or a stale broken instance can persist and make the fix look like it didn't work) loads 武馆门卫(Men wei) into the room correctly, responds to look/ask, and a completely fresh driver boot from scratch compiles this file with zero errors and the NPC is present and interactive on the very first visit.

First-ever live combat → death → resurrection cycle

Registered a fresh throwaway character (testnine, 测武生), used the admin summon command to bring it (bypassing the hall's exit gate, which only applies to normal valid_leave() walking) to /d/xiangyang/eroad1 (/d/xiangyang/damen itself has no_fight set, so combat had to happen one room over) alongside a wandering beggar-sect NPC (kungfu/class/gaibang/qigai.lpc, combat_exp ~43000+ vs. the fresh character's 0). kill qigai produced a fully real combat sequence (genuine attack/dodge/damage exchange, not an admin-forced kill) ending in death, correct routing through d/death/gate.lpcd/death/gateway.lpc (阎罗大殿) or mpting.lpc (孟婆亭, random 50/50 per gate.lpc's run()), and — after the death_stage() chain completed — full resurrection back into START_ROOM (武馆前院, since the test character never left the hall proper), HP/qi/jing fully restored, is_ghost() cleared, and the game auto-saved cleanly. Zero debug.log errors or crashes anywhere in this cycle, confirmed by diffing log/debug.log against a pre-test baseline. This is the first time this specific end-to-end path has been verified live on this lib across all four testing rounds.

New bug found and fixed: §7.112-class init()/call_out() duplication in d/death/npc/death.h

While chasing the death cycle, found that mengpo.lpc, yanluo.lpc, and pusa.lpc all #include "d/death/npc/death.h", a shared header defining init()/death_stage() for exactly the reincarnation-desk NPC shape AGENTS.md §7.112 describes — an unconditional call_out("death_stage", 90, me, 0) in init() with no re-entry guard, in a lib whose clone/user/user.lpc::reconnect() unconditionally calls enable_commands() (confirmed: adm/daemons/logind.lpc's driver-invoked reconnect() calls user->reconnect() unconditionally on both netdead- reconnect branches). Every one of the earlier §7.112 corpus sweeps missed this lib — it was never on either wave's batch list. This is a real, live, wide-reaching bug: any player who is a ghost in 阎罗大殿/孟婆亭/ 菩萨殿 and reconnects even once (a routine event, not an edge case) gets a second, independent death_stage() call_out chain stacked on the first, which can double-apply the death penalty or race-misroute the reincarnation.

Fixed with the project's established minimal fix shape — a death_stage_active per-victim temp-flag guard set in init() and cleared at every exit point of death_stage() — deliberately NOT importing the extra special_poison/special_die/no_fight divergences found in sibling libs' (bxsj, shujian3) later-evolved copies of this same file, since those are unrelated feature additions this lib's own code never references. Live-verified with the project's established callouts death_stage technique: put testnine into 阎罗大殿 as a ghost, confirmed exactly one death_stage call_out pending via callouts death_stage (as admin), forced an explicit reconnect ("重新连线完毕"), and re-checked — still exactly one call_out, confirming the guard works. Let the chain run to completion and confirmed full, clean resurrection.

d/death/npc/wgargoyle.lpc/bgargoyle.lpc in this lib are plain decorative gargoyle NPCs with NO death.h include and no call_out at all — not vulnerable, don't need the same fix. Not corpus-swept this pass (single-lib fix only, since it was found via this lib's own testing, not a dedicated sweep) — worth checking sibling libs sharing this exact data/group/groom lineage (bxsj, sjecl, sjtx2, shujian2008, shujian3) for whether their divergent death.h copies already carry this guard (spot-checked bxsj and shujian3 while looking for a reference fix shape — both already have it) or need it independently checked against their own enable_commands()-on-reconnect behavior.

§7.78 F_DBASE: confirmed live via real bare set/query/delete

Used the admin wizard call command (call me->set("test_dbase_marker", 424242) / call me->query(...) / call me->delete(...)) directly on the admin's own char.lpc-derived player object — genuinely bare calls, not wrapped through any other function. Value round-tripped correctly across an explicit save, full disconnect, and reconnect (new object instance, confirmed via a different #N clone id in the %O dump before and after), and delete() correctly zeroed it back out on a second disconnect/reconnect cycle. F_DBASE is confirmed genuinely working on this lib, not just inherited-by-assumption from a sibling lib as round one left it. Test marker was deleted before the final save; no residue in git diff on data/user/f/fluffos.o.

Sect apprenticeship: works correctly

bai tan (against a non-sect NPC, 冯坦, the wuguan instructor) correctly rejected with "既不属於任何门派,也没有开山立派,不能拜师" (matches cmds/skill/apprentice.lpc's family mapping check — not a bug, reasoned rejection). bai yue at 华山正气堂 against 岳不群 (d/huashan/npc/ yuebuqun.lpc, which does create_family("华山派", 13, "掌门")) succeeded cleanly: "岳不群决定收你为弟子" + kowtow message + "恭喜您成为华山派的 第十四代弟子". No crash, no error. Mechanism confirmed sound.

Standing checklist sanity pass — all confirmed, no regressions

Observation, not fixed (out of strict scope per this round's directive): possible §8.1-shape title-length check

clone/board/wuguan_b.lpc inherits inherit/misc/bboard.lpc's do_post(), which checks i = strlen(arg); if (i < 4 || i > 24 || i % 2) — on this driver strlen() counts real characters (not GBK byte pairs), so this rejects any title with an ODD number of Chinese characters (e.g. 5-character titles), only accepting even counts. This has the same byte-halving-not-adjusted shape as the already-fixed §8.1 check_legal_name() bug elsewhere in this lib, but it's a title-length validation quirk, not a crash or data-corruption — players can always pick a title with an even character count, so this doesn't block board posting (verified: a 4-character title posted successfully end-to-end). Left untouched per this round's explicit scope guidance (no crash/error/ stuck-state, so treated as a possible latent quirk to flag, not a bug to fix without further confirmation this is unintended).

Test methodology / cleanup

Native build-debug driver, two full boots (one to find/fix the menwei/death.h bugs, one clean reboot from scratch to verify neither regressed and to run the full combat/death/dbase/sect test sequence from a known-clean state). Admin fluffos/Mud2026Pass used for goto/ summon/call/callouts/update; a throwaway character testnine (测武生) used for all player-facing testing. Cleaned up before commit: removed testnine's save files (data/user/t/testnine.o, data/login/t/testnine.o), reverted the test board post on data/board/wuguan_b.o and an incidental resave of the unrelated, real-player-content data/board/post_b.o (both via git checkout --, confirmed no real player data was altered), and confirmed the admin's own resaved data/user/f/fluffos.o has no test-marker residue. Driver killed by exact PID after each boot (723700, then 726243) — never pattern-match pkill.

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

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(): the shared inherit/char/char.lpc setup() (called from every character's create()) itself calls enable_player(), and d/gb/npc/xixia-wushi.lpc redundantly calls setup() again from inside its own init() (on top of the setup() its create() already made) -- same shape as the originally-documented mhxy zhangmen.lpc case. 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 recurses back into enable_player() on the same call stack until "Too deep recursion" aborts the boot on a room's first-ever visit. Fixed with a true reentrancy flag (in_enable_player_now), NOT a living() guard (which would break legitimate re-enables from revive() in feature/damage.lpc and wakeup()/wakeup2() in cmds/std/sleep.lpc, both confirmed to re-invoke enable_player() on this lib while the object is still living()). Verified via lpcc --batch single-file compile check (PASS). Part of the corpus-wide §7.19 sweep (Batch E).