Merge pull request #135 from ViPro-Technologies/NEO-96-e5m2-10-npc-telemetry-hooks
NEO-96: E5M2-10 Slice 2 NPC telemetry hook sitespull/138/head
commit
1ce2b9893c
|
|
@ -7,7 +7,7 @@
|
|||
| **Module ID** | E5.M2 |
|
||||
| **Epic** | [Epic 5 — PvE Combat](../epics/epic_05_pve_combat.md) |
|
||||
| **Stage target** | Prototype |
|
||||
| **Status** | In Progress — Slice 2 backlog [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md): **E5M2-01** [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) catalog + CI landed · **E5M2-02** [NEO-88](https://linear.app/neon-sprawl/issue/NEO-88) server load landed · **E5M2-03** [NEO-89](https://linear.app/neon-sprawl/issue/NEO-89) registry + DI landed · **E5M2-04** [NEO-90](https://linear.app/neon-sprawl/issue/NEO-90) behavior-definitions GET landed · **E5M2-05** [NEO-91](https://linear.app/neon-sprawl/issue/NEO-91) NPC instance registry + combat-target migration landed · **E5M2-06** [NEO-92](https://linear.app/neon-sprawl/issue/NEO-92) aggro rule + threat store landed · **E5M2-07** [NEO-93](https://linear.app/neon-sprawl/issue/NEO-93) NPC runtime state machine landed · **E5M2-08** [NEO-94](https://linear.app/neon-sprawl/issue/NEO-94) runtime snapshot GET landed · **E5M2-09** [NEO-95](https://linear.app/neon-sprawl/issue/NEO-95) NPC attack resolve + player combat HP landed → **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) |
|
||||
| **Status** | In Progress — Slice 2 backlog [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md): **E5M2-01** [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) catalog + CI landed · **E5M2-02** [NEO-88](https://linear.app/neon-sprawl/issue/NEO-88) server load landed · **E5M2-03** [NEO-89](https://linear.app/neon-sprawl/issue/NEO-89) registry + DI landed · **E5M2-04** [NEO-90](https://linear.app/neon-sprawl/issue/NEO-90) behavior-definitions GET landed · **E5M2-05** [NEO-91](https://linear.app/neon-sprawl/issue/NEO-91) NPC instance registry + combat-target migration landed · **E5M2-06** [NEO-92](https://linear.app/neon-sprawl/issue/NEO-92) aggro rule + threat store landed · **E5M2-07** [NEO-93](https://linear.app/neon-sprawl/issue/NEO-93) NPC runtime state machine landed · **E5M2-08** [NEO-94](https://linear.app/neon-sprawl/issue/NEO-94) runtime snapshot GET landed · **E5M2-09** [NEO-95](https://linear.app/neon-sprawl/issue/NEO-95) NPC attack resolve + player combat HP landed · **E5M2-10** [NEO-96](https://linear.app/neon-sprawl/issue/NEO-96) NPC runtime telemetry hooks landed → **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) |
|
||||
| **Linear** | Label **`E5.M2`** · [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md) **E5M2-01** [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) · **E5M2-02** [NEO-88](https://linear.app/neon-sprawl/issue/NEO-88) · **E5M2-03** [NEO-89](https://linear.app/neon-sprawl/issue/NEO-89) · **E5M2-04** [NEO-90](https://linear.app/neon-sprawl/issue/NEO-90) · **E5M2-05** [NEO-91](https://linear.app/neon-sprawl/issue/NEO-91) → **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) |
|
||||
|
||||
## Purpose
|
||||
|
|
@ -91,6 +91,8 @@ The **first shipped three-archetype NPC spine** under `content/npc-behaviors/*.j
|
|||
|
||||
**Session player combat HP (NEO-95):** **`IPlayerCombatHealthStore`** + **`NpcAttackOperations.TryResolveTelegraphComplete`** in `server/NeonSprawl.Server/Game/Combat/` — telegraph complete applies catalog **`attackDamage`** to aggro holder; **`GET /game/players/{id}/combat-health`** read model for client HUD. Plan: [NEO-95 implementation plan](../../plans/NEO-95-implementation-plan.md); [server README — Session player combat HP (NEO-95)](../../../server/README.md#session-player-combat-hp-neo-95); Bruno `bruno/neon-sprawl-server/combat-health/`.
|
||||
|
||||
**NPC runtime telemetry hooks (NEO-96):** comment-only **`telegraph_fired`** + **`npc_state_transition`** hook sites in **`NpcRuntimeOperations.AdvanceAll`** (`TODO(E9.M1)`; no ingest). Plan: [NEO-96 implementation plan](../../plans/NEO-96-implementation-plan.md); [server README — NPC runtime telemetry hooks (NEO-96)](../../../server/README.md#npc-runtime-telemetry-hooks-neo-96).
|
||||
|
||||
**NPC behavior definitions HTTP (NEO-90):** **`GET /game/world/npc-behavior-definitions`** — versioned read-only projection (`schemaVersion` **1**, **`npcBehaviors`**) backed by **`INpcBehaviorDefinitionRegistry`**. Plan: [NEO-90 implementation plan](../../plans/NEO-90-implementation-plan.md); [server README — NPC behavior definitions (NEO-90)](../../../server/README.md#npc-behavior-definitions-neo-90); Bruno `bruno/neon-sprawl-server/npc-behavior-definitions/`.
|
||||
|
||||
## Risks and telemetry
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -322,8 +322,10 @@ Working backlog for **Epic 5 — Slice 2** ([NPC archetypes and telegraphs](../d
|
|||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Hook names match [epic_05 Slice 2](../decomposition/epics/epic_05_pve_combat.md#slice-2---npc-archetypes-and-telegraphs) vocabulary.
|
||||
- [ ] No runtime behavior change beyond comments.
|
||||
- [x] Hook names match [epic_05 Slice 2](../decomposition/epics/epic_05_pve_combat.md#slice-2---npc-archetypes-and-telegraphs) vocabulary.
|
||||
- [x] No runtime behavior change beyond comments.
|
||||
|
||||
**Landed ([NEO-96](https://linear.app/neon-sprawl/issue/NEO-96)):** comment-only **`telegraph_fired`** + **`npc_state_transition`** hook sites in **`NpcRuntimeOperations`**; plan [NEO-96-implementation-plan.md](NEO-96-implementation-plan.md).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
# NEO-96 — Implementation plan
|
||||
|
||||
## Story reference
|
||||
|
||||
| Field | Value |
|
||||
|--------|--------|
|
||||
| **Key** | NEO-96 |
|
||||
| **Title** | E5M2-10: Slice 2 NPC telemetry hook sites |
|
||||
| **Linear** | https://linear.app/neon-sprawl/issue/NEO-96/e5m2-10-slice-2-npc-telemetry-hook-sites |
|
||||
| **Module** | [E5.M2 — NpcAiAndBehaviorProfiles](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) · Epic 5 Slice 2 · backlog **E5M2-10** |
|
||||
| **Branch** | `NEO-96-e5m2-10-npc-telemetry-hooks` |
|
||||
| **Precursor** | [NEO-93](https://linear.app/neon-sprawl/issue/NEO-93) — NPC runtime state machine + lazy tick (**Done** on `main`; left `(NEO-96)` placeholder comments) |
|
||||
| **Pattern** | [NEO-64](https://linear.app/neon-sprawl/issue/NEO-64) / [NEO-84](https://linear.app/neon-sprawl/issue/NEO-84) — comment-only engine anchors + `TODO(E9.M1): catalog emit` + planned payload fields |
|
||||
| **Client counterpart** | None — server-only comment hooks; Godot poll/HUD is [NEO-97](https://linear.app/neon-sprawl/issue/NEO-97) |
|
||||
|
||||
## Kickoff clarifications
|
||||
|
||||
| Topic | Question | Agent recommendation | Answer |
|
||||
|--------|----------|----------------------|--------|
|
||||
| **Runtime behavior** | Comments-only vs dev `ILogger`? | **Comments-only** — `TODO(E9.M1)`; no production logging (NEO-64/NEO-84 precedent). | **Adopted** (repo precedent; not asked). |
|
||||
| **Hook anchor** | Engine only vs engine + snapshot HTTP API? | **Engine only** in **`NpcRuntimeOperations`** — snapshot GET calls `AdvanceAll`; no duplicate API-layer hooks (NEO-56/NEO-64 precedent). | **Adopted** (repo precedent; not asked). |
|
||||
| **`telegraph_fired` timing** | At telegraph start vs windup complete / attack resolve? | **At each `telegraph_windup` entry** — aggro→telegraph and recover→telegraph (E5M2 backlog: “at telegraph start”). | **Adopted** (backlog AC). |
|
||||
| **`npc_state_transition` scope** | Every `WriteState` commit including holder clear → idle? | **Yes** — emit anchor on every committed state change, including **`WriteIdle`** when aggro holder clears. | **Adopted** (backlog: “on state transition commit”). |
|
||||
| **Manual QA doc** | `docs/manual-qa/NEO-96.md`? | **No** — no client-facing change; NEO-93 server-only pattern (NEO-64 added manual QA for gather hooks with broader doc surface). | **User:** no manual QA — not client-facing. |
|
||||
|
||||
## Goal, scope, and out-of-scope
|
||||
|
||||
**Goal:** Formalize **comment-only** E9.M1 telemetry hook sites for Epic 5 Slice 2 product events **`telegraph_fired`** and **`npc_state_transition`** on the server-authoritative NPC runtime path.
|
||||
|
||||
**In scope (from Linear + [E5M2-10](E5M2-prototype-backlog.md#e5m2-10--slice-2-npc-telemetry-hook-sites)):**
|
||||
|
||||
- Replace NEO-93 `(NEO-96)` stub comments in **`NpcRuntimeOperations`** with full NEO-84-style blocks: event name, **`TODO(E9.M1): catalog emit`**, planned payload fields.
|
||||
- **`telegraph_fired`** at each **`telegraph_windup`** entry (telegraph start).
|
||||
- **`npc_state_transition`** at every state commit (idle↔aggro, phase transitions, holder clear → idle).
|
||||
- **`server/README.md`** NPC telemetry subsection + [E5_M2 module](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) landed note.
|
||||
- E5M2 backlog acceptance checkboxes + alignment register bump when complete.
|
||||
|
||||
**Out of scope (from Linear + backlog):**
|
||||
|
||||
- Production telemetry ingest, dashboards (**E9.M1**).
|
||||
- `ILogger` / metrics / dev-only log lines.
|
||||
- Godot / client hooks ([NEO-97](https://linear.app/neon-sprawl/issue/NEO-97)).
|
||||
- Duplicate hook comments in **`NpcRuntimeSnapshotWorldApi`** or **`NpcAttackOperations`** (engine orchestration layer only).
|
||||
- `docs/manual-qa/NEO-96.md` (kickoff decision).
|
||||
|
||||
## Acceptance criteria checklist
|
||||
|
||||
- [x] **`TODO(E9.M1)`** markers present at **`telegraph_fired`** and **`npc_state_transition`** hook sites in NPC runtime path.
|
||||
- [x] Hook names match [epic_05 Slice 2](../decomposition/epics/epic_05_pve_combat.md#slice-2---npc-archetypes-and-telegraphs) vocabulary (`telegraph_fired`, `npc_state_transition`).
|
||||
- [x] No runtime behavior change beyond comments / doc cross-links.
|
||||
|
||||
## Implementation reconciliation (shipped)
|
||||
|
||||
- NEO-93 `(NEO-96)` stub comments replaced with full NEO-84-style hook blocks in **`NpcRuntimeOperations.AdvanceAll`**.
|
||||
- **`telegraph_fired`** at aggro→telegraph and recover→telegraph entries; **`npc_state_transition`** on every state commit during **`AdvanceAll`** (holder clear → idle).
|
||||
- Dev fixture **`ResetAllPrototypeRows`** documented as bypassing **`AdvanceOne`** hooks; optional E9.M1 anchor on reset wrapper.
|
||||
- **`server/README.md`**, **E5_M2** module doc, **E5M2** backlog, and alignment register updated.
|
||||
- No new tests (comment-only); existing NPC runtime tests unchanged.
|
||||
|
||||
## Technical approach
|
||||
|
||||
### Authoritative surface
|
||||
|
||||
**`NpcRuntimeOperations.AdvanceAll` → `AdvanceOne`** — all prototype NPC state transitions and telegraph scheduling funnel here. **`GET /game/world/npc-runtime-snapshot`** invokes **`AdvanceAll`** before read ([NEO-94](https://linear.app/neon-sprawl/issue/NEO-94)); no HTTP-layer hooks.
|
||||
|
||||
### Hook site A — `telegraph_fired`
|
||||
|
||||
Immediately after **`WriteState(..., TelegraphWindup, ...)`** (both paths):
|
||||
|
||||
1. **aggro → telegraph_windup** — cooldown elapsed while holder set.
|
||||
2. **recover → telegraph_windup** — post-attack cooldown elapsed while holder set.
|
||||
|
||||
Comment block names future E9.M1 event **`telegraph_fired`**. **`TODO(E9.M1): catalog emit`** — once per telegraph start (each windup cycle).
|
||||
|
||||
Planned payload fields: `npcInstanceId`, `telegraphId`, `behaviorDefId`, `archetypeKind`, `aggroHolderPlayerId`, `windupStartedUtc`, `telegraphWindupSeconds`.
|
||||
|
||||
### Hook site B — `npc_state_transition`
|
||||
|
||||
Immediately after every **`WriteState`** / **`WriteIdle`** commit:
|
||||
|
||||
| Transition | `fromState` → `toState` |
|
||||
|------------|-------------------------|
|
||||
| Holder set while idle | `idle` → `aggro` |
|
||||
| Cooldown complete | `aggro` → `telegraph_windup` |
|
||||
| Windup complete | `telegraph_windup` → `recover` (logical attack; no visible `attack_execute` phase) |
|
||||
| Defensive `AttackExecute` branch | `attack_execute` → `recover` |
|
||||
| Recover cooldown complete | `recover` → `telegraph_windup` |
|
||||
| Holder cleared during **`AdvanceAll`** | any → `idle` |
|
||||
|
||||
**Not instrumented in NEO-96:** dev fixture **`ResetAllPrototypeRows`** writes idle directly (bypasses **`AdvanceOne`**); optional E9.M1 anchor noted on reset wrapper.
|
||||
|
||||
Comment block names future E9.M1 event **`npc_state_transition`**. **`TODO(E9.M1): catalog emit`** — once per committed transition.
|
||||
|
||||
Planned payload fields: `npcInstanceId`, `behaviorDefId`, `fromState`, `toState`, `phaseStartedUtc`, optional `aggroHolderPlayerId`, optional `telegraphId` when entering/leaving windup.
|
||||
|
||||
**Note:** At recover→telegraph, emit **both** `npc_state_transition` and `telegraph_fired` (state commit then telegraph start) — analogous to gather success + depletion hooks sharing a path but naming distinct catalog events.
|
||||
|
||||
### Cross-references
|
||||
|
||||
1. **`NpcRuntimeOperations.cs`** — extend class summary with NEO-96 telemetry hook anchor (NEO-84 style).
|
||||
2. **`server/README.md`** — new **NEO-96 NPC telemetry hooks** subsection under NPC runtime behavior (after NEO-93 block).
|
||||
3. **`E5_M2_NpcAiAndBehaviorProfiles.md`** — NEO-96 landed line when complete.
|
||||
4. **`E5M2-prototype-backlog.md`** — E5M2-10 checkboxes + landed note.
|
||||
5. **`documentation_and_implementation_alignment.md`** — E5.M2 row bump when E5M2-10 lands.
|
||||
|
||||
### NEO-93 placeholder replacement
|
||||
|
||||
Replace terse `// telemetry: … (NEO-96)` stubs with full `--- Telemetry hook site (NEO-96): … ---` blocks matching **`CombatOperations`** / **`GatherOperations`** formatting.
|
||||
|
||||
## Files to add
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `docs/plans/NEO-96-implementation-plan.md` | This plan. |
|
||||
|
||||
## Files to modify
|
||||
|
||||
| Path | Rationale |
|
||||
|------|-----------|
|
||||
| `server/NeonSprawl.Server/Game/Npc/NpcRuntimeOperations.cs` | NEO-96: formalize `telegraph_fired` + `npc_state_transition` comment-only hook sites at transition/telegraph anchors; extend class summary. |
|
||||
| `server/README.md` | Document NPC runtime telemetry hook sites + payload notes; link plan and epic vocabulary. |
|
||||
| `docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md` | NEO-96 landed note under Related implementation slices. |
|
||||
| `docs/plans/E5M2-prototype-backlog.md` | E5M2-10 acceptance checkboxes + landed note when complete. |
|
||||
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E5.M2 alignment register — E5M2-10 complete when shipped. |
|
||||
|
||||
## Tests
|
||||
|
||||
| File | Coverage |
|
||||
|------|----------|
|
||||
| *(none — no new or changed test files)* | Comment-only change; existing **`NpcRuntimeOperationsTests`** continue to verify state machine behavior. No runtime/API contract change to assert. |
|
||||
|
||||
## Open questions / risks
|
||||
|
||||
| Question / risk | Agent recommendation | Status |
|
||||
|-----------------|---------------------|--------|
|
||||
| **Linear blockedBy NEO-93** | Proceed — **Done** on `main`; placeholders already in place. | **adopted** |
|
||||
| **Dual emit on recover→telegraph** | Accept — distinct catalog events at same code point (state transition + telegraph start). | **adopted** |
|
||||
| **No manual QA doc** | Accept per kickoff — server-only comments; verify via code review + existing unit tests green. | **adopted** (kickoff) |
|
||||
| **No new unit tests for comments** | Accept — NEO-64/NEO-84 did not add comment-assertion tests; behavior unchanged. | **adopted** |
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# Code review — NEO-96 (E5M2-10)
|
||||
|
||||
**Date:** 2026-05-29
|
||||
**Scope:** Branch `NEO-96-e5m2-10-npc-telemetry-hooks` vs `origin/main` — commits `dedd9a9` … `5e1e374`
|
||||
**Base:** `origin/main`
|
||||
|
||||
**Follow-up:** Code review suggestions addressed (2026-05-29).
|
||||
|
||||
## Verdict
|
||||
|
||||
**Approve with nits**
|
||||
|
||||
## Summary
|
||||
|
||||
NEO-96 replaces NEO-93 telemetry stubs in **`NpcRuntimeOperations.AdvanceAll`** with full NEO-84-style comment blocks for Epic 5 Slice 2 catalog events **`telegraph_fired`** (at each **`telegraph_windup`** entry) and **`npc_state_transition`** (on every committed state change, including holder clear → **`idle`**). Class summary, **`server/README.md`**, E5.M2 module doc, E5M2 backlog E5M2-10, alignment register, and implementation plan are updated. No production ingest, no new tests — appropriate for comment-only scope. **`NpcRuntimeOperations`** tests pass (**21/21**). One mid-cycle holder-clear path adds a defensive **`TryGet`** guard beyond pure comments; dev fixture **`ResetAllPrototypeRows`** bypasses hook anchors (pre-existing gap, comment wording slightly overstates coverage).
|
||||
|
||||
## Documentation checked
|
||||
|
||||
| Path | Result |
|
||||
|------|--------|
|
||||
| `docs/plans/NEO-96-implementation-plan.md` | **Matches** — AC checked; kickoff decisions (comments-only, engine-only, telegraph at windup start, every state commit, no manual QA) reflected in code and docs; reconciliation section accurate. |
|
||||
| `docs/plans/E5M2-prototype-backlog.md` (E5M2-10) | **Matches** — AC checked, landed note present; no client counterpart (NEO-97 deferred). |
|
||||
| `docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md` | **Matches** — Related implementation slices landed line present; Summary **Status** row includes **E5M2-10 / NEO-96 landed**. |
|
||||
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | **Matches** — E5.M2 row includes **NEO-96 landed** with README/plan links. |
|
||||
| `docs/decomposition/epics/epic_05_pve_combat.md` (Slice 2 telemetry) | **Matches** — hook vocabulary `telegraph_fired`, `npc_state_transition`. |
|
||||
| `docs/decomposition/modules/client_server_authority.md` | **N/A** — no authority change; server-only comment anchors. |
|
||||
| `server/README.md` | **Matches** — NPC runtime telemetry hooks subsection with event timing and E9.M1 deferral. |
|
||||
|
||||
## Blocking issues
|
||||
|
||||
None.
|
||||
|
||||
## Suggestions
|
||||
|
||||
1. ~~**E5.M2 Summary status table** — Append **E5M2-10 / NEO-96 telemetry hooks landed** to the module Summary **Status** row (between E5M2-09 and E5M2-12), matching the pattern used when NEO-95 landed.~~ **Done.** Summary **Status** row updated.
|
||||
|
||||
2. ~~**Fixture reset vs hook comments** — Plan transition table and the holder-clear hook comment cite “fixture reset,” but **`ResetAllPrototypeRows`** writes idle directly in the store (dev fixture API) without passing through **`AdvanceOne`** hook sites. Either add a comment-only anchor on the reset path when E9.M1 lands, or narrow comment/plan wording to “holder cleared during **`AdvanceAll`**” so implementers do not assume fixture reset is already instrumented.~~ **Done.** Wording narrowed to “holder cleared during **`AdvanceAll`**”; reset wrapper documents bypass + optional E9.M1 anchor; plan and README updated.
|
||||
|
||||
## Nits
|
||||
|
||||
- ~~Nit: Mid-cycle holder-clear path (inside the **`while`** loop) adds **`TryGet`** before **`WriteIdle`** — slightly exceeds “comments only” AC; **`snapshot`** from the top of the same iteration should already be valid, so the guard is redundant unless retained for future emit code that reads **`fromState`** from **`snapshot`**.~~ **Done.** Redundant **`TryGet`** removed.
|
||||
|
||||
- Nit: **`telegraph_fired`** payload comments list **`archetypeKind`** but engine scope has **`behaviorDefId`** only — fine for planned fields; E9.M1 implementer will map via behavior catalog (same as snapshot DTO projection).
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
# NEO-96-adjacent runtime tests (passed 2026-05-29)
|
||||
dotnet test server/NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj \
|
||||
--filter "FullyQualifiedName~NpcRuntimeOperations"
|
||||
|
||||
# Full server suite (Postgres persistence tests require local DB)
|
||||
dotnet test server/NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj
|
||||
```
|
||||
|
||||
Manual: none required (server-only comment hooks; kickoff decision).
|
||||
|
|
@ -2,7 +2,11 @@ using NeonSprawl.Server.Game.Combat;
|
|||
|
||||
namespace NeonSprawl.Server.Game.Npc;
|
||||
|
||||
/// <summary>Deterministic prototype NPC behavior state machine + lazy tick advance (NEO-93).</summary>
|
||||
/// <summary>
|
||||
/// Deterministic prototype NPC behavior state machine + lazy tick advance (NEO-93).
|
||||
/// NEO-96 telemetry hook sites: state transition commits and telegraph starts in <see cref="AdvanceAll"/>
|
||||
/// (<c>telegraph_fired</c>, <c>npc_state_transition</c>).
|
||||
/// </summary>
|
||||
public static class NpcRuntimeOperations
|
||||
{
|
||||
/// <summary>Default per-advance simulation cap (seconds) for lazy poll ticks.</summary>
|
||||
|
|
@ -81,8 +85,12 @@ public static class NpcRuntimeOperations
|
|||
}
|
||||
|
||||
/// <summary>Resets all prototype NPC runtime rows to idle (dev fixture).</summary>
|
||||
public static void ResetAllPrototypeRows(INpcRuntimeStateStore runtimeStore) =>
|
||||
public static void ResetAllPrototypeRows(INpcRuntimeStateStore runtimeStore)
|
||||
{
|
||||
// NEO-96: dev fixture reset writes idle directly — bypasses AdvanceOne npc_state_transition hooks.
|
||||
// TODO(E9.M1): optional catalog emit anchor here if fixture reset transitions must be tracked.
|
||||
runtimeStore.ResetAllPrototypeRows();
|
||||
}
|
||||
|
||||
private static void AdvanceOne(
|
||||
string npcInstanceId,
|
||||
|
|
@ -114,6 +122,10 @@ public static class NpcRuntimeOperations
|
|||
if (snapshot.BehaviorState != NpcBehaviorState.Idle || snapshot.ActiveTelegraph is not null)
|
||||
{
|
||||
WriteIdle(runtimeStore, npcInstanceId);
|
||||
// --- Telemetry hook site (NEO-96): future E9.M1 catalog event `npc_state_transition` ---
|
||||
// TODO(E9.M1): catalog emit — holder cleared during AdvanceAll (any → idle).
|
||||
// Planned payload fields: npcInstanceId, behaviorDefId, fromState, toState (idle),
|
||||
// phaseStartedUtc, optional aggroHolderPlayerId (previous holder when known).
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
@ -127,7 +139,10 @@ public static class NpcRuntimeOperations
|
|||
NpcBehaviorState.Aggro,
|
||||
windowStart,
|
||||
activeTelegraph: null);
|
||||
// telemetry: npc_state_transition (NEO-96)
|
||||
// --- Telemetry hook site (NEO-96): future E9.M1 catalog event `npc_state_transition` ---
|
||||
// TODO(E9.M1): catalog emit — idle → aggro when aggro holder is set.
|
||||
// Planned payload fields: npcInstanceId, behaviorDefId, fromState (idle), toState (aggro),
|
||||
// phaseStartedUtc, aggroHolderPlayerId.
|
||||
if (!runtimeStore.TryGet(npcInstanceId, out snapshot))
|
||||
{
|
||||
return;
|
||||
|
|
@ -140,6 +155,10 @@ public static class NpcRuntimeOperations
|
|||
if (!TryHasAggroHolder(npcInstanceId, threatStore, out aggroHolderPlayerId))
|
||||
{
|
||||
WriteIdle(runtimeStore, npcInstanceId);
|
||||
// --- Telemetry hook site (NEO-96): future E9.M1 catalog event `npc_state_transition` ---
|
||||
// TODO(E9.M1): catalog emit — holder cleared mid-cycle during AdvanceAll (any → idle).
|
||||
// Planned payload fields: npcInstanceId, behaviorDefId, fromState, toState (idle),
|
||||
// phaseStartedUtc, optional aggroHolderPlayerId (previous holder).
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +189,14 @@ public static class NpcRuntimeOperations
|
|||
NpcBehaviorState.TelegraphWindup,
|
||||
cursor,
|
||||
telegraph);
|
||||
// telemetry: npc_state_transition (NEO-96)
|
||||
// --- Telemetry hook site (NEO-96): future E9.M1 catalog event `npc_state_transition` ---
|
||||
// TODO(E9.M1): catalog emit — aggro → telegraph_windup.
|
||||
// Planned payload fields: npcInstanceId, behaviorDefId, fromState (aggro),
|
||||
// toState (telegraph_windup), phaseStartedUtc, aggroHolderPlayerId, telegraphId.
|
||||
// --- Telemetry hook site (NEO-96): future E9.M1 catalog event `telegraph_fired` ---
|
||||
// TODO(E9.M1): catalog emit — once per telegraph start (each windup cycle).
|
||||
// Planned payload fields: npcInstanceId, telegraphId, behaviorDefId, archetypeKind,
|
||||
// aggroHolderPlayerId, windupStartedUtc, telegraphWindupSeconds.
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +221,10 @@ public static class NpcRuntimeOperations
|
|||
NpcBehaviorState.Recover,
|
||||
cursor,
|
||||
activeTelegraph: null);
|
||||
// telemetry: telegraph_fired / npc_state_transition (NEO-96); logical attack_execute → recover
|
||||
// --- Telemetry hook site (NEO-96): future E9.M1 catalog event `npc_state_transition` ---
|
||||
// TODO(E9.M1): catalog emit — telegraph_windup → recover (logical attack_execute instant).
|
||||
// Planned payload fields: npcInstanceId, behaviorDefId, fromState (telegraph_windup),
|
||||
// toState (recover), phaseStartedUtc, aggroHolderPlayerId, telegraphId (completed windup).
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +242,10 @@ public static class NpcRuntimeOperations
|
|||
NpcBehaviorState.Recover,
|
||||
cursor,
|
||||
activeTelegraph: null);
|
||||
// telemetry: npc_state_transition (NEO-96)
|
||||
// --- Telemetry hook site (NEO-96): future E9.M1 catalog event `npc_state_transition` ---
|
||||
// TODO(E9.M1): catalog emit — attack_execute → recover (defensive visible phase).
|
||||
// Planned payload fields: npcInstanceId, behaviorDefId, fromState (attack_execute),
|
||||
// toState (recover), phaseStartedUtc, aggroHolderPlayerId.
|
||||
break;
|
||||
|
||||
case NpcBehaviorState.Recover:
|
||||
|
|
@ -232,7 +264,14 @@ public static class NpcRuntimeOperations
|
|||
NpcBehaviorState.TelegraphWindup,
|
||||
cursor,
|
||||
telegraph);
|
||||
// telemetry: npc_state_transition (NEO-96)
|
||||
// --- Telemetry hook site (NEO-96): future E9.M1 catalog event `npc_state_transition` ---
|
||||
// TODO(E9.M1): catalog emit — recover → telegraph_windup.
|
||||
// Planned payload fields: npcInstanceId, behaviorDefId, fromState (recover),
|
||||
// toState (telegraph_windup), phaseStartedUtc, aggroHolderPlayerId, telegraphId.
|
||||
// --- Telemetry hook site (NEO-96): future E9.M1 catalog event `telegraph_fired` ---
|
||||
// TODO(E9.M1): catalog emit — once per telegraph start (each windup cycle).
|
||||
// Planned payload fields: npcInstanceId, telegraphId, behaviorDefId, archetypeKind,
|
||||
// aggroHolderPlayerId, windupStartedUtc, telegraphWindupSeconds.
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,10 @@ Per-NPC behavior states live in **`Game/Npc/`** as **`INpcRuntimeStateStore`** +
|
|||
|
||||
Plan: [NEO-93 implementation plan](../../docs/plans/NEO-93-implementation-plan.md).
|
||||
|
||||
### NPC runtime telemetry hooks (NEO-96)
|
||||
|
||||
Comment-only hook sites in **`NpcRuntimeOperations.AdvanceAll`** for future E9.M1 catalog events — **`telegraph_fired`** at each **`telegraph_windup`** entry (aggro→telegraph and recover→telegraph); **`npc_state_transition`** on every committed state change during **`AdvanceAll`** (including holder clear → **`idle`**). Dev fixture **`ResetAllPrototypeRows`** writes idle directly and bypasses **`AdvanceOne`** hooks (optional E9.M1 anchor noted on reset path). **`TODO(E9.M1)`** — no production ingest. Snapshot GET invokes **`AdvanceAll`** before read (no duplicate API-layer hooks). Plan: [NEO-96 implementation plan](../../docs/plans/NEO-96-implementation-plan.md).
|
||||
|
||||
## NPC runtime snapshot (NEO-94)
|
||||
|
||||
**`GET /game/world/npc-runtime-snapshot`** returns a versioned JSON body (`schemaVersion` **1**, **`serverTimeUtc`**, **`npcInstances`**) backed by **`INpcRuntimeStateStore`** + **`IThreatStateStore`** after **`NpcRuntimeOperations.AdvanceAll`** (lazy tick, **5.0 s** delta cap). Each row includes **`npcInstanceId`**, **`behaviorDefId`**, **`state`** (stable snake_case), **`aggroHolderPlayerId`**, and optional nested **`activeTelegraph`** during **`telegraph_windup`**. All three **`PrototypeNpcRegistry`** instances are always returned in ascending **`npcInstanceId`** order. Plan: [NEO-94 implementation plan](../../docs/plans/NEO-94-implementation-plan.md); Bruno: `bruno/neon-sprawl-server/npc-runtime-snapshot/`.
|
||||
|
|
|
|||
Loading…
Reference in New Issue