diff --git a/src/main/events/catalogue/update-game-unlocked-achievements.ts b/src/main/events/catalogue/update-game-unlocked-achievements.ts deleted file mode 100644 index f5e435b5..00000000 --- a/src/main/events/catalogue/update-game-unlocked-achievements.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { registerEvent } from "../register-event"; -import { updateLocalUnlockedAchivements } from "@main/services/achievements/update-local-unlocked-achivements"; - -const updateGameUnlockedAchievements = async ( - _event: Electron.IpcMainInvokeEvent, - objectId: string -) => { - return updateLocalUnlockedAchivements(false, objectId); -}; - -registerEvent("updateGameUnlockedAchievements", updateGameUnlockedAchievements); diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 11861116..22eb341f 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -10,7 +10,6 @@ import "./catalogue/search-games"; import "./catalogue/get-game-stats"; import "./catalogue/get-trending-games"; import "./catalogue/get-game-achievements"; -import "./catalogue/update-game-unlocked-achievements"; import "./hardware/get-disk-free-space"; import "./library/add-game-to-library"; import "./library/create-game-shortcut"; diff --git a/src/main/services/achievements/achievement-watcher.ts b/src/main/services/achievements/achievement-watcher.ts new file mode 100644 index 00000000..9d1564c0 --- /dev/null +++ b/src/main/services/achievements/achievement-watcher.ts @@ -0,0 +1,16 @@ +import { gameRepository } from "@main/repository"; +import { startGameAchievementObserver } from "./game-achievements-observer"; + +export const watchAchievements = async () => { + const games = await gameRepository.find({ + where: { + isDeleted: false, + }, + }); + + if (games.length === 0) return; + + for (const game of games) { + startGameAchievementObserver(game); + } +}; diff --git a/src/main/services/achievements/find-steam-game-achivement-files.ts b/src/main/services/achievements/find-steam-game-achivement-files.ts index 369cf61e..e6ea05f5 100644 --- a/src/main/services/achievements/find-steam-game-achivement-files.ts +++ b/src/main/services/achievements/find-steam-game-achivement-files.ts @@ -35,7 +35,40 @@ const getObjectIdsInFolder = (path: string) => { return []; }; -export const findSteamGameAchievementFiles = (objectId?: string) => { +export const findSteamGameAchievementFiles = (objectId: string) => { + const crackers = [ + Cracker.codex, + Cracker.goldberg, + Cracker.rune, + Cracker.onlineFix, + ]; + + const achievementFiles: AchievementFile[] = []; + for (const cracker of crackers) { + let achievementPath: string; + let fileLocation: string[]; + + if (cracker === Cracker.onlineFix) { + achievementPath = path.join(publicDir, Cracker.onlineFix); + fileLocation = ["Stats", "Achievements.ini"]; + } else if (cracker === Cracker.goldberg) { + achievementPath = path.join(appData, "Goldberg SteamEmu Saves"); + fileLocation = ["achievements.json"]; + } else { + achievementPath = path.join(publicDir, "Steam", cracker); + fileLocation = ["achievements.ini"]; + } + + achievementFiles.push({ + type: cracker, + filePath: path.join(achievementPath, objectId, ...fileLocation), + }); + } + + return achievementFiles; +}; + +export const findAllSteamGameAchievementFiles = () => { const gameAchievementFiles = new Map(); const crackers = [ @@ -60,9 +93,7 @@ export const findSteamGameAchievementFiles = (objectId?: string) => { fileLocation = ["achievements.ini"]; } - const objectIds = objectId - ? [objectId] - : getObjectIdsInFolder(achievementPath); + const objectIds = getObjectIdsInFolder(achievementPath); for (const objectId of objectIds) { addGame( diff --git a/src/main/services/achievements/game-achievements-observer.ts b/src/main/services/achievements/game-achievements-observer.ts index ae040b62..3a0125b5 100644 --- a/src/main/services/achievements/game-achievements-observer.ts +++ b/src/main/services/achievements/game-achievements-observer.ts @@ -37,14 +37,10 @@ const processAchievementFile = async (game: Game, file: AchievementFile) => { }; const startFileWatch = async (game: Game, file: AchievementFile) => { - const signal = gameAchievementObserver[game.id]?.signal; - try { await processAchievementFile(game, file); - const watcher = watch(file.filePath, { - signal, - }); + const watcher = watch(file.filePath); for await (const event of watcher) { if (event.eventType === "change") { @@ -62,8 +58,7 @@ export const startGameAchievementObserver = async (game: Game) => { if (game.shop !== "steam") return; if (gameAchievementObserver[game.id]) return; - const achievementFiles = - findSteamGameAchievementFiles(game.objectID).get(game.objectID) || []; + const achievementFiles = findSteamGameAchievementFiles(game.objectID); logger.log( "Achievements files to observe for:", @@ -84,8 +79,3 @@ export const startGameAchievementObserver = async (game: Game) => { startFileWatch(game, file); } }; - -export const stopGameAchievementObserver = (gameId: number) => { - gameAchievementObserver[gameId]?.abort(); - delete gameAchievementObserver[gameId]; -}; diff --git a/src/main/services/achievements/update-local-unlocked-achivements.ts b/src/main/services/achievements/update-local-unlocked-achivements.ts index 2a0d5f58..35e72a4e 100644 --- a/src/main/services/achievements/update-local-unlocked-achivements.ts +++ b/src/main/services/achievements/update-local-unlocked-achivements.ts @@ -1,16 +1,16 @@ import { gameAchievementRepository, gameRepository } from "@main/repository"; -import { findSteamGameAchievementFiles } from "./find-steam-game-achivement-files"; +import { + findAllSteamGameAchievementFiles, + findSteamGameAchievementFiles, +} from "./find-steam-game-achivement-files"; import { parseAchievementFile } from "./parse-achievement-file"; import { checkUnlockedAchievements } from "./check-unlocked-achievements"; import { mergeAchievements } from "./merge-achievements"; import type { UnlockedAchievement } from "@types"; import { getGameAchievementData } from "./get-game-achievement-data"; -export const updateLocalUnlockedAchivements = async ( - publishNotification: boolean, - objectId?: string -) => { - const gameAchievementFiles = findSteamGameAchievementFiles(objectId); +export const updateAllLocalUnlockedAchievements = async () => { + const gameAchievementFiles = findAllSteamGameAchievementFiles(); for (const objectId of gameAchievementFiles.keys()) { const [game, localAchievements] = await Promise.all([ @@ -62,11 +62,62 @@ export const updateLocalUnlockedAchivements = async ( } } - mergeAchievements( - objectId, - "steam", - unlockedAchievements, - publishNotification - ); + mergeAchievements(objectId, "steam", unlockedAchievements, false); } }; + +export const updateLocalUnlockedAchivements = async ( + publishNotification: boolean, + objectId: string +) => { + const gameAchievementFiles = findSteamGameAchievementFiles(objectId); + + const [game, localAchievements] = await Promise.all([ + gameRepository.findOne({ + where: { objectID: objectId, shop: "steam", isDeleted: false }, + }), + gameAchievementRepository.findOne({ + where: { objectId, shop: "steam" }, + }), + ]); + + if (!game) return; + + console.log("Achievements files for", game.title, gameAchievementFiles); + + if (!localAchievements || !localAchievements.achievements) { + await getGameAchievementData(objectId, "steam") + .then((achievements) => { + return gameAchievementRepository.upsert( + { + objectId, + shop: "steam", + achievements: JSON.stringify(achievements), + }, + ["objectId", "shop"] + ); + }) + .catch(() => {}); + } + + const unlockedAchievements: UnlockedAchievement[] = []; + + for (const achievementFile of gameAchievementFiles) { + const localAchievementFile = await parseAchievementFile( + achievementFile.filePath + ); + + if (localAchievementFile) { + unlockedAchievements.push( + ...checkUnlockedAchievements(achievementFile.type, localAchievementFile) + ); + } + } + + mergeAchievements( + objectId, + "steam", + unlockedAchievements, + publishNotification + ); +}; diff --git a/src/main/services/library-sync/upload-games-batch.ts b/src/main/services/library-sync/upload-games-batch.ts index f46b0f14..366f6c4e 100644 --- a/src/main/services/library-sync/upload-games-batch.ts +++ b/src/main/services/library-sync/upload-games-batch.ts @@ -4,7 +4,7 @@ import { IsNull } from "typeorm"; import { HydraApi } from "../hydra-api"; import { mergeWithRemoteGames } from "./merge-with-remote-games"; import { WindowManager } from "../window-manager"; -import { updateLocalUnlockedAchivements } from "../achievements/update-local-unlocked-achivements"; +import { updateAllLocalUnlockedAchievements } from "../achievements/update-local-unlocked-achivements"; export const uploadGamesBatch = async () => { const games = await gameRepository.find({ @@ -29,7 +29,7 @@ export const uploadGamesBatch = async () => { await mergeWithRemoteGames(); - await updateLocalUnlockedAchivements(false); + await updateAllLocalUnlockedAchievements(); if (WindowManager.mainWindow) WindowManager.mainWindow.webContents.send("on-library-batch-complete"); diff --git a/src/main/services/main-loop.ts b/src/main/services/main-loop.ts index f2ec51ba..f45956f2 100644 --- a/src/main/services/main-loop.ts +++ b/src/main/services/main-loop.ts @@ -1,6 +1,7 @@ import { sleep } from "@main/helpers"; import { DownloadManager } from "./download"; import { watchProcesses } from "./process-watcher"; +import { watchAchievements } from "./achievements/achievement-watcher"; export const startMainLoop = async () => { // eslint-disable-next-line no-constant-condition @@ -8,6 +9,7 @@ export const startMainLoop = async () => { await Promise.allSettled([ watchProcesses(), DownloadManager.watchDownloads(), + watchAchievements(), ]); await sleep(1000); diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index 162a3282..ac2e35e5 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -5,10 +5,6 @@ import { createGame, updateGamePlaytime } from "./library-sync"; import { GameRunning } from "@types"; import { PythonInstance } from "./download"; import { Game } from "@main/entity"; -import { - startGameAchievementObserver, - stopGameAchievementObserver, -} from "@main/services/achievements/game-achievements-observer"; export const gamesPlaytime = new Map< number, @@ -78,8 +74,6 @@ function onOpenGame(game: Game) { } else { createGame({ ...game, lastTimePlayed: new Date() }).catch(() => {}); } - - startGameAchievementObserver(game); } function onTickGame(game: Game) { @@ -116,8 +110,6 @@ function onTickGame(game: Game) { }) .catch(() => {}); } - - startGameAchievementObserver(game); } const onCloseGame = (game: Game) => { @@ -133,6 +125,4 @@ const onCloseGame = (game: Game) => { } else { createGame(game).catch(() => {}); } - - stopGameAchievementObserver(game.id); }; diff --git a/src/preload/index.ts b/src/preload/index.ts index 9e847b28..b24d1440 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -51,8 +51,6 @@ contextBridge.exposeInMainWorld("electron", { getTrendingGames: () => ipcRenderer.invoke("getTrendingGames"), getGameAchievements: (objectId: string, shop: GameShop) => ipcRenderer.invoke("getGameAchievements", objectId, shop), - updateGameUnlockedAchievements: (objectId: string) => - ipcRenderer.invoke("updateGameUnlockedAchievements", objectId), onAchievementUnlocked: ( cb: ( objectId: string, diff --git a/src/renderer/src/context/game-details/game-details.context.tsx b/src/renderer/src/context/game-details/game-details.context.tsx index 393dcf17..883a80a5 100644 --- a/src/renderer/src/context/game-details/game-details.context.tsx +++ b/src/renderer/src/context/game-details/game-details.context.tsx @@ -179,8 +179,6 @@ export function GameDetailsContextProvider({ }, [game?.id, isGameRunning, updateGame]); useEffect(() => { - window.electron.updateGameUnlockedAchievements(objectID!).catch(() => {}); - const unsubscribe = window.electron.onAchievementUnlocked( (objectId, shop) => { if (objectID !== objectId || shop !== shop) return; diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index e24567e8..9db51497 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -70,7 +70,6 @@ declare global { objectId: string, shop: GameShop ) => Promise; - updateGameUnlockedAchievements: (objectId: string) => Promise; onAchievementUnlocked: ( cb: ( objectId: string,