@@ -361,3 +361,182 @@ functions -- §3's own footnote that this warning is harmless -- and
361361- ** Save files to force-add** (untracked, NOT gitignored):
362362 ` libs/fengyun3dianzang/work/data/user/f/fluffos/fluffos.o ` ,
363363 ` libs/fengyun3dianzang/work/data/login/f/fluffos/fluffos.o ` .
364+
365+ ## 深度功能测试 / Deep functional test (AGENTS.md §10.7, round two)
366+
367+ One continuous native session (native driver, ` scripts/mudclient.py ` ),
368+ following the lib's own ` doc/help/newbie.txt ` as the intended test path
369+ (拜师 via ` apprentice ` , ` learn <skill> from <master> with <potential> ` ,
370+ ` fight ` for safe sparring, ` list ` /` buy ` for shop purchases). Several real
371+ Chinese-name test characters registered across the session (沈风/曲阳/
372+ 林风/北斗), landing correctly in nationality-specific start zones (汉族
373+ → 凤求凰客栈; 苗族 → 沉香镇/沉香南宫). ` look ` /` score ` /` i ` /` hp ` all
374+ correct at every state change.
375+
376+ ### Bugs found and fixed
377+
378+ 1 . ** ` file_owner() ` path-depth off-by-one — confirmed live instance of
379+ AGENTS.md §7.26** , ` adm/simul_efun/object.lpc ` . The original
380+ ` sscanf(file, "/u/%s/%s/%s", dir, name, rest) == 3; return name; `
381+ returned the SECOND segment after ` /u/ ` (e.g. ` "npc" ` for
382+ ` /u/guanwai/npc/petowner.lpc ` ) instead of the wizard's own directory
383+ name — correct only for the rare exactly-2-levels-deep case. Every
384+ compile diagnostic (even a harmless "Unused local variable" warning)
385+ on nested ` /u/ ` content (the normal case — ` npc/ ` , ` obj/ ` subdirs)
386+ made ` master.lpc ` 's ` log_error() ` write to a bogus path
387+ (` /u/n/npc/log ` ), throwing `* Wrong permissions for opening file ...
388+ for append. "No such file or directory"` — caught by the driver so
389+ nothing crashed visibly, but spammed ` debug.log ` with a real error on
390+ ordinary lazy compiles (reproduced live: any command that triggers a
391+ fresh compile of ` /u/guanwai/npc/* ` or ` /u/guanwai/obj/* ` content,
392+ e.g. an NPC wandering there, or an admin ` update ` ). Fixed per §7.26's
393+ own established pattern: capture only the first segment after ` /u/ `
394+ (` sscanf(file, "/u/%s/%s", name, rest) == 2; return name; ` ).
395+
396+ 2 . ** NEW bug class: ` user_path() ` /` user_cwd() ` assume a letter-sharded
397+ wizard-directory layout that this archive's ` /u/ ` tree never had** ,
398+ ` adm/simul_efun/path.lpc ` . ` user_cwd(name) ` returned `"/u/" +
399+ name[ 0..0] + "/" + name` (e.g. ` /u/g/guanwai/`, the ES II-family
400+ letter-sharding convention seen elsewhere in this project), but this
401+ archive's actual ` /u/ ` tree is flat (` /u/guanwai/ ` , ` /u/palace/ ` ,
402+ etc.) — confirmed present in the RAW, pre-conversion archive too
403+ (` raw/fy3dcb/风云典藏版/u/ ` has the same flat layout), so this is a
404+ pre-existing mismatch in the original code, not something the
405+ conversion pipeline introduced. Only 3 call sites lib-wide
406+ (` cmds/adm/cd.lpc ` , ` adm/obj/master.lpc ` 's ` log_error() ` ,
407+ ` path.lpc ` itself), both wizard/admin-facing: bare ` cd ` (no
408+ argument) resolved to a directory that never exists for EVERY
409+ wizard (` 没有这个目录。 ` ), and — chained with bug #1 above — even
410+ after fixing ` file_owner() ` , ` log_error() ` still failed one level
411+ further down (` /u/g/guanwai/log ` instead of ` /u/guanwai/log ` ) until
412+ this was also fixed. Fixed by dropping the letter-shard segment:
413+ ` return ("/u/" + name); ` . Verified live after a fresh driver
414+ restart: ` update /u/guanwai/npc/petowner ` now recompiles cleanly
415+ with zero ` debug.log ` errors, and ` cd ~guanwai ` correctly resolves
416+ to ` /u/guanwai/ ` (bare ` cd ` for ` fluffos ` itself still reports "没
417+ 有这个目录" — expected, since the seeded admin account has no real
418+ ` /u/fluffos/ ` wizard-content directory of its own, not a bug).
419+ ** Likely affects other ES II-lineage siblings with a flat ` /u/ `
420+ archive layout** — worth a quick ` ls u/ ` + grep ` user_cwd\( ` check
421+ on ` fengyun3xiuding ` (confirmed byte-identical ` master.c ` /
422+ ` simul_efun.c ` /` securityd.c ` /` chinese.c ` to this lib per the lineage
423+ table above) and the other 风云3 siblings.
424+
425+ 3 . ** ` cmds/usr/save.lpc ` 's unguarded ` environment(me)->query(...) ` —
426+ same class as the already-fixed AGENTS.md §7.14 instance in
427+ ` cmds/usr/quit.lpc ` (same lib, explicit code comment there credits
428+ "Same fix as fengyun2qinghua/fy2")** . The ` save ` command's
429+ ` valid_startroom ` check dereferenced ` environment(me) ` without a
430+ null guard; ` quit.lpc ` 's sibling call site already carries the
431+ defensive ` if (environment(me)) ` guard for the identical
432+ post-registration-race class, but ` save.lpc ` was missed. Added the
433+ same guard (` if (environment(me) && environment(me)->query(...)) ` ).
434+ Not independently reproduced live (the race window is narrow and
435+ this lib's ` enter_world() ` has no intervening ` input_to ` pause that
436+ would make it easy to hit), but the fix is cheap, safe, and directly
437+ mirrors an already-established, already-fixed pattern in the exact
438+ same file tree — applied proactively per AGENTS.md's own "port to
439+ every sibling immediately" guidance, scoped here to a sibling call
440+ site within the SAME lib rather than a different lib.
441+
442+ ### Confirmed working (no bug)
443+
444+ - ** Safe-sparring mechanism** : the ` fight ` command (documented in its
445+ own ` help fight ` : "这种形式的战斗纯粹是点到为止,因此只会消耗体力,
446+ 不会真的受伤" — pulls punches, costs stamina only, no real injury).
447+ Traced into ` adm/daemons/combatd.lpc ` : ` receive_wound() ` (real injury)
448+ only fires when ` me->is_killing(victim) || weapon ` is true; a bare
449+ ` fight ` never sets ` is_killing ` , so damage only depletes ` kee `
450+ (stamina) and the match auto-stops at 50% kee on either side. Verified
451+ live against 寒梅先生 (a "peaceful"-attitude NPC near the start zone,
452+ reachable from ` fqkhotel ` via one ` west ` ) — full HP/kee/gin/sen intact
453+ after a full exchange, "结果没有造成任何伤害" (no damage resulted) on
454+ the received hits. Note: ` accept_fight() ` (` std/char/npc.lpc ` ) always
455+ refuses for ` "friendly" ` -attitude NPCs (confirmed against ` npc/waiter `
456+ and the 黄衣卫 guards — "在下怎麽可能是小兄弟的对手?") — pick a
457+ ` "peaceful" ` - or unlabeled-attitude NPC for the safe-spar test, not a
458+ friendly shopkeeper.
459+ - ** Organic sect-join + skill-learning path** : ` apprentice master jin `
460+ (荆无命, 金钱帮/Money Gang, reachable from ` fqkhotel ` via
461+ west/south/west/west/south/south) worked exactly per ` attempt_apprentice() `
462+ → ` recruit ` → ` recruit_apprentice() ` ; ` score ` correctly updated title
463+ to "金钱帮第三代弟子" with "你的师父是荆无命". `learn move from master
464+ with 10` (exact syntax required — bare ` learn move` just prints the
465+ format string) correctly deducted 10 潜能/consumed 精力, and ` skills `
466+ showed the new skill at level 1. No sect-join shortcut/admin command
467+ found elsewhere to cross-check against (per §10.7 item 4) — the
468+ organic NPC path is confirmed to be the only route in this lib.
469+ - ** Net-dead handling, prompt reconnect** : ` obj/user.lpc ` 's ` net_dead() `
470+ does NOT void-park the player (no ` VOID_OB ` move at all — structurally
471+ immune to the AGENTS.md §7.20 bug class) — it just flags ` netdead ` ,
472+ schedules a ` user_dump ` force-quit ` call_out ` at ` NET_DEAD_TIMEOUT `
473+ (900s), and leaves the player body in place. A prompt reconnect
474+ (disconnect without ` quit ` , immediately reconnect with the same id)
475+ correctly hit ` reconnect() ` , which cancels the pending ` user_dump ` and
476+ restores the SAME room/state with zero loss — verified live (character
477+ ` 北斗 ` disconnected then promptly reconnected at ` 沉香南宫 ` , ` score `
478+ unchanged).
479+ - ** ` tell_room() ` 2-arg wrapper bug (§7.12)** : ` user_dump() ` 's
480+ ` DUMP_NET_DEAD ` branch calls the 2-arg form
481+ (` tell_room(environment(), "...") ` ); confirmed the lib-wide fix
482+ documented above (fix #6 in the original pass, ` exclude || ({}) ` ) is
483+ in effect, so this specific call site is NOT vulnerable to §7.12's
484+ crash shape.
485+ - ** Quit-time ` environment(me) ` guard (§7.14)** : ` cmds/usr/quit.lpc `
486+ already carries the defensive guard (see bug #3 above for the sibling
487+ gap this pass found and fixed in ` save.lpc ` ).
488+ - ** Admin account** : re-verified ` fluffos ` /` (admin) ` login and
489+ ` update /adm/daemons/combatd ` -equivalent access still work against the
490+ rebuilt driver (` update /u/guanwai/npc/petowner ` used as this pass's
491+ ACL-exercising check, succeeding cleanly after the fixes above).
492+
493+ ### Not independently verified this pass (honest gaps)
494+
495+ - ** Long-duration net-dead force-quit (past the 900s ` NET_DEAD_TIMEOUT ` )
496+ and a real-wall-clock-gap reconnect-after-clean-quit check** — per
497+ §10.7 item 8/9. A test character (` 北斗 ` ) was deliberately left
498+ net-dead specifically to run this check, but the only available
499+ multi-minute wait mechanism in this environment (a genuinely blocking
500+ ` sleep 900+ ` ) is blocked by this session's own tool policy (which
501+ requires either ` Monitor ` /` run_in_background ` , both of which this
502+ task's own instructions separately prohibit using passively), and a
503+ background sleep was armed and then explicitly stopped short per a
504+ live course-correction mid-task rather than left to complete. The
505+ short/prompt net-dead reconnect path IS verified live (see above); the
506+ driver's own ` user_dump() ` /` DUMP_NET_DEAD ` code path past the full
507+ 900s timeout, and whatever a real ≥45-minute gap would do to the
508+ ` cron.lpc ` autosave-adjacent state, were reviewed by reading the code
509+ (see "Confirmed working" above and the ` tell_room() ` /` environment() `
510+ guard checks) but not independently exercised live end-to-end this
511+ pass. ` 北斗 ` was left connected-then-disconnected (net-dead) at
512+ ` 沉香南宫 ` /南宫钱庄 mid-pass; its actual disk save reflects only the
513+ pre-net-dead state (no family/skills — this character never
514+ apprenticed or learned anything, only walked and looked).
515+ - ** Shop purchase / economy** : attempted (` list ` /`buy dumpling from
516+ waiter` at the start-zone waiter vendor) but every fresh character
517+ starts with 0 money ("你的钱不够" — insufficient funds) and no
518+ in-session way to earn any was exercised, so a successful purchase was
519+ never completed live. Code-reviewed only (F_VENDOR's ` vendor_goods `
520+ mapping, ` std/room/shop.lpc ` 's save-on-purchase) — not exercised.
521+ - ** Combat progression to death/respawn** : not reached — time budget
522+ went to the sect-join/skill-learn/safe-spar/net-dead checks instead,
523+ per §10.7 item 6's explicit permission to state this honestly rather
524+ than presenting it as tested.
525+ - ** Ambient NPC violence observation (not a bug, noting for context)** :
526+ partway through this pass, ` npc/waiter ` (the start-zone shopkeeper)
527+ was found dead (` 店小二的尸体 ` /Waiter's corpse) with no live
528+ replacement, most plausibly killed by one of the newbie-doc's own
529+ documented "see you, kill you" wandering hostiles (强盗/土匪/疯狗) —
530+ ambient world simulation the newbie doc itself warns new players
531+ about, not something this pass's testing triggered. ` std/room.lpc ` 's
532+ ` reset() ` /` make_inventory() ` correctly re-clones a dead NPC (` die() `
533+ destructs the original object, so ` objectp() ` on the tracked reference
534+ goes false and a fresh clone is made on the room's next ` reset() ` ) —
535+ confirmed by reading the code, not confirmed live within this pass's
536+ time budget (the room's ` time to reset ` is 900s and the corpse was
537+ still present when last checked, but that observation window was
538+ itself well under 900s of continuous room-idle time, so it isn't
539+ conclusive either way). If a future pass finds the shop permanently
540+ vendor-less across a real reset interval, that would be worth
541+ revisiting as a genuine reset/repopulation bug — not confirmed as one
542+ here.
0 commit comments