generated from nhcarrigan/template
fix: resolve all 8 open bug tickets (#242–#249) (#250)
## Summary - **#242** — Crystals in the resource bar now use `formatNumber` to respect the player's notation setting (suffix/scientific/engineering) - **#243** — Companion unlock progress includes current-run gold (`totalGoldEarned`) on both client and server, so companions unlock at the correct threshold - **#244** — Empty green reward bubbles no longer render for quest crystal rewards with a zero amount - **#245/#248** — Auto-save skips when `isAutoPrestigingReference.current` is true, preventing it from racing with an in-flight prestige and breaking the optimistic lock - **#246** — Generated and uploaded CDN images for `crystal_pulse`, `crystal_surge`, and `crystal_tempest` upgrades - **#247** — `validateAndSanitize` merges daily challenge progress by taking the max of client vs. server progress per challenge, so stale auto-saves can no longer roll back server-side completions - **#249** — Cached save signature is cleared after `buyPrestigeUpgrade` succeeds, preventing a stale-signature mismatch on the next auto-save ## Test plan - [ ] Lint passes (`pnpm lint`) - [ ] Build passes (`pnpm build`) - [ ] Tests pass with 100% coverage (`pnpm test`) - [ ] Crystals display in resource bar respects notation setting - [ ] No empty reward bubbles on quests that don't award crystals - [ ] Companion progress bar shows correct value including current-run gold - [ ] Auto-prestige no longer causes save errors - [ ] Crafting a recipe updates daily challenge progress persistently (not rolled back by next auto-save) - [ ] Buying a prestige upgrade does not cause a signature mismatch error on next save - [ ] Crystal upgrade images display correctly in-game ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: #250 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #250.
This commit is contained in:
@@ -681,6 +681,45 @@ const validateAndSanitize = (
|
||||
storySpread = { story: previous.story };
|
||||
}
|
||||
|
||||
/*
|
||||
* Merge daily challenge progress: take the maximum progress for each
|
||||
* challenge so a stale auto-save arriving after a craft/boss/etc. update
|
||||
* cannot silently roll back server-side challenge completions.
|
||||
*/
|
||||
// eslint-disable-next-line capitalized-comments -- v8 ignore
|
||||
/* v8 ignore next 35 -- @preserve */
|
||||
let dailyChallengesSpread: object = {};
|
||||
// eslint-disable-next-line stylistic/max-len -- Long condition; splitting would reduce readability
|
||||
if (incoming.dailyChallenges !== undefined && previous.dailyChallenges !== undefined) {
|
||||
const previousChallengeMap = new Map(
|
||||
previous.dailyChallenges.challenges.map((challenge) => {
|
||||
return [ challenge.id, challenge ];
|
||||
}),
|
||||
);
|
||||
// eslint-disable-next-line stylistic/max-len -- Long chain; splitting would reduce readability
|
||||
const mergedChallenges = incoming.dailyChallenges.challenges.map((challenge) => {
|
||||
const serverChallenge = previousChallengeMap.get(challenge.id);
|
||||
if (serverChallenge === undefined) {
|
||||
return challenge;
|
||||
}
|
||||
// eslint-disable-next-line stylistic/max-len -- Long expression; splitting would reduce readability
|
||||
const bestProgress = Math.max(challenge.progress, serverChallenge.progress);
|
||||
return {
|
||||
...challenge,
|
||||
completed: bestProgress >= challenge.target,
|
||||
progress: bestProgress,
|
||||
};
|
||||
});
|
||||
dailyChallengesSpread = {
|
||||
dailyChallenges: {
|
||||
...incoming.dailyChallenges,
|
||||
challenges: mergedChallenges,
|
||||
},
|
||||
};
|
||||
} else if (previous.dailyChallenges !== undefined) {
|
||||
dailyChallengesSpread = { dailyChallenges: previous.dailyChallenges };
|
||||
}
|
||||
|
||||
return {
|
||||
...incoming,
|
||||
achievements,
|
||||
@@ -693,6 +732,7 @@ const validateAndSanitize = (
|
||||
...apotheosisSpread,
|
||||
...explorationSpread,
|
||||
...storySpread,
|
||||
...dailyChallengesSpread,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1024,7 +1064,8 @@ gameRouter.post("/save", async(context) => {
|
||||
const companionUnlocks = computeUnlockedCompanionIds({
|
||||
apotheosisCount: stateToSave.apotheosis?.count ?? 0,
|
||||
lifetimeBossesDefeated: playerRecord?.lifetimeBossesDefeated ?? 0,
|
||||
lifetimeGoldEarned: playerRecord?.lifetimeGoldEarned ?? 0,
|
||||
// eslint-disable-next-line stylistic/max-len -- Long property; splitting would reduce readability
|
||||
lifetimeGoldEarned: (playerRecord?.lifetimeGoldEarned ?? 0) + stateToSave.player.totalGoldEarned,
|
||||
lifetimeQuestsCompleted: playerRecord?.lifetimeQuestsCompleted ?? 0,
|
||||
prestigeCount: stateToSave.prestige.count,
|
||||
transcendenceCount: stateToSave.transcendence?.count ?? 0,
|
||||
|
||||
Reference in New Issue
Block a user