Skip to content

fix(streaming-duel): harden duel lifecycle, oracle revenue path, and cinematic camera#1177

Open
binkyfishai wants to merge 1 commit into
PlayHyperia:mainfrom
binkyfishai:fix/streaming-duel-hardening
Open

fix(streaming-duel): harden duel lifecycle, oracle revenue path, and cinematic camera#1177
binkyfishai wants to merge 1 commit into
PlayHyperia:mainfrom
binkyfishai:fix/streaming-duel-hardening

Conversation

@binkyfishai
Copy link
Copy Markdown
Contributor

Summary

Hardens the streaming duel arena end-to-end — duels now actually run, finish cleanly, publish results on-chain reliably, and look better. Verified live against a deployed + "DuelOutcomeOracle" + on local anvil. Adds 14 regression tests, all validated by temporary rollback.

Changes

Duel lifecycle (DuelOrchestrator.ts, DuelCombatAI.ts)

  • Draw-path cleanup: timeout-draw now routes through + "startResolution()" + , so arena bounds clear and autonomy restores (previously stuck until next duel).
  • + "startCombatAIs()" + rollback on failure.
  • Positioning: agents spawn on adjacent tiles (distance 1) instead of 16 — melee + "startCombat" + now succeeds first try.
  • Weapon picker + ">= 0" + gate: previously kept any equipped item (wrong type included) when all scores tied at -1; ranged agents ended up holding swords + arrows.
  • Combat perf: hoisted prayer map, sync short-circuit in + "maybeActivateProtectionPrayer" + , memoised + "detectOpponentAttackType" + .
  • Trash-talk IIFE no longer chats after + "stop()" + .

Oracle / hyperbet path (DuelArenaOraclePublisher.ts, main.ts)

  • Bounded exponential-backoff retry (1s → 2.5s → 6s → 15s → 30s) with + "setTimeout.unref()" + and + "destroy()" + cancellation. Gives up cleanly after 6 attempts.
  • Boot re-enqueue: records with + "lastError" + set from a mid-retry crash resume publishing on startup.
  • Startup warning when + "STREAMING_DUEL_ENABLED=true" + but oracle disabled.

Operator triage (admin-routes.ts)

  • + "GET /admin/duels/oracle/stuck" + — list records with any failed target.
  • + "POST /admin/duels/oracle/clear/:duelId" + — clear + "lastError" + ; + "{forceRetry:true}" + refires with full retry budget.

Cinematic camera (ClientCameraSystem.ts)

  • FIGHTING: wider FOV (46→54), higher angle, wider radius, more orbit.
  • RESOLUTION: tighter hero framing, less top-down, winner-weighted focus.
  • COUNTDOWN push-in: dynamic 4s smoothstep from + "radiusMax" + + "radiusMin" + (replaces frozen pre-fight frame).
  • RESOLUTION hero push-in: 1.5s ease to + "radiusMin + 0.3" + timed with the victory emote.

Test plan

  • + "bun x vitest run tests/unit/oracle src/systems/StreamingDuelScheduler" + — 48 pass (34 existing + 14 new)
  • + "bun run typecheck" + — no new errors (2 pre-existing DuelCombatRole errors unchanged)
  • Each regression test verified by temporarily reverting the fix and confirming the test fails
  • End-to-end live: deployed + "DuelOutcomeOracle" + to local anvil, ran server, observed UPSERT(BETTING_OPEN) → UPSERT(LOCKED) → RESOLVE txs on block 17, 19, 37, 39; verified via + "cast receipt" +
  • Observed boot re-enqueue fire in the wild when the retry scheduler caught a real nonce error mid-duel
  • Observed 10+ clean duel cycles post-positioning-fix with real kills (9 dmg vs 5 dmg in 30-100 attacks), vs pre-fix all-draws (474 attempts / 1 dmg)

⚠️ Runbook note for ops: running + "node build/index.js" + directly (bypassing + "scripts/dev.mjs" + ) requires + "PUBLIC_CDN_URL=http://localhost:5555/game-assets`" + in the env, or the client has no assets. + "dev.mjs" + ` normally handles this via a health-check fallback.

…cinematic camera

**Duel lifecycle / agent behaviour (DuelOrchestrator.ts + DuelCombatAI.ts)**
- Draw-path cleanup: `endFightByTimeout()` draw now routes through
  `startResolution()` so `stopCombatAIs()` runs; previously arena bounds
  stayed clamped and autonomous behaviour stayed disabled until the next
  duel started.
- `startCombatAIs()` rollback: the fire-and-forget catch now calls
  `stopCombatAIs()` to release any services that had bounds set before
  the constructor threw.
- Positioning fix: agents spawn 0.5 units apart (tileDistance=1) instead
  of 16, so `startCombat` succeeds on the first try. Every duel was
  hitting the retry limit as a draw before this.
- Weapon picker `>= 0` gate: melee/ranged/mage pickers previously kept
  the equipped item when every score was -1 (wrong type). A ranged
  agent ended up holding a longsword + 500 arrows; now the gate forces
  a valid weapon for the role.
- Trash-talk IIFE guard: pending LLM calls respect `isRunning` so
  post-stop chat doesn't land on an agent back in the overworld.
- Perf: hoisted PROTECTION_PRAYER_MAP to module scope, added sync
  short-circuit in `maybeActivateProtectionPrayer`, memoised
  `detectOpponentAttackType` by weapon id (removes per-tick allocations).

**Oracle / hyperbet publish path (DuelArenaOraclePublisher.ts + main.ts)**
- Bounded exponential-backoff retry (1s → 2.5s → 6s → 15s → 30s) on
  publish failure, per (duelId, targetKey), with `setTimeout.unref()`
  and `destroy()` cancellation. Gives up with a clear "manual
  intervention required" log after 6 total attempts.
- Boot re-enqueue: on `init()`, records with `chainState[target].lastError`
  set are re-published via the full retry schedule. Stranded records
  from a mid-retry crash now resume automatically.
- Startup warning: if `STREAMING_DUEL_ENABLED=true` but
  `DUEL_ARENA_ORACLE_ENABLED=false`, log a clear ⚠️ so operators know
  on-chain settlement is disabled.

**Operator triage (admin-routes.ts)**
- `GET /admin/duels/oracle/stuck` — list records with any failed target.
- `POST /admin/duels/oracle/clear/:duelId` — clear lastError; with
  `{forceRetry:true}` also re-fires publish with the full retry budget.

**Cinematic camera (ClientCameraSystem.ts)**
- FIGHTING: wider FOV (46→54), higher angle (phi 0.27π→0.30π), wider
  radius band (4–7.5 → 5–10), more orbit drift.
- RESOLUTION: closer hero framing (radius 5.5–8 → 4.5–7), less top-down
  (phi 0.44π→0.35π), winner-weighted focusBias 0.5→0.7.
- COUNTDOWN push-in: dynamic smoothstep shrink from radiusMax to
  radiusMin over 4s to replace the frozen pre-fight frame.
- RESOLUTION hero push-in: dynamic 1.5s shrink to (radiusMin + 0.3)
  timed with the 600ms victory-emote delay.

**Tests (+14 regressions, all verified failing when the fix is reverted)**
- `DuelOrchestrator.test.ts` (new): draw-path arena cleanup,
  hp_advantage smoke test, phase-guard.
- `DuelArenaOraclePublisher.test.ts`: retry-then-succeed,
  exhaust-retries, destroy cancels timers, boot re-enqueue,
  getStuckRecords, clearStuckRecord with/without forceRetry,
  not_found handling.

Typecheck: no new errors (2 pre-existing DuelCombatRole errors already
on main). Oracle + scheduler suites: all 48 tests green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant