perf: stop rebuilding map source per frame, fix stale initial centre, prefetch at login - #745
Merged
Merged
Conversation
…entre Map.tsx's walker animation loop rebuilt the entire GeoJSON source (buildings/roads/fields/boundaries) every 16ms even though only character positions change per frame. Split buildVillageSourceData into buildStaticVillageFeatures (memoized, rebuilt only when features change) and buildCharacterPointFeatures (per-frame). The animation loop is now also gated on there actually being walking characters, instead of running unconditionally. Separately, useMap.ts's initial-camera logic assumed a single seeded village and picked "the first population centre" from the full population-centres list, which is now stale with multiple villages imported from locations/data/. Added a lightweight InitialMapCentreView (/map/initial-centre/) that returns just enough (id/name/bbox) to frame the camera, preferring the population centre of the requesting player's actively linked character (Player.active_link) and falling back to the lowest-pk centre otherwise. Frontend now fetches this cheaply before the bbox-scoped viewport poll takes over as the source of truth. Backend: locations/tests/test_initial_map_centre_view.py (3 tests). Frontend: MapPage.test.tsx updated for the new fetchInitialMapCentre mock; also fixed a flaky prefetch effect that used a dynamic import() for fetchPopulationCentreMap, which could race and skip the vi.mock when prefetching two different villages concurrently - now a normal static import.
GameContext now primes useInitialMapCentre's and useMapWorldBounds's
query cache entries as soon as fetch_info resolves, instead of only
firing them once the player navigates to the map page. Both are
cheap, one-shot fetches, so warming them at login lets MapPage skip a
network round-trip on mount for players who go on to open it.
Extracted the {queryKey, queryFn, staleTime, gcTime} for each into
exported query-option objects in useMap.ts, reused by both the hooks
and GameContext's prefetch, so the two can't drift onto different
cache keys.
Deliberately doesn't prefetch /map/viewport/: it needs a bbox only the
mounted map component can produce, and starts a 2s poll once enabled -
prefetching it at login would poll map data in the background for
every session regardless of whether the player ever opens the map.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C6BX7dFJWn2xMYn9qggdos
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
InitialMapCentreViewbackend endpoint that returns the requesting player's linked character's village (falling back to a deterministic default if unlinked), and wired the frontend'suseInitialMapCentreonto it./map/initial-centre/,/map/world-bounds/) as soon asfetch_inforesolves at login, instead of only firing them once the player navigates to the map page — soMapPagecan skip a network round-trip on mount. Deliberately does not prefetch/map/viewport/, since that needs a bbox only the mounted map component can produce and starts a 2s poll once enabled.Testing
tsc --noEmit) and lint (eslint) clean; vitest suite run by the user, all passing.🤖 Generated with Claude Code
https://claude.ai/code/session_01C6BX7dFJWn2xMYn9qggdos