UNPKG

@xmcl/installer

Version:

The installers of Minecraft/Forge/Fabric/Liteloader/Quilt

1,254 lines (1,241 loc) 80.6 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // index.ts var installer_exports = {}; __export(installer_exports, { BadForgeInstallerJarError: () => BadForgeInstallerJarError, BadOptifineJarError: () => BadOptifineJarError, DEFAULT_FORGE_MAVEN: () => DEFAULT_FORGE_MAVEN, DEFAULT_META_URL: () => DEFAULT_META_URL, DEFAULT_RESOURCE_ROOT_URL: () => DEFAULT_RESOURCE_ROOT_URL, DEFAULT_RUNTIME_ALL_URL: () => DEFAULT_RUNTIME_ALL_URL, DEFAULT_VERSION_MANIFEST: () => DEFAULT_VERSION_MANIFEST, DEFAULT_VERSION_MANIFEST_URL: () => DEFAULT_VERSION_MANIFEST_URL, DownloadForgeInstallerTask: () => DownloadForgeInstallerTask, DownloadJRETask: () => DownloadJRETask, DownloadNeoForgedInstallerTask: () => DownloadNeoForgedInstallerTask, DownloadTask: () => DownloadTask, InstallAssetIndexTask: () => InstallAssetIndexTask, InstallAssetTask: () => InstallAssetTask, InstallJarTask: () => InstallJarTask, InstallJsonTask: () => InstallJsonTask, InstallLibraryTask: () => InstallLibraryTask, JavaRuntimeTargetType: () => JavaRuntimeTargetType, LOADER_MAVEN_URL: () => LOADER_MAVEN_URL, LiteloaderVersionList: () => LiteloaderVersionList, MissingVersionJsonError: () => MissingVersionJsonError, ParseJavaVersionError: () => ParseJavaVersionError, PostProcessBadJarError: () => PostProcessBadJarError, PostProcessFailedError: () => PostProcessFailedError, PostProcessNoMainClassError: () => PostProcessNoMainClassError, PostProcessingTask: () => PostProcessingTask, UnzipTask: () => UnzipTask, YARN_MAVEN_URL: () => YARN_MAVEN_URL, diagnoseInstall: () => diagnoseInstall, fetchJavaRuntimeManifest: () => fetchJavaRuntimeManifest, generateOptifineVersion: () => generateOptifineVersion, getDefaultEntryResolver: () => getDefaultEntryResolver, getFabricArtifacts: () => getFabricArtifacts, getFabricLoaderArtifact: () => getFabricLoaderArtifact, getForgeVersionList: () => getForgeVersionList, getLabyModManifest: () => getLabyModManifest, getLiteloaderVersionList: () => getLiteloaderVersionList, getLoaderArtifactList: () => getLoaderArtifactList, getLoaderArtifactListFor: () => getLoaderArtifactListFor, getPotentialJavaLocations: () => getPotentialJavaLocations, getQuiltVersionsList: () => getQuiltVersionsList, getVersionList: () => getVersionList, getYarnArtifactList: () => getYarnArtifactList, getYarnArtifactListFor: () => getYarnArtifactListFor, install: () => install, installAssets: () => installAssets, installAssetsTask: () => installAssetsTask, installByInstallerTask: () => installByInstallerTask, installByProfile: () => installByProfile, installByProfileTask: () => installByProfileTask, installDependencies: () => installDependencies, installDependenciesTask: () => installDependenciesTask, installFabric: () => installFabric, installForge: () => installForge, installForgeTask: () => installForgeTask, installJavaRuntimeTask: () => installJavaRuntimeTask, installJreFromMojang: () => installJreFromMojang, installJreFromMojangTask: () => installJreFromMojangTask, installLaby4Mod: () => installLaby4Mod, installLabyMod4Task: () => installLabyMod4Task, installLibraries: () => installLibraries, installLibrariesTask: () => installLibrariesTask, installLiteloader: () => installLiteloader, installLiteloaderTask: () => installLiteloaderTask, installNeoForged: () => installNeoForged, installNeoForgedTask: () => installNeoForgedTask, installOptifine: () => installOptifine, installOptifineTask: () => installOptifineTask, installQuiltVersion: () => installQuiltVersion, installResolvedAssetsTask: () => installResolvedAssetsTask, installResolvedLibraries: () => installResolvedLibraries, installResolvedLibrariesTask: () => installResolvedLibrariesTask, installTask: () => installTask, installVersion: () => installVersion, installVersionTask: () => installVersionTask, isForgeInstallerEntries: () => isForgeInstallerEntries, isLegacyForgeInstallerEntries: () => isLegacyForgeInstallerEntries, parseJavaVersion: () => parseJavaVersion, postProcess: () => postProcess, resolveJava: () => resolveJava, resolveLibraryDownloadUrls: () => resolveLibraryDownloadUrls, resolveProcessors: () => resolveProcessors, scanLocalJava: () => scanLocalJava, unpackForgeInstaller: () => unpackForgeInstaller, walkForgeInstallerEntries: () => walkForgeInstallerEntries }); module.exports = __toCommonJS(installer_exports); // fabric.ts var import_core2 = require("@xmcl/core"); var import_promises2 = require("fs/promises"); var import_undici = require("undici"); // utils.ts var import_child_process = require("child_process"); var import_promises = require("fs/promises"); var import_path = require("path"); var import_core = require("@xmcl/core"); function missing(target) { return (0, import_promises.access)(target).then(() => false, () => true); } async function ensureDir(target) { try { await (0, import_promises.mkdir)(target); } catch (err) { const e = err; if (await (0, import_promises.stat)(target).then((s) => s.isDirectory()).catch(() => false)) { return; } if (e.code === "EEXIST") { return; } if (e.code === "ENOENT") { if ((0, import_path.dirname)(target) === target) { throw e; } try { await ensureDir((0, import_path.dirname)(target)); await (0, import_promises.mkdir)(target); } catch { if (await (0, import_promises.stat)(target).then((s) => s.isDirectory()).catch((e2) => false)) { return; } throw e; } return; } throw e; } } function ensureFile(target) { return ensureDir((0, import_path.dirname)(target)); } function normalizeArray(arr = []) { return arr instanceof Array ? arr : [arr]; } function spawnProcess(spawnJavaOptions, args, options) { const process2 = ((spawnJavaOptions == null ? void 0 : spawnJavaOptions.spawn) ?? import_child_process.spawn)(spawnJavaOptions.java ?? "java", args, options); return waitProcess(process2); } function waitProcess(process2) { return new Promise((resolve2, reject) => { var _a, _b, _c, _d; const errorMsg = []; process2.on("error", (err) => { reject(err); }); process2.on("close", (code) => { if (code !== 0) { reject(errorMsg.join("")); } else { resolve2(); } }); process2.on("exit", (code) => { if (code !== 0) { reject(errorMsg.join("")); } else { resolve2(); } }); (_a = process2.stdout) == null ? void 0 : _a.setEncoding("utf-8"); (_b = process2.stdout) == null ? void 0 : _b.on("data", (buf) => { }); (_c = process2.stderr) == null ? void 0 : _c.setEncoding("utf-8"); (_d = process2.stderr) == null ? void 0 : _d.on("data", (buf) => { errorMsg.push(buf.toString()); }); }); } function joinUrl(a, b) { if (a.endsWith("/") && b.startsWith("/")) { return a + b.substring(1); } if (!a.endsWith("/") && !b.startsWith("/")) { return a + "/" + b; } return a + b; } function errorToString(e) { if (e instanceof Error) { return e.stack ? e.stack : e.message; } return e.toString(); } // fabric.ts var YARN_MAVEN_URL = "https://maven.fabricmc.net/net/fabricmc/yarn/maven-metadata.xml"; var LOADER_MAVEN_URL = "https://maven.fabricmc.net/net/fabricmc/fabric-loader/maven-metadata.xml"; async function getFabricArtifacts(options) { const response = await (0, import_undici.request)("https://meta.fabricmc.net/v2/versions", { throwOnError: true, dispatcher: options == null ? void 0 : options.dispatcher }); const body = response.body.json(); return body; } async function getYarnArtifactList(options) { const response = await (0, import_undici.request)("https://meta.fabricmc.net/v2/versions/yarn", { throwOnError: true, dispatcher: options == null ? void 0 : options.dispatcher }); const body = response.body.json(); return body; } async function getYarnArtifactListFor(minecraft, options) { const response = await (0, import_undici.request)("https://meta.fabricmc.net/v2/versions/yarn/" + minecraft, { throwOnError: true, dispatcher: options == null ? void 0 : options.dispatcher }); const body = response.body.json(); return body; } async function getLoaderArtifactList(options) { const response = await (0, import_undici.request)("https://meta.fabricmc.net/v2/versions/loader", { throwOnError: true, dispatcher: options == null ? void 0 : options.dispatcher }); const body = response.body.json(); return body; } async function getLoaderArtifactListFor(minecraft, options) { const response = await (0, import_undici.request)("https://meta.fabricmc.net/v2/versions/loader/" + minecraft, { throwOnError: true, dispatcher: options == null ? void 0 : options.dispatcher }); const body = response.body.json(); return body; } async function getFabricLoaderArtifact(minecraft, loader, options) { const response = await (0, import_undici.request)("https://meta.fabricmc.net/v2/versions/loader/" + minecraft + "/" + loader, { throwOnError: true, dispatcher: options == null ? void 0 : options.dispatcher }); const body = response.body.json(); return body; } async function installFabric(loader, minecraft, options = {}) { const folder = import_core2.MinecraftFolder.from(minecraft); let yarn; const side = options.side ?? "client"; let id = options.versionId; let mcversion; if (options.yarnVersion) { const yarnVersion = options.yarnVersion; if (typeof yarnVersion === "string") { yarn = yarnVersion; mcversion = yarn.split("+")[0]; } else { yarn = yarnVersion.version; mcversion = yarnVersion.gameVersion || yarn.split("+")[0]; } } else { mcversion = loader.intermediary.version; } if (!id) { id = mcversion; if (yarn) { id += `-fabric${yarn}-loader${loader.loader.version}`; } else { id += `-fabric${loader.loader.version}`; } } const libraries = [ { name: loader.loader.maven, url: "https://maven.fabricmc.net/" }, { name: loader.intermediary.maven, url: "https://maven.fabricmc.net/" }, ...options.yarnVersion ? [{ name: `net.fabricmc:yarn:${yarn}`, url: "https://maven.fabricmc.net/" }] : [], ...loader.launcherMeta.libraries.common, ...loader.launcherMeta.libraries[side] ]; const mainClass = loader.launcherMeta.mainClass[side]; const inheritsFrom = options.inheritsFrom || mcversion; const jsonFile = folder.getVersionJson(id); await ensureFile(jsonFile); await (0, import_promises2.writeFile)(jsonFile, JSON.stringify({ id, inheritsFrom, mainClass, libraries, arguments: { game: [], jvm: [] }, releaseTime: (/* @__PURE__ */ new Date()).toJSON(), time: (/* @__PURE__ */ new Date()).toJSON() })); return id; } // liteloader.ts var import_core3 = require("@xmcl/core"); var import_task = require("@xmcl/task"); var import_promises3 = require("fs/promises"); var import_path2 = require("path"); var import_undici2 = require("undici"); var DEFAULT_VERSION_MANIFEST = "http://dl.liteloader.com/versions/versions.json"; function processLibraries(lib) { if (Object.keys(lib).length === 1 && lib.name) { if (lib.name.startsWith("org.ow2.asm")) { lib.url = "https://files.minecraftforge.net/maven/"; } } return lib; } var LiteloaderVersionList; ((LiteloaderVersionList2) => { function parse(content) { const result = JSON.parse(content); const metalist = { meta: result.meta, versions: {} }; for (const mcversion in result.versions) { const versions = metalist.versions[mcversion] = {}; const snapshots = result.versions[mcversion].snapshots; const artifacts = result.versions[mcversion].artefacts; const url = result.versions[mcversion].repo.url; if (snapshots) { const { stream, file, version, md5, timestamp, tweakClass, libraries } = snapshots["com.mumfrey:liteloader"].latest; const type = stream === "RELEASE" ? "RELEASE" : "SNAPSHOT"; versions.snapshot = { url, type, file, version, md5, timestamp, mcversion, tweakClass, libraries: libraries.map(processLibraries) }; } if (artifacts) { const { stream, file, version, md5, timestamp, tweakClass, libraries } = artifacts["com.mumfrey:liteloader"].latest; const type = stream === "RELEASE" ? "RELEASE" : "SNAPSHOT"; versions.release = { url, type, file, version, md5, timestamp, mcversion, tweakClass, libraries: libraries.map(processLibraries) }; } } return metalist; } LiteloaderVersionList2.parse = parse; })(LiteloaderVersionList || (LiteloaderVersionList = {})); var snapshotRoot = "http://dl.liteloader.com/versions/"; var releaseRoot = "http://repo.mumfrey.com/content/repositories/liteloader/"; var MissingVersionJsonError = class extends Error { constructor(version, path) { super(); this.version = version; this.path = path; this.name = "MissingVersionJson"; } }; async function getLiteloaderVersionList(options = {}) { const response = await (0, import_undici2.request)(DEFAULT_VERSION_MANIFEST, { dispatcher: options.dispatcher, throwOnError: true }); const body = await response.body.text(); return LiteloaderVersionList.parse(body); } function installLiteloader(versionMeta, location, options) { return installLiteloaderTask(versionMeta, location, options).startAndWait(); } function buildVersionInfo(versionMeta, mountedJSON) { const id = `${mountedJSON.id}-Liteloader${versionMeta.mcversion}-${versionMeta.version}`; const time = new Date(Number.parseInt(versionMeta.timestamp, 10) * 1e3).toISOString(); const releaseTime = time; const type = versionMeta.type; const libraries = [ { name: `com.mumfrey:liteloader:${versionMeta.version}`, url: type === "SNAPSHOT" ? snapshotRoot : releaseRoot }, ...versionMeta.libraries.map(processLibraries) ]; const mainClass = "net.minecraft.launchwrapper.Launch"; const inheritsFrom = mountedJSON.id; const jar = mountedJSON.jar || mountedJSON.id; const info = { id, time, releaseTime, type, libraries, mainClass, inheritsFrom, jar }; if (mountedJSON.arguments) { info.arguments = { game: ["--tweakClass", versionMeta.tweakClass], jvm: [] }; } else { info.minecraftArguments = `--tweakClass ${versionMeta.tweakClass} ` + mountedJSON.minecraftArguments; } return info; } function installLiteloaderTask(versionMeta, location, options = {}) { return (0, import_task.task)("installLiteloader", async function installLiteloader2() { const mc = import_core3.MinecraftFolder.from(location); const mountVersion = options.inheritsFrom || versionMeta.mcversion; const mountedJSON = await this.yield((0, import_task.task)("resolveVersionJson", async function resolveVersionJson() { if (await missing(mc.getVersionJson(mountVersion))) { throw new MissingVersionJsonError(mountVersion, mc.getVersionJson(mountVersion)); } return (0, import_promises3.readFile)(mc.getVersionJson(mountVersion)).then((b) => b.toString()).then(JSON.parse); })); const versionInf = await this.yield((0, import_task.task)("generateLiteloaderJson", async function generateLiteloaderJson() { const inf = buildVersionInfo(versionMeta, mountedJSON); inf.id = options.versionId || inf.id; inf.inheritsFrom = options.inheritsFrom || inf.inheritsFrom; const versionPath = mc.getVersionRoot(inf.id); await ensureDir(versionPath); await (0, import_promises3.writeFile)((0, import_path2.join)(versionPath, inf.id + ".json"), JSON.stringify(inf, void 0, 4)); return inf; })); return versionInf.id; }); } // forge.ts var import_core6 = require("@xmcl/core"); var import_forge_site_parser = require("@xmcl/forge-site-parser"); var import_task5 = require("@xmcl/task"); var import_unzip3 = require("@xmcl/unzip"); var import_fs = require("fs"); var import_promises6 = require("fs/promises"); var import_path5 = require("path"); var import_promises7 = require("stream/promises"); var import_undici4 = require("undici"); // downloadTask.ts var import_file_transfer = require("@xmcl/file-transfer"); var import_task2 = require("@xmcl/task"); var DownloadTask = class extends import_task2.AbortableTask { constructor(options) { super(); this.options = options; this._from = options.url instanceof Array ? options.url[0] : options.url; this._to = options.destination; } abort = () => { }; onProgress(url, chunkSize, progress, total) { this._progress = progress; this._total = total; this._from = url.toString(); this.update(chunkSize); } process() { const listeners = []; const aborted = () => this.isCancelled || this.isPaused; const signal = { get aborted() { return aborted(); }, addEventListener(event, listener) { if (event !== "abort") { return this; } listeners.push(listener); return this; }, removeEventListener(event, listener) { return this; } }; this.abort = () => { listeners.forEach((l) => l()); }; return (0, import_file_transfer.download)({ ...this.options, progressController: this, abortSignal: signal }); } isAbortedError(e) { if (e instanceof import_file_transfer.DownloadAbortError) { return true; } return false; } }; // minecraft.ts var import_core4 = require("@xmcl/core"); var import_file_transfer3 = require("@xmcl/file-transfer"); var import_task3 = require("@xmcl/task"); var import_promises4 = require("fs/promises"); var import_path3 = require("path"); var import_undici3 = require("undici"); // zipValdiator.ts var import_file_transfer2 = require("@xmcl/file-transfer"); var import_unzip = require("@xmcl/unzip"); var ZipValidator = class { async validate(destination, url) { try { const file = await (0, import_unzip.open)(destination, { autoClose: false, lazyEntries: true }); file.close(); } catch (e) { throw new import_file_transfer2.ValidationError("InvalidZipError", e.message); } } }; // minecraft.ts var DEFAULT_VERSION_MANIFEST_URL = "https://launchermeta.mojang.com/mc/game/version_manifest.json"; var DEFAULT_RESOURCE_ROOT_URL = "https://resources.download.minecraft.net"; async function getVersionList(options = {}) { const response = await (0, import_undici3.request)(DEFAULT_VERSION_MANIFEST_URL, { dispatcher: options.dispatcher, throwOnError: true }); return await response.body.json(); } function resolveDownloadUrls(original, version, option) { const result = []; if (typeof option === "function") { result.unshift(...normalizeArray(option(version))); } else { result.unshift(...normalizeArray(option)); } if (result.indexOf(original) === -1) { result.push(original); } return result; } async function install(versionMeta, minecraft, option = {}) { return installTask(versionMeta, minecraft, option).startAndWait(); } function installVersion(versionMeta, minecraft, options = {}) { return installVersionTask(versionMeta, minecraft, options).startAndWait(); } function installDependencies(version, options) { return installDependenciesTask(version, options).startAndWait(); } function installAssets(version, options = {}) { return installAssetsTask(version, options).startAndWait(); } function installLibraries(version, options = {}) { return installLibrariesTask(version, options).startAndWait(); } async function installResolvedLibraries(libraries, minecraft, option) { await installLibrariesTask({ libraries, minecraftDirectory: typeof minecraft === "string" ? minecraft : minecraft.root }, option).startAndWait(); } function installTask(versionMeta, minecraft, options = {}) { return (0, import_task3.task)("install", async function() { const version = await this.yield(installVersionTask(versionMeta, minecraft, options)); if (options.side !== "server") { await this.yield(installDependenciesTask(version, options)); } return version; }); } function installVersionTask(versionMeta, minecraft, options = {}) { return (0, import_task3.task)("version", async function() { await this.yield(new InstallJsonTask(versionMeta, minecraft, options)); const version = await import_core4.Version.parse(minecraft, versionMeta.id); if (version.downloads[options.side ?? "client"]) { await this.yield(new InstallJarTask(version, minecraft, options)); } return version; }, versionMeta); } function installDependenciesTask(version, options = {}) { return (0, import_task3.task)("dependencies", async function() { await Promise.all([ this.yield(installAssetsTask(version, options)), this.yield(installLibrariesTask(version, options)) ]); return version; }); } function installAssetsTask(version, options = {}) { return (0, import_task3.task)("assets", async function() { var _a, _b; const folder = import_core4.MinecraftFolder.from(version.minecraftDirectory); if ((_b = (_a = version.logging) == null ? void 0 : _a.client) == null ? void 0 : _b.file) { const file = version.logging.client.file; await this.yield(new DownloadTask({ url: file.url, validator: { algorithm: "sha1", hash: file.sha1 }, destination: folder.getLogConfig(file.id), agent: options.agent, headers: options.headers }).setName("asset", { name: file.id, hash: file.sha1, size: file.size })); } const jsonPath = folder.getPath("assets", "indexes", version.assets + ".json"); if (version.assetIndex) { await this.yield(new InstallAssetIndexTask(version, options)); } await ensureDir(folder.getPath("assets", "objects")); const getAssetIndexFallback = async () => { var _a2; const urls = resolveDownloadUrls(version.assetIndex.url, version, options.assetsIndexUrl); for (const url of urls) { try { const response = await (0, import_undici3.request)(url, { dispatcher: (_a2 = options.agent) == null ? void 0 : _a2.dispatcher }); const json = await response.body.json(); await (0, import_promises4.writeFile)(jsonPath, JSON.stringify(json)); return json; } catch { } } }; let objectArray; try { const { objects } = JSON.parse(await (0, import_promises4.readFile)(jsonPath).then((b) => b.toString())); objectArray = Object.keys(objects).map((k) => ({ name: k, ...objects[k] })); } catch (e) { if (e instanceof SyntaxError) { throw e; } const { objects } = await getAssetIndexFallback(); objectArray = Object.keys(objects).map((k) => ({ name: k, ...objects[k] })); } await this.all(objectArray.map((o) => new InstallAssetTask(o, folder, options)), { throwErrorImmediately: options.throwErrorImmediately ?? false, getErrorMessage: (errs) => `Errors during install Minecraft ${version.id}'s assets at ${version.minecraftDirectory}: ${errs.map(errorToString).join("\n")}` }); return version; }); } function installLibrariesTask(version, options = {}) { return (0, import_task3.task)("libraries", async function() { const folder = import_core4.MinecraftFolder.from(version.minecraftDirectory); await this.all(version.libraries.map((lib) => new InstallLibraryTask(lib, folder, options)), { throwErrorImmediately: options.throwErrorImmediately ?? false, getErrorMessage: (errs) => `Errors during install libraries at ${version.minecraftDirectory}: ${errs.map(errorToString).join("\n")}` }); }); } function installResolvedLibrariesTask(libraries, minecraft, option) { return installLibrariesTask({ libraries, minecraftDirectory: typeof minecraft === "string" ? minecraft : minecraft.root }, option); } function installResolvedAssetsTask(assets, folder, options = {}) { return (0, import_task3.task)("assets", async function() { await ensureDir(folder.getPath("assets", "objects")); await this.all(assets.map((o) => new InstallAssetTask(o, folder, options)), { throwErrorImmediately: false, getErrorMessage: (errs) => `Errors during install assets at ${folder.root}: ${errs.map(errorToString).join("\n")}` }); }); } var InstallJsonTask = class extends DownloadTask { constructor(version, minecraft, options) { var _a; const folder = import_core4.MinecraftFolder.from(minecraft); const destination = folder.getVersionJson(version.id); const expectSha1 = version.url.split("/")[5]; const urls = resolveDownloadUrls(version.url, version, options.json); super({ url: urls, headers: options.headers, agent: options.agent, validator: expectSha1 ? ((_a = options.checksumValidatorResolver) == null ? void 0 : _a.call(options, { algorithm: "sha1", hash: expectSha1 })) || { algorithm: "sha1", hash: expectSha1 } : new import_file_transfer3.JsonValidator(), destination, skipPrevalidate: options.skipPrevalidate, skipRevalidate: options.skipRevalidate }); this.name = "json"; this.param = version; } }; var InstallJarTask = class extends DownloadTask { constructor(version, minecraft, options) { var _a; const folder = import_core4.MinecraftFolder.from(minecraft); const type = options.side ?? "client"; const destination = (0, import_path3.join)( folder.getVersionRoot(version.id), type === "client" ? version.id + ".jar" : version.id + "-" + type + ".jar" ); const download2 = version.downloads[type]; if (!download2) { throw new Error(`Cannot find downloadable jar in ${type}`); } const urls = resolveDownloadUrls(download2.url, version, options[type]); const expectSha1 = download2.sha1; super({ url: urls, validator: ((_a = options.checksumValidatorResolver) == null ? void 0 : _a.call(options, { algorithm: "sha1", hash: expectSha1 })) || { algorithm: "sha1", hash: expectSha1 }, destination, headers: options.headers, agent: options.agent, skipPrevalidate: options.skipPrevalidate, skipRevalidate: options.skipRevalidate }); this.name = "jar"; this.param = version; } }; var InstallAssetIndexTask = class extends DownloadTask { constructor(version, options = {}) { var _a; const folder = import_core4.MinecraftFolder.from(version.minecraftDirectory); const jsonPath = folder.getPath("assets", "indexes", version.assets + ".json"); const expectSha1 = version.assetIndex.sha1; super({ url: resolveDownloadUrls(version.assetIndex.url, version, options.assetsIndexUrl), destination: jsonPath, validator: ((_a = options.checksumValidatorResolver) == null ? void 0 : _a.call(options, { algorithm: "sha1", hash: expectSha1 })) || { algorithm: "sha1", hash: expectSha1 }, headers: options.headers, agent: options.agent, skipPrevalidate: options.skipPrevalidate, skipRevalidate: options.skipRevalidate }); this.name = "assetIndex"; this.param = version; } }; var InstallLibraryTask = class extends DownloadTask { constructor(lib, folder, options) { var _a; const libraryPath = lib.download.path; const destination = (0, import_path3.join)(folder.libraries, libraryPath); const urls = resolveLibraryDownloadUrls(lib, options); const expectSha1 = lib.download.sha1; super({ url: urls, validator: lib.download.sha1 === "" ? new ZipValidator() : ((_a = options.checksumValidatorResolver) == null ? void 0 : _a.call(options, { algorithm: "sha1", hash: expectSha1 })) || { algorithm: "sha1", hash: expectSha1 }, destination, headers: options.headers, agent: options.agent, skipPrevalidate: options.skipPrevalidate, skipRevalidate: options.skipRevalidate }); this.name = "library"; this.param = lib; } }; var InstallAssetTask = class extends DownloadTask { constructor(asset, folder, options) { var _a; const assetsHosts = normalizeArray(options.assetsHost || []); if (assetsHosts.indexOf(DEFAULT_RESOURCE_ROOT_URL) === -1) { assetsHosts.push(DEFAULT_RESOURCE_ROOT_URL); } const { hash, size, name } = asset; const head = hash.substring(0, 2); const dir = folder.getPath("assets", "objects", head); const file = (0, import_path3.join)(dir, hash); const urls = assetsHosts.map((h) => `${h}/${head}/${hash}`); super({ url: urls, destination: file, validator: options.prevalidSizeOnly ? { async validate(destination, url) { const fstat = await (0, import_promises4.stat)(destination).catch(() => ({ size: -1 })); if (fstat.size !== size) { throw new import_file_transfer3.ChecksumNotMatchError("size", size.toString(), fstat.size.toString(), destination, url); } } } : ((_a = options.checksumValidatorResolver) == null ? void 0 : _a.call(options, { algorithm: "sha1", hash })) || { algorithm: "sha1", hash }, headers: options.headers, agent: options.agent, skipPrevalidate: options.skipPrevalidate, skipRevalidate: options.skipRevalidate }); this._total = size; this.name = "asset"; this.param = asset; } }; var DEFAULT_MAVENS = ["https://repo1.maven.org/maven2/"]; function resolveLibraryDownloadUrls(library, libraryOptions) { var _a; const libraryHosts = ((_a = libraryOptions.libraryHost) == null ? void 0 : _a.call(libraryOptions, library)) ?? []; const url = new URL(library.download.url); return [.../* @__PURE__ */ new Set([ // user defined alternative host to download ...normalizeArray(libraryHosts), ...normalizeArray(libraryOptions.mavenHost).map((m) => joinUrl(m, url.pathname)), library.download.url, ...normalizeArray(libraryOptions.mavenHost).map((m) => joinUrl(m, library.download.path)), ...DEFAULT_MAVENS.map((m) => joinUrl(m, library.download.path)) ])]; } // profile.ts var import_core5 = require("@xmcl/core"); var import_task4 = require("@xmcl/task"); var import_unzip2 = require("@xmcl/unzip"); var import_child_process2 = require("child_process"); var import_promises5 = require("fs/promises"); var import_path4 = require("path"); function resolveProcessors(side, installProfile, minecraft) { function normalizePath(val) { if (val && val.match(/^\[.+\]$/g)) { const name = val.substring(1, val.length - 1); return minecraft.getLibraryByPath(import_core5.LibraryInfo.resolve(name).path); } return val; } function normalizeVariable(val) { if (val && val.match(/^{.+}$/g)) { const key = val.substring(1, val.length - 1); return variables[key][side]; } return val; } const variables = { SIDE: { client: "client", server: "server" }, MINECRAFT_JAR: { client: minecraft.getVersionJar(installProfile.minecraft), server: minecraft.getVersionJar(installProfile.minecraft, "server") } }; if (installProfile.data) { for (const key in installProfile.data) { const { client, server } = installProfile.data[key]; variables[key] = { client: normalizePath(client), server: normalizePath(server) }; } } if (variables.INSTALLER) { variables.ROOT = { client: (0, import_path4.dirname)(variables.INSTALLER.client), server: (0, import_path4.dirname)(variables.INSTALLER.server) }; } const resolveOutputs = (proc, args) => { const original = proc.outputs ? Object.entries(proc.outputs).map(([k, v]) => ({ [normalizeVariable(k)]: normalizeVariable(v) })).reduce((a, b) => Object.assign(a, b), {}) : {}; for (const [key, val] of Object.entries(original)) { original[key] = val.replace(/'/g, ""); } const outputIndex = args.indexOf("--output") === -1 ? args.indexOf("--out-jar") : args.indexOf("--output"); const outputFile = outputIndex !== -1 ? args[outputIndex + 1] : void 0; if (outputFile && !original[outputFile]) { original[outputFile] = ""; } return original; }; const processors = (installProfile.processors || []).map((proc) => { const args = proc.args.map(normalizePath).map(normalizeVariable); return { ...proc, args, outputs: resolveOutputs(proc, args) }; }).filter((proc) => proc.sides ? proc.sides.indexOf(side) !== -1 : true); return processors; } function postProcess(processors, minecraft, javaOptions) { return new PostProcessingTask(processors, minecraft, javaOptions).startAndWait(); } function installByProfile(installProfile, minecraft, options = {}) { return installByProfileTask(installProfile, minecraft, options).startAndWait(); } function installByProfileTask(installProfile, minecraft, options = {}) { return (0, import_task4.task)("installByProfile", async function() { const minecraftFolder = import_core5.MinecraftFolder.from(minecraft); const processor = resolveProcessors(options.side || "client", installProfile, minecraftFolder); const versionJson = await (0, import_promises5.readFile)(minecraftFolder.getVersionJson(installProfile.version)).then((b) => b.toString()).then(JSON.parse); const installRequiredLibs = import_core5.Version.resolveLibraries(installProfile.libraries); await this.all(installRequiredLibs.map((lib) => new InstallLibraryTask(lib, minecraftFolder, options)), { throwErrorImmediately: options.throwErrorImmediately ?? false, getErrorMessage: (errs) => `Errors during install libraries at ${minecraftFolder.root}: ${errs.map(errorToString).join("\n")}` }); await this.yield(new PostProcessingTask(processor, minecraftFolder, options)); const libraries = import_core5.Version.resolveLibraries(versionJson.libraries); await this.all(libraries.map((lib) => new InstallLibraryTask(lib, minecraftFolder, options)), { throwErrorImmediately: options.throwErrorImmediately ?? false, getErrorMessage: (errs) => `Errors during install libraries at ${minecraftFolder.root}: ${errs.map(errorToString).join("\n")}` }); }); } var PostProcessBadJarError = class extends Error { constructor(jarPath, causeBy) { super(`Fail to post process bad jar: ${jarPath}`); this.jarPath = jarPath; this.causeBy = causeBy; } name = "PostProcessBadJarError"; }; var PostProcessNoMainClassError = class extends Error { constructor(jarPath) { super(`Fail to post process bad jar without main class: ${jarPath}`); this.jarPath = jarPath; } name = "PostProcessNoMainClassError"; }; var PostProcessFailedError = class extends Error { constructor(jarPath, commands, message) { super(message); this.jarPath = jarPath; this.commands = commands; } name = "PostProcessFailedError"; }; var PAUSEED = Symbol("PAUSED"); var PostProcessingTask = class extends import_task4.AbortableTask { constructor(processors, minecraft, java) { super(); this.processors = processors; this.minecraft = minecraft; this.java = java; this.param = processors; this._total = processors.length; } name = "postProcessing"; pointer = 0; _abort = () => { }; async findMainClass(lib) { var _a; let zip; let mainClass; try { zip = await (0, import_unzip2.open)(lib, { lazyEntries: true }); for await (const entry of (0, import_unzip2.walkEntriesGenerator)(zip)) { if (entry.fileName === "META-INF/MANIFEST.MF") { const content = await (0, import_unzip2.readEntry)(zip, entry).then((b) => b.toString()); mainClass = (_a = content.split("\n").map((l) => l.split(": ")).find((arr) => arr[0] === "Main-Class")) == null ? void 0 : _a[1].trim(); break; } } } catch (e) { throw new PostProcessBadJarError(lib, e); } finally { zip == null ? void 0 : zip.close(); } if (!mainClass) { throw new PostProcessNoMainClassError(lib); } return mainClass; } async isInvalid(outputs) { for (const [file, expect] of Object.entries(outputs)) { const sha1 = await (0, import_core.checksum)(file, "sha1").catch((e) => ""); if (!sha1) return true; if (!expect) return false; const expected = expect.replace(/'/g, ""); if (expected !== sha1) { return true; } } return false; } async postProcess(mc, proc, javaOptions) { const jarRealPath = mc.getLibraryByPath(import_core5.LibraryInfo.resolve(proc.jar).path); const mainClass = await this.findMainClass(jarRealPath); this._to = proc.jar; const cp = [...proc.classpath, proc.jar].map(import_core5.LibraryInfo.resolve).map((p) => mc.getLibraryByPath(p.path)).join(import_path4.delimiter); const cmd = ["-cp", cp, mainClass, ...proc.args]; try { await new Promise((resolve2, reject) => { const process2 = ((javaOptions == null ? void 0 : javaOptions.spawn) ?? import_child_process2.spawn)(javaOptions.java ?? "java", cmd); waitProcess(process2).then(resolve2, reject); this._abort = () => { reject(PAUSEED); process2.kill(1); }; }); } catch (e) { if (typeof e === "string") { throw new PostProcessFailedError(proc.jar, [javaOptions.java ?? "java", ...cmd], e); } throw e; } if (proc.outputs && await this.isInvalid(proc.outputs)) { throw new PostProcessFailedError(proc.jar, [javaOptions.java ?? "java", ...cmd], "Validate the output of process failed!"); } } async process() { for (; this.pointer < this.processors.length; this.pointer++) { const proc = this.processors[this.pointer]; if (this.isCancelled) { throw new import_task4.CancelledError(); } if (this.isPaused) { throw PAUSEED; } if (!proc.outputs || await this.isInvalid(proc.outputs)) { await this.postProcess(this.minecraft, proc, this.java); } if (this.isCancelled) { throw new import_task4.CancelledError(); } if (this.isPaused) { throw PAUSEED; } this._progress = this.pointer; this.update(1); } } async abort(isCancelled) { this._abort(); } isAbortedError(e) { return e === PAUSEED; } }; // forge.ts var DEFAULT_FORGE_MAVEN = "http://files.minecraftforge.net/maven"; var DownloadForgeInstallerTask = class extends DownloadTask { installJarPath; constructor(forgeVersion, installer, minecraft, options) { var _a; const path = installer ? installer.path : `net/minecraftforge/forge/${forgeVersion}/forge-${forgeVersion}-installer.jar`; let url; if (installer) { try { const parsedUrl = new URL(path); url = parsedUrl.toString(); } catch (e) { const forgeMavenPath = path.replace("/maven", "").replace("maven", ""); url = joinUrl(DEFAULT_FORGE_MAVEN, forgeMavenPath); } } else { const forgeMavenPath = path.replace("/maven", "").replace("maven", ""); url = joinUrl(DEFAULT_FORGE_MAVEN, forgeMavenPath); } const library = import_core6.Version.resolveLibrary({ name: `net.minecraftforge:forge:${forgeVersion}:installer`, downloads: { artifact: { url, path: `net/minecraftforge/forge/${forgeVersion}/forge-${forgeVersion}-installer.jar`, size: -1, sha1: (installer == null ? void 0 : installer.sha1) || "" } } }); const mavenHost = options.mavenHost ? normalizeArray(options.mavenHost) : []; if (mavenHost.indexOf(DEFAULT_FORGE_MAVEN) === -1) { mavenHost.push(DEFAULT_FORGE_MAVEN); } const urls = resolveLibraryDownloadUrls(library, { ...options, mavenHost }); const installJarPath = minecraft.getLibraryByPath(library.path); super({ url: urls, destination: installJarPath, validator: (installer == null ? void 0 : installer.sha1) ? ((_a = options.checksumValidatorResolver) == null ? void 0 : _a.call(options, { algorithm: "sha1", hash: installer == null ? void 0 : installer.sha1 })) || { algorithm: "sha1", hash: installer == null ? void 0 : installer.sha1 } : new ZipValidator(), agent: options.agent, skipPrevalidate: options.skipPrevalidate, skipRevalidate: options.skipRevalidate }); this.installJarPath = installJarPath; this.name = "downloadInstaller"; this.param = { version: forgeVersion }; } }; function getLibraryPathWithoutMaven(mc, name) { return mc.getLibraryByPath(name.substring(name.indexOf("/") + 1)); } function extractEntryTo(zip, e, dest) { return (0, import_unzip3.openEntryReadStream)(zip, e).then((stream) => (0, import_promises7.pipeline)(stream, (0, import_fs.createWriteStream)(dest))); } async function installLegacyForgeFromZip(zip, entries, profile, mc, jarFilePath, options) { const versionJson = profile.versionInfo; if (!versionJson) { throw new Error(`Malform legacy installer json ${profile.version}`); } versionJson.id = options.versionId || versionJson.id; versionJson.inheritsFrom = options.inheritsFrom || versionJson.inheritsFrom; const rootPath = mc.getVersionRoot(versionJson.id); const versionJsonPath = (0, import_path5.join)(rootPath, `${versionJson.id}.json`); await ensureFile(versionJsonPath); const forgeLib = versionJson.libraries.find((l) => l.name.startsWith("net.minecraftforge:forge") || l.name.startsWith("net.minecraftforge:minecraftforge")); if (!forgeLib) { throw new BadForgeInstallerJarError(jarFilePath); } const library = import_core6.LibraryInfo.resolve(forgeLib); const jarPath = mc.getLibraryByPath(library.path); await ensureFile(jarPath); await Promise.all([ (0, import_promises6.writeFile)(versionJsonPath, JSON.stringify(versionJson, void 0, 4)), extractEntryTo(zip, entries.legacyUniversalJar, jarPath) ]); return versionJson.id; } async function unpackForgeInstaller(zip, entries, forgeVersion, profile, mc, jarPath, options) { const versionJson = await (0, import_unzip3.readEntry)(zip, entries.versionJson).then((b) => b.toString()).then(JSON.parse); versionJson.id = options.versionId || versionJson.id; versionJson.inheritsFrom = options.inheritsFrom || versionJson.inheritsFrom; const rootPath = mc.getVersionRoot(versionJson.id); const versionJsonPath = (0, import_path5.join)(rootPath, `${versionJson.id}.json`); const installJsonPath = (0, import_path5.join)(rootPath, "install_profile.json"); const dataRoot = (0, import_path5.dirname)(jarPath); const unpackData = (entry) => { promises.push(extractEntryTo(zip, entry, (0, import_path5.join)(dataRoot, entry.fileName.substring("data/".length)))); }; await ensureFile(versionJsonPath); const promises = []; if (entries.forgeUniversalJar) { promises.push(extractEntryTo(zip, entries.forgeUniversalJar, getLibraryPathWithoutMaven(mc, entries.forgeUniversalJar.fileName))); } if (!profile.data) { profile.data = {}; } const installerMaven = `net.minecraftforge:forge:${forgeVersion}:installer`; profile.data.INSTALLER = { client: `[${installerMaven}]`, server: `[${installerMaven}]` }; if (entries.serverLzma) { const serverMaven = `net.minecraftforge:forge:${forgeVersion}:serverdata@lzma`; profile.data.BINPATCH.server = `[${serverMaven}]`; const serverBinPath = mc.getLibraryByPath(import_core6.LibraryInfo.resolve(serverMaven).path); await ensureFile(serverBinPath); promises.push(extractEntryTo(zip, entries.serverLzma, serverBinPath)); } if (entries.clientLzma) { const clientMaven = `net.minecraftforge:forge:${forgeVersion}:clientdata@lzma`; profile.data.BINPATCH.client = `[${clientMaven}]`; const clientBinPath = mc.getLibraryByPath(import_core6.LibraryInfo.resolve(clientMaven).path); await ensureFile(clientBinPath); promises.push(extractEntryTo(zip, entries.clientLzma, clientBinPath)); } if (entries.forgeJar) { promises.push(extractEntryTo(zip, entries.forgeJar, getLibraryPathWithoutMaven(mc, entries.forgeJar.fileName))); } if (entries.runBat) { unpackData(entries.runBat); } if (entries.runSh) { unpackData(entries.runSh); } if (entries.winArgs) { unpackData(entries.winArgs); } if (entries.unixArgs) { unpackData(entries.unixArgs); } if (entries.userJvmArgs) { unpackData(entries.userJvmArgs); } promises.push( (0, import_promises6.writeFile)(installJsonPath, JSON.stringify(profile)), (0, import_promises6.writeFile)(versionJsonPath, JSON.stringify(versionJson)) ); await Promise.all(promises); return versionJson.id; } function isLegacyForgeInstallerEntries(entries) { return !!entries.legacyUniversalJar && !!entries.installProfileJson; } function isForgeInstallerEntries(entries) { return !!entries.installProfileJson && !!entries.versionJson; } async function walkForgeInstallerEntries(zip, forgeVersion) { const [forgeJar, forgeUniversalJar, clientLzma, serverLzma, installProfileJson, versionJson, legacyUniversalJar, runSh, runBat, unixArgs, userJvmArgs, winArgs] = await (0, import_unzip3.filterEntries)(zip, [ `maven/net/minecraftforge/forge/${forgeVersion}/forge-${forgeVersion}.jar`, `maven/net/minecraftforge/forge/${forgeVersion}/forge-${forgeVersion}-universal.jar`, "data/client.lzma", "data/server.lzma", "install_profile.json", "version.json", (e) => e.fileName === `forge-${forgeVersion}-universal.jar` || e.fileName.startsWith("forge-") && e.fileName.endsWith("-universal.jar") || e.fileName.startsWith("minecraftforge-universal-"), // legacy installer format "data/run.sh", "data/run.bat", "data/unix_args.txt", "data/user_jvm_args.txt", "data/win_args.txt" ]); return { forgeJar, forgeUniversalJar, clientLzma, serverLzma, installProfileJson, versionJson, legacyUniversalJar, runSh, runBat, unixArgs, userJvmArgs, winArgs }; } var BadForgeInstallerJarError = class extends Error { constructor(jarPath, entry) { super(entry ? `Missing entry ${entry} in forge installer jar: ${jarPath}` : `Bad forge installer: ${jarPath}`); this.jarPath = jarPath; this.entry = entry; } name = "BadForgeInstallerJarError"; }; function installByInstallerTask(version, minecraft, options) { return (0, import_task5.task)("installForge", async function() { function getForgeArtifactVersion() { const [_, minor] = version.mcversion.split("."); const minorVersion = Number.parseInt(minor); if (minorVersion >= 7 && minorVersion <= 8) { return `${version.mcversion}-${version.version}-${version.mcversion}`; } if (version.version.startsWith(version.mcversion)) { return version.version; } return `${version.mcversion}-${version.version}`; } const forgeVersion = getForgeArtifactVersion(); const mc = import_core6.MinecraftFolder.from(minecraft); const jarPath = await this.yield(new DownloadForgeInstallerTask(forgeVersion, version.installer, mc, options).map(function() { return this.installJarPath; })); const zip = await (0, import_unzip3.open)(jarPath, { lazyEntries: true, autoClose: false }); const entries = await walkForgeInstallerEntries(zip, forgeVersion); if (!entries.installProfileJson) { throw new BadForgeInstallerJarError(jarPath, "install_profile.json"); } const profile = await (0, import_unzip3.readEntry)(zip, entries.installProfileJson).then((b) => b.toString()).then(JSON.parse); if (isForgeInstallerEntries(entries)) { const versionId = await unpackForgeInstaller(zip, entries, forgeVersion, profile, mc, jarPath, options); await this.concat(installByProfileTask(profile, minecraft, options)); return versionId; } else if (isLegacyForgeInstallerEntries(entries)) { return installLegacyForgeFromZip(zip, entries, profile, mc, jarPath, options); } else { throw new BadForgeInstallerJarError(jarPath); } }); } function installForge(version, minecraft, options) { return installForgeTask(version, minecraft, options).startAndWait(); } function installForgeTask(version, minecraft, options = {}) { return installByInstallerTask(version, minecraft, options); } async function getForgeVersionList(options = {}) { const mcversion = options.minecraft || ""; const url = mcversion === "" ? "http://files.minecraftforge.net/maven/net/minecraftforge/forge/index.html" : `http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_${mcversion}.html`; const response = await (0, import_undici4.request)(url, { dispatcher: options.dispatcher, maxRedirections: 3 }); const body = (0, import_forge_site_parser.parse)(await response.body.text()); return body; } // neoForged.ts var import_core7 = require("@xmcl/core"); var import_unzip4 = re