From 5d21adcbb1f3dc095cde5fe44e35a950fa543207 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sun, 6 Oct 2024 02:48:46 -0300 Subject: [PATCH 1/3] wip: get alternate objectIds --- .../achievements/achievement-watcher.ts | 29 ++++--- .../achievements/find-achivement-files.ts | 16 ++-- .../update-local-unlocked-achivements.ts | 86 ++++++++++--------- 3 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/main/services/achievements/achievement-watcher.ts b/src/main/services/achievements/achievement-watcher.ts index eecb71d5..15496a0e 100644 --- a/src/main/services/achievements/achievement-watcher.ts +++ b/src/main/services/achievements/achievement-watcher.ts @@ -6,6 +6,7 @@ import fs, { readdirSync } from "node:fs"; import { findAchievementFileInExecutableDirectory, findAllAchievementFiles, + getAlternativeObjectIds, } from "./find-achivement-files"; import type { AchievementFile } from "@types"; import { achievementsLogger, logger } from "../logger"; @@ -23,25 +24,27 @@ export const watchAchievements = async () => { if (games.length === 0) return; - const achievementFiles = await findAllAchievementFiles(); + const achievementFiles = findAllAchievementFiles(); for (const game of games) { - const gameAchievementFiles = achievementFiles.get(game.objectID) || []; - const achievementFileInsideDirectory = - findAchievementFileInExecutableDirectory(game); + for (const objectId of getAlternativeObjectIds(game.objectID)) { + const gameAchievementFiles = achievementFiles.get(objectId) || []; + const achievementFileInsideDirectory = + findAchievementFileInExecutableDirectory(game); - gameAchievementFiles.push(...achievementFileInsideDirectory); + gameAchievementFiles.push(...achievementFileInsideDirectory); - if (!gameAchievementFiles.length) continue; + if (!gameAchievementFiles.length) continue; - console.log( - "Achievements files to observe for:", - game.title, - gameAchievementFiles - ); + console.log( + "Achievements files to observe for:", + game.title, + gameAchievementFiles + ); - for (const file of gameAchievementFiles) { - compareFile(game, file); + for (const file of gameAchievementFiles) { + compareFile(game, file); + } } } }; diff --git a/src/main/services/achievements/find-achivement-files.ts b/src/main/services/achievements/find-achivement-files.ts index c7aa4950..d13ffb63 100644 --- a/src/main/services/achievements/find-achivement-files.ts +++ b/src/main/services/achievements/find-achivement-files.ts @@ -27,7 +27,7 @@ const crackers = [ Cracker.flt, ]; -const getPathFromCracker = async (cracker: Cracker) => { +const getPathFromCracker = (cracker: Cracker) => { if (cracker === Cracker.codex) { return [ { @@ -167,7 +167,7 @@ const getPathFromCracker = async (cracker: Cracker) => { throw new Error(`Cracker ${cracker} not implemented`); }; -const getAlternativeObjectIds = (objectId: string) => { +export const getAlternativeObjectIds = (objectId: string) => { if (objectId === "205100") { return ["205100", "217980", "31292"]; } @@ -175,13 +175,11 @@ const getAlternativeObjectIds = (objectId: string) => { return [objectId]; }; -export const findAchievementFiles = async (game: Game) => { +export const findAchievementFiles = (game: Game) => { const achievementFiles: AchievementFile[] = []; for (const cracker of crackers) { - for (const { folderPath, fileLocation } of await getPathFromCracker( - cracker - )) { + for (const { folderPath, fileLocation } of getPathFromCracker(cracker)) { for (const objectId of getAlternativeObjectIds(game.objectID)) { const filePath = path.join(folderPath, objectId, ...fileLocation); @@ -229,13 +227,11 @@ export const findAchievementFileInExecutableDirectory = ( ]; }; -export const findAllAchievementFiles = async () => { +export const findAllAchievementFiles = () => { const gameAchievementFiles = new Map(); for (const cracker of crackers) { - for (const { folderPath, fileLocation } of await getPathFromCracker( - cracker - )) { + for (const { folderPath, fileLocation } of getPathFromCracker(cracker)) { if (!fs.existsSync(folderPath)) { continue; } diff --git a/src/main/services/achievements/update-local-unlocked-achivements.ts b/src/main/services/achievements/update-local-unlocked-achivements.ts index 963ce602..a58cd69f 100644 --- a/src/main/services/achievements/update-local-unlocked-achivements.ts +++ b/src/main/services/achievements/update-local-unlocked-achivements.ts @@ -3,6 +3,7 @@ import { findAllAchievementFiles, findAchievementFiles, findAchievementFileInExecutableDirectory, + getAlternativeObjectIds, } from "./find-achivement-files"; import { parseAchievementFile } from "./parse-achievement-file"; import { mergeAchievements } from "./merge-achievements"; @@ -11,7 +12,7 @@ import { getGameAchievementData } from "./get-game-achievement-data"; import { achievementsLogger } from "../logger"; export const updateAllLocalUnlockedAchievements = async () => { - const gameAchievementFilesMap = await findAllAchievementFiles(); + const gameAchievementFilesMap = findAllAchievementFiles(); const games = await gameRepository.find({ where: { @@ -20,53 +21,54 @@ export const updateAllLocalUnlockedAchievements = async () => { }); for (const game of games) { - const gameAchievementFiles = - gameAchievementFilesMap.get(game.objectID) || []; - const achievementFileInsideDirectory = - findAchievementFileInExecutableDirectory(game); + for (const objectId of getAlternativeObjectIds(game.objectID)) { + const gameAchievementFiles = gameAchievementFilesMap.get(objectId) || []; + const achievementFileInsideDirectory = + findAchievementFileInExecutableDirectory(game); - gameAchievementFiles.push(...achievementFileInsideDirectory); + gameAchievementFiles.push(...achievementFileInsideDirectory); - const localAchievements = await gameAchievementRepository.findOne({ - where: { objectId: game.objectID, shop: "steam" }, - }); + const localAchievements = await gameAchievementRepository.findOne({ + where: { objectId: game.objectID, shop: "steam" }, + }); - if (!localAchievements || !localAchievements.achievements) { - await getGameAchievementData(game.objectID, "steam") - .then((achievements) => { - return gameAchievementRepository.upsert( - { - objectId: game.objectID, - shop: "steam", - achievements: JSON.stringify(achievements), - }, - ["objectId", "shop"] - ); - }) - .catch(() => {}); - } - - const unlockedAchievements: UnlockedAchievement[] = []; - - for (const achievementFile of gameAchievementFiles) { - const parsedAchievements = await parseAchievementFile( - achievementFile.filePath, - achievementFile.type - ); - - if (parsedAchievements.length) { - unlockedAchievements.push(...parsedAchievements); + if (!localAchievements || !localAchievements.achievements) { + await getGameAchievementData(game.objectID, "steam") + .then((achievements) => { + return gameAchievementRepository.upsert( + { + objectId: game.objectID, + shop: "steam", + achievements: JSON.stringify(achievements), + }, + ["objectId", "shop"] + ); + }) + .catch(() => {}); } - achievementsLogger.log( - "Achievement file for", - game.title, - achievementFile.filePath, - parsedAchievements - ); - } + const unlockedAchievements: UnlockedAchievement[] = []; - mergeAchievements(game.objectID, "steam", unlockedAchievements, false); + for (const achievementFile of gameAchievementFiles) { + const parsedAchievements = await parseAchievementFile( + achievementFile.filePath, + achievementFile.type + ); + + if (parsedAchievements.length) { + unlockedAchievements.push(...parsedAchievements); + } + + achievementsLogger.log( + "Achievement file for", + game.title, + achievementFile.filePath, + parsedAchievements + ); + } + + mergeAchievements(game.objectID, "steam", unlockedAchievements, false); + } } }; From af83152997c13df54bf22bb0ee4c0250d41bd9e8 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sun, 6 Oct 2024 11:18:14 -0300 Subject: [PATCH 2/3] feat: creamapi process --- .../achievements/parse-achievement-file.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main/services/achievements/parse-achievement-file.ts b/src/main/services/achievements/parse-achievement-file.ts index 251fd1f9..5817b032 100644 --- a/src/main/services/achievements/parse-achievement-file.ts +++ b/src/main/services/achievements/parse-achievement-file.ts @@ -66,6 +66,11 @@ export const parseAchievementFile = async ( }); } + if (type === Cracker.creamAPI) { + const parsed = await iniParse(filePath); + return processCreamAPI(parsed); + } + achievementsLogger.log(`${type} achievements found on ${filePath}`); return []; }; @@ -125,6 +130,27 @@ const processOnlineFix = (unlockedAchievements: any): UnlockedAchievement[] => { return parsedUnlockedAchievements; }; +const processCreamAPI = (unlockedAchievements: any): UnlockedAchievement[] => { + const parsedUnlockedAchievements: UnlockedAchievement[] = []; + + for (const achievement of Object.keys(unlockedAchievements)) { + const unlockedAchievement = unlockedAchievements[achievement]; + + if (unlockedAchievement?.achieved) { + const unlockTime = unlockedAchievement.unlocktime; + parsedUnlockedAchievements.push({ + name: achievement, + unlockTime: + unlockTime.length === 7 + ? unlockTime * 1000 * 1000 + : unlockTime * 1000, + }); + } + } + + return parsedUnlockedAchievements; +}; + const processSkidrow = (unlockedAchievements: any): UnlockedAchievement[] => { const parsedUnlockedAchievements: UnlockedAchievement[] = []; const achievements = unlockedAchievements["Achievements"]; From 7f09a7796f8dd6cc0c17dd5b2d30d4eea97727dd Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sun, 6 Oct 2024 13:39:26 -0300 Subject: [PATCH 3/3] feat: remove awaits --- .../events/catalogue/get-game-achievements.ts | 67 ++++--------------- .../events/library/add-game-to-library.ts | 4 +- .../achievements/achievement-watcher.ts | 5 +- .../achievements/find-achivement-files.ts | 1 + .../achievements/get-game-achievement-data.ts | 20 +++++- .../achievements/parse-achievement-file.ts | 51 ++++++-------- .../update-local-unlocked-achivements.ts | 64 +++++------------- src/main/services/main-loop.ts | 2 +- 8 files changed, 72 insertions(+), 142 deletions(-) diff --git a/src/main/events/catalogue/get-game-achievements.ts b/src/main/events/catalogue/get-game-achievements.ts index f0fa2c94..456babf4 100644 --- a/src/main/events/catalogue/get-game-achievements.ts +++ b/src/main/events/catalogue/get-game-achievements.ts @@ -1,88 +1,45 @@ import type { GameAchievement, GameShop } from "@types"; import { registerEvent } from "../register-event"; -import { HydraApi } from "@main/services"; -import { - gameAchievementRepository, - gameRepository, - userPreferencesRepository, -} from "@main/repository"; -import { UserNotLoggedInError } from "@shared"; -import { Game } from "@main/entity"; - -const getAchievementsDataFromApi = async ( - objectId: string, - shop: string, - game: Game | null -) => { - const userPreferences = await userPreferencesRepository.findOne({ - where: { id: 1 }, - }); - - return HydraApi.get("/games/achievements", { - objectId, - shop, - language: userPreferences?.language || "en", - }) - .then((achievements) => { - if (game) { - gameAchievementRepository.upsert( - { - objectId, - shop, - achievements: JSON.stringify(achievements), - }, - ["objectId", "shop"] - ); - } - - return achievements; - }) - .catch((err) => { - if (err instanceof UserNotLoggedInError) throw err; - return []; - }); -}; +import { gameAchievementRepository } from "@main/repository"; +import { getGameAchievementData } from "@main/services/achievements/get-game-achievement-data"; const getGameAchievements = async ( _event: Electron.IpcMainInvokeEvent, objectId: string, shop: GameShop ): Promise => { - const [game, cachedAchievements] = await Promise.all([ - gameRepository.findOne({ - where: { objectID: objectId, shop }, - }), - gameAchievementRepository.findOne({ where: { objectId, shop } }), - ]); + const cachedAchievements = await gameAchievementRepository.findOne({ + where: { objectId, shop }, + }); - const gameAchievements = cachedAchievements?.achievements + const achievementsData = cachedAchievements?.achievements ? JSON.parse(cachedAchievements.achievements) - : await getAchievementsDataFromApi(objectId, shop, game); + : await getGameAchievementData(objectId, shop); const unlockedAchievements = JSON.parse( cachedAchievements?.unlockedAchievements || "[]" ) as { name: string; unlockTime: number }[]; - return gameAchievements - .map((achievement) => { + return achievementsData + .map((achievementData) => { const unlockedAchiement = unlockedAchievements.find( (localAchievement) => { return ( localAchievement.name.toUpperCase() == - achievement.name.toUpperCase() + achievementData.name.toUpperCase() ); } ); if (unlockedAchiement) { return { - ...achievement, + ...achievementData, unlocked: true, unlockTime: unlockedAchiement.unlockTime, }; } - return { ...achievement, unlocked: false, unlockTime: null }; + return { ...achievementData, unlocked: false, unlockTime: null }; }) .sort((a, b) => { if (a.unlocked && !b.unlocked) return -1; diff --git a/src/main/events/library/add-game-to-library.ts b/src/main/events/library/add-game-to-library.ts index c6ec63c0..a47c96e5 100644 --- a/src/main/events/library/add-game-to-library.ts +++ b/src/main/events/library/add-game-to-library.ts @@ -44,10 +44,10 @@ const addGameToLibrary = async ( }); } - updateLocalUnlockedAchivements(objectID); - const game = await gameRepository.findOne({ where: { objectID } }); + updateLocalUnlockedAchivements(game!); + createGame(game!).catch(() => {}); }); }; diff --git a/src/main/services/achievements/achievement-watcher.ts b/src/main/services/achievements/achievement-watcher.ts index 15496a0e..166e6cff 100644 --- a/src/main/services/achievements/achievement-watcher.ts +++ b/src/main/services/achievements/achievement-watcher.ts @@ -53,10 +53,7 @@ const processAchievementFileDiff = async ( game: Game, file: AchievementFile ) => { - const unlockedAchievements = await parseAchievementFile( - file.filePath, - file.type - ); + const unlockedAchievements = parseAchievementFile(file.filePath, file.type); logger.log("Achievements from file", file.filePath, unlockedAchievements); diff --git a/src/main/services/achievements/find-achivement-files.ts b/src/main/services/achievements/find-achivement-files.ts index d13ffb63..ebfbefca 100644 --- a/src/main/services/achievements/find-achivement-files.ts +++ b/src/main/services/achievements/find-achivement-files.ts @@ -168,6 +168,7 @@ const getPathFromCracker = (cracker: Cracker) => { }; export const getAlternativeObjectIds = (objectId: string) => { + // Dishonored if (objectId === "205100") { return ["205100", "217980", "31292"]; } diff --git a/src/main/services/achievements/get-game-achievement-data.ts b/src/main/services/achievements/get-game-achievement-data.ts index 79e551ac..7f1e6b5a 100644 --- a/src/main/services/achievements/get-game-achievement-data.ts +++ b/src/main/services/achievements/get-game-achievement-data.ts @@ -1,4 +1,7 @@ -import { userPreferencesRepository } from "@main/repository"; +import { + gameAchievementRepository, + userPreferencesRepository, +} from "@main/repository"; import { HydraApi } from "../hydra-api"; export const getGameAchievementData = async ( @@ -13,5 +16,18 @@ export const getGameAchievementData = async ( shop, objectId, language: userPreferences?.language || "en", - }); + }) + .then(async (achievements) => { + await gameAchievementRepository.upsert( + { + objectId, + shop, + achievements: JSON.stringify(achievements), + }, + ["objectId", "shop"] + ); + + return achievements; + }) + .catch(() => []); }; diff --git a/src/main/services/achievements/parse-achievement-file.ts b/src/main/services/achievements/parse-achievement-file.ts index 5817b032..47c8e805 100644 --- a/src/main/services/achievements/parse-achievement-file.ts +++ b/src/main/services/achievements/parse-achievement-file.ts @@ -1,57 +1,51 @@ import { Cracker } from "@shared"; import { UnlockedAchievement } from "@types"; -import { - existsSync, - createReadStream, - readFileSync, - readdirSync, -} from "node:fs"; -import readline from "node:readline"; +import { existsSync, readFileSync, readdirSync } from "node:fs"; import { achievementsLogger } from "../logger"; -export const parseAchievementFile = async ( +export const parseAchievementFile = ( filePath: string, type: Cracker -): Promise => { +): UnlockedAchievement[] => { if (!existsSync(filePath)) return []; if (type == Cracker.codex) { - const parsed = await iniParse(filePath); + const parsed = iniParse(filePath); return processDefault(parsed); } if (type == Cracker.rune) { - const parsed = await iniParse(filePath); + const parsed = iniParse(filePath); return processDefault(parsed); } if (type === Cracker.onlineFix) { - const parsed = await iniParse(filePath); + const parsed = iniParse(filePath); return processOnlineFix(parsed); } if (type === Cracker.goldberg) { - const parsed = await jsonParse(filePath); + const parsed = jsonParse(filePath); return processGoldberg(parsed); } if (type == Cracker.userstats) { - const parsed = await iniParse(filePath); + const parsed = iniParse(filePath); return processUserStats(parsed); } if (type == Cracker.rld) { - const parsed = await iniParse(filePath); + const parsed = iniParse(filePath); return processRld(parsed); } if (type === Cracker.skidrow) { - const parsed = await iniParse(filePath); + const parsed = iniParse(filePath); return processSkidrow(parsed); } if (type === Cracker._3dm) { - const parsed = await iniParse(filePath); + const parsed = iniParse(filePath); return process3DM(parsed); } @@ -67,27 +61,24 @@ export const parseAchievementFile = async ( } if (type === Cracker.creamAPI) { - const parsed = await iniParse(filePath); + const parsed = iniParse(filePath); return processCreamAPI(parsed); } - achievementsLogger.log(`${type} achievements found on ${filePath}`); + achievementsLogger.log( + `Unprocessed ${type} achievements found on ${filePath}` + ); return []; }; -const iniParse = async (filePath: string) => { +const iniParse = (filePath: string) => { try { - const file = createReadStream(filePath); - - const lines = readline.createInterface({ - input: file, - crlfDelay: Infinity, - }); + const lines = readFileSync(filePath, "utf-8").split(/[\r\n]+/); let objectName = ""; const object: Record> = {}; - for await (const line of lines) { + for (const line of lines) { if (line.startsWith("###") || !line.length) continue; if (line.startsWith("[") && line.endsWith("]")) { @@ -100,7 +91,8 @@ const iniParse = async (filePath: string) => { } return object; - } catch { + } catch (err) { + achievementsLogger.error(`Error parsing ${filePath}`, err); return null; } }; @@ -108,7 +100,8 @@ const iniParse = async (filePath: string) => { const jsonParse = (filePath: string) => { try { return JSON.parse(readFileSync(filePath, "utf-8")); - } catch { + } catch (err) { + achievementsLogger.error(`Error parsing ${filePath}`, err); return null; } }; diff --git a/src/main/services/achievements/update-local-unlocked-achivements.ts b/src/main/services/achievements/update-local-unlocked-achivements.ts index a58cd69f..13f33fcd 100644 --- a/src/main/services/achievements/update-local-unlocked-achivements.ts +++ b/src/main/services/achievements/update-local-unlocked-achivements.ts @@ -10,6 +10,7 @@ import { mergeAchievements } from "./merge-achievements"; import type { UnlockedAchievement } from "@types"; import { getGameAchievementData } from "./get-game-achievement-data"; import { achievementsLogger } from "../logger"; +import { Game } from "@main/entity"; export const updateAllLocalUnlockedAchievements = async () => { const gameAchievementFilesMap = findAllAchievementFiles(); @@ -28,29 +29,20 @@ export const updateAllLocalUnlockedAchievements = async () => { gameAchievementFiles.push(...achievementFileInsideDirectory); - const localAchievements = await gameAchievementRepository.findOne({ - where: { objectId: game.objectID, shop: "steam" }, - }); - - if (!localAchievements || !localAchievements.achievements) { - await getGameAchievementData(game.objectID, "steam") - .then((achievements) => { - return gameAchievementRepository.upsert( - { - objectId: game.objectID, - shop: "steam", - achievements: JSON.stringify(achievements), - }, - ["objectId", "shop"] - ); - }) - .catch(() => {}); - } + gameAchievementRepository + .findOne({ + where: { objectId: game.objectID, shop: "steam" }, + }) + .then((localAchievements) => { + if (!localAchievements || !localAchievements.achievements) { + getGameAchievementData(game.objectID, "steam"); + } + }); const unlockedAchievements: UnlockedAchievement[] = []; for (const achievementFile of gameAchievementFiles) { - const parsedAchievements = await parseAchievementFile( + const parsedAchievements = parseAchievementFile( achievementFile.filePath, achievementFile.type ); @@ -72,19 +64,8 @@ export const updateAllLocalUnlockedAchievements = async () => { } }; -export const updateLocalUnlockedAchivements = async (objectId: string) => { - const [game, localAchievements] = await Promise.all([ - gameRepository.findOne({ - where: { objectID: objectId, shop: "steam", isDeleted: false }, - }), - gameAchievementRepository.findOne({ - where: { objectId, shop: "steam" }, - }), - ]); - - if (!game) return; - - const gameAchievementFiles = await findAchievementFiles(game); +export const updateLocalUnlockedAchivements = async (game: Game) => { + const gameAchievementFiles = findAchievementFiles(game); const achievementFileInsideDirectory = findAchievementFileInExecutableDirectory(game); @@ -93,25 +74,10 @@ export const updateLocalUnlockedAchivements = async (objectId: string) => { 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( + const localAchievementFile = parseAchievementFile( achievementFile.filePath, achievementFile.type ); @@ -121,5 +87,5 @@ export const updateLocalUnlockedAchivements = async (objectId: string) => { } } - mergeAchievements(objectId, "steam", unlockedAchievements, false); + mergeAchievements(game.objectID, "steam", unlockedAchievements, false); }; diff --git a/src/main/services/main-loop.ts b/src/main/services/main-loop.ts index f45956f2..5ba57fc3 100644 --- a/src/main/services/main-loop.ts +++ b/src/main/services/main-loop.ts @@ -12,6 +12,6 @@ export const startMainLoop = async () => { watchAchievements(), ]); - await sleep(1000); + await sleep(1500); } };