Naruto X Hunter (NaHu)

✅ 可玩

獵火影忍者人 (Naruto X Hunter)

naruto

更新 fdf581a 2026-09-12 源码 下载 ZIP 上游 MudRen/mudos-game-naruto

▶ 开始游玩 · Play Now

本遊戲自稱「獵火影忍者人(NaHu)」,是《火影忍者》與《獵人》(HUNTER×HUNTER)兩部日本動漫的同人跨界 MUD,登入畫面會依序顯示兩幅 ANSI 彩色橫幅(分別標示「Hunter X Hunter」與「火影忍者」)。底層引擎與本收藏中的 `huoying`(同樣源自 "Neolith 0.0.2" / ES2 架構的早期「火影」原型)同宗,但本檔案是該引擎實際發展成熟後的完整版本:擁有近二十個地圖分區(木葉、砂之村、鯨魚島等火影場景,以及依循獵人「念能力」六大系分類 -- 放出/操作/変化/特質/具現化/強化 -- 設計的完整技能系統),而非僅有一個巫師測試房間。

English

A Chinese-community MUD self-branded "獵火影忍者人(NaHu)" -- a deliberate portmanteau of 獵人 (Hunter x Hunter) and 火影忍者 (Naruto), crossing over the two anime series. Login displays two ANSI-art banners, one captioned "Hunter X Hunter" and one captioned "火影忍者". A fully built-out ~20-domain world spans both source series' iconography: 木叶 (Leaf Village) and 砂 (Sand Village) imagery from Naruto alongside four_wheel_tower, god_forest, and whale_island locations. A dual class/skill system lets characters train Naruto-style ninjutsu classes or Hunter x Hunter's license-based Hunter classes, with the source material's six Nen categories -- 放出 (Emission), 操作 (Manipulation), 変化 (Transmutation), 特質 (Specialization), 具現化 (Materialization), and 強化 (Enhancement) -- implemented as real, trainable skill categories. New characters register with an English account id, a Chinese display name, and a gender, then land in 巫師神殿 (Wizard's Temple), the game's starting area.

README

內容亮點

在線試玩

https://mudlibs.fluffos.info/naruto/

管理員帳號 / Admin account

警告:Mud@2026 是本地遊玩用的公開預設密碼。若要正式對外開放主機,
請先修改此密碼(或刪除該帳號及 adm/etc/wizlist 中的授權行)。

WASM 狀態

playable。WASM 環境下開機、註冊(含中文姓名)、look/score/quit 全部 正常,無需任何修正。詳見 NOTES.md

本地運行

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

遊戲端口:40235

NOTES · 移植与修复记录

naruto — 獵火影忍者人 / "Naruto X Hunter" (ES2/Neolith lineage)

Source: git clone https://github.com/MudRen/mudos-game-naruto (commit 000f286645e810e1cd786130c52d7b9c1747cae8, cloned 2026-08-26). Slug naruto, number 933, port 40235 (932/40234 were picked first but collided live with a concurrent session's nightmare4 onboarding; reassigned before first boot). GitHub self-describes the repo as "source code of Naruto X Hunter mud"; a sibling repo MudRen/Naruto (no relation confirmed below) was a tiny, effectively-empty stub and not the archive used here. MudRen's copy is a fork of https://github.com/zwshen/mudos-game-naruto. Rechecked 2026-09-04: zwshen master is 1 commit behind our snapshot (000f286 is MudRen's "增加驱动自动编译和运行脚本" only). No extra mudlib content on the original. Do not re-onboard.

Relationship to the already-onboarded huoying — read before assuming duplicate

