feat: adding drive mapping

This commit is contained in:
Chubby Granny Chaser
2024-10-08 03:33:57 +01:00
26 changed files with 620 additions and 421 deletions

View File

@@ -1,88 +1,74 @@
import type { GameAchievement, GameShop } from "@types";
import type { GameAchievement, GameShop, UnlockedAchievement } from "@types";
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import {
gameAchievementRepository,
gameRepository,
userPreferencesRepository,
userAuthRepository,
} from "@main/repository";
import { UserNotLoggedInError } from "@shared";
import { Game } from "@main/entity";
import { getGameAchievementData } from "@main/services/achievements/get-game-achievement-data";
import { HydraApi } from "@main/services";
const getAchievementsDataFromApi = async (
objectId: string,
const getAchievements = async (
shop: string,
game: Game | null
objectId: string,
userId?: string
) => {
const userPreferences = await userPreferencesRepository.findOne({
where: { id: 1 },
const userAuth = await userAuthRepository.findOne({ where: { userId } });
const cachedAchievements = await gameAchievementRepository.findOne({
where: { objectId, shop },
});
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"]
);
}
const achievementsData = cachedAchievements?.achievements
? JSON.parse(cachedAchievements.achievements)
: await getGameAchievementData(objectId, shop);
return achievements;
})
.catch((err) => {
if (err instanceof UserNotLoggedInError) throw err;
return [];
});
if (!userId || userAuth) {
const unlockedAchievements = JSON.parse(
cachedAchievements?.unlockedAchievements || "[]"
) as UnlockedAchievement[];
return { achievementsData, unlockedAchievements };
}
const unlockedAchievements = await HydraApi.get<UnlockedAchievement[]>(
`/users/${userId}/games/achievements`,
{ shop, objectId, language: "en" }
);
return { achievementsData, unlockedAchievements };
};
const getGameAchievements = async (
_event: Electron.IpcMainInvokeEvent,
export const getGameAchievements = async (
objectId: string,
shop: GameShop
shop: GameShop,
userId?: string
): Promise<GameAchievement[]> => {
const [game, cachedAchievements] = await Promise.all([
gameRepository.findOne({
where: { objectID: objectId, shop },
}),
gameAchievementRepository.findOne({ where: { objectId, shop } }),
]);
const { achievementsData, unlockedAchievements } = await getAchievements(
shop,
objectId,
userId
);
const gameAchievements = cachedAchievements?.achievements
? JSON.parse(cachedAchievements.achievements)
: await getAchievementsDataFromApi(objectId, shop, game);
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;
@@ -91,4 +77,13 @@ const getGameAchievements = async (
});
};
registerEvent("getGameAchievements", getGameAchievements);
const getGameAchievementsEvent = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop,
userId?: string
): Promise<GameAchievement[]> => {
return getGameAchievements(objectId, shop, userId);
};
registerEvent("getGameAchievements", getGameAchievementsEvent);

View File

@@ -20,11 +20,6 @@ export interface LudusaviBackup {
};
}
const getPathDrive = (key: string) => {
const parts = key.split("/");
return parts[0];
};
const replaceLudusaviBackupWithCurrentUser = (
backupPath: string,
title: string

View File

@@ -44,12 +44,12 @@ const addGameToLibrary = async (
});
}
updateLocalUnlockedAchivements(objectId);
const game = await gameRepository.findOne({
where: { objectID: objectId },
});
updateLocalUnlockedAchivements(game!);
createGame(game!).catch(() => {});
});
};

View File

@@ -30,4 +30,4 @@ export const isPortableVersion = () =>
process.env.PORTABLE_EXECUTABLE_FILE !== null;
export const normalizePath = (str: string) =>
path.normalize(str).replace(/\\/g, "/");
path.posix.normalize(str).replace(/\\/g, "/");

View File

@@ -1,81 +0,0 @@
import { parseAchievementFile } from "./parse-achievement-file";
import { Game } from "@main/entity";
import { mergeAchievements } from "./merge-achievements";
import fs from "node:fs";
import {
findAchievementFileInExecutableDirectory,
findAllAchievementFiles,
} from "./find-achivement-files";
import type { AchievementFile } from "@types";
import { logger } from "../logger";
const fileStats: Map<string, number> = new Map();
const processAchievementFileDiff = async (
game: Game,
file: AchievementFile
) => {
const unlockedAchievements = await parseAchievementFile(
file.filePath,
file.type
);
logger.log("Achievements from file", file.filePath, unlockedAchievements);
if (unlockedAchievements.length) {
return mergeAchievements(
game.objectID,
game.shop,
unlockedAchievements,
true
);
}
};
const compareFile = async (game: Game, file: AchievementFile) => {
try {
const stat = fs.statSync(file.filePath);
const currentFileStat = fileStats.get(file.filePath);
fileStats.set(file.filePath, stat.mtimeMs);
if (!currentFileStat || currentFileStat === stat.mtimeMs) {
return;
}
logger.log(
"Detected change in file",
file.filePath,
stat.mtimeMs,
fileStats.get(file.filePath)
);
await processAchievementFileDiff(game, file);
} catch (err) {
fileStats.set(file.filePath, -1);
}
};
export const checkAchievementFileChange = async (games: Game[]) => {
const achievementFiles = await findAllAchievementFiles();
for (const game of games) {
const gameAchievementFiles = achievementFiles.get(game.objectID) || [];
const achievementFileInsideDirectory =
findAchievementFileInExecutableDirectory(game);
if (achievementFileInsideDirectory) {
gameAchievementFiles.push(achievementFileInsideDirectory);
}
if (!gameAchievementFiles.length) continue;
logger.log(
"Achievements files to observe for:",
game.title,
gameAchievementFiles
);
for (const file of gameAchievementFiles) {
compareFile(game, file);
}
}
};

View File

