feat:基本最终完成

This commit is contained in:
Tianpao
2025-07-03 01:19:27 +08:00
parent 38c0aeb516
commit fa83d5095f
11 changed files with 158 additions and 31 deletions

1
.gitignore vendored
View File

@@ -135,3 +135,4 @@ ndist
# test files # test files
*.zip *.zip
*.mrpack *.mrpack
instance/

13
package-lock.json generated
View File

@@ -12,6 +12,7 @@
"adm-zip": "^0.5.16", "adm-zip": "^0.5.16",
"chalk": "^5.4.1", "chalk": "^5.4.1",
"cli-progress": "^3.12.0", "cli-progress": "^3.12.0",
"dotenv": "^17.0.1",
"fs-extra": "^11.3.0", "fs-extra": "^11.3.0",
"got": "^14.4.7", "got": "^14.4.7",
"inquirer": "^12.6.3", "inquirer": "^12.6.3",
@@ -2524,6 +2525,18 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/dotenv": {
"version": "17.0.1",
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-17.0.1.tgz",
"integrity": "sha512-GLjkduuAL7IMJg/ZnOPm9AnWKJ82mSE2tzXLaJ/6hD6DhwGfZaXG77oB8qbReyiczNxnbxQKyh0OE5mXq0bAHA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/download": { "node_modules/download": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmmirror.com/download/-/download-8.0.0.tgz", "resolved": "https://registry.npmmirror.com/download/-/download-8.0.0.tgz",

View File

@@ -18,6 +18,7 @@
"adm-zip": "^0.5.16", "adm-zip": "^0.5.16",
"chalk": "^5.4.1", "chalk": "^5.4.1",
"cli-progress": "^3.12.0", "cli-progress": "^3.12.0",
"dotenv": "^17.0.1",
"fs-extra": "^11.3.0", "fs-extra": "^11.3.0",
"got": "^14.4.7", "got": "^14.4.7",
"inquirer": "^12.6.3", "inquirer": "^12.6.3",

View File

@@ -1,13 +1,26 @@
import inquirer from "inquirer"; import inquirer from "inquirer";
import yauzl from "yauzl"; import yauzl from "yauzl";
import process from "node:process"; import process from "node:process";
import { join, basename } from "node:path"; import fse from "fs-extra";
import { join, basename, dirname } from "node:path";
import { platform, what_platform } from "./platform/index.js"; import { platform, what_platform } from "./platform/index.js";
import { readzipentry } from "./utils/utils.js"; import { isDevelopment, readzipentry } from "./utils/utils.js";
import fabric from "./ml_install/fabric.js"
import forge from "./ml_install/forge.js"
import neoforge from "./ml_install/neoforge.js"
import { DeEarthMain } from "./utils/DeEarth.js";
import { LOGGER } from "./utils/logger.js";
import { fileURLToPath } from "node:url";
interface Answers { interface Answers {
modpack_path: string | undefined; modpack_path: string | undefined;
} }
const unzip_path = "./instance"; let unzip_path:string = ""
if(isDevelopment){
unzip_path = join("./","instance")
}else{
unzip_path = join(getCurrnetDir(),"instance")
}
let zipnamew: string = ""
const argv = process.argv.slice(2)[0]; const argv = process.argv.slice(2)[0];
if (!argv) { if (!argv) {
@@ -15,16 +28,25 @@ if (!argv) {
{ type: "input", name: "modpack_path", message: "请输入整合包路径" }, { type: "input", name: "modpack_path", message: "请输入整合包路径" },
]); ]);
if (answer.modpack_path) { if (answer.modpack_path) {
initdir()
await main(answer.modpack_path); await main(answer.modpack_path);
} }
} else { } else {
initdir()
await main(argv); await main(argv);
} }
function initdir(){
if(!fse.existsSync(unzip_path)){
fse.ensureDirSync(join(unzip_path,"rubbish"))
}
}
async function main(modpack_path: string) { async function main(modpack_path: string) {
const zipname = basename(modpack_path) const zipname= basename(modpack_path)
.replace(".zip", "") .replace(".zip", "")
.replace(".mrpack", ""); .replace(".mrpack", "");
zipnamew=zipname
let dud_files: Array<string> = []; let dud_files: Array<string> = [];
let pack_info: object; let pack_info: object;
//unzip //unzip
@@ -33,10 +55,15 @@ async function main(modpack_path: string) {
zipfile.on("entry", async (entry) => { zipfile.on("entry", async (entry) => {
if (/\/$/.test(entry.fileName)) { if (/\/$/.test(entry.fileName)) {
} else if (entry.fileName.includes("overrides/")) { } else if (entry.fileName.includes("overrides/")) {
/*zipfile.openReadStream(entry,(err,stream)=>{ //读取overrides文件夹下的所有文件和文件夹 const zipfilex = join(unzip_path, zipname,entry.fileName.replace("overrides/",""))
if(!fse.existsSync(zipfilex)){
})*/ zipfile.openReadStream(entry,(err,stream)=>{ //读取overrides文件夹下的所有文件和文件夹
const dir = dirname(zipfilex)
fse.ensureDirSync(dir);
stream.pipe(fse.createWriteStream(zipfilex))
})
//console.log(entry.fileName) //console.log(entry.fileName)
}
} else { } else {
const name: string = entry.fileName; const name: string = entry.fileName;
dud_files.push(name); dud_files.push(name);
@@ -45,15 +72,41 @@ async function main(modpack_path: string) {
(await readzipentry(zipfile, entry)).toString() (await readzipentry(zipfile, entry)).toString()
); );
} }
console.log(name);
} }
zipfile.readEntry(); zipfile.readEntry();
}); });
zipfile.on("end", () => { zipfile.on("end", async () => {
//zip //zip
platform(what_platform(dud_files)); const dirx = join(unzip_path,zipname)
//console.log(dud_files) const plat = platform(what_platform(dud_files))
const info = await plat.getinfo(pack_info);
await plat.downloadfile(pack_info,dirx)
await install(info.loader,info.minecraft,info.loader_version,dirx)
await DeEarthMain(join(dirx,"mods"),join(unzip_path,"rubbish"))
LOGGER.info("DeEarthX已将服务端制作完成");
zipfile.close(); zipfile.close();
}); });
}); });
} }
async function install(type: string,minecraft:string,loaderver:string,path:string){
switch(type){
case "fabric":
await fabric(minecraft,loaderver,path)
break;
case "fabric-loader":
await fabric(minecraft,loaderver,path)
break;
case "forge":
await forge(minecraft,loaderver,path)
break;
case "neoforge":
await neoforge(minecraft,loaderver,path)
break;
}
}
function getCurrnetDir () {
const url = new URL(".", import.meta.url);
return fileURLToPath(url);
}

