feat: add support for older forge (down to 1.7.10)

This commit is contained in:
HerozDotExe 2026-03-16 17:45:39 +01:00
parent 8e6408fb7b
commit 8bcf3680c0
5 changed files with 127 additions and 56 deletions

View file

@ -163,7 +163,7 @@ export async function generateLaunchArguments(
} else {
console.log(versionManifest.minecraftArguments)
game = fillArguments(
versionManifest.minecraftArguments,
versionManifest.minecraftArguments!,
versionManifest,
assetsPath,
instancePath,

View file

@ -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,
});
}
}
}
}

View file

@ -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;
}

View file

@ -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<LauncherProfiles>(
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`))
}
}

View file

@ -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;