Skip to content

Commit e682fb1

Browse files
thefallentreeclaude
andcommitted
Fix 3 bugs found in fengyun3dianzang's deep functional test
- adm/simul_efun/object.lpc: §7.26 fix -- file_owner()'s sscanf pattern captured the SECOND segment after /u/ instead of the wizard's own directory name, correct only for exactly-2-levels-deep content (the rare case). Misattributed nested wizard content, crashing log_error()'s write on any compile diagnostic for it. - adm/simul_efun/path.lpc: new bug -- user_cwd() assumed a letter- sharded /u/ layout this archive's own tree never used (confirmed pre-existing in the raw archive, not a conversion artifact), silently resolving to a nonexistent directory. Broke bare `cd` for every wizard and, via the same log_error() path as the fix above, any compile diagnostic on nested content. Live-verified post-fix: admin cwd now resolves correctly. - cmds/usr/save.lpc: added an environment(me) null guard, the same §7.14-class fix already present in this lib's own cmds/usr/quit.lpc (ported from fengyun2qinghua/fy2). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VyQCUoTo1Z93Py9aVFHQi1
1 parent 387cc04 commit e682fb1

5 files changed

Lines changed: 217 additions & 5 deletions

File tree

libs/fengyun3dianzang/NOTES.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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.

libs/fengyun3dianzang/work/adm/simul_efun/object.lpc

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,26 @@ varargs int getoid(object ob) {
99
}
1010

1111
// Get the owner of a file. Used by log_error() in master.c.
12+
// AGENTS.md §7.26: the original 2-capture-group sscanf below returned
13+
// the SECOND segment after /u/ (e.g. "npc" for "/u/guanwai/npc/foo.lpc")
14+
// instead of the wizard's own directory name -- only correct for files
15+
// exactly 2 levels under /u/, which is the rare case; virtually all real
16+
// wizard content lives 3+ levels down (npc/, obj/, room/ subdirs). This
17+
// misattribution sent log_error()'s write to a nonexistent directory
18+
// (e.g. "/u/n/npc/log"), aborting with "Wrong permissions for opening
19+
// file ... for append" on ANY compile diagnostic (warning or error) for
20+
// nested /u/ content -- confirmed live via /u/guanwai/npc/* compile
21+
// warnings during a deep functional test. Fix: return only the first
22+
// segment after /u/, regardless of nesting depth (matches the
23+
// first-segment discipline domain_file()/creator_file() below already
24+
// use for /d/ and other roots).
1225
string file_owner(string file) {
13-
string name, rest, dir;
26+
string name, rest;
1427

1528
if (file[0] != '/') {
1629
file = "/" + file;
1730
}
18-
if (sscanf(file, "/u/%s/%s/%s", dir, name, rest) == 3) {
31+
if (sscanf(file, "/u/%s/%s", name, rest) == 2) {
1932
return name;
2033
}
2134
return 0;

libs/fengyun3dianzang/work/adm/simul_efun/path.lpc

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
// path.c
22

33
string user_cwd(string name) {
4-
return ("/u/" + name[0..0] + "/" + name);
4+
// This lineage's user_path()/user_cwd() assumed a letter-sharded
5+
// wizard-directory layout ("/u/<first-letter>/<name>/", the ES II
6+
// convention seen elsewhere in this project) but this archive's own
7+
// /u/ tree has always been flat ("/u/<name>/") -- confirmed present
8+
// in the raw, pre-conversion archive too, so this is a pre-existing
9+
// mismatch in the original code, not a conversion artifact. The
10+
// letter-sharded path silently resolves to a directory that never
11+
// exists, breaking bare `cd` (cmds/adm/cd.lpc's no-arg default) for
12+
// every wizard and, via master.lpc's log_error(), the write path for
13+
// ANY compile diagnostic on nested /u/ content (same externally-
14+
// visible failure shape as AGENTS.md §7.26, different root cause --
15+
// confirmed live via /u/guanwai/npc/* compile warnings during a deep
16+
// functional test, after fixing file_owner()'s own §7.26-class bug
17+
// first surfaced the SAME "no such file or directory" one level
18+
// further down the same call chain).
19+
return ("/u/" + name);
520
}
621

722
string user_path(string name) {

libs/fengyun3dianzang/work/cmds/usr/save.lpc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ int main(object me, string arg) {
1010
if (!objectp(link_ob = me->query_temp("link_ob")))
1111
return notify_fail("你不是经由正常连线进入,不能储存。\n");
1212

13-
if (environment(me)->query("valid_startroom")) {
13+
// Guard against environment(me) being 0 -- same class of mistimed-call
14+
// crash already fixed in cmds/usr/quit.lpc (AGENTS.md §7.14, ported
15+
// from fengyun2qinghua/fy2): a player with commands enabled but no
16+
// environment yet (or a corpse mid-transition) would otherwise crash
17+
// message-dispatch-adjacent code on a void environment.
18+
if (environment(me) && environment(me)->query("valid_startroom")) {
1419
me->set("startroom", base_name(environment(me)));
1520
write("当你下次连线进来时,会从这里开始。\n");
1621
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#/obj/user.lpc
2-
dbase (["sen":320,"title":"普通百姓","fle":10,"agi":10,"water":200,"cwf":"/adm/daemons/combatd.lpc","channels":({"chat","fy","new","rumor",}),"birthday":1784876131,"per":20,"default_actions":,"race":"人类","env":(["wimpy":50,]),"kee":300,"gender":"男性","startroom":"/d/fy/fqkhotel","con":10,"dur":10,"max_gin":300,"cor":5,"spi":10,"potential":299,"max_kee":300,"mud_age":0,"name":"浮浮","int":12,"tol":5,"eff_gin":300,"id":"fluffos","combat_exp":2000,"actions":,"cps":10,"food":200,"kar":10,"eff_sen":320,"age":10,"gift_points":5,"eff_kee":300,"national":"汉族","str":10,"max_sen":320,"gin":300,])
2+
dbase (["sen":320,"title":"普通百姓","fle":10,"agi":10,"water":200,"cwf":"/u/guanwai/npc/petowner.lpc","channels":({"chat","fy","new","rumor",}),"birthday":1784876131,"per":20,"default_actions":,"race":"人类","env":(["wimpy":50,]),"kee":300,"gender":"男性","startroom":"/d/fy/fqkhotel","con":10,"dur":10,"max_gin":300,"cor":5,"spi":10,"potential":299,"max_kee":300,"mud_age":0,"name":"浮浮","int":12,"tol":5,"eff_gin":300,"id":"fluffos","combat_exp":2000,"actions":,"cps":10,"cwd":"/u/guanwai/","food":200,"kar":10,"eff_sen":320,"age":10,"gift_points":5,"eff_kee":300,"national":"汉族","str":10,"max_sen":320,"gin":300,])
33
autoload ({})

0 commit comments

Comments
 (0)