mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-12 11:12:07 +00:00
feat: refactoring achievements watcher
This commit is contained in:
parent
c18c41ac95
commit
44e59a5f6f
12 changed files with 120 additions and 57 deletions
|
@ -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);
|
|
@ -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";
|
||||
|
|
16
src/main/services/achievements/achievement-watcher.ts
Normal file
16
src/main/services/achievements/achievement-watcher.ts
Normal 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);
|
||||
}
|
||||
};
|
|
@ -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(
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
1
src/renderer/src/declaration.d.ts
vendored
1
src/renderer/src/declaration.d.ts
vendored
|
@ -70,7 +70,6 @@ declare global {
|
|||
objectId: string,
|
||||
shop: GameShop
|
||||
) => Promise<GameAchievement[]>;
|
||||
updateGameUnlockedAchievements: (objectId: string) => Promise<void>;
|
||||
onAchievementUnlocked: (
|
||||
cb: (
|
||||
objectId: string,
|
||||
|
|
Loading…
Reference in a new issue