View File

@@ -27,6 +27,7 @@ export default async function install(
.get(`version/${minecraftversion}/json`) .get(`version/${minecraftversion}/json`)
.json()) as mcinfoX; //获取Minecraft版本JSON .json()) as mcinfoX; //获取Minecraft版本JSON
const forgepath = path; const forgepath = path;
console.log(loaderversion)
const forgedata = ( const forgedata = (
await gotx.get( await gotx.get(
`forge/download?mcversion=${minecraftversion}&version=${loaderversion}&category=installer&format=jar` `forge/download?mcversion=${minecraftversion}&version=${loaderversion}&category=installer&format=jar`
@@ -49,7 +50,7 @@ export default async function install(
//下载依赖1 //下载依赖1
const t = fvdata[c].downloads.artifact; const t = fvdata[c].downloads.artifact;
await xfastdownload( await xfastdownload(
`maven${new URL(t.url).pathname}`, `https://bmclapi2.bangbang93.com/maven${new URL(t.url).pathname}`,
`${forgepath}/libraries/${t.path}`, `${forgepath}/libraries/${t.path}`,
16 16
); );
@@ -61,7 +62,7 @@ export default async function install(
//下载依赖2 //下载依赖2
const t = fvdata[c].downloads.artifact; const t = fvdata[c].downloads.artifact;
await xfastdownload( await xfastdownload(
`maven${new URL(t.url).pathname}`, `https://bmclapi2.bangbang93.com/maven${new URL(t.url).pathname}`,
`${forgepath}/libraries/${t.path}`, `${forgepath}/libraries/${t.path}`,
16 16
); );
@@ -69,7 +70,7 @@ export default async function install(
//下载MAPPING与MOJMAPS //下载MAPPING与MOJMAPS
/*MOJMAPS*/ /*MOJMAPS*/
await xfastdownload( await xfastdownload(
new URL(mcinfo.downloads.server_mappings.url).pathname, `https://bmclapi2.bangbang93.com${new URL(mcinfo.downloads.server_mappings.url).pathname}`,
`${forgepath}/libraries/${mavenToUrl( `${forgepath}/libraries/${mavenToUrl(
json.data.MOJMAPS.server.replace(/[[\]]/g, ""), json.data.MOJMAPS.server.replace(/[[\]]/g, ""),
"" ""
@@ -83,7 +84,7 @@ export default async function install(
/['"]/g, /['"]/g,
"" ""
)}.zip`; )}.zip`;
await xfastdownload(`maven/${tmp}`, `${forgepath}/libraries/${tmp}`); await xfastdownload(`https://bmclapi2.bangbang93.com/maven/${tmp}`, `${forgepath}/libraries/${tmp}`);
LOGGER.info("下载MAPPING与MOJMAPS完成"); LOGGER.info("下载MAPPING与MOJMAPS完成");
} }
} }
@@ -91,14 +92,14 @@ export default async function install(
for (let d = 0; d < mcinfo.libraries.length; d++) { for (let d = 0; d < mcinfo.libraries.length; d++) {
const g = mcinfo.libraries[d].downloads.artifact; const g = mcinfo.libraries[d].downloads.artifact;
await xfastdownload( await xfastdownload(
`maven${new URL(g.url).pathname}`, `https://bmclapi2.bangbang93.com/maven${new URL(g.url).pathname}`,
`${forgepath}/libraries/${g.path}`, `${forgepath}/libraries/${g.path}`,
16 16
); );
} }
LOGGER.info(`下载Minecraft的Maven完成`); LOGGER.info(`下载Minecraft的Maven完成`);
await xfastdownload( await xfastdownload(
`version/${minecraftversion}/server`, `https://bmclapi2.bangbang93.com/version/${minecraftversion}/server`,
`${forgepath}/libraries/net/minecraft/server/${minecraftversion}/server-${minecraftversion}.jar`, `${forgepath}/libraries/net/minecraft/server/${minecraftversion}/server-${minecraftversion}.jar`,
1 1
); );

View File

@@ -1,11 +1,14 @@
import { fastdownload } from "../utils/utils.js"; import got from "got";
import { fastdownload, usemirror } from "../utils/utils.js";
import { modpack_info, XPlatform } from "./index.js"; import { modpack_info, XPlatform } from "./index.js";
const cf_url = (()=>{if(usemirror){return "https://mod.mcimirror.top/curseforge"}else{return "https://api.curseforge.com"}})()
export interface CurseForgeManifest { export interface CurseForgeManifest {
minecraft: { minecraft: {
version: string; version: string;
modLoaders: Array<{ id: string }>; modLoaders: Array<{ id: string }>;
}; };
files: Array<{ projectID: number; fileID: number }>;
} }
export class CurseForge implements XPlatform { export class CurseForge implements XPlatform {
@@ -14,15 +17,39 @@ export class CurseForge implements XPlatform {
const local_manifest = manifest as CurseForgeManifest; const local_manifest = manifest as CurseForgeManifest;
if (result && local_manifest) if (result && local_manifest)
result.minecraft = local_manifest.minecraft.version; result.minecraft = local_manifest.minecraft.version;
const loader_all = local_manifest.minecraft.modLoaders[0].id.match( const id = local_manifest.minecraft.modLoaders[0].id;
const loader_all = id.match(
/(.*)-/ /(.*)-/
) as RegExpMatchArray; ) as RegExpMatchArray;
result.loader = loader_all[1]; result.loader = loader_all[1];
result.loader_version = loader_all[0]; result.loader_version = id.replace(loader_all[0],"");
return result; return result;
} }
async downloadfile(urls: [string, string]): Promise<void> { async downloadfile(manifest: object,path:string): Promise<void> {
await fastdownload(urls); const local_manifest = manifest as CurseForgeManifest;
const FileID = JSON.stringify({
fileIds: local_manifest.files.map((file: { fileID: number; }) => file.fileID),
});
let tmp: [string, string] | string[][] = [];
await got.post(cf_url+"/v1/mods/files",{
body: FileID,
headers: {
"Content-Type": "application/json",
"x-api-key":"$2a$10$ydk0TLDG/Gc6uPMdz7mad.iisj2TaMDytVcIW4gcVP231VKngLBKy"
}
}).json().then((res:any)=>{
res.data.forEach((e: { fileName: string; downloadUrl: null|string; }) => {
if (e.fileName.endsWith(".zip")||e.downloadUrl == null) {
return;
}
if (usemirror){
tmp.push(["https://mod.mcimirror.top"+new URL(e.downloadUrl).pathname,path+"/mods/" + e.fileName])
}else{
tmp.push([e.downloadUrl,path+"/mods/" + e.fileName])
}
});
})
await fastdownload(tmp as unknown as [string, string]) //下载文件
} }
} }

View File

@@ -4,9 +4,7 @@ import { Modrinth } from "./modrinth.js";
export interface XPlatform { export interface XPlatform {
getinfo(manifest: object): Promise<modpack_info>; getinfo(manifest: object): Promise<modpack_info>;
downloadfile( downloadfile(manifest: object,path:string): Promise<void>;
urls: [string, string] | [string, string, string]
): Promise<void>;
} }
export interface modpack_info { export interface modpack_info {

View File

@@ -1,7 +1,9 @@
import { mr_fastdownload } from "../utils/utils.js"; import fs from "node:fs";
import { mr_fastdownload, usemirror } from "../utils/utils.js";
import { modpack_info, XPlatform } from "./index.js"; import { modpack_info, XPlatform } from "./index.js";
interface ModrinthManifest { interface ModrinthManifest {
files: Array<{ path: string; downloads: string[]; fileSize: number; }>;
dependencies: { dependencies: {
minecraft: string; minecraft: string;
forge: string; forge: string;
@@ -27,7 +29,19 @@ export class Modrinth implements XPlatform {
} }
return result; return result;
} }
async downloadfile(urls: [string, string, string]): Promise<void> { async downloadfile(manifest: object,path:string): Promise<void> {
await mr_fastdownload(urls); const index = manifest as ModrinthManifest;
let tmp: [string, string, string][] = []
index.files.forEach(async (e: { path: string; downloads: string[]; fileSize: number;}) => {
if (e.path.endsWith(".zip")) {
return;
}
if (usemirror){
tmp.push(["https://mod.mcimirror.top"+new URL(e.downloads[0]).pathname,path+e.path,String(e.fileSize)])
}else{
tmp.push([e.downloads[0],path + e.path,String(e.fileSize)])
}
});
await mr_fastdownload(tmp as unknown as [string, string, string])
} }
} }

View File

@@ -18,6 +18,8 @@ export async function DeEarthMain(modspath: string, movepath: any) {
fs.mkdirSync(movepath) fs.mkdirSync(movepath)
} }
LOGGER.info(`DeEarth V1.0.0`) LOGGER.info(`DeEarth V1.0.0`)
LOGGER.info(`如有无法筛选的mods请前往 https://dearth.0771010.xyz/ 提交未成功筛选的模组的modid`)
LOGGER.info(`Probejs 7.0.0以上版本为非客户端mod如rubbish中有请自行添加回去`)
const resaddr = fs.readdirSync(modspath) const resaddr = fs.readdirSync(modspath)
LOGGER.info(`获取目录列表,一共${resaddr.length}个jar文件。`) LOGGER.info(`获取目录列表,一共${resaddr.length}个jar文件。`)
const totalBar = multibar.create(resaddr.length, 0, { filename: '总文件数' }) const totalBar = multibar.create(resaddr.length, 0, { filename: '总文件数' })

View File

@@ -32,7 +32,7 @@ function error(error: object | string) {
console.log( console.log(
`[${chalk.blue( `[${chalk.blue(
new Date().toLocaleDateString() + " " + new Date().toLocaleTimeString() new Date().toLocaleDateString() + " " + new Date().toLocaleTimeString()
)}](${process.pid})[${chalk.red("ERROR")}:${error.toString()}` )}](${process.pid})[${chalk.red("ERROR")}:${JSON.stringify(error)}`
); );
} else { } else {
console.log( console.log(

View File

@@ -3,7 +3,9 @@ import pRetry from "p-retry";
import fse from "fs-extra" import fse from "fs-extra"
import yauzl from "yauzl"; import yauzl from "yauzl";
import got from "got"; import got from "got";
import env from "dotenv"
import { MultiBar } from "cli-progress"; import { MultiBar } from "cli-progress";
import {URL, fileURLToPath } from "node:url";
import { LOGGER } from "./logger.js"; import { LOGGER } from "./logger.js";
export async function readzipentry(zipfile: yauzl.ZipFile, entry: yauzl.Entry):Promise<string|Buffer> { export async function readzipentry(zipfile: yauzl.ZipFile, entry: yauzl.Entry):Promise<string|Buffer> {
@@ -120,3 +122,18 @@ export async function xfastdownload(
} }
); );
} }
export const usemirror = (()=>{
env.config()
const mirror = process.env.MIRROR
if(mirror){
return false
}else{
return true
}
})()
export const isDevelopment = (()=>{
env.config()
return process.env.DEVELOPMENT
})()