feat: add support for version older than 1.13
This commit is contained in:
parent
697ae0d040
commit
4b0410f4f3
8 changed files with 141 additions and 60 deletions
|
|
@ -1,17 +1,18 @@
|
|||
import path from "path";
|
||||
import { isNeeded } from "../utils/rules";
|
||||
import { Argument, Auth, PoolFile, Version } from "../utils/types";
|
||||
import { Argument, Auth, Version } from "../utils/types";
|
||||
import { getLibraries } from "./libraries";
|
||||
import { version as packageVersion } from "../../package.json";
|
||||
import { getArgument } from "./log4j";
|
||||
|
||||
// Fill templates
|
||||
function fillArguments(
|
||||
arg: string,
|
||||
versionManifest: Version,
|
||||
assetsPath: string,
|
||||
instancePath: string,
|
||||
librariesPath: string,
|
||||
classPaths: PoolFile[],
|
||||
classPaths: string,
|
||||
auth: Auth,
|
||||
) {
|
||||
const argumentsToFill = {
|
||||
|
|
@ -25,6 +26,7 @@ function fillArguments(
|
|||
"${version_type}": versionManifest.type,
|
||||
"${assets_index_name}": versionManifest.assets,
|
||||
"${assets_root}": assetsPath,
|
||||
"${game_assets}": assetsPath,
|
||||
"${auth_access_token}": auth.access_token,
|
||||
"${auth_session}": auth.access_token,
|
||||
"${auth_player_name}": auth.name,
|
||||
|
|
@ -44,11 +46,10 @@ function fillArguments(
|
|||
}
|
||||
}
|
||||
|
||||
console.log(arg)
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
// Check if each arg is needed and fill templates
|
||||
function parseArg(
|
||||
this: string[],
|
||||
arg: Argument,
|
||||
|
|
@ -56,7 +57,7 @@ function parseArg(
|
|||
assetsPath: string,
|
||||
instancePath: string,
|
||||
librariesPath: string,
|
||||
classPaths: PoolFile[],
|
||||
classPaths: string,
|
||||
auth: Auth,
|
||||
) {
|
||||
if (isNeeded(arg)) {
|
||||
|
|
@ -133,16 +134,15 @@ export async function generateLaunchArguments(
|
|||
customGameArgs?: string;
|
||||
},
|
||||
) {
|
||||
// Log4j /!\
|
||||
// Replace templates /!\
|
||||
|
||||
const jvm: string[] = [];
|
||||
const game: string[] = [];
|
||||
let game: string[] = [];
|
||||
|
||||
const versionJar = path.join(versionRoot, `${versionManifest.id}.jar`);
|
||||
|
||||
const classPaths = generateClassPaths(
|
||||
versionManifest,
|
||||
librariesPath,
|
||||
path.join(versionRoot, `${versionManifest.id}.jar`),
|
||||
versionJar,
|
||||
);
|
||||
|
||||
function p(args: string[], arg: Argument) {
|
||||
|
|
@ -157,11 +157,33 @@ export async function generateLaunchArguments(
|
|||
]);
|
||||
}
|
||||
|
||||
for (const arg of versionManifest.arguments.jvm) {
|
||||
p(jvm, arg);
|
||||
}
|
||||
for (const arg of versionManifest.arguments.game) {
|
||||
p(game, arg);
|
||||
if (versionManifest.arguments) {
|
||||
for (const arg of versionManifest.arguments.jvm) {
|
||||
p(jvm, arg);
|
||||
}
|
||||
for (const arg of versionManifest.arguments.game) {
|
||||
p(game, arg);
|
||||
}
|
||||
} else {
|
||||
console.log(versionManifest.minecraftArguments)
|
||||
game = fillArguments(
|
||||
versionManifest.minecraftArguments,
|
||||
versionManifest,
|
||||
assetsPath,
|
||||
instancePath,
|
||||
librariesPath,
|
||||
classPaths,
|
||||
auth,
|
||||
).split(" ");
|
||||
|
||||
jvm.push(`-Djava.library.path=${path.join(instancePath, "natives")}`);
|
||||
jvm.push(`-Dminecraft.launcher.brand=nlk`);
|
||||
jvm.push(`-Dminecraft.launcher.version=${packageVersion}`);
|
||||
jvm.push(
|
||||
`-Dminecraft.client.jar=${path.join(versionRoot, `${versionManifest.id}.jar`)}`,
|
||||
);
|
||||
jvm.push("-cp");
|
||||
jvm.push(classPaths)
|
||||
}
|
||||
|
||||
if (options.customJvmArgs !== "") {
|
||||
|
|
@ -178,21 +200,26 @@ export async function generateLaunchArguments(
|
|||
|
||||
const log4j = await getArgument(versionManifest, versionRoot);
|
||||
|
||||
let launchArguments = [
|
||||
...jvm,
|
||||
`-Xms${options.minRam || "2G"}`,
|
||||
`-Xmx${options.maxRam || "2G"}`,
|
||||
"-XX:+UnlockExperimentalVMOptions",
|
||||
"-XX:+UseG1GC",
|
||||
"-XX:G1NewSizePercent=20",
|
||||
"-XX:G1ReservePercent=20",
|
||||
"-XX:MaxGCPauseMillis=50",
|
||||
"-XX:G1HeapRegionSize=32M",
|
||||
]
|
||||
|
||||
if (log4j) {
|
||||
launchArguments.push(log4j)
|
||||
}
|
||||
|
||||
launchArguments = [...launchArguments, versionManifest.mainClass, ...game,]
|
||||
|
||||
return {
|
||||
command: javaExecutable,
|
||||
args: [
|
||||
...jvm,
|
||||
`-Xms${options.minRam || "2G"}`,
|
||||
`-Xmx${options.maxRam || "2G"}`,
|
||||
"-XX:+UnlockExperimentalVMOptions",
|
||||
"-XX:+UseG1GC",
|
||||
"-XX:G1NewSizePercent=20",
|
||||
"-XX:G1ReservePercent=20",
|
||||
"-XX:MaxGCPauseMillis=50",
|
||||
"-XX:G1HeapRegionSize=32M",
|
||||
log4j,
|
||||
versionManifest.mainClass,
|
||||
...game,
|
||||
],
|
||||
args: launchArguments,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { version as packageVersion } from "../../package.json";
|
||||
import { spawn } from "child_process";
|
||||
import { spawn, spawnSync } from "child_process";
|
||||
import { LaunchArguments } from "../utils/types";
|
||||
|
||||
export function launch(
|
||||
|
|
@ -11,6 +11,7 @@ export function launch(
|
|||
console.log(
|
||||
`[nlk ${packageVersion}] Launching command : ${launchArguments.command} ${launchArguments.args.join(" ")}`,
|
||||
);
|
||||
|
||||
const process = spawn(launchArguments.command, launchArguments.args, {
|
||||
detached,
|
||||
cwd: gameRoot,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ 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)) {
|
||||
if (isNeeded(library) && library.downloads.artifact) {
|
||||
libs.push({
|
||||
url: library.downloads.artifact.url,
|
||||
path: path.join(librariesRoot, library.downloads.artifact.path),
|
||||
|
|
|
|||
|
|
@ -5,37 +5,42 @@ import { exists } from "../utils/fs";
|
|||
import { hash } from "crypto";
|
||||
import fs from "fs/promises";
|
||||
|
||||
async function patchFileLog4j(versionManifest: Version, xmlDestination: string) {
|
||||
const original = await fs.readFile(xmlDestination, {encoding: "utf-8"})
|
||||
async function patchFileLog4j(xmlDestination: string) {
|
||||
const original = await fs.readFile(xmlDestination, { encoding: "utf-8" })
|
||||
await fs.writeFile(xmlDestination, original.replace("<LegacyXMLLayout />", ""))
|
||||
}
|
||||
|
||||
export async function getArgument(
|
||||
versionManifest: Version,
|
||||
destination: string,
|
||||
) {
|
||||
const xmlDestination = path.join(
|
||||
destination,
|
||||
versionManifest.logging.client.file.id,
|
||||
);
|
||||
if (!(await exists(xmlDestination))) {
|
||||
await downloadFile({
|
||||
url: versionManifest.logging.client.file.url,
|
||||
path: xmlDestination,
|
||||
});
|
||||
): Promise<string | null> {
|
||||
if (versionManifest.logging?.client) {
|
||||
const xmlDestination = path.join(
|
||||
destination,
|
||||
versionManifest.logging.client.file.id,
|
||||
);
|
||||
if (!(await exists(xmlDestination))) {
|
||||
await downloadFile({
|
||||
url: versionManifest.logging.client.file.url,
|
||||
path: xmlDestination,
|
||||
});
|
||||
|
||||
if (
|
||||
hash("sha1", await fs.readFile(xmlDestination, { encoding: "utf-8" })) !==
|
||||
versionManifest.logging.client.file.sha1
|
||||
) {
|
||||
throw new Error("Wrong hash for log4j's xml config");
|
||||
if (
|
||||
hash("sha1", await fs.readFile(xmlDestination, { encoding: "utf-8" })) !==
|
||||
versionManifest.logging.client.file.sha1
|
||||
) {
|
||||
throw new Error("Wrong hash for log4j's xml config");
|
||||
}
|
||||
|
||||
await patchFileLog4j(xmlDestination)
|
||||
|
||||
return versionManifest.logging.client.argument.replace(
|
||||
"${path}",
|
||||
xmlDestination,
|
||||
);
|
||||
}
|
||||
|
||||
await patchFileLog4j(versionManifest, xmlDestination)
|
||||
}
|
||||
|
||||
return versionManifest.logging.client.argument.replace(
|
||||
"${path}",
|
||||
xmlDestination,
|
||||
);
|
||||
// if logging is not present it means that the verson is older than 1.7 hence the fix for log4j isn't needed
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {
|
|||
} from "../utils/types";
|
||||
import * as core from "../core";
|
||||
import path from "path";
|
||||
import { os, arch } from "../utils/systemInfo";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { InstallError, LaunchError } from "../utils/errors";
|
||||
import { installForge } from "../core/modloaders";
|
||||
|
|
@ -149,7 +148,7 @@ export class Instance extends EventEmitter<InstanceEvents> {
|
|||
|
||||
try {
|
||||
const nativesDownloader = await core.NativesDownloader(
|
||||
this.instanceLocation,
|
||||
path.join(this.instanceLocation, "natives"),
|
||||
this.versionManifest,
|
||||
);
|
||||
nativesDownloader.on("completed", () => {
|
||||
|
|
|
|||
|
|
@ -116,10 +116,11 @@ export type Library = {
|
|||
};
|
||||
|
||||
export type Version = {
|
||||
arguments: {
|
||||
arguments?: {
|
||||
game: Argument[];
|
||||
jvm: Argument[];
|
||||
};
|
||||
minecraftArguments: string;
|
||||
assetIndex: {
|
||||
id: string;
|
||||
sha1: string;
|
||||
|
|
@ -141,7 +142,7 @@ export type Version = {
|
|||
majorVersion: number;
|
||||
};
|
||||
libraries: Library[];
|
||||
logging: {
|
||||
logging?: {
|
||||
client: {
|
||||
argument: string;
|
||||
file: { id: string; sha1: string; size: number; url: string };
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ export function unzipAll(from: string, to: string, filters: string[]) {
|
|||
for (const filter of filters) {
|
||||
if (entry.entryName.includes(filter)) {
|
||||
shouldSkip = true;
|
||||
console.log("skipping", entry.entryName);
|
||||
}
|
||||
}
|
||||
if (shouldSkip) continue;
|
||||
|
|
|
|||
49
tests/instance/modloaders/oldForge.test.ts
Normal file
49
tests/instance/modloaders/oldForge.test.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { test } from "vitest";
|
||||
import { Instance, offlineAuth, RuntimeManager, getJavaComponent } from "../../../dist/index.js";
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
|
||||
const gameRoot = path.join(import.meta.dirname, "..", "temp");
|
||||
await fs.rm(gameRoot, { recursive: true, force: true });
|
||||
await fs.mkdir(gameRoot, { recursive: true });
|
||||
|
||||
test("launch game", { timeout: 0 }, async () => {
|
||||
const instance = new Instance();
|
||||
const javaManager = new RuntimeManager(path.join(gameRoot, "java"));
|
||||
|
||||
javaManager.on("progress", console.log)
|
||||
|
||||
const java = await javaManager.use(await getJavaComponent("1.12.2"))
|
||||
|
||||
const auth = offlineAuth("player");
|
||||
|
||||
instance.setVersion("1.12.2");
|
||||
instance.setPaths(gameRoot);
|
||||
instance.setAuth(auth);
|
||||
instance.setJavaExecutable(java)
|
||||
instance.setModLoader("forge", "14.23.5.2864");
|
||||
|
||||
instance.on("progress", console.log);
|
||||
|
||||
await instance.install();
|
||||
const p = await instance.launch();
|
||||
|
||||
p.stdout.on("data", (d: Buffer) => {
|
||||
console.log(d.toString());
|
||||
});
|
||||
|
||||
p.stderr.on("data", (d: Buffer) => {
|
||||
console.log(d.toString());
|
||||
});
|
||||
|
||||
await new Promise<void>((res) => {
|
||||
p.on("error", (d: Buffer) => {
|
||||
console.log(d.toString());
|
||||
res();
|
||||
});
|
||||
p.on("close", () => {
|
||||
console.log("closed");
|
||||
res();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue