Skip to content

fix: unwrap v2 delta-wrapped mutation responses in the play client#154

Merged
cahaseler merged 2 commits into
mainfrom
claude/game-state-travel-refresh-jbabr7
Jul 1, 2026
Merged

fix: unwrap v2 delta-wrapped mutation responses in the play client#154
cahaseler merged 2 commits into
mainfrom
claude/game-state-travel-refresh-jbabr7

Conversation

@cahaseler

Copy link
Copy Markdown
Contributor

Summary

The gameserver's HTTP v2 API opts every mutation request into "v2 state deltas," so a completed mutation response (dock, mine, craft, salvage, complete_mission, jump/travel arrival, etc.) comes back with the real typed response nested under a details field instead of at the top level. The /play client's sendCommand() and its WebSocket action_result handler read fields like result.action/result.message directly off the raw response, which silently broke once the server started delta-wrapping — most visibly as "no Mine button until refresh" after traveling to a POI, but the same issue affected several other panels that read a mutation's resolved value directly:

  • Travel/jump arrival not refreshing POI resources (the reported bug)
  • Mission completion rewards (credits/items/skill XP/chain missions) never displayed
  • Crafting queue and dry-run cost quotes rendering blank
  • Salvage-complete modal always showing 0/undefined yields
  • Auto-travel's in-combat abort safety check on jump never firing
  • Order-repricing "listing fee" notice missing

Technical Approach

Added unwrapActionResult() in GameProvider.tsx, which merges a response's details field back over its top level when present (details fields win on key collision; delta-only fields like player/ship/location are preserved). Applied it at both places a mutation response is consumed — the WS action_result handler and sendCommand()'s HTTP response path — so every panel that calls sendCommand() gets the flat shape it expects, without touching each panel individually.

Added GameProvider.test.ts (bun:test, matching the project's existing pure-function testing convention) covering the delta-wrapped case, the already-flat passthrough case, a defensive non-object details case, and preservation of delta-only top-level fields.

Verified with pnpm lint (tsc --noEmit, clean) and pnpm build (clean production build), plus the full bun test suite.

User-Facing Changes

  • Fixed: after traveling or jumping to a location with mineable resources, the Mine button now appears immediately instead of requiring a page refresh
  • Fixed: mission completion now correctly shows credit/item/skill XP rewards and any follow-up chained mission
  • Fixed: the crafting queue and cost quotes now populate correctly after queuing a job
  • Fixed: salvaging a wreck now shows the actual scrap/materials/credits/XP gained instead of a blank result
  • Fixed: auto-travel now correctly aborts if pulled into combat mid-jump

Generated by Claude Code

claude added 2 commits July 1, 2026 12:39
HTTP v2 mutations opt every request into v2 state deltas, so gameserver
delivers a multi-tick travel/jump arrival over WS with the typed action
response (action/poi/poi_data) nested under `details` instead of at the
top level. GameProvider read `result.action` directly, which is always
undefined on that shape, so the arrived/jumped check never matched and
the get_system/get_poi refresh that populates poi.resources (and thus
the Mine button) never fired until a manual page reload.
…vel/jump

The gameserver's HTTP v2 API opts every mutation request into v2 state
deltas, so ANY completed mutation response (dock, mine, craft, salvage,
complete_mission, modify_order, jump, etc.) — not just deferred
travel/jump arrivals — comes back with the typed handler response
nested under a `details` field instead of at the top level.

sendCommand()'s HTTP response path never unwrapped this, so panels
reading fields directly off a mutation's resolved value were silently
getting undefined:
- MissionsPanel: complete_mission rewards (credits/items/skill XP/chain)
  never shown
- CraftingPanel: job queue, optimistic job cards, and dry-run quotes all
  blank
- SalvagePanel: salvage yield modal always blank
- useAutoTravel: the in_combat abort check on jump never fired
- OrdersView: listing fee notice missing after repricing an order

unwrapActionResult now merges `details` back over the top level (kept
as a merge, not a replace, so delta-only fields like player/ship/
location survive alongside it), and sendCommand()'s response handling
applies it the same way the WS action_result path already does.
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
www Ready Ready Preview, Comment Jul 1, 2026 1:01pm

Request Review

@cahaseler cahaseler merged commit fa2573a into main Jul 1, 2026
3 checks passed
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.

2 participants