feat: 支持PCL导出的zip格式

This commit is contained in:
Tianpao
2026-02-02 11:42:45 +08:00
parent 0f8ceba972
commit 295f1fbece
4 changed files with 139 additions and 13 deletions

View File

@@ -11,6 +11,7 @@ import { execPromise } from "./utils/utils.js";
import { MessageWS } from "./utils/ws.js";
import { logger } from "./utils/logger.js";
import { yauzl_promise } from "./utils/ziplib.js";
import yauzl from "yauzl";
export class Dex {
wsx!: WebSocketServer;
@@ -22,11 +23,17 @@ export class Dex {
});
}
public async Main(buffer: Buffer, dser: boolean) {
public async Main(buffer: Buffer, dser: boolean, filename?: string) {
try {
const first = Date.now();
const zps = await this._zips(buffer);
const processedBuffer = await this._processModpack(buffer, filename);
const zps = await this._zips(processedBuffer);
const { contain, info } = await zps._getinfo();
if (!contain || !info) {
logger.error("Modpack info is empty");
this.message.handleError(new Error("It seems that the modpack is not a valid modpack."));
return;
}
const plat = what_platform(contain);
logger.debug("Platform detected", plat);
logger.debug("Modpack info", info);
@@ -72,8 +79,86 @@ export class Dex {
}
}
private async _processModpack(buffer: Buffer, filename?: string): Promise<Buffer> {
if (!filename || (!filename.endsWith('.zip') && !filename.endsWith('.mrpack'))) {
return buffer;
}
try {
const zip = await (new Promise((resolve, reject) => {
yauzl.fromBuffer(buffer, { lazyEntries: true }, (err, zipfile) => {
if (err) {
reject(err);
return;
}
resolve(zipfile);
});
}) as Promise<yauzl.ZipFile>);
return new Promise((resolve, reject) => {
let mrpackBuffer: Buffer | null = null;
let hasProcessed = false;
zip.on('entry', (entry: yauzl.Entry) => {
if (hasProcessed || !entry.fileName.endsWith('modpack.mrpack')) {
zip.readEntry();
return;
}
if (entry.fileName === 'modpack.mrpack') {
hasProcessed = true;
zip.openReadStream(entry, (err, stream) => {
if (err) {
zip.close();
reject(err);
return;
}
const chunks: Buffer[] = [];
stream.on('data', (chunk) => {
chunks.push(chunk);
});
stream.on('end', () => {
mrpackBuffer = Buffer.concat(chunks);
zip.close();
resolve(mrpackBuffer);
});
stream.on('error', (err) => {
zip.close();
reject(err);
});
});
} else {
zip.readEntry();
}
});
zip.on('end', () => {
if (!hasProcessed) {
zip.close();
resolve(buffer);
}
});
zip.on('error', (err) => {
zip.close();
reject(err);
});
zip.readEntry();
});
} catch (e) {
logger.warn('Failed to check for modpack.mrpack, using original buffer', e);
return buffer;
}
}
private async _zips(buffer: Buffer) {
if (buffer.length === 0) {
throw new Error("zip buffer is empty");
}
const zip = await yauzl_promise(buffer);
let index = 0;
const _getinfo = async () => {
const importantFiles = ["manifest.json", "modrinth.index.json"];
for await (const entry of zip) {
@@ -83,10 +168,13 @@ export class Dex {
logger.debug("Found important file", { fileName: entry.fileName, info });
return { contain: entry.fileName, info };
}
index++;
}
throw new Error("No manifest file found in modpack");
}
if (index === zip.length) {
throw new Error("No manifest file found in modpack");
}
const _unzip = async (instancename: string) => {
logger.info("Starting unzip process", { instancename });
const instancePath = `./instance/${instancename}`;

View File

@@ -93,7 +93,7 @@ export class Core {
logger.info("Starting task", { isServerMode });
// 非阻塞执行主要任务
this.dex.Main(req.file.buffer, isServerMode).catch(err => {
this.dex.Main(req.file.buffer, isServerMode, req.file.originalname).catch(err => {
logger.error("Task execution failed", err);
});

View File

@@ -2,6 +2,7 @@ import { Fabric } from "./fabric.js";
import { Forge } from "./forge.js";
import { Minecraft } from "./minecraft.js";
import { NeoForge } from "./neoforge.js";
import fs from "node:fs";
/**
* 模组加载器接口
@@ -54,4 +55,18 @@ export async function mlsetup(ml: string, mcv: string, mlv: string, path: string
*/
export async function dinstall(ml: string, mcv: string, mlv: string, path: string): Promise<void> {
await modloader(ml, mcv, mlv, path).installer();
let cmd = '';
if (ml === 'forge' || ml === 'neoforge') {
cmd = `java -jar forge-${mcv}-${mlv}-installer.jar --installServer`;
} else if (ml === 'fabric' || ml === 'fabric-loader') {
await fs.promises.writeFile(`${path}/run.bat`,`@echo off\njava -jar fabric-server-launch.jar\n`)
await fs.promises.writeFile(`${path}/run.sh`,`#!/bin/bash\njava -jar fabric-server-launch.jar\n`)
cmd = `java -jar fabric-installer.jar server -dir . -mcversion ${mcv} -loader ${mlv} -downloadMinecraft`;
}
if (cmd) {
await fs.promises.writeFile(`${path}/install.bat`, `@echo off\n${cmd}\necho Install Successfully,Enter Some Key to Exit!\npause\n`);
await fs.promises.writeFile(`${path}/install.sh`, `#!/bin/bash\n${cmd}\n`);
}
}

View File

@@ -5,7 +5,7 @@ import pRetry from "p-retry";
import fs from "node:fs";
import fse from "fs-extra";
import { WebSocket } from "ws";
import { ExecOptions, exec} from "node:child_process";
import { ExecOptions, exec, spawn} from "node:child_process";
/**
* Java版本信息接口
@@ -135,20 +135,43 @@ export async function checkJava(): Promise<JavaCheckResult> {
export function execPromise(cmd:string,options?:ExecOptions){
logger.debug(`Executing command: ${cmd}`);
return new Promise((resolve,reject)=>{
exec(cmd,options,(err,stdout,stderr)=>{
if(err){
logger.error(`Command execution failed: ${cmd}`, err);
return new Promise<number>((resolve,reject)=>{
const args = cmd.split(' ');
const command = args.shift() || '';
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();
});
child.on('close', (code) => {
if (code !== 0) {
logger.error(`Command execution failed: ${cmd}`);
logger.debug(`Stderr: ${stderr}`);
reject(err)
reject(new Error(`Command failed with exit code ${code}`));
return;
}
if (stdout) logger.debug(`Command stdout: ${stdout}`);
if (stderr) logger.debug(`Command stderr: ${stderr}`);
}).on('exit',(code)=>{
logger.debug(`Command completed with exit code: ${code}`);
resolve(code)
})
resolve(code || 0);
});
child.on('error', (err) => {
logger.error(`Command execution error: ${cmd}`, err);
reject(err);
});
})
}