diff --git a/src/core/arguments.ts b/src/core/arguments.ts index b8930af..24d19ba 100644 --- a/src/core/arguments.ts +++ b/src/core/arguments.ts @@ -163,7 +163,7 @@ export async function generateLaunchArguments( } else { console.log(versionManifest.minecraftArguments) game = fillArguments( - versionManifest.minecraftArguments, + versionManifest.minecraftArguments!, versionManifest, assetsPath, instancePath, diff --git a/src/core/libraries.ts b/src/core/libraries.ts index 3a04be9..363760b 100644 --- a/src/core/libraries.ts +++ b/src/core/libraries.ts @@ -9,12 +9,25 @@ export function getLibraries(versionManifest: Version, librariesRoot: string) { for (const key in versionManifest.libraries) { if (Object.prototype.hasOwnProperty.call(versionManifest.libraries, key)) { const library = versionManifest.libraries[key]; - if (isNeeded(library) && library.downloads.artifact) { - libs.push({ - url: library.downloads.artifact.url, - path: path.join(librariesRoot, library.downloads.artifact.path), - size: library.downloads.artifact.size, - }); + if (library.downloads) { + if (isNeeded(library) && library.downloads.artifact) { + libs.push({ + url: library.downloads.artifact.url, + path: path.join(librariesRoot, library.downloads.artifact.path), + size: library.downloads.artifact.size, + }); + } + } else { + // forge lib ? + if (!library.rules && !library.downloads && !library.natives) { + const parsedLib = library.name.split(":") + const libPath = path.join(parsedLib[0].replaceAll(".", "/"), parsedLib[1], parsedLib[2], `${parsedLib[1]}-${parsedLib[2]}.jar`) + libs.push({ + url: "", + path: path.join(librariesRoot, libPath), + size: 0, + }); + } } } } diff --git a/src/core/mergeManifests.ts b/src/core/mergeManifests.ts index 748b049..3ed5d36 100644 --- a/src/core/mergeManifests.ts +++ b/src/core/mergeManifests.ts @@ -3,36 +3,62 @@ import { Version } from "../utils/types"; export function mergeManifests(base: Version, layer: Version) { // Copy needed properties to base // Those properties should be enough for forge at least - base.mainClass = layer.mainClass; + const result = { ...base } + result.libraries = []; + result.mainClass = layer.mainClass; + console.log(base) - const librariesHashes = base.libraries.filter(lib => lib.downloads.artifact).map( - (lib) => { - return lib.downloads.artifact.sha1 - }, - ); + // const librariesNames = base.libraries.map( + // (lib) => { + // return lib.name + // }, + // ); - for (const lib of layer.libraries) { - if (!librariesHashes.includes(lib.downloads.artifact.sha1)) // remove duplicates - base.libraries.push(lib); + // for (const lib of layer.libraries) { + // // remove duplicates + // if (!librariesNames.includes(lib.name)) { + // base.libraries.push(lib); + // } + // } + + for (const bLib of base.libraries) { + let isInLayer = false + for (const lLib of layer.libraries) { + const lId = lLib.name.split(":")[1] + const bId = bLib.name.split(":")[1] + + if (lId === bId) { + isInLayer = true + break + } + } + + if (!isInLayer) { + result.libraries.push(bLib) + } + } + + for (const lLib of layer.libraries) { + result.libraries.push(lLib) } // modern versions - if (layer.arguments && base.arguments) { + if (layer.arguments && result.arguments) { if (layer.arguments.game) { for (const arg of layer.arguments.game) { - base.arguments.game.push(arg); + result.arguments.game.push(arg); } } if (layer.arguments.jvm) { for (const arg of layer.arguments.jvm) { - base.arguments.jvm.push(arg); + result.arguments.jvm.push(arg); } } } else { // older versions - base.minecraftArguments = layer.minecraftArguments + result.minecraftArguments = layer.minecraftArguments } - return base; + return result; } diff --git a/src/core/modloaders.ts b/src/core/modloaders.ts index 6a8169d..185226b 100644 --- a/src/core/modloaders.ts +++ b/src/core/modloaders.ts @@ -5,8 +5,18 @@ import { LauncherProfiles, Modloader, Version } from "../utils/types"; import fs from "fs/promises"; import { execSync, spawn } from "child_process"; import { version as packageVersion } from "../../package.json"; -import { readJson } from "../utils/fs"; +import { ensureDir, readJson } from "../utils/fs"; import { InstallError } from "../utils/errors"; +import AdmZip from "adm-zip"; + +const versionWithDoubleName = ["1.9.4", "1.9.0", "1.8.9", "1.8.8", "1.8", "1.7.10"] +function fixVersionWithDoubleName(version: string, modloader: Modloader) { + if (versionWithDoubleName.includes(version)) { + // these versions have a different file name on forge's maven + modloader.version = modloader.version + `-${version}` + } + return modloader +} async function downloadJar( minecraftVersion: string, @@ -15,6 +25,7 @@ async function downloadJar( destination: string, ) { let filePath = ""; + modloader = fixVersionWithDoubleName(minecraftVersion, modloader) try { if (modloader.name === "forge") { if (type === "universal") { @@ -58,17 +69,18 @@ export function runForgeInstaller( javaExecutable: string, forgeInstallerPath: string, fakeLauncher: string, + installType: "Client" | "Server" ) { console.log(execSync(`${javaExecutable} -version`).toString()); console.log( - `[nlk ${packageVersion}] Running forge installer : "${javaExecutable} -jar ${forgeInstallerPath} --installClient ${path.join(fakeLauncher)}"`, + `[nlk ${packageVersion}] Running forge installer : "${javaExecutable} -jar ${forgeInstallerPath} --install${installType} ${path.join(fakeLauncher)}"`, ); const forgeInstaller = spawn(javaExecutable, [ "-jar", forgeInstallerPath, - "--installClient", + `--install${installType}`, path.join(fakeLauncher), - ]); + ], { cwd: fakeLauncher }); forgeInstaller.stdout.on("data", (data: Buffer) => console.log(data.toString()), @@ -112,34 +124,36 @@ export async function installForge( versionsPath: string ) { const tempFolder = await getTempFolder("forge"); + // Download and extract installer file + const forgeInstallerPath = await downloadJar( + versionManifest.id, + modloader, + "installer", + tempFolder, + ); + + const fakeLauncher = path.join(tempFolder, "fakeLauncher"); + + await fs.mkdir(fakeLauncher); + await fs.writeFile(path.join(fakeLauncher, "launcher_profiles.json"), `{"profiles":{}}`); + + const modernInstaller = isModernForge(versionManifest.id) + + await runForgeInstaller(javaExecutable, forgeInstallerPath, fakeLauncher, modernInstaller ? "Client" : "Server"); + + // Copy libraries and versions files + for (const file of await fs.readdir(path.join(fakeLauncher, "libraries"))) { + await fs.cp( + path.join(fakeLauncher, "libraries", file), + path.join(librariesPath, file), + { + recursive: true, + }, + ); + } if (isModernForge(versionManifest.id)) { - // Download and extract installer file - const forgeInstallerPath = await downloadJar( - versionManifest.id, - modloader, - "installer", - tempFolder, - ); - - const fakeLauncher = path.join(tempFolder, "fakeLauncher"); - - await fs.mkdir(fakeLauncher); - await fs.writeFile(path.join(fakeLauncher, "launcher_profiles.json"), "{}"); - - await runForgeInstaller(javaExecutable, forgeInstallerPath, fakeLauncher); - - // Copy libraries and versions files - for (const file of await fs.readdir(path.join(fakeLauncher, "libraries"))) { - await fs.cp( - path.join(fakeLauncher, "libraries", file), - path.join(librariesPath, file), - { - recursive: true, - }, - ); - } - + // Modern forge (version >= 1.12.2) const fakeLauncherProfiles = await readJson( path.join(fakeLauncher, "launcher_profiles.json"), ); @@ -152,5 +166,23 @@ export async function installForge( path.join(versionsPath, originalVersionId), { recursive: true }, ); + } else { + // Older versions (from 1.5.2 to 1.12.2, even older versions are not supported) + //await runForgeInstaller(javaExecutable, forgeInstallerPath, fakeLauncher, "Server"); + + const universalPath = path.join(fakeLauncher, `forge-${versionManifest.id}-${modloader.version}-universal.jar`) + + // Copy forge jar + await ensureDir(path.join(root, "libraries", "net", "minecraftforge", "forge", `${versionManifest.id}-${modloader.version}`), true) + await fs.cp( + universalPath, + path.join(root, "libraries", "net", "minecraftforge", "forge", `${versionManifest.id}-${modloader.version}`, `forge-${versionManifest.id}-${modloader.version}.jar`), + { recursive: true }, + ); + + // Copy version json + const zip = new AdmZip(universalPath) + zip.extractEntryTo("version.json", path.join(versionsPath, `${versionManifest.id}-forge-${modloader.version}`)) + await fs.rename(path.join(versionsPath, `${versionManifest.id}-forge-${modloader.version}`, "version.json"), path.join(versionsPath, `${versionManifest.id}-forge-${modloader.version}`, `${versionManifest.id}-forge-${modloader.version}.json`)) } } diff --git a/src/utils/types.ts b/src/utils/types.ts index 20ca5f1..20a1564 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -93,13 +93,13 @@ type Rules = { export type Argument = | { - rules: Rules[]; - value: string[] | string; - } + rules: Rules[]; + value: string[] | string; + } | string; export type Library = { - downloads: { + downloads?: { artifact: { path: string; sha1: string; @@ -120,7 +120,7 @@ export type Version = { game: Argument[]; jvm: Argument[]; }; - minecraftArguments: string; + minecraftArguments?: string; assetIndex: { id: string; sha1: string;