feat: refactoring achievements watcher

This commit is contained in:
Zamitto 2024-10-01 22:21:41 -03:00
parent c18c41ac95
commit 44e59a5f6f
12 changed files with 120 additions and 57 deletions

View file

@ -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);

View file

@ -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";

View file

@ -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);
}
};

View file

@ -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<string, AchievementFile[]>();
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(

View file

@ -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];
};

View file

@ -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
);
};

View file

@ -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");

View file

@ -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);

View file

@ -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);
};

View file

@ -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,

View file

@ -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;

View file

@ -70,7 +70,6 @@ declare global {
objectId: string,
shop: GameShop
) => Promise<GameAchievement[]>;
updateGameUnlockedAchievements: (objectId: string) => Promise<void>;
onAchievementUnlocked: (
cb: (
objectId: string,