generated from nhcarrigan/template
fix: use server-computed endsAt for exploration timer to prevent clock drift (#68)
## Summary - Exploration timers showed more time than the area's stated duration when the server clock was ahead of the client's - The timer was derived from `startedAt = endsAt - durationMs`, then computed as `durationSeconds - (clientNow - startedAt) / 1000` — any server/client clock skew directly inflated the result - Now stores `endsAt` (the server-computed completion timestamp) directly in `ExplorationAreaState` and computes the timer as `(endsAt - Date.now()) / 1000`, which is immune to clock drift - Old saves without `endsAt` fall back gracefully to the previous `startedAt`-based calculation ## Test plan - [ ] Start a new exploration — timer should show exactly the area's stated duration (no more "1h area shows 1h15m") - [ ] Refresh the page mid-exploration — timer should resume from the correct remaining time (using server-anchored `endsAt`) - [ ] Old saves with `startedAt` but no `endsAt` should still display a timer via the fallback path Closes #53 ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: #68 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #68.
This commit is contained in:
@@ -52,7 +52,6 @@ import {
|
||||
transcend as transcendApi,
|
||||
} from "../api/client.js";
|
||||
import { CODEX_ENTRIES } from "../data/codex.js";
|
||||
import { EXPLORATION_AREAS } from "../data/explorations.js";
|
||||
import { RECIPES } from "../data/recipes.js";
|
||||
import {
|
||||
RESOURCE_CAP,
|
||||
@@ -1671,14 +1670,6 @@ export const GameProvider = ({
|
||||
const startExploration = useCallback(async(areaId: string) => {
|
||||
try {
|
||||
const response = await startExplorationApi({ areaId });
|
||||
const areaData = EXPLORATION_AREAS.find((a) => {
|
||||
return a.id === areaId;
|
||||
});
|
||||
if (areaData === undefined) {
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line stylistic/no-mixed-operators -- duration * 1000 is clear
|
||||
const startedAt = response.endsAt - areaData.durationSeconds * 1000;
|
||||
setState((previous) => {
|
||||
if (previous?.exploration === undefined) {
|
||||
return previous;
|
||||
@@ -1689,7 +1680,11 @@ export const GameProvider = ({
|
||||
...previous.exploration,
|
||||
areas: previous.exploration.areas.map((a) => {
|
||||
return a.id === areaId
|
||||
? { ...a, startedAt: startedAt, status: "in_progress" as const }
|
||||
? {
|
||||
...a,
|
||||
endsAt: response.endsAt,
|
||||
status: "in_progress" as const,
|
||||
}
|
||||
: a;
|
||||
}),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user