mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
feat: real time achievement track
This commit is contained in:
parent
24d21b9839
commit
5c790edb2c
14 changed files with 181 additions and 407 deletions
|
@ -2,80 +2,77 @@ import { watch } from "node:fs/promises";
|
||||||
import { getGameAchievementsToWatch } from "./get-game-achievements-to-watch";
|
import { getGameAchievementsToWatch } from "./get-game-achievements-to-watch";
|
||||||
import { checkUnlockedAchievements } from "./util/check-unlocked-achievements";
|
import { checkUnlockedAchievements } from "./util/check-unlocked-achievements";
|
||||||
import { parseAchievementFile } from "./util/parseAchievementFile";
|
import { parseAchievementFile } from "./util/parseAchievementFile";
|
||||||
import { gameAchievementRepository } from "@main/repository";
|
import { Game } from "@main/entity";
|
||||||
|
import { mergeAchievements } from "./merge-achievements";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import { AchievementFile } from "./types";
|
||||||
|
|
||||||
type GameAchievementObserver = {
|
type GameAchievementObserver = {
|
||||||
[id: number]: AbortController | null;
|
[id: number]: AbortController;
|
||||||
};
|
};
|
||||||
|
|
||||||
const gameAchievementObserver: GameAchievementObserver = {};
|
const gameAchievementObserver: GameAchievementObserver = {};
|
||||||
|
|
||||||
export const startGameAchievementObserver = async (gameId: number) => {
|
const processAchievementFile = async (game: Game, file: AchievementFile) => {
|
||||||
if (
|
const localAchievementFile = await parseAchievementFile(file.filePath);
|
||||||
gameAchievementObserver[gameId] === null ||
|
console.log(localAchievementFile);
|
||||||
gameAchievementObserver[gameId]
|
|
||||||
) {
|
if (localAchievementFile) {
|
||||||
return;
|
const unlockedAchievements = checkUnlockedAchievements(
|
||||||
|
file.type,
|
||||||
|
localAchievementFile
|
||||||
|
);
|
||||||
|
console.log(unlockedAchievements);
|
||||||
|
|
||||||
|
if (unlockedAchievements.length) {
|
||||||
|
mergeAchievements(game.objectID, game.shop, unlockedAchievements);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
console.log(`Starting: ${gameId}`);
|
export const startGameAchievementObserver = async (game: Game) => {
|
||||||
|
if (gameAchievementObserver[game.id]) return;
|
||||||
|
|
||||||
const achievementsToWatch = await getGameAchievementsToWatch(gameId);
|
console.log(`Starting: ${game.title}`);
|
||||||
|
|
||||||
if (!achievementsToWatch) {
|
const achievementFiles = await getGameAchievementsToWatch(game.id);
|
||||||
console.log("No achievements to observe");
|
|
||||||
gameAchievementObserver[gameId] = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { steamId, checkedAchievements, achievementFiles } =
|
console.log(
|
||||||
achievementsToWatch;
|
"Achievements files to observe for:",
|
||||||
|
game.title,
|
||||||
gameAchievementObserver[gameId] = new AbortController();
|
achievementFiles
|
||||||
|
);
|
||||||
const achievements = checkedAchievements.all;
|
|
||||||
|
|
||||||
for (const file of achievementFiles) {
|
for (const file of achievementFiles) {
|
||||||
const signal = gameAchievementObserver[gameId]?.signal;
|
if (!fs.existsSync(file.filePath)) {
|
||||||
if (!signal) return;
|
continue;
|
||||||
console.log(`cracker: ${file.type}, steamId: ${steamId}`);
|
}
|
||||||
|
|
||||||
|
console.log(`cracker: ${file.type}, objectId: ${game.objectID}`);
|
||||||
|
|
||||||
|
if (!gameAchievementObserver[game.id]) {
|
||||||
|
const abortController = new AbortController();
|
||||||
|
gameAchievementObserver[game.id] = abortController;
|
||||||
|
}
|
||||||
|
|
||||||
|
const signal = gameAchievementObserver[game.id]?.signal;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
|
processAchievementFile(game, file);
|
||||||
|
|
||||||
const watcher = watch(file.filePath, {
|
const watcher = watch(file.filePath, {
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
for await (const event of watcher) {
|
for await (const event of watcher) {
|
||||||
if (event.eventType === "change") {
|
if (event.eventType === "change") {
|
||||||
console.log("file modified");
|
processAchievementFile(game, file);
|
||||||
const localAchievementFile = await parseAchievementFile(
|
|
||||||
file.filePath
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!localAchievementFile) continue;
|
|
||||||
|
|
||||||
const checked = checkUnlockedAchievements(
|
|
||||||
file.type,
|
|
||||||
localAchievementFile,
|
|
||||||
achievements
|
|
||||||
);
|
|
||||||
|
|
||||||
if (checked.new) {
|
|
||||||
console.log(checked.new);
|
|
||||||
|
|
||||||
gameAchievementRepository.update(
|
|
||||||
{
|
|
||||||
game: { id: steamId },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
achievements: JSON.stringify(checked.all),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.log(`cracker: ${file.type}, steamId ${steamId}`);
|
|
||||||
if (err?.name === "AbortError") return;
|
if (err?.name === "AbortError") return;
|
||||||
|
console.log(`cracker: ${file.type}, steamId ${game.objectID}`);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,61 +1,25 @@
|
||||||
import { gameRepository, gameAchievementRepository } from "@main/repository";
|
import { gameRepository } from "@main/repository";
|
||||||
import { steamGetAchivement } from "./steam/steam-get-achivement";
|
|
||||||
import { steamFindGameAchievementFiles } from "./steam/steam-find-game-achivement-files";
|
import { steamFindGameAchievementFiles } from "./steam/steam-find-game-achivement-files";
|
||||||
import { AchievementFile, CheckedAchievements } from "./types";
|
import { AchievementFile } from "./types";
|
||||||
import { parseAchievementFile } from "./util/parseAchievementFile";
|
|
||||||
import { checkUnlockedAchievements } from "./util/check-unlocked-achievements";
|
|
||||||
|
|
||||||
export const getGameAchievementsToWatch = async (
|
export const getGameAchievementsToWatch = async (
|
||||||
gameId: number
|
gameId: number
|
||||||
): Promise<
|
): Promise<AchievementFile[]> => {
|
||||||
| {
|
|
||||||
steamId: number;
|
|
||||||
checkedAchievements: CheckedAchievements;
|
|
||||||
achievementFiles: AchievementFile[];
|
|
||||||
}
|
|
||||||
| undefined
|
|
||||||
> => {
|
|
||||||
const game = await gameRepository.findOne({ where: { id: gameId } });
|
const game = await gameRepository.findOne({ where: { id: gameId } });
|
||||||
|
|
||||||
if (!game || game.shop !== "steam") return;
|
if (!game || game.shop !== "steam") return [];
|
||||||
|
|
||||||
const steamId = Number(game.objectID);
|
const steamId = Number(game.objectID);
|
||||||
|
|
||||||
const achievements = await steamGetAchivement(game);
|
|
||||||
|
|
||||||
console.log(achievements);
|
|
||||||
|
|
||||||
if (!achievements || !achievements.length) return;
|
|
||||||
|
|
||||||
const achievementFiles = steamFindGameAchievementFiles(game.objectID)[
|
const achievementFiles = steamFindGameAchievementFiles(game.objectID)[
|
||||||
steamId
|
steamId
|
||||||
];
|
];
|
||||||
console.log(achievementFiles);
|
console.log(
|
||||||
if (!achievementFiles || !achievementFiles.length) return;
|
"achivements files:",
|
||||||
|
achievementFiles,
|
||||||
|
game.title,
|
||||||
|
game.objectID
|
||||||
|
);
|
||||||
|
|
||||||
const checkedAchievements: CheckedAchievements = {
|
return achievementFiles || [];
|
||||||
all: achievements,
|
|
||||||
new: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const achievementFile of achievementFiles) {
|
|
||||||
const file = await parseAchievementFile(achievementFile.filePath);
|
|
||||||
|
|
||||||
checkedAchievements.new.push(
|
|
||||||
...checkUnlockedAchievements(achievementFile.type, file, achievements).new
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkedAchievements.new.length) {
|
|
||||||
await gameAchievementRepository.update(
|
|
||||||
{
|
|
||||||
game: { id: gameId },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
unlockedAchievements: JSON.stringify(checkedAchievements.all),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { steamId, checkedAchievements, achievementFiles };
|
|
||||||
};
|
};
|
||||||
|
|
51
src/main/services/achievements/merge-achievements.ts
Normal file
51
src/main/services/achievements/merge-achievements.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { gameAchievementRepository } from "@main/repository";
|
||||||
|
import { UnlockedAchievement } from "./types";
|
||||||
|
|
||||||
|
export const mergeAchievements = async (
|
||||||
|
objectId: string,
|
||||||
|
shop: string,
|
||||||
|
achievements: UnlockedAchievement[]
|
||||||
|
) => {
|
||||||
|
const localGameAchievement = await gameAchievementRepository.findOne({
|
||||||
|
where: {
|
||||||
|
objectId,
|
||||||
|
shop,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const unlockedAchievements = JSON.parse(
|
||||||
|
localGameAchievement?.unlockedAchievements || "[]"
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("file achievemets:", achievements);
|
||||||
|
const newAchievements = achievements.filter((achievement) => {
|
||||||
|
return !unlockedAchievements.some((localAchievement) => {
|
||||||
|
return localAchievement.name === achievement.name;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const mergedAchievements = unlockedAchievements.concat(newAchievements);
|
||||||
|
|
||||||
|
console.log("merged achievemetns", mergedAchievements);
|
||||||
|
gameAchievementRepository.upsert(
|
||||||
|
{
|
||||||
|
objectId,
|
||||||
|
shop,
|
||||||
|
unlockedAchievements: JSON.stringify(mergedAchievements),
|
||||||
|
},
|
||||||
|
["objectId", "shop"]
|
||||||
|
);
|
||||||
|
|
||||||
|
// return HydraApi.get("/profile/games/achievements").then(async (response) => {
|
||||||
|
// console.log(response);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (game.remoteId) {
|
||||||
|
// HydraApi.put("/profile/games/achievements", {
|
||||||
|
// id: game.remoteId,
|
||||||
|
// achievements: unlockedAchievements,
|
||||||
|
// }).catch(() => {
|
||||||
|
// console.log("erro");
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
};
|
|
@ -1,7 +0,0 @@
|
||||||
import { HydraApi } from "../hydra-api";
|
|
||||||
|
|
||||||
export const mergeWithRemoteAchievements = async () => {
|
|
||||||
return HydraApi.get("/profile/games/achievements").then(async (response) => {
|
|
||||||
console.log(response);
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -2,13 +2,14 @@ import { gameAchievementRepository, gameRepository } from "@main/repository";
|
||||||
import { steamFindGameAchievementFiles } from "./steam/steam-find-game-achivement-files";
|
import { steamFindGameAchievementFiles } from "./steam/steam-find-game-achivement-files";
|
||||||
import { parseAchievementFile } from "./util/parseAchievementFile";
|
import { parseAchievementFile } from "./util/parseAchievementFile";
|
||||||
import { HydraApi } from "@main/services";
|
import { HydraApi } from "@main/services";
|
||||||
|
import { checkUnlockedAchievements } from "./util/check-unlocked-achievements";
|
||||||
|
import { mergeAchievements } from "./merge-achievements";
|
||||||
|
import { UnlockedAchievement } from "./types";
|
||||||
|
|
||||||
export const saveAllLocalSteamAchivements = async () => {
|
export const saveAllLocalSteamAchivements = async () => {
|
||||||
const gameAchievementFiles = steamFindGameAchievementFiles();
|
const gameAchievementFiles = steamFindGameAchievementFiles();
|
||||||
|
|
||||||
for (const key of Object.keys(gameAchievementFiles)) {
|
for (const objectId of Object.keys(gameAchievementFiles)) {
|
||||||
const objectId = key;
|
|
||||||
|
|
||||||
const [game, localAchievements] = await Promise.all([
|
const [game, localAchievements] = await Promise.all([
|
||||||
gameRepository.findOne({
|
gameRepository.findOne({
|
||||||
where: { objectID: objectId, shop: "steam", isDeleted: false },
|
where: { objectID: objectId, shop: "steam", isDeleted: false },
|
||||||
|
@ -42,40 +43,20 @@ export const saveAllLocalSteamAchivements = async () => {
|
||||||
.catch(console.log);
|
.catch(console.log);
|
||||||
}
|
}
|
||||||
|
|
||||||
const unlockedAchievements: { name: string; unlockTime: number }[] = [];
|
const unlockedAchievements: UnlockedAchievement[] = [];
|
||||||
|
|
||||||
for (const achievementFile of gameAchievementFiles[key]) {
|
for (const achievementFile of gameAchievementFiles[objectId]) {
|
||||||
const localAchievementFile = await parseAchievementFile(
|
const localAchievementFile = await parseAchievementFile(
|
||||||
achievementFile.filePath
|
achievementFile.filePath
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(achievementFile.filePath);
|
console.log(achievementFile.filePath);
|
||||||
|
|
||||||
for (const a of Object.keys(localAchievementFile)) {
|
unlockedAchievements.push(
|
||||||
// TODO: use checkUnlockedAchievements after refactoring it to be generic
|
...checkUnlockedAchievements(achievementFile.type, localAchievementFile)
|
||||||
unlockedAchievements.push({
|
);
|
||||||
name: a,
|
|
||||||
unlockTime: localAchievementFile[a].UnlockTime,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gameAchievementRepository.upsert(
|
mergeAchievements(objectId, "steam", unlockedAchievements);
|
||||||
{
|
|
||||||
objectId,
|
|
||||||
shop: "steam",
|
|
||||||
unlockedAchievements: JSON.stringify(unlockedAchievements),
|
|
||||||
},
|
|
||||||
["objectId", "shop"]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (game.remoteId) {
|
|
||||||
HydraApi.put("/profile/games/achievements", {
|
|
||||||
id: game.remoteId,
|
|
||||||
achievements: unlockedAchievements,
|
|
||||||
}).catch(() => {
|
|
||||||
console.log("erro");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
import { logger } from "@main/services";
|
|
||||||
import { AchievementInfo } from "../types";
|
|
||||||
import { JSDOM } from "jsdom";
|
|
||||||
|
|
||||||
export const steamAchievementInfo = async (
|
|
||||||
objectId: string
|
|
||||||
): Promise<AchievementInfo[] | undefined> => {
|
|
||||||
const fetchUrl = `https://steamcommunity.com/stats/${objectId}/achievements`;
|
|
||||||
|
|
||||||
const achievementInfosHtmlText = await fetch(fetchUrl, {
|
|
||||||
method: "GET",
|
|
||||||
//headers: { "Accept-Language": "" },
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res.status === 200) return res.text();
|
|
||||||
throw new Error();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
logger.error(err, { method: "getSteamGameAchievements" });
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!achievementInfosHtmlText) return;
|
|
||||||
|
|
||||||
const achievementInfos: AchievementInfo[] = [];
|
|
||||||
|
|
||||||
const window = new JSDOM(achievementInfosHtmlText).window;
|
|
||||||
|
|
||||||
const itens = Array.from(
|
|
||||||
window.document.getElementsByClassName("achieveRow")
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const item of itens) {
|
|
||||||
const imageUrl = item
|
|
||||||
.getElementsByClassName("achieveImgHolder")?.[0]
|
|
||||||
.getElementsByTagName("img")?.[0]?.src;
|
|
||||||
|
|
||||||
const achievementName = item
|
|
||||||
.getElementsByClassName("achieveTxt")?.[0]
|
|
||||||
.getElementsByTagName("h3")?.[0].innerHTML;
|
|
||||||
|
|
||||||
const achievementDescription = item
|
|
||||||
.getElementsByClassName("achieveTxt")?.[0]
|
|
||||||
.getElementsByTagName("h5")?.[0].innerHTML;
|
|
||||||
|
|
||||||
achievementInfos.push({
|
|
||||||
imageUrl: imageUrl ?? "",
|
|
||||||
title: achievementName ?? "",
|
|
||||||
description: achievementDescription ?? "",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return achievementInfos;
|
|
||||||
};
|
|
|
@ -1,28 +0,0 @@
|
||||||
import { Achievement, AchievementInfo, AchievementPercentage } from "../types";
|
|
||||||
|
|
||||||
export const steamAchievementMerge = (
|
|
||||||
achievementPercentage: AchievementPercentage[],
|
|
||||||
achievementInfo: AchievementInfo[]
|
|
||||||
): Achievement[] | undefined => {
|
|
||||||
if (achievementPercentage.length > achievementInfo.length) return;
|
|
||||||
|
|
||||||
const size = achievementPercentage.length;
|
|
||||||
|
|
||||||
const achievements: Achievement[] = new Array(size);
|
|
||||||
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
achievements[i] = {
|
|
||||||
id: achievementPercentage[i].name,
|
|
||||||
percent: achievementPercentage[i].percent,
|
|
||||||
imageUrl: achievementInfo[i].imageUrl,
|
|
||||||
title: achievementInfo[i].title,
|
|
||||||
description: achievementInfo[i].description,
|
|
||||||
achieved: false,
|
|
||||||
curProgress: 0,
|
|
||||||
maxProgress: 0,
|
|
||||||
unlockTime: 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return achievements;
|
|
||||||
};
|
|
|
@ -12,16 +12,14 @@ const addGame = (
|
||||||
) => {
|
) => {
|
||||||
const filePath = path.join(achievementPath, objectId, ...fileLocation);
|
const filePath = path.join(achievementPath, objectId, ...fileLocation);
|
||||||
|
|
||||||
if (fs.existsSync(filePath)) {
|
const achivementFile = {
|
||||||
const achivementFile = {
|
type,
|
||||||
type,
|
filePath,
|
||||||
filePath,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
achievementFiles[objectId]
|
achievementFiles[objectId]
|
||||||
? achievementFiles[objectId].push(achivementFile)
|
? achievementFiles[objectId].push(achivementFile)
|
||||||
: (achievementFiles[objectId] = [achivementFile]);
|
: (achievementFiles[objectId] = [achivementFile]);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const steamFindGameAchievementFiles = (
|
export const steamFindGameAchievementFiles = (
|
||||||
|
@ -55,30 +53,16 @@ export const steamFindGameAchievementFiles = (
|
||||||
fileLocation = ["achievements.ini"];
|
fileLocation = ["achievements.ini"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(achievementPath)) continue;
|
const objectIds = objectId ? [objectId] : fs.readdirSync(achievementPath);
|
||||||
|
|
||||||
const objectIds = fs.readdirSync(achievementPath);
|
for (const objectId of objectIds) {
|
||||||
|
addGame(
|
||||||
if (objectId) {
|
gameAchievementFiles,
|
||||||
if (objectIds.includes(objectId)) {
|
achievementPath,
|
||||||
addGame(
|
objectId,
|
||||||
gameAchievementFiles,
|
fileLocation,
|
||||||
achievementPath,
|
cracker
|
||||||
objectId,
|
);
|
||||||
fileLocation,
|
|
||||||
cracker
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const objectId of objectIds) {
|
|
||||||
addGame(
|
|
||||||
gameAchievementFiles,
|
|
||||||
achievementPath,
|
|
||||||
objectId,
|
|
||||||
fileLocation,
|
|
||||||
cracker
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import { gameAchievementRepository } from "@main/repository";
|
|
||||||
import { steamGlobalAchievementPercentages } from "./steam-global-achievement-percentages";
|
|
||||||
import { steamAchievementInfo } from "./steam-achievement-info";
|
|
||||||
import { steamAchievementMerge } from "./steam-achievement-merge";
|
|
||||||
import { Achievement } from "../types";
|
|
||||||
import { Game } from "@main/entity";
|
|
||||||
|
|
||||||
export const steamGetAchivement = async (
|
|
||||||
game: Game
|
|
||||||
): Promise<Achievement[] | undefined> => {
|
|
||||||
const gameAchivement = await gameAchievementRepository.findOne({
|
|
||||||
where: { game: game },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!gameAchivement) {
|
|
||||||
const achievementPercentage = await steamGlobalAchievementPercentages(
|
|
||||||
game.objectID
|
|
||||||
);
|
|
||||||
console.log(achievementPercentage);
|
|
||||||
if (!achievementPercentage) {
|
|
||||||
await gameAchievementRepository.save({
|
|
||||||
game,
|
|
||||||
achievements: "[]",
|
|
||||||
});
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const achievementInfo = await steamAchievementInfo(game.objectID);
|
|
||||||
console.log(achievementInfo);
|
|
||||||
if (!achievementInfo) return;
|
|
||||||
|
|
||||||
const achievements = steamAchievementMerge(
|
|
||||||
achievementPercentage,
|
|
||||||
achievementInfo
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!achievements) return;
|
|
||||||
|
|
||||||
await gameAchievementRepository.save({
|
|
||||||
game,
|
|
||||||
achievements: JSON.stringify(achievements),
|
|
||||||
});
|
|
||||||
|
|
||||||
return achievements;
|
|
||||||
} else {
|
|
||||||
return JSON.parse(gameAchivement.achievements);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,33 +0,0 @@
|
||||||
import { logger } from "@main/services";
|
|
||||||
import { AchievementPercentage } from "../types";
|
|
||||||
|
|
||||||
interface GlobalAchievementPercentages {
|
|
||||||
achievementpercentages: {
|
|
||||||
achievements: Array<AchievementPercentage>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const steamGlobalAchievementPercentages = async (
|
|
||||||
objectId: string
|
|
||||||
): Promise<AchievementPercentage[] | undefined> => {
|
|
||||||
const fetchUrl = `https://api.steampowered.com/ISteamUserStats/GetGlobalAchievementPercentagesForApp/v0002/?gameid=${objectId}`;
|
|
||||||
|
|
||||||
const achievementPercentages: Array<AchievementPercentage> | undefined = (
|
|
||||||
await fetch(fetchUrl, {
|
|
||||||
method: "GET",
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res.status === 200) return res.json();
|
|
||||||
return;
|
|
||||||
})
|
|
||||||
.then((data: GlobalAchievementPercentages) => data)
|
|
||||||
.catch((err) => {
|
|
||||||
logger.error(err, { method: "getSteamGameAchievements" });
|
|
||||||
return;
|
|
||||||
})
|
|
||||||
)?.achievementpercentages.achievements;
|
|
||||||
|
|
||||||
if (!achievementPercentages) return;
|
|
||||||
|
|
||||||
return achievementPercentages;
|
|
||||||
};
|
|
|
@ -10,6 +10,11 @@ export interface CheckedAchievements {
|
||||||
new: Achievement[];
|
new: Achievement[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UnlockedAchievement {
|
||||||
|
name: string;
|
||||||
|
unlockTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Achievement {
|
export interface Achievement {
|
||||||
id: string;
|
id: string;
|
||||||
percent: number;
|
percent: number;
|
||||||
|
|
|
@ -1,102 +1,63 @@
|
||||||
import { Achievement, CheckedAchievements, Cracker } from "../types";
|
import { Cracker, UnlockedAchievement } from "../types";
|
||||||
|
|
||||||
export const checkUnlockedAchievements = (
|
export const checkUnlockedAchievements = (
|
||||||
type: Cracker,
|
type: Cracker,
|
||||||
unlockedAchievements: any,
|
unlockedAchievements: any
|
||||||
achievements: Achievement[]
|
): UnlockedAchievement[] => {
|
||||||
): CheckedAchievements => {
|
if (type === Cracker.onlineFix) return onlineFixMerge(unlockedAchievements);
|
||||||
if (type === Cracker.onlineFix)
|
|
||||||
return onlineFixMerge(unlockedAchievements, achievements);
|
|
||||||
if (type === Cracker.goldberg)
|
if (type === Cracker.goldberg)
|
||||||
return goldbergUnlockedAchievements(unlockedAchievements, achievements);
|
return goldbergUnlockedAchievements(unlockedAchievements);
|
||||||
return defaultMerge(unlockedAchievements, achievements);
|
return defaultMerge(unlockedAchievements);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onlineFixMerge = (
|
const onlineFixMerge = (unlockedAchievements: any): UnlockedAchievement[] => {
|
||||||
unlockedAchievements: any,
|
const parsedUnlockedAchievements: UnlockedAchievement[] = [];
|
||||||
achievements: Achievement[]
|
|
||||||
): CheckedAchievements => {
|
|
||||||
const newUnlockedAchievements: Achievement[] = [];
|
|
||||||
|
|
||||||
for (const achievement of achievements) {
|
for (const achievement of Object.keys(unlockedAchievements)) {
|
||||||
if (achievement.achieved) continue;
|
const unlockedAchievement = unlockedAchievements[achievement];
|
||||||
|
|
||||||
const unlockedAchievement = unlockedAchievements[achievement.id];
|
if (unlockedAchievement?.achieved) {
|
||||||
|
parsedUnlockedAchievements.push({
|
||||||
if (!unlockedAchievement) continue;
|
name: achievement,
|
||||||
|
unlockTime: unlockedAchievement.timestamp,
|
||||||
achievement.achieved = Boolean(
|
});
|
||||||
unlockedAchievement?.achieved ?? achievement.achieved
|
|
||||||
);
|
|
||||||
|
|
||||||
achievement.unlockTime =
|
|
||||||
unlockedAchievement?.timestamp ?? achievement.unlockTime;
|
|
||||||
|
|
||||||
if (achievement.achieved) {
|
|
||||||
newUnlockedAchievements.push(achievement);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { all: achievements, new: newUnlockedAchievements };
|
return parsedUnlockedAchievements;
|
||||||
};
|
};
|
||||||
|
|
||||||
const goldbergUnlockedAchievements = (
|
const goldbergUnlockedAchievements = (
|
||||||
unlockedAchievements: any,
|
unlockedAchievements: any
|
||||||
achievements: Achievement[]
|
): UnlockedAchievement[] => {
|
||||||
): CheckedAchievements => {
|
const newUnlockedAchievements: UnlockedAchievement[] = [];
|
||||||
const newUnlockedAchievements: Achievement[] = [];
|
|
||||||
|
|
||||||
for (const achievement of achievements) {
|
for (const achievement of Object.keys(unlockedAchievements)) {
|
||||||
if (achievement.achieved) continue;
|
const unlockedAchievement = unlockedAchievements[achievement];
|
||||||
|
|
||||||
const unlockedAchievement = unlockedAchievements[achievement.id];
|
if (unlockedAchievement?.earned) {
|
||||||
|
newUnlockedAchievements.push({
|
||||||
if (!unlockedAchievement) continue;
|
name: achievement,
|
||||||
|
unlockTime: unlockedAchievement.earned_time,
|
||||||
achievement.achieved = Boolean(
|
});
|
||||||
unlockedAchievement?.earned ?? achievement.achieved
|
|
||||||
);
|
|
||||||
|
|
||||||
achievement.unlockTime =
|
|
||||||
unlockedAchievement?.earned_time ?? achievement.unlockTime;
|
|
||||||
|
|
||||||
if (achievement.achieved) {
|
|
||||||
newUnlockedAchievements.push(achievement);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { all: achievements, new: newUnlockedAchievements };
|
return newUnlockedAchievements;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultMerge = (
|
const defaultMerge = (unlockedAchievements: any): UnlockedAchievement[] => {
|
||||||
unlockedAchievements: any,
|
const newUnlockedAchievements: UnlockedAchievement[] = [];
|
||||||
achievements: Achievement[]
|
|
||||||
): CheckedAchievements => {
|
|
||||||
const newUnlockedAchievements: Achievement[] = [];
|
|
||||||
console.log("checkUnlockedAchievements");
|
|
||||||
for (const achievement of achievements) {
|
|
||||||
if (achievement.achieved) continue;
|
|
||||||
|
|
||||||
const unlockedAchievement = unlockedAchievements[achievement.id];
|
for (const achievement of Object.keys(unlockedAchievements)) {
|
||||||
|
const unlockedAchievement = unlockedAchievements[achievement];
|
||||||
|
|
||||||
if (!unlockedAchievement) continue;
|
if (unlockedAchievement?.Achieved) {
|
||||||
|
newUnlockedAchievements.push({
|
||||||
achievement.achieved = Boolean(
|
name: achievement,
|
||||||
unlockedAchievement?.Achieved ?? achievement.achieved
|
unlockTime: unlockedAchievement.UnlockTime,
|
||||||
);
|
});
|
||||||
|
|
||||||
achievement.curProgress =
|
|
||||||
unlockedAchievement?.CurProgress ?? achievement.curProgress;
|
|
||||||
|
|
||||||
achievement.maxProgress =
|
|
||||||
unlockedAchievement?.MaxProgress ?? achievement.maxProgress;
|
|
||||||
|
|
||||||
achievement.unlockTime =
|
|
||||||
unlockedAchievement?.UnlockTime ?? achievement.unlockTime;
|
|
||||||
|
|
||||||
if (achievement.achieved) {
|
|
||||||
newUnlockedAchievements.push(achievement);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("newUnlocked: ", newUnlockedAchievements);
|
|
||||||
return { all: achievements, new: newUnlockedAchievements };
|
return newUnlockedAchievements;
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,7 +79,7 @@ function onOpenGame(game: Game) {
|
||||||
createGame({ ...game, lastTimePlayed: new Date() }).catch(() => {});
|
createGame({ ...game, lastTimePlayed: new Date() }).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
startGameAchievementObserver(game.id);
|
startGameAchievementObserver(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTickGame(game: Game) {
|
function onTickGame(game: Game) {
|
||||||
|
@ -116,6 +116,8 @@ function onTickGame(game: Game) {
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startGameAchievementObserver(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
const onCloseGame = (game: Game) => {
|
const onCloseGame = (game: Game) => {
|
||||||
|
|
|
@ -123,7 +123,6 @@ export function GameDetailsContextProvider({
|
||||||
if (statsResult.status === "fulfilled") setStats(statsResult.value);
|
if (statsResult.status === "fulfilled") setStats(statsResult.value);
|
||||||
|
|
||||||
if (achievements.status === "fulfilled") {
|
if (achievements.status === "fulfilled") {
|
||||||
console.log(achievements.value);
|
|
||||||
setAchievements(achievements.value);
|
setAchievements(achievements.value);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue