Return of the King (nt7 / Mire 7)

✅ 可玩

王者归来

nt7

🔑 fluffos / Mud@2026wiz 更新 0121c3f 2026-09-12 源码 下载 ZIP 上游 fluffos/nt7

▶ 开始游玩 · Play Now

底层标注"NTLIB V7",属于本项目"泥潭/NT/nitan/Lonely"系列引擎家族的又一独立分支——与 nitan170911(仙剑奇侠传)、nitan6(笑傲江湖)、hhsj(洪荒世界)等库同宗同源,但核心档案(master.lpc、logind.lpc、securityd.lpc 等)均已各自独立演化,属于不同的具体游戏。开局同样是盘古开天地的创世场景:在"生命之谷"选择角色性格(`choose 1`-`4`),然后在忘忧池"洗"天赋(`washto <膂力> <悟性> <根骨> <身法>`,13-30 之间、总和不超过 80),随即自动"投胎"进入古村的世界之树;门派、帮派威望、军功、结婚生子等系统与同家族的 hhsj/nitan6 类似,并提供 5555(GBK)、6666(UTF-8)、8888(网页版 WebSocket)三个不同字符集/协议的端口。源码来自 GitHub 上一个已经完成 UTF-8 转码、并已适配现代 FluffOS v2019 驱动的社群维护分支(`github.com/fluffos/nt7`)。

English

A member of the NT/nitan/Lonely engine family (see nitan170911, nitan6, hhsj, xfbhh, nt1, nt6). Core daemons (master.lpc, logind.lpc, securityd.lpc, chinesed.lpc) share the family's overall shape but have diverged substantially in content from every sibling checked (hundreds to thousands of differing lines even after whitespace normalization) -- a real independent branch, not a duplicate. New characters undergo the same Pangu creation-myth 'birth ceremony' as hhsj/nt6 (choose a personality, wash attributes in the Valley of Life, land in the Ancient Village).

README

本馆只保留说明和启动脚本。 work/fluffos/nt7 子模块(泥潭根就是仓库根,pin 04a1a75e,leftover 689)。LPC 已在那个仓库改成 .lpc + §9;修复请提交到那个仓库,不要在本馆再改一份。管理员试玩种子在 overlay/

内容亮点

内容与角色创建流程详见上方简介。

在线试玩

(本库尚未纳入 WASM 站点批量打包流程;本地驱动运行方式见下方。)

管理员账号 / Admin account

警告:对外公开架设前请务必修改这两个密码。

本地运行

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

游戏端口:40211

NOTES · 移植与修复记录

git clone https://github.com/fluffos/nt7nt7

Source: fluffos/nt7 submodule at 04a1a75e (leftover 689: .lpc + §9 formatted on that repo; mudlib root is the repo root; filename-extension slices widened for .lpc). In-game name 王者归来 / NTLIB V7. Slug nt7, number 163, port 40211.

leftover 689: work/ is the fluffos/nt7 submodule

hosting is fluffos-upstream. work/ is now the fluffos/nt7 gitlink (pin 04a1a75e8f137138a1da21aae7c204572d872339), not a second formatted fork. config.fluffos mudlib directory still points at work/ (this repo's root *is* the mudlib). patches/ stays empty — the rename, §9 format, .lpc slices, and staticnosave keywords live on that fluffos-org repo. log_file("static/...") path literals were not rewritten (convert_lib's \bstatic\b also hits those; they stay "static/"). The catalog-only dead #ifndef __SENSIBLE_MODIFIERS__ shim in include/globals.h was not landed upstream. help/ntbook encoding sidecar stayed upstream-as-shipped. Three pre-existing lexerrors were left untouched: quest/skybook/update.lpc, inherit/condition/damo.lpc, d/dragon/outlet.lpc. PRs #3/#4/#5 were already on 53dce3a1 before this rename. Catalog-only overlay: data/login|user for fluffos / Mud@2026wiz (regular) + Mud@2026 (admin/recovery), including fluffos.package.o. Native 巫师休息室 (/d/wizard/ wizard_room) look/score/quit on fluffos / Mud@2026wiz (浮云, (boss)). score said 还没有出生呐 — by design (wizard skips the birth ceremony). Driver debug.log and work/log/ log_error were this boot (2026-09-12 08:17); unused-local compile warnings only. error_handler was not written (no caught runtime error). Zip pack: 29918 .lpc, overlay seed, no .git/ store. Full choose/washto/古村 registration was already verified at onboard.

earlier snapshot note (superseded)

Onboard convert was from 53dce3a1. Leftover 689 landed the catalog LPC delta on fluffos/nt7 and pinned that SHA.

Status: native + WASM playable, full registration verified both ways

Lineage vs. the existing NT/nitan/Lonely family (AGENTS.md §2.1/§11)

Genuinely distinct branch, not a duplicate of any existing lib. Spot-checked core files (adm/kernel/master.lpc, adm/daemons/ logind.lpc, adm/daemons/securityd.lpc, adm/daemons/named.lpc, adm/daemons/chinesed.lpc) against nitan170911, nitan6, nt1, nt6, nt6nitan6win, xfbhh, and hhsj: every file differs substantially (hundreds to ~900 diff lines even under whitespace- insensitive diff -bw, out of files 150–1,850 lines long) — this is NOT the "byte-identical modulo reformatting" shape that predicts a wholesale fix-port (contrast: nt7's feature/dbase.lpc byte content vs. hhsj's post-reformat copy WAS identical modulo indentation style, confirming shared engine ancestry, but the higher-level daemon files have each evolved independently since the fork). Directory layout (adm/, clone/, cmds/, d/, data/, feature/, inherit/, kungfu/, quest/, u/, template/ …) and world content (d/ dongtian/, family/sect system, data/business bsave format) closely mirror hhsj's, and securityd.lpc's own file header credits "Doing Lu for ULTRA HELL" (the same "Doing"/hell lineage credit chain documented elsewhere in AGENTS.md §11) — strong evidence of a shared distant ancestor, not a recent fork of any single already-onboarded lib.

The single biggest payoff of checking lineage first: the family's signature architecture bug (AGENTS.md §7.15, "the nitan set/query/ dbase architecture bug") is ALREADY FIXED upstream here, and does not need porting. §7.15 describes the NT/nitan/Lonely lineage's classic trap: bare set()/query()/delete()/addn() simul_efun calls resolving this_object() to the SIMUL_EFUN OBJECT (wrong) rather than the caller, requiring a two-part fix (real local set/query in feature/dbase.lpc + a previous_object()-based simul_efun fallback). nt7's feature/dbase.lpc has NO local set/query/delete functions at all (same shape as the historically-BROKEN state on nitan170911/nitan6/hhsj/xfbhh/nt6/nt6nitan6win/nt1 before this project's own fixes) — but nt7's simul_efun shim (adm/kernel/simul_efun/ntefun.lpc, guarded #ifndef __PACKAGE_DBASE__, which is always true on this driver) correctly uses previous_object() (the CALLER) rather than this_object() (the simul_efun itself) to resolve the redirect target:

varargs mixed query(string prop, object ob) {
    if (!objectp(ob)) ob = previous_object();
    return ob->query_db(prop);
}

This is architecturally correct and was verified live (registration, washto, score all read/write player properties correctly). Since every bare call anywhere in the whole tree routes through this ONE correctly-implemented simul_efun (there being no local override anywhere to create the split-resolution shape §7.78 describes), the downstream §7.78 (mixin-file bare-call mis-resolution) and §7.79 (addn/addn_temp misdirection) bug classes don't apply here either — addn's shim uses the same previous_object() pattern. check_legal_name also already uses a real Unicode pcre_match(str, "^\\p{Han}+$") check (§8.1's "byte-range Chinese-detection" bug class does NOT apply — the GBK-byte-oriented is_chinese2() variant still exists in the same file but is unreachable dead code, confirmed via corpus-wide grep, left alone). Net effect: this branch appears to have either independently fixed, or never actually shipped, the exact bug class that was this project's single biggest recurring finding across the rest of this lineage — a useful confirmation that the bug is a fork-time regression in SOME branches, not baked into the shared upstream design itself as §7.15's own text once concluded.

Bugs found + fixed (programming/driver-compat only, per AGENTS.md §6)

1. §7.80-shaped bug, confirmed present, fixed: adm/daemons/ eventd.lpc's create() builds its event-file list with map_array(event_name, (: $1[0..<3] :)) to strip the .lpc extension — same off-by-one as the corpus-wide §7.80 sweep (str[0..<n] keeps len-n+1 chars on this driver, so stripping a 4-char suffix needs [0..<5], not [0..<3]). This lib wasn't part of that sweep (never onboarded before), so it still had the bug. Fixed [0..<3][0..<5]. Filed upstream, bundled with the 29 sibling sites found in round two below, as PR #3. 2. §4.3 string-literal collision, self-inflicted by the mechanical staticnosave conversion, fixed: 150 files/282 call sites had log_file("static/...", ...)-style path literals rewritten to "nosave/... by convert_lib.sh's blind \bstatic\b sed (the original source, confirmed via raw/, has zero "nosave/ strings anywhere — this is 100% conversion-tool fallout, not an upstream bug). Reverted every "nosave/"static/ across the whole tree. Not upstream-worthy (audited for the fluffos-org-PR queue, 2026-08-31): this is self-inflicted by this collection's own staticnosave conversion pass, not present in the upstream source at all — no PR filed. 3. #ifndef __SENSIBLE_MODIFIERS__ shim in include/globals.h, confirmed DEAD (not a live bug on this driver), removed for cleanliness anyway: the classic AGENTS.md §4.3 "#define protected static" shim was present (#define protected nosave), but this driver's LPC preprocessor already defines __SENSIBLE_MODIFIERS__ unconditionally (confirmed via the driver's own predefines dump), so the #ifndef branch was always compiled out — protected was already the real keyword, not aliased. Removed the dead shim block rather than leave a misleading comment; verified via a clean recompile (no new warnings/errors), and confirms feature/ command.lpc's protected nomask int command_hook(...) (§8.3a) was never actually broken here either — no fix needed for that one. Not upstream-worthy (audited for the fluffos-org-PR queue, 2026-08-31): a dead-code cleanup with no functional-bug signature, not a genuine bug fix — no PR filed. 4. New WASM-only bug, found + fixed, root-caused via bisection: the very FIRST connection under the WASM build always failed logon() silently (driver log: new_conn_handler: logon() on object clone/user/login#0 has failed, the user is disconnected., with ZERO LPC-level error/trace anywhere — safe_apply() just returns null) — reproducibly, at the exact same point, regardless of what was typed. Bisected with temporary write() markers spliced into logon(): everything up through chinese_number(max_usr) printed fine; the very next call, TIME_D->replace_ctime(time1), never even entered the target function body. Root cause: TIME_D (adm/daemons/timed.lpc) is listed in adm/etc/preload and DOES compile clean during boot preload (no warnings/errors at all), but is NOT actually resident (find_object(TIME_D) returns 0) by the time of the very first connection — and a bare call_other() (TIME_D->replace_ctime(...)) to a not-yet-loaded object, which transparently triggers an implicit load_object() and works fine under the NATIVE driver, silently fails the whole logon() call under the WASM build instead (confirmed empirically: an EXPLICIT catch(load_object(TIME_D)) immediately before the call fixes it completely, and every following line — including replace_ctime()'s own body — then executes normally). This looks like a genuine WASM-specific driver-level gap (implicit load-on-call_other not reliable there), not a mudlib logic bug, but per AGENTS.md §1.3's "mudlib-side guards are encouraged" policy, fixed at the mudlib level: if (!find_object(TIME_D)) catch(load_object(TIME_D)); immediately before the TIME_D->replace_ctime(time1) call in logind.lpc's logon(). Did NOT attempt to preemptively guard every other daemon call in this ~30k-file mega-lib against the same theoretical class — this was the one call site actually exercised and confirmed broken on the critical first-connection path; note for a future WASM pass on this lib (or a sibling) if another first-connection daemon call turns up the same symptom. Filed upstream, bundled with fix #5 below, as PR #5. 5. Unrelated hardening applied alongside #4: logind.lpc's logon() also had an UNCONDITIONAL DATABASE_D->db_count_user() call (real MySQL db_connect()) feeding a purely cosmetic "N registered players" banner line — every other DATABASE_D-> call in the same file is already correctly gated behind #ifdef DB_SAVE (disabled in this checkout's include/unixconf.h/winconf.h), so this one call was inconsistent with the file's own convention. Fixed to match: #ifdef DB_SAVE ... #else reg_usr = 0; #endif. Confirmed via bisection this was NOT the actual cause of bug #4 (removing it alone didn't fix the WASM hang) — kept anyway since it's a genuine, independent correctness/consistency fix (and doubly so under WASM, where PACKAGE_DB isn't compiled in at all, per the WASM build's own CMakePresets.json). Filed upstream bundled with fix #4, see PR #5.

Interactive verification

Native (~/src/fluffos/build-debug/src/driver config.fluffos, scripts/mudclient.py): full flow scripted end-to-end multiple times — id → confirm → surname/given-name (real Chinese, e.g. 秦风/秦峰/秦枫) → admin password → confirm → play password → confirm → gender → notice board → lands in /d/register/regroom ("泥潭注册室") → reg <email> completes the stub email-registration step (mail-sending is commented out upstream; registered flag just gets set) → moves to /d/register/entry ("生命之谷", meets 盘古/Pangu by name) → choose 1-4washto <str> <int> <con> <dex> (13–30 each, ≤80 total) auto- completes the "投胎" (reincarnation) step and moves the character straight into /d/city.../世界之树 in the Ancient Village (no separate born <city> command needed once washto succeeds — confirmed the game's own flow, not a bug: do_washto() calls the equivalent of do_born() internally). look/score both produce correct, fully-rendered output (character sheet shows the exact str/int/con/dex values from washto, correct gender/personality/age, zero mojibake). quit triggers the same "new account must stay online 30 minutes or it's deleted" policy already documented as intentional design on hhsj — confirmed by completing it with y: clean "账号...档案被 删除" message, save file actually removed, zero debug.log errors throughout any of the runs.

WASM (scripts/wasm_client.js against ~/src/fluffos/build-wasm/src): same full flow, same script, same result — registration through choose/washto/world-entry/score/quit all produce identical correct output to the native run, after fix #4 above. Before that fix, EVERY WASM connection attempt failed at the exact same point (silently, right after the "已经执行了N秒" uptime banner) regardless of what was typed — this was the actual §1.3/§1.4 WASM playability blocker for this lib, not an IP/socket/pcre-package issue like the family's other usual WASM traps.

Not fixed (deliberately, out of scope)

Admin account seeding (AGENTS.md §1.5)

adm/etc/wizlist already shipped fluffos (boss) (seeded upstream by this project's own author in the pre-existing 254262ec commit, before this pass even started). No save file existed for it yet, so registered it for real through the normal flow (this lib's registration requires BOTH an admin/recovery password and a distinct regular login password — sending the same value for both is rejected): id fluffos, name 浮云 (surname 浮, given name 云 — picking the same character twice for both, e.g. "浮浮", trips a same-surname-name re-confirmation branch in get_name() that isn't a bug, just needs the joined full name typed back; used two different characters instead), admin password Mud@2026, regular login password Mud@2026wiz (matching this project's own established convention for this specific repo, from the 254262ec commit message). Since fluffos already resolves to (boss) via wizlist, enter_world()'s own startroom logic correctly routes it straight to WIZARD_ROOM (/d/wizard/wizard_room) instead of through the normal player birth-ceremony rooms — confirmed correct behavior, not a bug (wizard accounts are expected to skip reg/ choose/washto). Verified live: reconnect with the regular password succeeds, update /adm/daemons/timed reports "成功!". score reports "还没有出生呐" (not yet "born") for this account, since the normal birth-ceremony rooms that set the born property are never visited by a wizard-status login — did not chase this further (out of scope, likely by design for a staff-only account that isn't meant to use score).

Deep functional test (round two, 2026-08-27)

Full §10.7 playthrough (never previously done on this lib) against a freshly-rebuilt ~/src/fluffos/build-debug/src/driver, native only. Also checked all of AGENTS.md's standing cross-cutting bug-pattern list (§7.121, §8.3a, §7.112, §7.118, §7.122–§7.136, §7.139, §7.141– §7.148) via systematic grep — see per-pattern notes below. No sibling NT/nitan-lineage bug classes applied here beyond what's already documented above (§7.15 architecture bug already fixed upstream, per the earlier onboarding writeup).

Housekeeping note, not an nt7 bug: found an unrelated stray driver process (PID bound to port 40211, cwd in a completely different lib sanguozhi, reparented to init) squatting on this lib's assigned port before boot could even start — killed by exact PID before proceeding. Unrelated to nt7's own code; flagged here only so a future session knows this can happen on a shared host.

Bug found and fixed #1 — §7.80-shaped filename-slice bug, 29 more call sites in the kungfu/skill tree

explode(__FILE__, "/")[<1][0..<3] (and the dirs[<1][0..<3] variant) appears in 27 individual kungfu/skill/**/*.lpc files plus the two shared mixins inherit/meskill/skill_model_{weapon,unarmed}.lpc and the shared header kungfu/skill/force.h — the exact same str[0..<n] off-by-one this project already fixed in this very lib's adm/daemons/ eventd.lpc (.lpc is 4 characters, [0..<3] only strips 2, so "guiyuan-tunafa.lpc" yields "guiyuan-tunafa.l" instead of "guiyuan-tunafa"). This project's original onboarding pass only grepped for eventd.lpc's own exact line shape and never turned up this second, much larger family of call sites in the same lib. Two confirmed-live consequences:

The remaining ~20 kungfu/skill call sites compute the same corrupted id but only pass it to SCBORN_D->valid_learn()/valid_perform() — a prerequisite-checking daemon whose file (adm/daemons/scbornd.lpc) does not exist anywhere in this archive at all (confirmed via corpus-wide grep for scbornd/SCBORN_D). Every such call unconditionally fails regardless of the id's correctness, so fixing the slice has no observable behavior change at those specific call sites — flagging this as a separate, out-of-scope missing-content gap (inventing what a whole prerequisite daemon should do would be a content/design decision, not a programming fix) rather than chasing it further. Fixed the slice arithmetic at all 29 sites anyway ([0..<3][0..<5]), since it's the same unambiguous mechanical bug independent of what currently consumes the result. Filed upstream, bundled with the eventd.lpc instance above, as PR #3.

One additional match, adm/daemons/skillsd.lpc's real valid_perform( object me, string file) (the actively-used SKILLS_D, as opposed to the unreferenced dev-sandbox copy at u/lonely/skillsd.lpc), had already had its own [0..<3] slice commented out and replaced with a bare dirs[<1] (no stripping at all) by a prior author — checked this against its real caller (inherit/skill/skill.lpc's perform_action(), which passes an already-extension-less path via perform_action_file()) and confirmed the no-slice form is actually CORRECT for that call site's real argument shape (the one other caller that would pass a .lpc-suffixed path is fully commented out, dead code). Left untouched.

