@xmcl/installer
Version:
The installers of Minecraft/Forge/Fabric/Liteloader/Quilt
1,254 lines (1,241 loc) • 80.6 kB
JavaScript
"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