diff --git a/src/main/services/mods/beat-mods-api.service.ts b/src/main/services/mods/beat-mods-api.service.ts index f35ae07d..ead1384b 100644 --- a/src/main/services/mods/beat-mods-api.service.ts +++ b/src/main/services/mods/beat-mods-api.service.ts @@ -3,7 +3,6 @@ import { BbmFullMod, BbmMod, BbmModVersion, BbmPlatform } from "../../../shared/ import { RequestService } from "../request.service"; import { BsStore } from "../../../shared/models/bs-store.enum"; import log from "electron-log" -import { tryit } from "shared/helpers/error.helpers"; export class BeatModsApiService { private static instance: BeatModsApiService; @@ -60,42 +59,17 @@ export class BeatModsApiService { }); } - public async getModByHash(hashs: T[]): Promise> { - - const getModsFromCache = (hashs: T[]): Record => { - const mods = {} as Record; - - for (const hash of hashs) { - const mod = this.modsHashCache.get(hash); - if(mod){ - mods[hash] = mod; - } - } - - return mods; + public getModByHash(hash: string): Promise { + if (this.modsHashCache.has(hash)) { + return Promise.resolve(this.modsHashCache.get(hash)); } - const mods = getModsFromCache(hashs); - const missingHashs = hashs.filter(hash => !mods[hash]); - - if(!missingHashs.length){ - return mods; - } - - const url = new URL(`${this.MODS_REPO_API_URL}/hashlookup`); - missingHashs.forEach(hash => url.searchParams.append("hash", hash)); - - const res = await tryit(() => this.requestService.getJSON<{ modVersions: BbmModVersion[] }>(url.toString())); - - if(res.error){ - log.error(`Failed to get mod by hashes`, res.error); + return this.requestService.getJSON<{ modVersions: BbmModVersion[] }>(`${this.MODS_REPO_API_URL}/hashlookup?hash=${hash}`).then(({ data }) => { + this.updateModsHashCache(data?.modVersions ?? []); + return data?.modVersions?.at(0); + }).catch((e): undefined => { + log.error(`Failed to get mod by hash: ${hash}`, e); return undefined; - } - - this.updateModsHashCache(res.result?.data?.modVersions ?? []); - - const missingMods = getModsFromCache(missingHashs); - - return {...mods, ...missingMods}; + }); } } diff --git a/src/main/services/mods/bs-mods-manager.service.ts b/src/main/services/mods/bs-mods-manager.service.ts index 993edb04..4121cf31 100644 --- a/src/main/services/mods/bs-mods-manager.service.ts +++ b/src/main/services/mods/bs-mods-manager.service.ts @@ -19,7 +19,6 @@ import crypto from "crypto"; import { BsmZipExtractor } from "main/models/bsm-zip-extractor.class"; import { BsmShellLog, bsmSpawn } from "main/helpers/os.helpers"; import { BbmFullMod, BbmModVersion, ExternalMod } from "../../../shared/models/mods/mod.interface"; -import { Stats } from "fs"; export class BsModsManagerService { private static instance: BsModsManagerService; @@ -29,6 +28,8 @@ export class BsModsManagerService { private readonly linuxService: LinuxService; private readonly requestService: RequestService; + private manifestMatches: BbmModVersion[]; + public static getInstance(): BsModsManagerService { if (!BsModsManagerService.instance) { BsModsManagerService.instance = new BsModsManagerService(); @@ -43,9 +44,14 @@ export class BsModsManagerService { this.requestService = RequestService.getInstance(); } - private async getModsFromHashes(hashes: string[]): Promise { - const mods = await this.beatModsApi.getModByHash(hashes) - return Object.values(mods).filter(mod => !mod.contentHashes.some(content => content.path.includes("IPA"))); + private async getModFromHash(hash: string): Promise { + const mod = await this.beatModsApi.getModByHash(hash); + + if(mod?.contentHashes?.some(content => content.path.includes("IPA.exe"))){ + return undefined; + } + + return mod; } @@ -57,16 +63,40 @@ export class BsModsManagerService { return []; } - const ignoreFunc = (file: string, stats: Stats): boolean => { - if(!stats.isFile()) { return true; } - const ext = path.extname(file); - return !(ext === ".dll" || ext === ".exe" || ext === ".manifest"); - } + const files = await recursiveReadDir(modsPath); - const files = await recursiveReadDir(modsPath, [ignoreFunc]); - const hashes = await Promise.all(files.map(file => md5File(file))); + const promises = files.map(async filePath => { + const ext = path.extname(filePath); - const mods = await this.getModsFromHashes(hashes); + if (ext !== ".dll" && ext !== ".exe" && ext !== ".manifest") { + return undefined; + } + const hash = await md5File(filePath); + const mod = await this.getModFromHash(hash); + + if (!mod) { + return undefined; + } + + if (ext === ".manifest") { + this.manifestMatches.push(mod); + return undefined; + } + + if (filePath.toLowerCase().includes("libs")) { + const manifestIndex = this.manifestMatches.findIndex(m => m.id === mod.id); + + if (manifestIndex < 0) { + return undefined; + } + + this.manifestMatches.splice(manifestIndex, 1); + } + + return mod; + }); + + const mods = await Promise.all(promises); return mods.filter(Boolean); } @@ -77,7 +107,7 @@ export class BsModsManagerService { return undefined; } const injectorMd5 = await md5File(injectorPath); - return (await this.beatModsApi.getModByHash([injectorMd5]))[injectorMd5]; + return this.beatModsApi.getModByHash(injectorMd5); } private async downloadZip(zipUrl: string): Promise { @@ -308,10 +338,12 @@ export class BsModsManagerService { } public async getInstalledMods(version: BSVersion): Promise { + this.manifestMatches = []; + const bsipa = await this.getBsipaInstalled(version); - const pluginsMods = await Promise.all([this.getModsInDir(version, ModsInstallFolder.PLUGINS_PENDING), this.getModsInDir(version, ModsInstallFolder.PLUGINS)]); - const libsMods = await Promise.all([this.getModsInDir(version, ModsInstallFolder.LIBS_PENDING), this.getModsInDir(version, ModsInstallFolder.LIBS)]); + const pluginsMods = await Promise.all([this.getModsInDir(version, ModsInstallFolder.PLUGINS), this.getModsInDir(version, ModsInstallFolder.PLUGINS_PENDING)]); + const libsMods = await Promise.all([this.getModsInDir(version, ModsInstallFolder.LIBS), this.getModsInDir(version, ModsInstallFolder.LIBS_PENDING)]); const dirMods = pluginsMods.flat().concat(libsMods.flat());