This is the same base engine as huoying, not the same game. huoying's adm/obj/master.c/.lpc is byte-identical in structure to this archive's (same copyright header, same connect(int port) MUD_PORT/HTTP_PORT switch, same function bodies — diffed directly, confirmed) — both trace to the same "Neolith 0.0.2" / ES2 (Annihilator) Naruto-reskin bootstrap template, and huoying's own NOTES.md already documents the identical hardcoded-MUD_PORT bug class this archive also has. But content-wise they are not comparable: huoying's world/area/ contains exactly one subdirectory (wizard, the bare template's own test room, no built-out game world at all — its own archive was sourced from a Naruto.rar distribution, presumably an early/minimal snapshot), while this archive has ~20 fully-built domains (muye, sand_hole, four_wheel_tower, god_forest, whale_island, etc.) plus a large multi-wizard u/ personal-build tree, and — per its own GitHub description and its own in-game login banners/system announcements (see below) — has evolved into an explicit crossover with a *second* anime franchise (Hunter × Hunter), not just a bigger Naruto game. Treated as a legitimate, independently-developed sibling in the same spirit as this collection's existing zhyx/yanhuangwuhun/ demonangel/es2/haiyang2 ES2-family cluster (shared engine ancestry, explicitly cross-referenced, but each with its own substantively distinct game world) — not marked duplicate_of huoying. Flagging this relationship explicitly per this project's own convention for shared-lineage siblings, and because the evidence (a byte-identical shared boilerplate file, literally overlapping "火影"/Naruto branding) is unusually strong compared to the typical "shared framework, independently diverged" case.

The game's own branding is genuinely dual/crossover, not just Naruto-themed: the login sequence displays two separate ANSI-art banners in sequence (one captioned "Hunter X Hunter", one captioned "火影忍者"), the repo ships both hunter_welcome.jpg and naruto_welcome.jpg screenshots, an in-game system announcement reads "獵火影忍者人(NaHu) 重大公告" (literally 獵人 "Hunter" + 火影忍者 "Naruto" + 人 "person", stitched into one portmanteau with an explicit "NaHu" abbreviation), and the daemon/class + daemon/skill trees contain parallel daemon/class/naruto/* (ninjutsu) and daemon/class/hxh/* (Hunter license classes, with Hunter x Hunter's six Nen categories — 放出/操作/変化/特質/具現化/強化 — implemented as real skill categories, e.g. skill:emit for 放出系). This dual branding/content is original to the archive, not something introduced by this porting pass.

Encoding

file(1) initially misidentified the raw archive as ISO-8859 text with occasional escape sequences; decoding a sample string with both GB18030 and BIG5 and eyeballing a write("...") literal (naruto.conf's own name : 火影 line) confirmed BIG5 (Traditional Chinese, Taiwan origin), not the mainland GBK/GB18030 default this collection's other archives mostly use.

scripts/convert_lib.sh libs/naruto/raw/naruto libs/naruto/work BIG5: already_utf8=262 converted=4168 lossy=22 skipped_binary=20; renamed 2,895 files to .lpc; fixed 2,653 literal .c" references; converted 1 local angle-bracket include to quotes; staticnosave touched 15 files.

- doc/help/class/hunter/skill/variation/variation (52 bytes) — file(1) reported "OpenPGP Secret Key" purely because its first byte happened to match that format's tag byte (the claimed packet length, 7524, wildly exceeds the actual 52-byte file size, confirming the false positive); actual content is a two-line BIG5 help-text snippet ("變化系程度 (variation)"). Converted with iconv -f BIG5 -t UTF-8. - u/r/roger/book — genuinely binary (a directory-listing-style dump embedding filenames like e100-ar1.c), zero references anywhere in the tree by that name; a personal wizard's orphaned scratch file. Left as-is, matching this project's standard "genuine binary, correctly left alone" handling.

Fixes applied (driver-incompatibility bugs)

``lpc if (strsrch(message, "Warning") == -1 && strsrch(message, "warning") == -1) ``

- world/area/four_wheel_tower/npc/fire_sk.h (a shared combat message string, message_vision(sprintf("...龜派氣功\"NOR"..., \" immediately before what should be the closing quote of a string-macro-string concatenation chain) — this single header is #included by 10 NPC files (monkey1/2/3, kingkong1/2/3, ape1/2/3, baboon3), all 10 of which failed to compile until this one shared fix. - world/area/muye/npc/nightkid.lpc (5 instances in one acupoint-name array literal — "腎俞\", "肺俞\", "心俞\", "氣海俞\", "厥陰俞\"). - world/area/sand_hole/npc/doctor.lpc ("阿蓋\"), world/eq/leg/gai.lpc (set_name(HIG"阿蓋\"NOR, ...) — a real, reachable equipment item, not personal-sandbox content). - 6 further daemon/skill/piece/martial/*.lpc files (gun_finger, gun_ten, gun_gentian, animal_yan, briar_suffuse, gun_ban, gun_whip — all sharing one copy-pasted "膝蓋\", "膝部", ... body-part-list literal). - u/r/roger/work_q/wwizall.lpc (a slightly different shape: not a missing backslash-escape but a misplaced closing quote — set_name("【"HIR"神化"HIW"卷軸"NOR"】, ({ "book"... was missing the " that should have closed the string right after , so the string swallowed the following , ({ and the bare identifier book broke the syntax on the next token; fixed by adding the missing " in the right place: "】", ({ "book"...). - u/s/savior/board/savior.lpc: unrelated one-off typo found in the same investigation pass — inherit BULLETIN_BOARD missing its terminating semicolon. Added it.

``lpc room = 0; if( stringp(user->query("startroom")) ) err = catch(room=load_object(user->query("startroom"))); ... room = 0; if( stringp(user->query("recoverroom")) ) err = catch(room = load_object(user->query("recoverroom"))); ` Verified: log/catch_error` is empty after a fresh registration, where it previously always had these two entries.

``lpc int restore() { string file; mixed err; int ret; seteuid(getuid()); if( stringp(file = this_object()->query_save_file()) ) { err = catch(ret = restore_object(file, 1)); if( err ) return 0; return ret; } return 0; } ` Verified live: both boards' rooms now load without error; the two still-corrupt saves surface as caught, properly-logged log/catch_error entries (routed through master.lpc's real error_handler()` apply) instead of aborting anything. The underlying board *content* remains genuinely corrupted/unreadable (both boards effectively start empty) — a pre-existing data integrity gap, not something restored.

Confirmed NOT needed / pre-existing content, not fixed

Network / outbound-connections check (AGENTS.md caveat)

Two real socket-touching subsystems exist in this archive: adm/intermud/i2d.lpc (a full Intermud-2 UDP service — create() immediately does socket_create(DATAGRAM, ...) + socket_bind() + resolve()) and adm/daemons/userid.lpc (a port-113 ident-lookup client). Neither is preloaded (adm/etc/preload has no i2d/userid/dns_master entry — the archive doesn't even ship a dns_master.lpc), and grepping every caller: i2d.lpc's only non-adm/intermud/-internal caller is cmds/wiz/mudlist.lpc (an explicit wizard-only command); userid.lpc's only two call sites, both in logind.lpc's enter_world(), are commented out (// IDENT_D->query_userid(...)) — confirmed dead on the actual login path (a cmds/wiz/userid.lpc wizard command is the only live caller). This lib makes zero outbound network connections during boot or during the full registration/gameplay test below; safe for an automated reboot loop.

Boot / registration verification

Not run this session

深度功能測試(§10.7 round two, 2026-08-27)

Full continuous playthrough per AGENTS.md §10.7: registered a real test character (qinfengf / 秦風德, deleted before commit), tested look/score/inventory/who/whoami through several state changes (post- register, post-village-join, post-combat), village join, safe-sparring combat, quit/relogin persistence (twice, including a real reconnect after several minutes of intervening investigation work). Admin account fluffos/Mud@2026 re-verified working (login, wizard room-path suffix, no password-policy issue). Two real, severe programming bugs found and fixed live; the five standing cross-cutting patterns (§7.121/§8.3a/§7.122/§7.123/§7.124) were checked systematically, one more (benign) §8.3a instance found and fixed, the rest confirmed clean.

Bug 1 — §8.1 length-gate variant: is_chinese()'s strlen(str)>=2 tail-slice gate rejected every odd-length Chinese name

adm/simul_efun/chinese.lpc's is_chinese(str) { if( strlen(str)>=2 && str[0] > 160 ) return 1; return 0; } is the exact length-gate variant already documented in AGENTS.md §8.1 (found previously on dfgsiiv13b). adm/daemons/logind.lpc's check_legal_name() calls it on a *tail slice* of the candidate name at every even byte-position (is_chinese(name[i..<0]), a GBK-byte-era idiom meaning "is there a full 2-byte hanzi starting here") — under this driver's per-codepoint strlen(), the LAST character's tail slice is always exactly 1 character long, so strlen(str)>=2 is false and is_chinese() always returns 0 for it whenever that lands on an even loop index, i.e. whenever the name's total character count is odd. Confirmed live: a 2-character name (秦楓) registered fine, but a 3-character name (秦風德) was rejected every time with "對不起,請您用「中文」取名字。" — exactly the "some lengths work, some don't, no obvious pattern from one test name" symptom AGENTS.md §8.1 warns about. This also explains why the *onboarding* session's own registration verification missed it entirely: all 4 of its test names (秦風/秦楓/秦雪/浮浮) happened to be 2 characters long.

Fix (adm/simul_efun/chinese.lpc), per §8.1's documented remedy — drop the length requirement, check only the first character's codepoint range regardless of the slice's total length:

int is_chinese(string str)
{
    if( !strlen(str) ) return 0;
    return str[0] >= 0x4e00 && str[0] <= 0x9fff;
}

Verified live: a fresh 3-char-name registration (王小明) and a 5-char one both now succeed; a 2-char name still works as before. is_chinese()'s two other call sites (chinese.lpc's own break_chinese_string(), channeld.lpc's emote-detection) also pass tail-slices or whole strings — the first-character-only check is correct for all of them.

Sibling check: huoying (byte-identical master.lpc) is worth a quick grep for the same is_chinese()/check_legal_name() shape if it is ever deep-tested, though its own NOTES.md describes it as having no built-out game content beyond the bootstrap wizard room, so this may never be exercised there.

Bug 2 — new bug class, added as AGENTS.md §7.126: two independent driver-incompatibility bugs together made the ENTIRE built-out game world (not just the tutorial hub) unreachable from a fresh registration

Found by trying to walk from the starting room (世界巫師神殿) to anywhere else. Two separate, compounding bugs, both fixed:

2a — command_hook's movement gate rejected any non-string exit value, silently blocking the bare compass word the room itself advertises. feature/char/command.lpc's central dispatcher only routes a bare direction word (e.g. east) to /cmds/std/go.lpc when stringp(environment()->query("exits/" + verb)) is true:

else if( (verb != "go") && environment()
         &&	stringp(environment()->query("exits/" + verb))
         &&	GO_CMD->main(this_object(), verb) )

But an "exit into a coordinate-based AREA" is legitimately authored as a mapping ({"filename":..., "x_axis":N, "y_axis":N}) — used by 180+ room files in this archive, e.g. world/area/wizard/propose.lpc's own "east" exit — and go.lpc's do_room_move() already fully supports and correctly resolves that shape. The stringp() gate rejected it outright, so typing the *exact word the room's own look listing shows* (這裡明顯的出口是 west 和 east。) produced the driver's generic unrecognized-command fallback (config.fluffos's default fail message, which happens to be in Simplified Chinese — initially mistaken for a mudlib string, traced to the driver's __DEFAULT_FAIL_MESSAGE__ config value via strings + a grep of add_action.cc's notify_no_command()) with zero indication the real problem was a type check, not a missing exit. The single-letter alias route (ealiasd.lpcgo east) happened to bypass this gate entirely (verb=="go" skips the branch), which is why the onboarding session's registration test — which never left the starting room via a coordinate-based exit — never hit this.

Fix: accept any defined exit value, matching go.lpc's own type handling, instead of requiring a string:

else if( (verb != "go") && environment()
         &&	environment()->query("exits/" + verb)
         &&	GO_CMD->main(this_object(), verb) )

2b — NEW BUG CLASS (added as AGENTS.md §7.126): stale .c extensions baked into .o save-file DATA (not source) for every coordinate-AREA room-exit door, surviving this project's own .c.lpc rename. Even after fixing 2a, walking onto several of the wizard-temple AREA's own door tiles (e.g. the free transfer room that is the *sole* route from the tutorial hub to muye/whale_island/sifa_isle) still failed with a generic "這個方 向的出口有問題,請通知管理者來處理" error. Root cause: std/area/map.lpc's valid_leave() resolves a door tile's room_exit/area_exit value through the AREA's own file_path() helper (substituting the __DIR__/_DIR_*_ macro placeholders these values are stored with), then calls load_object() on the result — but the stored value itself still carries a literal .c extension from before this archive's .c.lpc conversion (e.g. "__DIR__transfer.c" resolves to /world/area/wizard/transfer.c, but the real file is now transfer.lpc). Unlike the original MudOS driver (.c/.lpc interchangeable), this driver resolves an explicit extension exactly, so load_object() silently returns 0 and valid_leave() rejects the move. This is not confined to one room — a corpus grep across every world/area/**/*.o file found 210 affected save files / ~969 stale .c references to room_exit/area_exit/filename values, spanning virtually every built-out domain (muye, whale_island, sifa_isle, four_wheel_tower, god_forest, sand_hole, etc.) — in practice this made the ENTIRE hand-built game world beyond the tutorial-area's plain-string exits unreachable via ordinary movement, since the free transfer teleport hub (the game's own documented "level 1 is free" route out of the tutorial area) was itself one of the broken doors.

This is a distinct class from AGENTS.md §4.2 item 2 ("bare paths in plain-text data files") — that class is a literal, static path string; this one is a macro-style placeholder ("__DIR__foo.c") resolved at runtime through a custom substitution function, which is why it wasn't caught by convert_lib.sh's source-only .c-reference fixups (.o save data is out of scope for that sweep) or by any live look/short single-room registration test. Fix — patch the single choke point both room_exit and area_exit resolution flow through (std/area/map.lpc's file_path()), stripping a trailing bare .c after the macro substitutions:

if( strlen(dir) > 2 && dir[strlen(dir)-2..] == ".c" )
    dir = dir[0..strlen(dir)-3];

This transparently fixes all 210 affected .o files without touching any of them directly (.lpc-suffixed values are unaffected — .lpc's last two characters are pc, never matching the check). Verified live: load_object() on transfer's resolved path now succeeds (confirmed via a direct eval), and a test character walking east from world/area/wizard/propose.lpc — previously blocked by bug 2a — now correctly lands in the wizard.lpc AREA at the exact coordinate the room's own exit mapping specifies; from there the character reached muye village (via an eval-assisted reposition, since the coordinate grid's own live-redrawing ANSI map view made scripted multi-step compass-walk timing unreliable for further live confirmation — see "Not fully confirmed live" below), joined the 木葉 (Muye) village via say 加入 to 伊魯卡 (confirmed: title changed to 木葉村成員), and fought the safe practice dummy (world/npc/stake.lpc, no_combat/ no_defend/no_evade, commented-out receive_damage() — this archive's own designated §10.7 "safe sparring mechanism") via fight stake with real damage messages and energy-point cost, confirmed persisting across quit/relogin.

Added as new AGENTS.md entry ### 7.126 (this is a materially different mechanism from every existing .c/.lpc-rename catalog entry in §4.2 — data-embedded macro placeholders resolved by a custom per-lib function, not a literal path) — flagged there for a corpus-wide check on any other archive using a similar coordinate-AREA engine with a __DIR__-substitution-style save format (this ES2/Neolith lineage's std/area/map.lpc in particular — huoying, zhyx, yanhuangwuhun, demonangel, es2, haiyang2 are the already-identified siblings, though most have far smaller or no built-out AREA-grid game worlds so the blast radius will vary; worth a grep -c '\.c"' world/area/**/*.o sanity check on each before assuming it's unaffected).

Standing cross-cutting patterns (§7.121/§8.3a/§7.122/§7.123/§7.124) — checked explicitly

Not fully confirmed live

ES2-family cross-check (2026-08-27): AGENTS.md §7.129 tell_room() bug found and fixed — NOT on the core death path, but real elsewhere

Following up on es1's severe §7.129 find (a shared tell_room() wrapper forwards its omitted exclude arg to message() as a literal int(0), which this driver's message() rejects), checked this direct ES2-family descendant. std/char.lpc's core die() uses message_vision(), not tell_room(), for its death announcement — explaining why this bug went unreported before, despite this lib already having real bugs found and fixed earlier in this same document. But adm/simul_efun/message.lpc's tell_room(ob, str, exclude) still unconditionally forwarded the bare, defaulted exclude straight to message(), and a real 2-argument call site exists in obj/user.lpc's net_dead() (the "時空一陣波動...瞬間由這個 世界消失" announcement made on every abrupt disconnect when the player isn't inside an area-room). This would have crashed uncaught on every ordinary net-dead disconnect. Fix: message("tell_room", str, ob, exclude)message("tell_room", str, ob, exclude || ({})), identical pattern to es1/es2/haiyang2/etc. Verified live using this lib's own cmds/adm/eval.lpc (a genuine eval command, unlike most siblings): post-fix, eval tell_room(environment(me), "TESTMSG_2ARG_OK\n"); return 1; and the 3-arg form both completed with no crash. include/compress_obj.h (the sibling §7.14 bug from the same es1 finding) does not exist anywhere in this lib's tree — not applicable.

WASM status audit (2026-09-01)

playable. Booted ~/src/fluffos/build-wasm/src via scripts/wasm_client.js -- clean boot, zero fixes needed. Full session verified with a real Chinese name: registration (English id, y/n confirm, password + confirm, email, gender m/f, Chinese display name), landing in 巫師神殿 (Wizard's Temple), look/score/quit all correct. Consistent with this lib's own "confirmed making zero outbound network connections" finding above -- no sockets-package issues encountered.