feat: try add FLT and fix possible bug on unlockedAchievements

This commit is contained in:
Zamitto 2024-10-05 22:17:00 -03:00
parent 71e7f1ee58
commit 002028130b
6 changed files with 153 additions and 97 deletions

View File

@ -1,79 +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);
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,18 @@
import { gameRepository } from "@main/repository"; 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,
} 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 () => { export const watchAchievements = async () => {
const games = await gameRepository.find({ const games = await gameRepository.find({
@ -12,3 +25,96 @@ export const watchAchievements = async () => {
await searchForAchievements(games); await searchForAchievements(games);
}; };
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 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 || 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);
}
};
const searchForAchievements = async (games: Game[]) => {
const achievementFiles = await findAllAchievementFiles();
for (const game of games) {
const gameAchievementFiles = achievementFiles.get(game.objectID) || [];
const achievementFileInsideDirectory =
findAchievementFileInExecutableDirectory(game);
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

@ -24,6 +24,7 @@ const crackers = [
Cracker.skidrow, Cracker.skidrow,
Cracker.smartSteamEmu, Cracker.smartSteamEmu,
Cracker.empress, Cracker.empress,
Cracker.flt,
]; ];
const getPathFromCracker = async (cracker: Cracker) => { const getPathFromCracker = async (cracker: Cracker) => {
@ -131,7 +132,7 @@ const getPathFromCracker = async (cracker: Cracker) => {
return [ return [
{ {
folderPath: path.join(appData, "SmartSteamEmu"), folderPath: path.join(appData, "SmartSteamEmu"),
fileLocation: ["User", "Achievements"], fileLocation: ["User", "Achievements.ini"],
}, },
]; ];
} }
@ -140,6 +141,15 @@ const getPathFromCracker = async (cracker: Cracker) => {
return []; return [];
} }
if (cracker === Cracker.flt) {
return [
{
folderPath: path.join(appData, "FLT"),
fileLocation: ["stats"],
},
];
}
achievementsLogger.error(`Cracker ${cracker} not implemented`); achievementsLogger.error(`Cracker ${cracker} not implemented`);
throw new Error(`Cracker ${cracker} not implemented`); throw new Error(`Cracker ${cracker} not implemented`);
}; };

View File

@ -47,7 +47,7 @@ export const mergeAchievements = async (
const unlockedAchievements = JSON.parse( const unlockedAchievements = JSON.parse(
localGameAchievement?.unlockedAchievements || "[]" localGameAchievement?.unlockedAchievements || "[]"
); ).filter((achievement) => achievement.name);
const newAchievements = achievements const newAchievements = achievements
.filter((achievement) => { .filter((achievement) => {
@ -60,7 +60,7 @@ export const mergeAchievements = async (
.map((achievement) => { .map((achievement) => {
return { return {
name: achievement.name.toUpperCase(), name: achievement.name.toUpperCase(),
unlockTime: achievement.unlockTime * 1000, unlockTime: achievement.unlockTime,
}; };
}); });

View File

@ -1,6 +1,11 @@
import { Cracker } from "@shared"; import { Cracker } from "@shared";
import { UnlockedAchievement } from "@types"; import { UnlockedAchievement } from "@types";
import { existsSync, createReadStream, readFileSync } from "node:fs"; import {
existsSync,
createReadStream,
readFileSync,
readdirSync,
} from "node:fs";
import readline from "node:readline"; import readline from "node:readline";
import { achievementsLogger } from "../logger"; import { achievementsLogger } from "../logger";
@ -50,6 +55,17 @@ export const parseAchievementFile = async (
return process3DM(parsed); return process3DM(parsed);
} }
if (type === Cracker.flt) {
const achievements = readdirSync(filePath);
return achievements.map((achievement) => {
return {
name: achievement,
unlockTime: Date.now(),
};
});
}
achievementsLogger.log(`${type} achievements found on ${filePath}`); achievementsLogger.log(`${type} achievements found on ${filePath}`);
return []; return [];
}; };
@ -101,7 +117,7 @@ const processOnlineFix = (unlockedAchievements: any): UnlockedAchievement[] => {
if (unlockedAchievement?.achieved) { if (unlockedAchievement?.achieved) {
parsedUnlockedAchievements.push({ parsedUnlockedAchievements.push({
name: achievement, name: achievement,
unlockTime: unlockedAchievement.timestamp, unlockTime: unlockedAchievement.timestamp * 1000,
}); });
} }
} }
@ -119,7 +135,7 @@ const processSkidrow = (unlockedAchievements: any): UnlockedAchievement[] => {
if (unlockedAchievement[0] === "1") { if (unlockedAchievement[0] === "1") {
parsedUnlockedAchievements.push({ parsedUnlockedAchievements.push({
name: achievement, name: achievement,
unlockTime: unlockedAchievement[unlockedAchievement.length - 1], unlockTime: unlockedAchievement[unlockedAchievement.length - 1] * 1000,
}); });
} }
} }
@ -136,7 +152,7 @@ const processGoldberg = (unlockedAchievements: any): UnlockedAchievement[] => {
if (unlockedAchievement?.earned) { if (unlockedAchievement?.earned) {
newUnlockedAchievements.push({ newUnlockedAchievements.push({
name: achievement, name: achievement,
unlockTime: unlockedAchievement.earned_time, unlockTime: unlockedAchievement.earned_time * 1000,
}); });
} }
} }
@ -155,9 +171,10 @@ const process3DM = (unlockedAchievements: any): UnlockedAchievement[] => {
newUnlockedAchievements.push({ newUnlockedAchievements.push({
name: achievement, name: achievement,
unlockTime: new DataView( unlockTime:
new DataView(
new Uint8Array(Buffer.from(time.toString(), "hex")).buffer new Uint8Array(Buffer.from(time.toString(), "hex")).buffer
).getUint32(0, true), ).getUint32(0, true) * 1000,
}); });
} }
} }
@ -174,7 +191,7 @@ const processDefault = (unlockedAchievements: any): UnlockedAchievement[] => {
if (unlockedAchievement?.Achieved) { if (unlockedAchievement?.Achieved) {
newUnlockedAchievements.push({ newUnlockedAchievements.push({
name: achievement, name: achievement,
unlockTime: unlockedAchievement.UnlockTime, unlockTime: unlockedAchievement.UnlockTime * 1000,
}); });
} }
} }
@ -193,11 +210,12 @@ const processRld = (unlockedAchievements: any): UnlockedAchievement[] => {
if (unlockedAchievement?.State) { if (unlockedAchievement?.State) {
newUnlockedAchievements.push({ newUnlockedAchievements.push({
name: achievement, name: achievement,
unlockTime: new DataView( unlockTime:
new DataView(
new Uint8Array( new Uint8Array(
Buffer.from(unlockedAchievement.Time.toString(), "hex") Buffer.from(unlockedAchievement.Time.toString(), "hex")
).buffer ).buffer
).getUint32(0, true), ).getUint32(0, true) * 1000,
}); });
} }
} }
@ -222,7 +240,7 @@ const processUserStats = (unlockedAchievements: any): UnlockedAchievement[] => {
if (!isNaN(unlockTime)) { if (!isNaN(unlockTime)) {
newUnlockedAchievements.push({ newUnlockedAchievements.push({
name: achievement.replace(/"/g, ``), name: achievement.replace(/"/g, ``),
unlockTime: unlockTime, unlockTime: unlockTime * 1000,
}); });
} }
} }

View File

@ -36,4 +36,5 @@ export enum Cracker {
creamAPI = "CreamAPI", creamAPI = "CreamAPI",
smartSteamEmu = "SmartSteamEmu", smartSteamEmu = "SmartSteamEmu",
_3dm = "3dm", _3dm = "3dm",
flt = "FLT",
} }