@@ -1,5 +1,19 @@
import { gameRepository } from "@main/repository";
import { checkAchievementFileChange as searchForAchievements } from "./achievement-file-observer";
import { parseAchievementFile } from "./parse-achievement-file";
import { Game } from "@main/entity";
import { mergeAchievements } from "./merge-achievements";
import fs, { readdirSync } from "node:fs";
import {
findAchievementFileInExecutableDirectory,
findAllAchievementFiles,
getAlternativeObjectIds,
} from "./find-achivement-files";
import type { AchievementFile } from "@types";
import { achievementsLogger, logger } from "../logger";
import { Cracker } from "@shared";
const fileStats: Map<string, number> = new Map();
const fltFiles: Map<string, Set<string>> = new Map();
export const watchAchievements = async () => {
const games = await gameRepository.find({
@@ -10,5 +24,100 @@ export const watchAchievements = async () => {
if (games.length === 0) return;
await searchForAchievements(games);
const achievementFiles = findAllAchievementFiles();
for (const game of games) {
for (const objectId of getAlternativeObjectIds(game.objectID)) {
const gameAchievementFiles = achievementFiles.get(objectId) || [];
const achievementFileInsideDirectory =
findAchievementFileInExecutableDirectory(game);
gameAchievementFiles.push(...achievementFileInsideDirectory);
if (!gameAchievementFiles.length) continue;
console.log(
"Achievements files to observe for:",
game.title,
gameAchievementFiles
);
for (const file of gameAchievementFiles) {
compareFile(game, file);
}
}
}
};
const processAchievementFileDiff = async (
game: Game,
file: AchievementFile
) => {
const unlockedAchievements = parseAchievementFile(file.filePath, file.type);
logger.log("Achievements from file", file.filePath, unlockedAchievements);
if (unlockedAchievements.length) {
return mergeAchievements(
game.objectID,
game.shop,
unlockedAchievements,
true
);
}
};
const compareFltFolder = async (game: Game, file: AchievementFile) => {
try {
const currentAchievements = new Set(readdirSync(file.filePath));
const previousAchievements = fltFiles.get(file.filePath);
fltFiles.set(file.filePath, currentAchievements);
if (
!previousAchievements ||
currentAchievements.difference(previousAchievements).size === 0
) {
return;
}
logger.log("Detected change in FLT folder", file.filePath);
await processAchievementFileDiff(game, file);
} catch (err) {
achievementsLogger.error(err);
fltFiles.set(file.filePath, new Set());
}
};
const compareFile = async (game: Game, file: AchievementFile) => {
if (file.type === Cracker.flt) {
await compareFltFolder(game, file);
return;
}
try {
const currentStat = fs.statSync(file.filePath);
const previousStat = fileStats.get(file.filePath);
fileStats.set(file.filePath, currentStat.mtimeMs);
if (!previousStat) {
if (currentStat.mtimeMs) {
await processAchievementFileDiff(game, file);
return;
}
}
if (previousStat === currentStat.mtimeMs) {
return;
}
logger.log(
"Detected change in file",
file.filePath,
currentStat.mtimeMs,
fileStats.get(file.filePath)
);
await processAchievementFileDiff(game, file);
} catch (err) {
fileStats.set(file.filePath, -1);
}
};

View File

@@ -24,9 +24,10 @@ const crackers = [
Cracker.skidrow,
Cracker.smartSteamEmu,
Cracker.empress,
Cracker.flt,
];
const getPathFromCracker = async (cracker: Cracker) => {
const getPathFromCracker = (cracker: Cracker) => {
if (cracker === Cracker.codex) {
return [
{
@@ -85,6 +86,10 @@ const getPathFromCracker = async (cracker: Cracker) => {
folderPath: path.join(programData, "Steam", "Player"),
fileLocation: ["stats", "achievements.ini"],
},
{
folderPath: path.join(programData, "Steam", "dodi"),
fileLocation: ["stats", "achievements.ini"],
},
];
}
@@ -131,7 +136,33 @@ const getPathFromCracker = async (cracker: Cracker) => {
return [
{
folderPath: path.join(appData, "SmartSteamEmu"),
fileLocation: ["User", "Achievements"],
fileLocation: ["User", "Achievements.ini"],
},
];
}
if (cracker === Cracker._3dm) {
return [];
}
if (cracker === Cracker.flt) {
return [
{
folderPath: path.join(appData, "FLT"),
fileLocation: ["stats"],
},
];
}
if (cracker == Cracker.rle) {
return [
{
folderPath: path.join(appData, "RLE"),
fileLocation: ["achievements.ini"],
},
{
folderPath: path.join(appData, "RLE"),
fileLocation: ["Achievements.ini"],
},
];
}
@@ -140,20 +171,29 @@ const getPathFromCracker = async (cracker: Cracker) => {
throw new Error(`Cracker ${cracker} not implemented`);
};
export const findAchievementFiles = async (game: Game) => {
export const getAlternativeObjectIds = (objectId: string) => {
// Dishonored
if (objectId === "205100") {
return ["205100", "217980", "31292"];
}
return [objectId];
};
export const findAchievementFiles = (game: Game) => {
const achievementFiles: AchievementFile[] = [];
for (const cracker of crackers) {
for (const { folderPath, fileLocation } of await getPathFromCracker(
cracker
)) {
const filePath = path.join(folderPath, game.objectID, ...fileLocation);
for (const { folderPath, fileLocation } of getPathFromCracker(cracker)) {
for (const objectId of getAlternativeObjectIds(game.objectID)) {
const filePath = path.join(folderPath, objectId, ...fileLocation);
if (fs.existsSync(filePath)) {
achievementFiles.push({
type: cracker,
filePath,
});
if (fs.existsSync(filePath)) {
achievementFiles.push({
type: cracker,
filePath,
});
}
}
}
}
@@ -163,31 +203,40 @@ export const findAchievementFiles = async (game: Game) => {
export const findAchievementFileInExecutableDirectory = (
game: Game
): AchievementFile | null => {
): AchievementFile[] => {
if (!game.executablePath) {
return null;
return [];
}
const steamDataPath = path.join(
game.executablePath,
"..",
"SteamData",
"user_stats.ini"
);
return {
type: Cracker.userstats,
filePath: steamDataPath,
};
return [
{
type: Cracker.userstats,
filePath: path.join(
game.executablePath,
"..",
"SteamData",
"user_stats.ini"
),
},
{
type: Cracker._3dm,
filePath: path.join(
game.executablePath,
"..",
"3DMGAME",
"Player",
"stats",
"achievements.ini"
),
},
];
};
export const findAllAchievementFiles = async () => {
export const findAllAchievementFiles = () => {
const gameAchievementFiles = new Map<string, AchievementFile[]>();
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;
}

View File

@@ -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(() => []);
};

View File

@@ -2,6 +2,7 @@ import { gameAchievementRepository, gameRepository } from "@main/repository";
import type { GameShop, UnlockedAchievement } from "@types";
import { WindowManager } from "../window-manager";
import { HydraApi } from "../hydra-api";
import { getGameAchievements } from "@main/events/catalogue/get-game-achievements";
const saveAchievementsOnLocal = async (
objectId: string,
@@ -17,11 +18,10 @@ const saveAchievementsOnLocal = async (
},
["objectId", "shop"]
)
.then(() => {
.then(async () => {
WindowManager.mainWindow?.webContents.send(
"on-achievement-unlocked",
objectId,
shop
`on-update-achievements-${objectId}-${shop}`,
await getGameAchievements(objectId, shop as GameShop)
);
});
};
@@ -47,7 +47,7 @@ export const mergeAchievements = async (
const unlockedAchievements = JSON.parse(
localGameAchievement?.unlockedAchievements || "[]"
);
).filter((achievement) => achievement.name);
const newAchievements = achievements
.filter((achievement) => {
@@ -60,7 +60,7 @@ export const mergeAchievements = async (
.map((achievement) => {
return {
name: achievement.name.toUpperCase(),
unlockTime: achievement.unlockTime * 1000,
unlockTime: achievement.unlockTime,
};
});

View File

@@ -1,67 +1,84 @@
import { Cracker } from "@shared";
import { UnlockedAchievement } from "@types";
import { existsSync, createReadStream, readFileSync } 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[]> => {
): 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);
}
achievementsLogger.log(`${type} achievements found on ${filePath}`);
if (type === Cracker._3dm) {
const parsed = iniParse(filePath);
return process3DM(parsed);
}
if (type === Cracker.flt) {
const achievements = readdirSync(filePath);
return achievements.map((achievement) => {
return {
name: achievement,
unlockTime: Date.now(),
};
});
}
if (type === Cracker.creamAPI) {
const parsed = iniParse(filePath);
return processCreamAPI(parsed);
}
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<string, Record<string, string | number>> = {};
for await (const line of lines) {
for (const line of lines) {
if (line.startsWith("###") || !line.length) continue;
if (line.startsWith("[") && line.endsWith("]")) {
@@ -69,13 +86,13 @@ const iniParse = async (filePath: string) => {
object[objectName] = {};
} else {
const [name, ...value] = line.split("=");
object[objectName][name.trim()] = value.join("").trim();
object[objectName][name.trim()] = value.join("=").trim();
}
}
console.log("Parsed ini", object);
return object;
} catch {
} catch (err) {
achievementsLogger.error(`Error parsing ${filePath}`, err);
return null;
}
};
@@ -83,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;
}
};
@@ -97,7 +115,28 @@ const processOnlineFix = (unlockedAchievements: any): UnlockedAchievement[] => {
if (unlockedAchievement?.achieved) {
parsedUnlockedAchievements.push({
name: achievement,
unlockTime: unlockedAchievement.timestamp,
unlockTime: unlockedAchievement.timestamp * 1000,
});
}
}
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,
});
}
}
@@ -115,7 +154,7 @@ const processSkidrow = (unlockedAchievements: any): UnlockedAchievement[] => {
if (unlockedAchievement[0] === "1") {
parsedUnlockedAchievements.push({
name: achievement,
unlockTime: unlockedAchievement[unlockedAchievement.length - 1],
unlockTime: unlockedAchievement[unlockedAchievement.length - 1] * 1000,
});
}
}
@@ -132,13 +171,36 @@ const processGoldberg = (unlockedAchievements: any): UnlockedAchievement[] => {
if (unlockedAchievement?.earned) {
newUnlockedAchievements.push({
name: achievement,
unlockTime: unlockedAchievement.earned_time,
unlockTime: unlockedAchievement.earned_time * 1000,
});
}
}
return newUnlockedAchievements;
};
const process3DM = (unlockedAchievements: any): UnlockedAchievement[] => {
const newUnlockedAchievements: UnlockedAchievement[] = [];
const achievements = unlockedAchievements["State"];
const times = unlockedAchievements["Time"];
for (const achievement of Object.keys(achievements)) {
if (achievements[achievement] == "0101") {
const time = times[achievement];
newUnlockedAchievements.push({
name: achievement,
unlockTime:
new DataView(
new Uint8Array(Buffer.from(time.toString(), "hex")).buffer
).getUint32(0, true) * 1000,
});
}
}
return newUnlockedAchievements;
};
const processDefault = (unlockedAchievements: any): UnlockedAchievement[] => {
const newUnlockedAchievements: UnlockedAchievement[] = [];
@@ -148,7 +210,7 @@ const processDefault = (unlockedAchievements: any): UnlockedAchievement[] => {
if (unlockedAchievement?.Achieved) {
newUnlockedAchievements.push({
name: achievement,
unlockTime: unlockedAchievement.UnlockTime,
unlockTime: unlockedAchievement.UnlockTime * 1000,
});
}
}
@@ -167,11 +229,12 @@ const processRld = (unlockedAchievements: any): UnlockedAchievement[] => {
if (unlockedAchievement?.State) {
newUnlockedAchievements.push({
name: achievement,
unlockTime: new DataView(
new Uint8Array(
Buffer.from(unlockedAchievement.Time.toString(), "hex")
).buffer
).getUint32(0, true),
unlockTime:
new DataView(
new Uint8Array(
Buffer.from(unlockedAchievement.Time.toString(), "hex")
).buffer
).getUint32(0, true) * 1000,
});
}
}
@@ -195,8 +258,8 @@ const processUserStats = (unlockedAchievements: any): UnlockedAchievement[] => {
if (!isNaN(unlockTime)) {
newUnlockedAchievements.push({
name: achievement,
unlockTime: unlockTime,
name: achievement.replace(/"/g, ``),
unlockTime: unlockTime * 1000,
});
}
}

View File

@@ -2,96 +2,82 @@ import { gameAchievementRepository, gameRepository } from "@main/repository";
import {
findAllAchievementFiles,
findAchievementFiles,
findAchievementFileInExecutableDirectory,
getAlternativeObjectIds,
} from "./find-achivement-files";
import { parseAchievementFile } from "./parse-achievement-file";
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 = await findAllAchievementFiles();
const gameAchievementFilesMap = findAllAchievementFiles();
for (const objectId of gameAchievementFilesMap.keys()) {
const gameAchievementFiles = gameAchievementFilesMap.get(objectId)!;
const games = await gameRepository.find({
where: {
isDeleted: false,
},
});
const [game, localAchievements] = await Promise.all([
gameRepository.findOne({
where: { objectID: objectId, shop: "steam", isDeleted: false },
}),
gameAchievementRepository.findOne({
where: { objectId, shop: "steam" },
}),
]);
for (const game of games) {
for (const objectId of getAlternativeObjectIds(game.objectID)) {
const gameAchievementFiles = gameAchievementFilesMap.get(objectId) || [];
const achievementFileInsideDirectory =
findAchievementFileInExecutableDirectory(game);
if (!game) continue;
gameAchievementFiles.push(...achievementFileInsideDirectory);
if (!localAchievements || !localAchievements.achievements) {
await getGameAchievementData(objectId, "steam")
.then((achievements) => {
return gameAchievementRepository.upsert(
{
objectId,
shop: "steam",
achievements: JSON.stringify(achievements),
},
["objectId", "shop"]
);
gameAchievementRepository
.findOne({
where: { objectId: game.objectID, shop: "steam" },
})
.catch(() => {});
}
.then((localAchievements) => {
if (!localAchievements || !localAchievements.achievements) {
getGameAchievementData(game.objectID, "steam");
}
});
const unlockedAchievements: UnlockedAchievement[] = [];
const unlockedAchievements: UnlockedAchievement[] = [];
for (const achievementFile of gameAchievementFiles) {
const parsedAchievements = await parseAchievementFile(
achievementFile.filePath,
achievementFile.type
);
console.log("Parsed for", game.title, parsedAchievements);
if (parsedAchievements.length) {
unlockedAchievements.push(...parsedAchievements);
for (const achievementFile of gameAchievementFiles) {
const parsedAchievements = parseAchievementFile(
achievementFile.filePath,
achievementFile.type
);
if (parsedAchievements.length) {
unlockedAchievements.push(...parsedAchievements);
}
achievementsLogger.log(
"Achievement file for",
game.title,
achievementFile.filePath,
parsedAchievements
);
}
}
mergeAchievements(objectId, "steam", unlockedAchievements, false);
mergeAchievements(game.objectID, "steam", unlockedAchievements, false);
}
}
};
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" },
}),
]);
export const updateLocalUnlockedAchivements = async (game: Game) => {
const gameAchievementFiles = findAchievementFiles(game);
if (!game) return;
const achievementFileInsideDirectory =
findAchievementFileInExecutableDirectory(game);
const gameAchievementFiles = await findAchievementFiles(game);
gameAchievementFiles.push(...achievementFileInsideDirectory);
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
);
@@ -101,5 +87,5 @@ export const updateLocalUnlockedAchivements = async (objectId: string) => {
}
}
mergeAchievements(objectId, "steam", unlockedAchievements, false);
mergeAchievements(game.objectID, "steam", unlockedAchievements, false);
};

View File

@@ -10,6 +10,10 @@ log.transports.file.resolvePathFn = (
return path.join(logsPath, "pythoninstance.txt");
}
if (message?.scope == "achievements") {
return path.join(logsPath, "achievements.txt");
}
if (message?.level === "error") {
return path.join(logsPath, "error.txt");
}

View File

@@ -12,6 +12,6 @@ export const startMainLoop = async () => {
watchAchievements(),
]);
await sleep(1000);
await sleep(1500);
}
};