diff --git a/backend/src/Dex.ts b/backend/src/Dex.ts index 6d2d40d..8260087 100644 --- a/backend/src/Dex.ts +++ b/backend/src/Dex.ts @@ -43,7 +43,7 @@ export class Dex { await Promise.all([ zps._unzip(mpname), platform(plat).downloadfile(info, unpath, this.message) - ]).catch(e=>{ + ]).catch(e => { console.log(e); }); this.message.statusChange(); //改变状态 @@ -70,7 +70,7 @@ export class Dex { if (config.oaf) { await execPromise(`start ${p.join("./instance")}`); } - + logger.info(`Task completed in ${latest - first}ms`); } catch (e) { const err = e as Error; @@ -182,30 +182,45 @@ export class Dex { for await (const entry of zip) { const isDir = entry.fileName.endsWith("/"); logger.info(`index: ${index}, fileName: ${entry.fileName}`); + + // 只解压 overrides/ 目录下的内容,跳过其他所有文件和目录 + if (!entry.fileName.startsWith("overrides/")) { + logger.info("Skip non-overrides file", entry.fileName); + this.message.unzip(entry.fileName, zip.length, index); + index++; + continue; + } + + // 跳过 overrides 目录本身 + if (entry.fileName === "overrides/") { + logger.info("Skip overrides directory", entry.fileName); + this.message.unzip(entry.fileName, zip.length, index); + index++; + continue; + } + + // 跳过黑名单文件/目录 + if (this._ublack(entry.fileName)) { + logger.info("Skip blacklist file", entry.fileName); + this.message.unzip(entry.fileName, zip.length, index); + index++; + continue; + } + if (isDir) { - if (this._ublack(entry.fileName)) { - this.message.unzip(entry.fileName, zip.length, index); - index++; - continue; - } - await fs.promises.mkdir(`${instancePath}/${entry.fileName}`, { + let targetPath = entry.fileName.replace("overrides/", ""); + await fs.promises.mkdir(`${instancePath}/${targetPath}`, { recursive: true, }); - } else if (entry.fileName.startsWith("overrides/")) { - // 跳过黑名单文件 - if (this._ublack(entry.fileName)) { - logger.info("Skip blacklist file", entry.fileName); - this.message.unzip(entry.fileName, zip.length, index); - index++; - continue; - } + } else { + let targetPath = entry.fileName.replace("overrides/", ""); + // 创建目标目录 - const targetPath = entry.fileName.replace("overrides/", ""); const dirPath = `${instancePath}/${targetPath.substring(0, targetPath.lastIndexOf("/"))}`; await fs.promises.mkdir(dirPath, { recursive: true }); + // 解压文件 const stream = await entry.openReadStream; - console.log(entry.fileName); const write = fs.createWriteStream(`${instancePath}/${targetPath}`); await pipeline(stream, write); } @@ -223,17 +238,25 @@ export class Dex { * @returns 是否在黑名单中 */ private _ublack(filename: string): boolean { - if (filename === "overrides/") return true; const blacklist = [ "overrides/options.txt", - "shaderpacks", - "essential", - "resourcepacks", - "PCL", - "CustomSkinLoader", - "overrides" + "overrides/shaderpacks", + "overrides/essential", + "overrides/resourcepacks", + "overrides/PCL", + "overrides/CustomSkinLoader" ]; - - return blacklist.some(item => filename.includes(item)); + + // 跳过 overrides/ 目录本身 + if (filename === "overrides/" || filename === "overrides") { + return true; + } + + // 统一处理:确保黑名单项和文件名都以 / 结尾进行比较 + return blacklist.some(item => { + const normalizedItem = item.endsWith("/") ? item : item + "/"; + const normalizedFilename = filename.endsWith("/") ? filename : filename + "/"; + return normalizedFilename === normalizedItem || normalizedFilename.startsWith(normalizedItem); + }); } } diff --git a/backend/src/utils/utils.ts b/backend/src/utils/utils.ts index 11b3253..de18c40 100644 --- a/backend/src/utils/utils.ts +++ b/backend/src/utils/utils.ts @@ -39,7 +39,7 @@ export class Utils { this.modrinth_url = "https://api.modrinth.com"; this.curseforge_url = "https://api.curseforge.com"; this.modrinth_Durl = "https://cdn.modrinth.com"; - this.curseforge_Durl = "https://media.forgecdn.net"; + this.curseforge_Durl = "https://edge.forgecdn.net"; if (config.mirror.mcimirror) { this.modrinth_url = "https://mod.mcimirror.top/modrinth"; this.curseforge_url = "https://mod.mcimirror.top/curseforge"; @@ -141,29 +141,16 @@ export function execPromise(cmd:string,options?:ExecOptions){ const child = spawn(command, args, { ...options, - shell: true - }); - - let stdout = ''; - let stderr = ''; - - child.stdout?.on('data', (data) => { - stdout += data.toString(); - }); - - child.stderr?.on('data', (data) => { - stderr += data.toString(); + shell: true, + stdio: ['ignore', 'ignore', 'ignore'] }); child.on('close', (code) => { if (code !== 0) { logger.error(`Command execution failed: ${cmd}`); - logger.debug(`Stderr: ${stderr}`); reject(new Error(`Command failed with exit code ${code}`)); return; } - if (stdout) logger.debug(`Command stdout: ${stdout}`); - if (stderr) logger.debug(`Command stderr: ${stderr}`); logger.debug(`Command completed with exit code: ${code}`); resolve(code || 0); }); @@ -175,8 +162,31 @@ export function execPromise(cmd:string,options?:ExecOptions){ }) } +async function downloadFile(url: string, filePath: string, onProgress?: (total: number, current: number, path: string) => void) { + await pRetry( + async () => { + if (!fs.existsSync(filePath)) { + logger.debug(`Downloading ${url} to ${filePath}`); + const res = await got.get(url, { + responseType: "buffer", + headers: { "user-agent": "DeEarthX" }, + }); + fse.outputFileSync(filePath, res.rawBody); + logger.debug(`Downloaded ${url} successfully`); + } else { + logger.debug(`File already exists, skipping: ${filePath}`); + } + }, + { + retries: 3, + onFailedAttempt: (error) => { + logger.warn(`Download attempt failed for ${url}, retrying (${error.attemptNumber}/3)`); + } + } + ); +} + export async function fastdownload(data: [string, string]|string[][]) { - // 确保downloadList始终是[string, string][]类型 const downloadList: [string, string][] = Array.isArray(data[0]) ? (data as string[][]).map(item => item as [string, string]) : [data as [string, string]]; @@ -187,24 +197,7 @@ export async function fastdownload(data: [string, string]|string[][]) { async (item: [string, string]) => { const [url, filePath] = item; try { - await pRetry( - async () => { - if (!fs.existsSync(filePath)) { - logger.debug(`Downloading ${url} to ${filePath}`); - const res = await got.get(url, { - responseType: "buffer", - headers: { "user-agent": "DeEarthX" }, - }); - fse.outputFileSync(filePath, res.rawBody); - logger.debug(`Downloaded ${url} successfully`); - } else { - logger.debug(`File already exists, skipping: ${filePath}`); - } - }, - { retries: 3, onFailedAttempt: (error) => { - logger.warn(`Download attempt failed for ${url}, retrying (${error.attemptNumber}/3)`); - }} - ); + await downloadFile(url, filePath); } catch (error) { logger.error(`Failed to download ${url} after 3 attempts`, error); throw error; @@ -219,30 +212,11 @@ export async function Wfastdownload(data: string[][], ws: MessageWS) { let index = 0; return await pMap( data, - async (item: string[], idx: number) => { + async (item: string[]) => { const [url, filePath] = item; try { - await pRetry( - async () => { - if (!fs.existsSync(filePath)) { - logger.debug(`Downloading ${url} to ${filePath}`); - const res = await got.get(url, { - responseType: "buffer", - headers: { "user-agent": "DeEarthX" }, - }); - fse.outputFileSync(filePath, res.rawBody); - logger.debug(`Downloaded ${url} successfully`); - } else { - logger.debug(`File already exists, skipping: ${filePath}`); - } - - // 更新下载进度 - ws.download(data.length, ++index, filePath); - }, - { retries: 3, onFailedAttempt: (error) => { - logger.warn(`Download attempt failed for ${url}, retrying (${error.attemptNumber}/3)`); - }} - ); + await downloadFile(url, filePath); + ws.download(data.length, ++index, filePath); } catch (error) { logger.error(`Failed to download ${url} after 3 attempts`, error); throw error;