diff --git a/src/main/events/library/add-game-to-library.ts b/src/main/events/library/add-game-to-library.ts index 5b74cc8c..01495a39 100644 --- a/src/main/events/library/add-game-to-library.ts +++ b/src/main/events/library/add-game-to-library.ts @@ -1,13 +1,13 @@ import { registerEvent } from "../register-event"; import type { GameShop } from "@types"; import { createGame } from "@main/services/library-sync"; -import { updateLocalUnlockedAchievements } from "@main/services/achievements/update-local-unlocked-achivements"; import { downloadsSublevel, gamesShopAssetsSublevel, gamesSublevel, levelKeys, } from "@main/level"; +import { AchievementWatcherManager } from "@main/services/achievements/achievement-watcher-manager"; const addGameToLibrary = async ( _event: Electron.IpcMainInvokeEvent, @@ -43,7 +43,10 @@ const addGameToLibrary = async ( await createGame(game).catch(() => {}); - updateLocalUnlockedAchievements(game); + AchievementWatcherManager.firstSyncWithRemoteIfNeeded( + game.shop, + game.objectId + ); }; registerEvent("addGameToLibrary", addGameToLibrary); diff --git a/src/main/events/user/get-compared-unlocked-achievements.ts b/src/main/events/user/get-compared-unlocked-achievements.ts index 697ad716..be641f2a 100644 --- a/src/main/events/user/get-compared-unlocked-achievements.ts +++ b/src/main/events/user/get-compared-unlocked-achievements.ts @@ -3,6 +3,7 @@ import { registerEvent } from "../register-event"; import { HydraApi } from "@main/services"; import { db, levelKeys } from "@main/level"; +import { AchievementWatcherManager } from "@main/services/achievements/achievement-watcher-manager"; const getComparedUnlockedAchievements = async ( _event: Electron.IpcMainInvokeEvent, @@ -10,6 +11,8 @@ const getComparedUnlockedAchievements = async ( shop: GameShop, userId: string ) => { + await AchievementWatcherManager.firstSyncWithRemoteIfNeeded(shop, objectId); + const userPreferences = await db.get( levelKeys.userPreferences, { diff --git a/src/main/events/user/get-unlocked-achievements.ts b/src/main/events/user/get-unlocked-achievements.ts index 21aad7a0..2bf4c76e 100644 --- a/src/main/events/user/get-unlocked-achievements.ts +++ b/src/main/events/user/get-unlocked-achievements.ts @@ -2,12 +2,15 @@ import type { GameShop, UserAchievement, UserPreferences } from "@types"; import { registerEvent } from "../register-event"; import { getGameAchievementData } from "@main/services/achievements/get-game-achievement-data"; import { db, gameAchievementsSublevel, levelKeys } from "@main/level"; +import { AchievementWatcherManager } from "@main/services/achievements/achievement-watcher-manager"; export const getUnlockedAchievements = async ( objectId: string, shop: GameShop, useCachedData: boolean ): Promise => { + AchievementWatcherManager.firstSyncWithRemoteIfNeeded(shop, objectId); + const cachedAchievements = await gameAchievementsSublevel.get( levelKeys.game(shop, objectId) ); diff --git a/src/main/services/achievements/achievement-watcher-manager.ts b/src/main/services/achievements/achievement-watcher-manager.ts index 5cf09d4f..cf9a544c 100644 --- a/src/main/services/achievements/achievement-watcher-manager.ts +++ b/src/main/services/achievements/achievement-watcher-manager.ts @@ -10,6 +10,7 @@ import { import type { AchievementFile, Game, + GameShop, UnlockedAchievement, UserPreferences, } from "@types"; @@ -146,7 +147,48 @@ const processAchievementFileDiff = async ( }; export class AchievementWatcherManager { - private static hasFinishedMergingWithRemote = false; + private static _hasFinishedMergingWithRemote = false; + + public static get hasFinishedMergingWithRemote() { + return this._hasFinishedMergingWithRemote; + } + + public static readonly alreadySyncedGames: Map = new Map(); + + public static async firstSyncWithRemoteIfNeeded( + shop: GameShop, + objectId: string + ) { + const gameKey = levelKeys.game(shop, objectId); + if (this.alreadySyncedGames.get(gameKey)) return; + + const game = await gamesSublevel.get(gameKey).catch(() => null); + if (!game) return; + + const gameAchievementFiles = findAchievementFiles(game); + + const achievementFileInsideDirectory = + findAchievementFileInExecutableDirectory(game); + + gameAchievementFiles.push(...achievementFileInsideDirectory); + + const unlockedAchievements: UnlockedAchievement[] = []; + + for (const achievementFile of gameAchievementFiles) { + const localAchievementFile = parseAchievementFile( + achievementFile.filePath, + achievementFile.type + ); + + if (localAchievementFile.length) { + unlockedAchievements.push(...localAchievementFile); + } + } + + this.alreadySyncedGames.set(gameKey, true); + + return mergeAchievements(game, unlockedAchievements, false); + } public static watchAchievements() { if (!this.hasFinishedMergingWithRemote) return; @@ -295,6 +337,6 @@ export class AchievementWatcherManager { achievementsLogger.error("Error on preSearchAchievements", err); } - this.hasFinishedMergingWithRemote = true; + this._hasFinishedMergingWithRemote = true; } } diff --git a/src/main/services/achievements/merge-achievements.ts b/src/main/services/achievements/merge-achievements.ts index 2674e451..058c2bc6 100644 --- a/src/main/services/achievements/merge-achievements.ts +++ b/src/main/services/achievements/merge-achievements.ts @@ -14,6 +14,7 @@ import { SubscriptionRequiredError } from "@shared"; import { achievementsLogger } from "../logger"; import { db, gameAchievementsSublevel, levelKeys } from "@main/level"; import { getGameAchievementData } from "./get-game-achievement-data"; +import { AchievementWatcherManager } from "./achievement-watcher-manager"; const isRareAchievement = (points: number) => { const rawPercentage = (50 - Math.sqrt(points)) * 2; @@ -56,9 +57,9 @@ export const mergeAchievements = async ( achievements: UnlockedAchievement[], publishNotification: boolean ) => { - let localGameAchievement = await gameAchievementsSublevel.get( - levelKeys.game(game.shop, game.objectId) - ); + const gameKey = levelKeys.game(game.shop, game.objectId); + + let localGameAchievement = await gameAchievementsSublevel.get(gameKey); const userPreferences = await db.get( levelKeys.userPreferences, { @@ -68,9 +69,7 @@ export const mergeAchievements = async ( if (!localGameAchievement) { await getGameAchievementData(game.objectId, game.shop, true); - localGameAchievement = await gameAchievementsSublevel.get( - levelKeys.game(game.shop, game.objectId) - ); + localGameAchievement = await gameAchievementsSublevel.get(gameKey); } const achievementsData = localGameAchievement?.achievements ?? []; @@ -153,7 +152,12 @@ export const mergeAchievements = async ( } } - if (game.remoteId) { + const shouldSyncWithRemote = + game.remoteId && + (newAchievements.length || + AchievementWatcherManager.hasFinishedMergingWithRemote); + + if (shouldSyncWithRemote) { await HydraApi.put( "/profile/games/achievements", { @@ -194,8 +198,11 @@ export const mergeAchievements = async ( mergedLocalAchievements, publishNotification ); + }) + .finally(() => { + AchievementWatcherManager.alreadySyncedGames.set(gameKey, true); }); - } else { + } else if (newAchievements.length) { await saveAchievementsOnLocal( game.objectId, game.shop, diff --git a/src/main/services/achievements/update-local-unlocked-achivements.ts b/src/main/services/achievements/update-local-unlocked-achivements.ts deleted file mode 100644 index 44f2693a..00000000 --- a/src/main/services/achievements/update-local-unlocked-achivements.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - findAchievementFiles, - findAchievementFileInExecutableDirectory, -} from "./find-achivement-files"; -import { parseAchievementFile } from "./parse-achievement-file"; -import { mergeAchievements } from "./merge-achievements"; -import type { Game, UnlockedAchievement } from "@types"; - -export const updateLocalUnlockedAchievements = async (game: Game) => { - const gameAchievementFiles = findAchievementFiles(game); - - const achievementFileInsideDirectory = - findAchievementFileInExecutableDirectory(game); - - gameAchievementFiles.push(...achievementFileInsideDirectory); - - const unlockedAchievements: UnlockedAchievement[] = []; - - for (const achievementFile of gameAchievementFiles) { - const localAchievementFile = parseAchievementFile( - achievementFile.filePath, - achievementFile.type - ); - - if (localAchievementFile.length) { - unlockedAchievements.push(...localAchievementFile); - } - } - - mergeAchievements(game, unlockedAchievements, false); -};