fix(daily): reset Endfield's European day on the server it's on

Endfield has two server groups, not three: Europe is served off the
Americas machine on a fixed UTC-5, so a European player's day rolls at
09:00 UTC. We were resetting it six hours early, at 03:00, which ticked
the wrong box every morning between those two instants.

Adds GameMeta.resetOffsets, a sparse per-region override, and threads an
optional `game` through every day-key function. Per region rather than
per game on purpose: a blanket offset would drag Asia — which does have
its own Endfield server — onto the Americas clock, moving day keys for
readers who never had the bug. A regression test pins Asia's output as
identical to before.

Day keys are localStorage keys, so this re-labels ticks logged between
03:00 and 09:00 UTC by European Endfield players, one day backward. No
tick is deleted and past days stay editable, but a streak can read as
broken for a day. That is the cost of correcting a wrong reset; leaving
it wrong is worse.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-16 20:37:47 +02:00
co-authored by Claude Opus 5
parent 6c5546ba53
commit e6e4f7085a
10 changed files with 259 additions and 64 deletions
+25 -2
View File
@@ -1,4 +1,4 @@
import type { GameId } from "./schema.ts";
import type { GameId, Region } from "./schema.ts";
export interface GameMeta {
id: GameId;
@@ -20,6 +20,24 @@ export interface GameMeta {
* no hint at all.
*/
dailyTasks: string;
/**
* Server clock offsets that differ from the regional default, per region.
*
* Not every game runs one server per region. Where a game serves two of our
* regions off a single machine, the reader's region is still the right
* question — it just gets a different answer for that game than
* `REGION_RESET_UTC_OFFSET` gives.
*
* Deliberately a sparse override rather than a full table: listing only the
* regions that actually differ keeps the diff to the fact that changed, and
* a region absent here keeps the default answer it has always had.
*
* This feeds `dayKey`, which is a **localStorage key**. Adding or changing an
* entry re-labels the game-day some already-logged ticks fall in, for readers
* in that region only — see `src/shared/daily.ts` § shift and
* docs/DATA-MODEL.md.
*/
resetOffsets?: Partial<Record<Region, number>> | undefined;
}
export const GAMES: Record<GameId, GameMeta> = {
@@ -28,7 +46,12 @@ export const GAMES: Record<GameId, GameMeta> = {
zzz: { id: "zzz", name: "Zenless Zone Zero", short: "ZZZ", hue: "#F2A03D" , studio: "HoYoverse", dailyTasks: "Daily missions, battery" },
wuwa: { id: "wuwa", name: "Wuthering Waves", short: "Wuwa", hue: "#3DD6A0" , studio: "Kuro Games", dailyTasks: "Daily activity, waveplate" },
arknights: { id: "arknights", name: "Arknights", short: "Arknights", hue: "#9AA3B8" , studio: "Hypergryph", dailyTasks: "Daily missions, sanity" },
endfield: { id: "endfield", name: "Arknights: Endfield", short: "Endfield", hue: "#E8635A" , studio: "Hypergryph", dailyTasks: "Daily missions" },
// Endfield has two server groups, not three: Europe is served off the same
// machine as the Americas, on a fixed UTC-5. So a European player's day rolls
// at 09:00 UTC — 11:00 in Copenhagen in summer, 10:00 in winter — six hours
// after the HoYo/Kuro pattern above. Asia has its own server and is unchanged,
// and `america` already resolves to -5, so Europe is the only real override.
endfield: { id: "endfield", name: "Arknights: Endfield", short: "Endfield", hue: "#E8635A" , studio: "Hypergryph", dailyTasks: "Daily missions", resetOffsets: { europe: -5 } },
nte: { id: "nte", name: "Neverness to Everness", short: "NTE", hue: "#C77DFF" , studio: "Hotta Studio", dailyTasks: "Daily tasks" },
};