@@ -639,3 +639,235 @@ Save files (untracked, NOT gitignored — orchestrator must add all FOUR):
639639** Retest:** fresh normal registration (ceshisi / 秦岭甲) end-to-end OK
640640(look/score/quit correct; all four test-char save/key files removed);
641641no new errors in debug.log.
642+
643+ ## 深度功能测试 / Deep functional test (AGENTS.md §10.7, round two)
644+
645+ One continuous native-driver session (plus a few short admin-assisted side
646+ sessions for reachability, disclosed below), `~ /src/fluffos/build-debug/src/driver
647+ config.fluffos` from ` libs/longyunmeng/` , driven via ` scripts/mudclient.py`.
648+ Read ` doc/help/intro ` and ` doc/help/newbie.txt ` first — both already fully
649+ UTF-8 and readable, no re-conversion needed. Key facts learned from them and
650+ used to plan the test: ` fight ` is the lib's own explicit safe-sparring verb
651+ ("点到为止... 只会消耗体力,不会真的受伤" — stamina-only, no real injury);
652+ newbie.txt itself warns ** quitting within the first 30 real minutes of a
653+ new account deletes it** ("进入游戏后半小时之内不可以退出游戏否则ID将会被
654+ 系统删除") — confirmed in ` cmds/usr/quit.lpc ` and treated throughout as an
655+ intentional, already-cataloged content timer (AGENTS.md §1.3e's "KEEP
656+ in-game content timers" bullet — same shape as ` xiyouji2003 ` ), not touched.
657+
658+ ** Registration** : real Chinese name ` 沈惊鸿 ` (id ` shenym ` ), male, landed in
659+ 悦来客栈 (Inn) on 南阳城/Nanyang — ` look ` /` score ` /` i ` all correct immediately
660+ after registration and after every later relogin. A second full registration
661+ (` 林测试 ` /lintest) and a third (` 测试贰号 ` /cesitwo) were also run clean, both
662+ also self-deleted correctly via the in-game quit-before-30-min flow at the
663+ end of the session (see below) — three independent registrations, zero
664+ failures, corroborating the existing NOTES.md registration verification.
665+
666+ ** Safe-sparring mechanism** : found and used the documented training-dummy
667+ NPC, 木人 (` d/shaolin/npc/mu-ren.lpc ` , placed in the Shaolin 练武场/
668+ ` d/shaolin/wuchang.lpc ` ) — matches the checklist's own "` accept_fight() ` +
669+ stat-copy loop" pattern exactly (copies the attacker's own skills/stats
670+ into the dummy on ` accept_fight() ` , ` set("no_die", 1) ` , a
671+ ` last_fighter ` /` fight_times ` cooldown so the same player can't grind it
672+ back-to-back). ` fight mu ren ` produced a full, real combat exchange ending
673+ in an automatic concede ("这场比试算我输了,佩服,佩服!") with no
674+ character death and no server error; retrying immediately correctly
675+ rejected with "你刚跟这个木人练过功!" (cooldown working). Reached via
676+ ` goto ` as the seeded admin account (` fluffos ` /` Mud@2026 ` ) rather than
677+ on-foot navigation — the Shaolin room graph from the ` fly sl ` entrance
678+ (` guangchang1 ` ) into the interior practice-ground cluster is large and not
679+ straightforwardly traceable by grep alone; using ` goto ` tests the * mechanism*
680+ identically to a player physically walking there (same ` fight ` command, same
681+ ` accept_fight() ` code path), just skips the maze-solving. Disclosed here,
682+ not hidden.
683+
684+ ** Sect-join + organic learn-from-teacher** : both tested together against
685+ 华山派 (Huashan sect) master 岳不群 (` kungfu/class/huashan/yue-buqun.lpc ` ),
686+ again reached via admin ` goto ` for the same navigation-cost reason (see the
687+ "orphan room" finding below — this NPC's room turned out to have NO
688+ in-bound exit from anywhere in the map, so on-foot navigation was never
689+ actually possible to begin with, not just impractical). ` apprentice yue `
690+ correctly ran the full ` attempt_apprentice() ` → ` do_recruit() ` (` call_out ` ,
691+ 2s) → ` command("recruit ...") ` chain and produced "恭喜您成为华山派的第
692+ 十四代弟子" plus a ` score ` showing the new 师傅/称谓 fields set correctly.
693+ ` learn huashan-sword at 3 from yue ` correctly gated on insufficient 内力
694+ ("你的内力不够,没有办法练华山剑法") — a resource/design gate, not a crash;
695+ ` learn unarmed at 1 from yue ` (a free/no-cost skill) succeeded end-to-end
696+ ("你听了岳不群的指导,似乎有些心得。你的「基本拳脚」进步了!"). Both
697+ mechanisms work correctly; no ` debug.log ` errors from either.
698+
699+ ** ` quit ` , debug.log grep, real-time reconnect (checklist items 7–9)** : ` 沈惊鸿 `
700+ was quit-and-reconnected many times over the session, both cleanly (` quit ` )
701+ and uncleanly (closing the socket without ` quit ` , which reliably triggers
702+ ` net_dead() ` — this lib does NOT void-park a net-dead player, §7.20 does not
703+ apply here, they stay exactly where they were, ` set_temp("netdead",1) ` only):
704+ - ** Prompt net-dead reconnect** : works correctly — a reconnect within the
705+ 10-second post-disconnect cooldown (` quit_time ` -gated, ` logind.lpc:~403 ` ,
706+ an intentional anti-thrash gate, not a bug) is rejected with "系统繁忙,
707+ 请等待 10秒 再行尝试连线", and a reconnect just after that window correctly
708+ routes through ` user->reconnect() ` (verified: position, gender, inventory
709+ all intact after several such cycles).
710+ - ** Clean ` quit ` after the 30-minute account-age threshold** : no deletion
711+ prompt (age-gated, correctly bypassed once ` birthday ` age > 1800s),
712+ "欢迎下次再来!", save file ` mtime ` updated, ` debug.log ` unchanged.
713+ - ** Real wall-clock reconnect after a clean quit** : waited ~ 130 real seconds
714+ (` until ` -loop blocking wait, not a background Monitor) after the clean
715+ quit above, then reconnected — room, Chinese name, gender, gift item
716+ (布衣/Cloth) all persisted correctly, zero new ` debug.log ` lines.
717+ - ** Full 900-second net-dead timeout** (` NET_DEAD_TIMEOUT ` in
718+ ` include/user.h ` , ` user_dump(DUMP_NET_DEAD) ` → ` command("quit") ` ): let a
719+ throwaway character (王大腿/wangdatui) sit net-dead across the entire
720+ real-time window (~ 18 minutes total wall-clock waited, combining this with
721+ pushing ` 沈惊鸿 ` 's own account age past 1800s in the same wait, per
722+ AGENTS.md's "one blocking wait, not stacked sleeps" guidance) — see the
723+ next finding for what this surfaced.
724+
725+ ** FINDING (driver-fatal crash, corroborates AGENTS.md §10.8) — FIXED,
726+ mudlib-side mitigation applied** : partway through the same real-time soak
727+ window above, the native driver process ** aborted outright**
728+ (` md: debugmalloc: attempted to free non-malloc'd pointer ... ` →
729+ ` abort() ` inside ` dealloc_object ` /` debugfree ` /` MDfree ` — the exact same
730+ crash signature already catalogued for ` shenzhou ` /` nitan170911 ` in §10.8),
731+ killing the whole server. This is the ** sixth** independent occurrence of
732+ this driver-level memory-corruption class across this project's round-two
733+ testing, still not root-caused to the driver's C++ internals (out of scope
734+ for an LPC-level pass; flagged to the human maintainer per §10.8's own
735+ standing request for a dedicated ASan/valgrind driver-level investigation).
736+
737+ ** Unlike every prior occurrence, this one left a concrete, actionable clue
738+ in ` debug.log ` immediately before the abort** — worth a partial revision to
739+ §10.8's "debug.log shows nothing whatsoever" framing (still true for the
740+ * abort itself* , which logs nothing, but a * contributing* uncaught runtime
741+ error can be visible just before it): a ` adm/daemons/questd.lpc `
742+ cron-driven quest-spreading routine (` spread_quest() ` /` init_dynamic_quest() ` ,
743+ called from ` adm/daemons/cron.lpc ` 's ` start_task() ` ) repeatedly picked
744+ random not-yet-loaded rooms from ` d/baituo/ ` (caoping, fende, dongkou,
745+ menlang, xiaolu3, ...) and called ` reset() ` on them; each one logged an
746+ UNCAUGHT `执行时段错误:* cannot bind a functional to an object with a
747+ pending replace_program()` from ` inherit/room/room.lpc:96`, immediately
748+ followed (same debug.log tail) by the fatal abort.
749+
750+ Root cause, confirmed by reading ` inherit/room/room.lpc ` : every room file in
751+ this lib ends its own ` create() ` with ` replace_program(ROOM) ` (the standard
752+ "generic clone becomes a real room" MudOS idiom, used lib-wide, not just in
753+ ` d/baituo/ ` ). ` inherit/room/room.lpc ` 's own ` reset() ` (and its sibling
754+ ` xyzx_system_clean_up() ` ) does
755+ ` inv = filter_array(inv, (: clonep($1) && !$1->is_character() :)); ` —
756+ creating a closure bound to ` this_object() ` . If ` reset() ` runs (whether via
757+ the driver's own post-` create() ` reset pass, matching the exact mechanism
758+ already catalogued in AGENTS.md §7.17, or via an explicit caller like
759+ ` spread_quest() ` ) ** before** that room's own ` replace_program() ` has
760+ actually committed, binding the closure throws — uncaught, since neither
761+ ` room.lpc ` 's ` reset() ` nor ` questd.lpc ` 's call site wrap it in ` catch() ` .
762+ Reproduced live and deterministically: on a fresh boot, ` goto ` -ing (as
763+ admin) to any of the five rooms named in the crash log reliably reproduced
764+ the identical uncaught error on cold first load, every time.
765+
766+ ** This is very likely a material contributor to the crash** (strong
767+ temporal correlation — the exact same shape §7.12's escalation note and
768+ §10.8 already document: a ` call_out ` /cron-driven function with no enclosing
769+ ` catch() ` , hit repeatedly right before an abort) but, consistent with every
770+ other §10.8 entry, ** not proven as the sole cause** — the driver-level
771+ memory corruption itself remains unexplained. Fixed the mudlib-side part
772+ regardless, since it's a real, reproducible, uncaught runtime error in its
773+ own right (matches AGENTS.md's "missing guard around a driver-documented
774+ throw" fixable class, same shape as §7.17/§7.19/§7.25): wrapped both
775+ ` filter_array(...) ` calls in ` inherit/room/room.lpc ` (` xyzx_system_clean_up() `
776+ and ` reset() ` ) in ` catch() ` , skipping just that one cleanup/population pass
777+ on failure rather than aborting the whole calling chain. ** Verified fixed
778+ live** : after the fix, ` goto ` -ing to all five previously-affected rooms
779+ produced zero occurrences of the error string in ` debug.log ` (previously
780+ 100% reproducible for exactly these rooms). Did not have time/opportunity
781+ to re-run the full 900s+ soak a second time to confirm the * crash itself*
782+ doesn't recur — the fix targets a confirmed, reproducible contributing
783+ error, not a confirmed-eliminated crash; note this honestly rather than
784+ claim more than was verified.
785+
786+ ** Draft new AGENTS.md bug-class candidate** (since this shape doesn't
787+ exactly match any existing §7.x entry — closest are §7.17 and §7.25, but
788+ both are scoped to NPC/room * population* helpers, not a generic * periodic
789+ daemon reset() call* ): "A universal ` replace_program(ROOM) ` -in-` create() `
790+ room idiom, combined with an unguarded ` filter_array() ` -closure in the
791+ shared room base class's ` reset() ` , throws an uncaught runtime error
792+ whenever ANY caller (the driver's own post-create reset, or a periodic
793+ daemon like a quest-spreader) reaches a room's ` reset() ` before its
794+ ` replace_program() ` commits — reproduces reliably on that room's cold
795+ first load specifically (not on later resets), and is easy to miss because
796+ ordinary login/registration testing rarely cold-loads obscure zone rooms
797+ that a login-daemon walkthrough never visits." Left as a draft here per
798+ the task instructions rather than editing AGENTS.md directly.
799+
800+ ** FINDING (observation only, NOT fixed — insufficient reproducibility to
801+ be confident in a root cause or a fix)** : during the same net-dead soak,
802+ the throwaway character 王大腿 (id ` wangdatui ` )'s ** login-object companion
803+ save file** (` data/login/w/wangdatui.o ` , written by ` clone/user/login.lpc ` 's
804+ own ` ->save() ` , separate from the player body's ` data/user/w/wangdatui.o ` )
805+ was observed ** missing** for an extended period after a normal, successful
806+ registration + uncontrolled (socket-close) disconnect — confirmed via
807+ ` stat ` : the ` data/login/w/ ` directory itself didn't exist until a later,
808+ unrelated manual reconnect attempt created it. Because ` logind.lpc ` 's
809+ "does this id already exist" check (` get_id() ` , ~ line 322) tests
810+ ` file_size(ob->query_save_file() + ext) >= 0 ` against the LOGIN object's
811+ own file, not the player body's, this made the game ** incorrectly treat a
812+ real, already-registered account as available for a brand-new
813+ registration** on the very next connection attempt ("使用 wangdatui 这个
814+ 名字将会创造一个新的人物,您确定吗?") — a genuinely concerning shape,
815+ since confirming that prompt could plausibly have orphaned or overwritten
816+ the real ` data/user/w/wangdatui.o ` (not tested — did not want to risk
817+ actually destroying evidence of the bug before writing it up).
818+
819+ ** Why this is documented, not fixed** : a second, deliberately clean and
820+ isolated repro attempt immediately afterward (a fresh character, ` cesitwo ` ,
821+ same registration flow, same subsequent uncontrolled disconnect) did ** NOT**
822+ reproduce the missing-login-file symptom — its ` data/login/c/cesitwo.o `
823+ was written correctly and immediately, during registration itself, exactly
824+ as ` enter_world() ` 's ` user->save(); ob->save(); ` (both unconditional, no
825+ guard) is supposed to do. So this is not a deterministic, on-demand
826+ reproducible bug — closer in spirit to the low-reproducibility class
827+ AGENTS.md §10.8 already normalizes for the driver-crash findings, just at
828+ the mudlib level this time. ` wangdatui ` 's own data (both copies) had fully
829+ disappeared by the time this was investigated further, consistent with the
830+ account eventually being swept up by the same age-based cleanup this lib
831+ already runs elsewhere for abandoned sub-1-hour registrations (plausible,
832+ not confirmed — the exact deletion trigger and timing were not pinned down
833+ either). Per AGENTS.md §10.7's own scope note ("when genuinely unsure...
834+ document it honestly... and leave the code untouched — don't guess"), no
835+ code change was made for this one. ** Suggested direction for a future
836+ pass, not applied** : the ` get_id() ` existence check could be made more
837+ robust by also/instead consulting ` data/user/<shard>/<id>.o ` (the player
838+ body's own save, which this session's evidence suggests is the more
839+ reliably-written of the two), rather than trusting the login object's
840+ companion file alone as the sole signal that an id is taken.
841+
842+ ** Shop / economy** : reached a 当铺 (pawnshop, ` 南大街 ` off ` 扬州 ` /Yangzhou,
843+ per the newbie doc's own documented path ` fly yz;w;s;s;e ` ) with ` 沈惊鸿 ` .
844+ ` list ` returned "目前没有可以卖的东西" (nothing currently sellable) — this
845+ particular shop's ` list ` command is geared toward the player * selling* to
846+ the pawnshop rather than browsing a buy-menu, and the fresh newbie
847+ character had nothing pawnable equipped beyond the starter cloth. Command
848+ dispatch itself worked correctly (no crash, sensible response) but an
849+ actual gold-for-item purchase transaction was ** not completed** — noting
850+ this honestly as unverified-live rather than claiming it was tested, per
851+ the checklist's own instruction. Did not reach combat/death against a real
852+ (non-training-dummy) opponent either, given the time already spent on the
853+ net-dead soak and the crash investigation above — also explicitly flagged
854+ as unverified-live.
855+
856+ ** Process note** : the native driver process was involuntarily SIGTERM'd
857+ once early in this session (visible in ` /tmp/longyunmeng_driver_stdout*.log `
858+ as a clean ` attempt_shutdown ` /backtrace-then-exit, not a crash) between two
859+ otherwise-unrelated tool calls, consistent with this environment's known
860+ "stray SIGTERM between tool calls" issue already documented in AGENTS.md
861+ §10.5 despite using the recommended ` setsid nohup ... & disown ` launch
862+ pattern — restarted and continued; not a mudlib issue.
863+
864+ ** Files modified** : ` libs/longyunmeng/work/inherit/room/room.lpc ` (the
865+ ` catch() ` fix above). ` libs/longyunmeng/work/data/user/f/fluffos.o ` also
866+ shows as modified in ` git status ` — normal drift from the admin account
867+ being logged into several times during this pass (` last_on ` -style fields),
868+ not a content change. All throwaway test-character save/key files
869+ (` shenym ` /沈惊鸿, ` lintest ` /林测试, ` cesitwo ` /测试贰号) were removed at the
870+ end of the session — two via the game's own quit-before-30-min deletion
871+ flow (exercising that code path as a side effect), one (` shenym ` , past the
872+ 30-minute mark) via direct file removal. ` wangdatui ` /王大腿's own data was
873+ already gone by the end of the session (see the finding above).
0 commit comments