Verified live: bai wu bo (拜师) then learn wu bo unarmed 1 correctly raised a fresh character's unarmed skill (skills showed 基本拳脚 (unarmed) - 不堪一击 6/14 after one learn call) — this exercises the general skill-learning path, not the specific 9 force-skill files, since reaching those requires much deeper progression; the force.h self-exclusion fix was verified by direct code-level tracing (confirmed the corrected id now matches what member_array() checks against) rather than a live multi-level force skill grind, given time budget — documented here as verified-by-code- reading rather than fully live-verified for that specific consequence.

Bug found and fixed #2 — a missing-content-factory null check silently truncates the crontab scheduler on every single boot

100%-reproducible on every fresh boot (not a random-roll fluke): adm/daemons/timed.lpc's init_crontab() reads adm/etc/crontab (140 lines) and does find_object(table[1]) || load_object(table[1]) on every referenced object to verify it resolves — one of the very first entries (line 120) references /u/redl/cangku (a wizard's personal treasure room, target of 6 华山论剑/牛人三部曲 tournament-scheduling crontab lines). Loading it for the first time runs its create(), which does EQUIPMENT_D->create_dynamic("", 60, 600)->move(this_ object()) in a loop — an unguarded §7.147-shaped chain. Worse, create_dynamic() ITSELF (adm/daemons/equipmentd.lpc:1368) has no null check either: ob = TEMPLATE_D->create_object(filename, obj_type, temp_status); can return 0 (whenever the randomly-rolled obj_type/ level combination doesn't resolve to a real shipped template file), and the very next statements unconditionally do set(..., ob) and ob->set_color(color) — a bare call_other() on int(0), which throws uncaught: *Bad argument 1 to EFUN call_other() Expected: object, string, array, Got: int(0) (debug.log, reproduced on every single boot in this session, both before and after the fix was verified absent).

Severity beyond the immediate crash: the error is only caught by an OUTER catch() several frames further up the call stack (logind. lpc's pre-existing catch(load_object(TIME_D)), from this lib's own bug #4 above) — which means the entire unwind aborts init_crontab()'s for-loop at that exact point, silently discarding EVERY crontab entry after line 120 (the 华山论剑 sect-tournament scheduler, the 牛人三部曲 PK-event scheduler, and anything else later in the 140-line file) on every single boot, not merely failing to spawn one random equipment drop in one wizard's personal room.

Fix: added if (!objectp(ob)) return 0; immediately after the TEMPLATE_D->create_object() call in equipmentd.lpc's create_dynamic(), restoring the function's own already-documented "returns 0 on failure" contract (mirrors the identical early-return guard already present at the top of the same function for ilvl < 1). Also fixed the caller-side chain in cangku.lpc: stored the return in the already-available ob local and guarded the ->move() call with if (objectp(ob)). Verified live: debug.log goes from this error firing deterministically on every single fresh boot (both via a first connection reaching logind.lpc's TIME_D lazy-load path, and via a direct wizard update /u/redl/cangku.lpc/update /adm/daemons/ equipmentd.lpc) to zero occurrences across multiple clean reboots post-fix. Filed upstream as PR #4. Added as a new confirmed instance of AGENTS.md §7.147, with a new note about the extra severity when an unguarded factory chain is reached from inside a bootstrap/scheduler loop rather than ordinary gameplay.

Standing cross-cutting patterns checked, confirmed clean

Interactive verification (this pass)

Registered a fresh Chinese-named test character (qinfengnt, 秦风, admin pw fluffos, play pw Mud2026play) via a raw Python socket bridge script, one continuous session:

RSS stayed under 130MB throughout (well below this lineage's documented §7.110 OOM risk threshold) — no mass-restore operation was exercised this session, so that risk wasn't specifically stress-tested here.

Local run

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

Port: 40211.