feat: 支持PCL导出的zip格式
This commit is contained in:
@@ -11,6 +11,7 @@ import { execPromise } from "./utils/utils.js";
|
|||||||
import { MessageWS } from "./utils/ws.js";
|
import { MessageWS } from "./utils/ws.js";
|
||||||
import { logger } from "./utils/logger.js";
|
import { logger } from "./utils/logger.js";
|
||||||
import { yauzl_promise } from "./utils/ziplib.js";
|
import { yauzl_promise } from "./utils/ziplib.js";
|
||||||
|
import yauzl from "yauzl";
|
||||||
|
|
||||||
export class Dex {
|
export class Dex {
|
||||||
wsx!: WebSocketServer;
|
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 {
|
try {
|
||||||
const first = Date.now();
|
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();
|
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);
|
const plat = what_platform(contain);
|
||||||
logger.debug("Platform detected", plat);
|
logger.debug("Platform detected", plat);
|
||||||
logger.debug("Modpack info", info);
|
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) {
|
private async _zips(buffer: Buffer) {
|
||||||
|
if (buffer.length === 0) {
|
||||||
|
throw new Error("zip buffer is empty");
|
||||||
|
}
|
||||||
const zip = await yauzl_promise(buffer);
|
const zip = await yauzl_promise(buffer);
|
||||||
|
let index = 0;
|
||||||
const _getinfo = async () => {
|
const _getinfo = async () => {
|
||||||
const importantFiles = ["manifest.json", "modrinth.index.json"];
|
const importantFiles = ["manifest.json", "modrinth.index.json"];
|
||||||
for await (const entry of zip) {
|
for await (const entry of zip) {
|
||||||
@@ -83,10 +168,13 @@ export class Dex {
|
|||||||
logger.debug("Found important file", { fileName: entry.fileName, info });
|
logger.debug("Found important file", { fileName: entry.fileName, info });
|
||||||
return { contain: entry.fileName, info };
|
return { contain: entry.fileName, info };
|
||||||
}
|
}
|
||||||
|
index++;
|
||||||
}
|
}
|
||||||
throw new Error("No manifest file found in modpack");
|
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) => {
|
const _unzip = async (instancename: string) => {
|
||||||
logger.info("Starting unzip process", { instancename });
|
logger.info("Starting unzip process", { instancename });
|
||||||
const instancePath = `./instance/${instancename}`;
|
const instancePath = `./instance/${instancename}`;
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export class Core {
|
|||||||
logger.info("Starting task", { isServerMode });
|
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);
|
logger.error("Task execution failed", err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Fabric } from "./fabric.js";
|
|||||||
import { Forge } from "./forge.js";
|
import { Forge } from "./forge.js";
|
||||||
import { Minecraft } from "./minecraft.js";
|
import { Minecraft } from "./minecraft.js";
|
||||||
import { NeoForge } from "./neoforge.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> {
|
export async function dinstall(ml: string, mcv: string, mlv: string, path: string): Promise<void> {
|
||||||
await modloader(ml, mcv, mlv, path).installer();
|
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`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import pRetry from "p-retry";
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import fse from "fs-extra";
|
import fse from "fs-extra";
|
||||||
import { WebSocket } from "ws";
|
import { WebSocket } from "ws";
|
||||||
import { ExecOptions, exec} from "node:child_process";
|
import { ExecOptions, exec, spawn} from "node:child_process";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java版本信息接口
|
* Java版本信息接口
|
||||||
@@ -135,20 +135,43 @@ export async function checkJava(): Promise<JavaCheckResult> {
|
|||||||
|
|
||||||
export function execPromise(cmd:string,options?:ExecOptions){
|
export function execPromise(cmd:string,options?:ExecOptions){
|
||||||
logger.debug(`Executing command: ${cmd}`);
|
logger.debug(`Executing command: ${cmd}`);
|
||||||
return new Promise((resolve,reject)=>{
|
return new Promise<number>((resolve,reject)=>{
|
||||||
exec(cmd,options,(err,stdout,stderr)=>{
|
const args = cmd.split(' ');
|
||||||
if(err){
|
const command = args.shift() || '';
|
||||||
logger.error(`Command execution failed: ${cmd}`, err);
|
|
||||||
|
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}`);
|
logger.debug(`Stderr: ${stderr}`);
|
||||||
reject(err)
|
reject(new Error(`Command failed with exit code ${code}`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stdout) logger.debug(`Command stdout: ${stdout}`);
|
if (stdout) logger.debug(`Command stdout: ${stdout}`);
|
||||||
if (stderr) logger.debug(`Command stderr: ${stderr}`);
|
if (stderr) logger.debug(`Command stderr: ${stderr}`);
|
||||||
}).on('exit',(code)=>{
|
|
||||||
logger.debug(`Command completed with exit code: ${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);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user