feat: better error handling

This commit is contained in:
HerozDotExe 2025-12-11 21:41:21 +01:00
parent 5ed1a240c1
commit 22d68bd378
4 changed files with 203 additions and 80 deletions

View file

@ -1,3 +1,4 @@
import { version as packageVersion } from "../../package.json";
import { spawn } from "child_process";
import { LaunchArguments } from "../utils/types";
@ -6,9 +7,9 @@ export function launch(
gameRoot: string,
detached = false,
) {
console.log(`Working directory : ${gameRoot}`);
console.log(`[nlk ${packageVersion}] Working directory : ${gameRoot}`);
console.log(
`Launching command : ${launchArguments.command} ${launchArguments.args.join(" ")}`,
`[nlk ${packageVersion}] Launching command : ${launchArguments.command} ${launchArguments.args.join(" ")}`,
);
const process = spawn(launchArguments.command, launchArguments.args, {
detached,

View file

@ -3,6 +3,7 @@ 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";
function getJavaOs() {
switch (os()) {
@ -38,7 +39,13 @@ function getJavaOs() {
}
interface InstanceEvents {
progress: [type: string, done: number, total: number, doneSize: number, totalSize: number];
progress: [
type: string,
done: number,
total: number,
doneSize: number,
totalSize: number,
];
}
export class Instance extends EventEmitter<InstanceEvents> {
@ -48,11 +55,13 @@ export class Instance extends EventEmitter<InstanceEvents> {
paths: Paths;
args: { java: string; game: string };
versionManifest: Version;
javaLocation?: string
javaLocation?: string;
ready: boolean;
constructor() {
super();
this.args = { java: "", game: "" };
this.ready = false;
}
setVersion(version: string) {
@ -97,93 +106,152 @@ export class Instance extends EventEmitter<InstanceEvents> {
this.args.game = game || "";
}
async install() {
async initialize() {
this.versionManifest = await core.version.getVersionManifest(
this.version,
this.paths.version,
);
await core.version.downloadJar(this.versionManifest, this.paths.version);
const librariesDownloader = await core.LibrariesDownloader(
this.paths.libraries,
this.versionManifest,
);
librariesDownloader.on("completed", () => {
this.emit(
"progress",
"libraries",
librariesDownloader.done,
librariesDownloader.total,
librariesDownloader.doneSize,
librariesDownloader.totalSize,
);
});
await librariesDownloader.run();
const assetsDownloader = await core.AssetsDownloader(
this.paths.assets,
this.versionManifest,
);
assetsDownloader.on("completed", () => {
this.emit(
"progress",
"assets",
assetsDownloader.done,
assetsDownloader.total,
assetsDownloader.doneSize,
assetsDownloader.totalSize,
);
});
await assetsDownloader.run();
const nativesDownloader = await core.NativesDownloader(
this.paths.natives,
this.versionManifest,
);
nativesDownloader.on("completed", () => {
this.emit(
"progress",
"natives",
nativesDownloader.done,
nativesDownloader.total,
nativesDownloader.doneSize,
nativesDownloader.totalSize,
);
});
await nativesDownloader.run();
this.javaLocation = path.join(this.paths.javaRoot, this.versionManifest.javaVersion.component)
const javaDownloader = await core.java.JavaDownloader(
getJavaOs(),
this.versionManifest.javaVersion.component,
this.javaLocation = path.join(
this.paths.javaRoot,
this.versionManifest.javaVersion.component,
);
javaDownloader.on("completed", () => {
this.emit(
"progress",
"java",
javaDownloader.done,
javaDownloader.total,
javaDownloader.doneSize,
javaDownloader.totalSize,
}
async install() {
try {
await this.initialize();
this.ready = true;
await core.version.downloadJar(this.versionManifest, this.paths.version);
} catch (original) {
const error = new InstallError("version", original);
error.throw();
}
try {
const librariesDownloader = await core.LibrariesDownloader(
this.paths.libraries,
this.versionManifest,
);
});
await javaDownloader.run();
librariesDownloader.on("completed", () => {
this.emit(
"progress",
"libraries",
librariesDownloader.done,
librariesDownloader.total,
librariesDownloader.doneSize,
librariesDownloader.totalSize,
);
});
await librariesDownloader.run();
} catch (original) {
const error = new InstallError("libraries", original);
error.throw();
}
try {
const assetsDownloader = await core.AssetsDownloader(
this.paths.assets,
this.versionManifest,
);
assetsDownloader.on("completed", () => {
this.emit(
"progress",
"assets",
assetsDownloader.done,
assetsDownloader.total,
assetsDownloader.doneSize,
assetsDownloader.totalSize,
);
});
await assetsDownloader.run();
} catch (original) {
const error = new InstallError("assets", original);
error.throw();
}
try {
const nativesDownloader = await core.NativesDownloader(
this.paths.natives,
this.versionManifest,
);
nativesDownloader.on("completed", () => {
this.emit(
"progress",
"natives",
nativesDownloader.done,
nativesDownloader.total,
nativesDownloader.doneSize,
nativesDownloader.totalSize,
);
});
await nativesDownloader.run();
} catch (original) {
const error = new InstallError("natives", original);
error.throw();
}
try {
const javaDownloader = await core.java.JavaDownloader(
getJavaOs(),
this.versionManifest.javaVersion.component,
this.paths.javaRoot,
);
javaDownloader.on("completed", () => {
this.emit(
"progress",
"java",
javaDownloader.done,
javaDownloader.total,
javaDownloader.doneSize,
javaDownloader.totalSize,
);
});
await javaDownloader.run();
} catch (original) {
const error = new InstallError("java", original);
error.throw();
}
}
async launch() {
const args = await core.arguments.generateLaunchArguments(
await core.version.getVersionManifest(this.version, this.paths.version),
this.javaLocation,
this.paths.root,
this.paths.version,
this.auth,
{ customGameArgs: this.args.game, customJvmArgs: this.args.java },
);
try {
if (!this.ready) {
try {
await this.initialize();
} catch (original) {
const error = new InstallError("version", original);
error.throw();
}
}
const process = core.launch(args, this.paths.root);
const args = await core.arguments.generateLaunchArguments(
await core.version.getVersionManifest(this.version, this.paths.version),
this.javaLocation,
this.paths.root,
this.paths.version,
this.auth,
{ customGameArgs: this.args.game, customJvmArgs: this.args.java },
);
return process;
const process = core.launch(args, this.paths.root);
return process;
} catch (original) {
const error = new LaunchError(
{
version: this.version,
versionPath: this.paths.version,
javaLocation: this.javaLocation,
rootPath: this.paths.root,
auth: this.auth,
customGameArgs: this.args.game,
customJvmArgs: this.args.java,
},
original,
);
error.throw();
}
}
}

44
src/utils/errors.ts Normal file
View file

@ -0,0 +1,44 @@
import { version as packageVersion } from "../../package.json";
import { LaunchErrorInfos } from "./types";
export class InstallError extends Error {
step: string;
original: Error;
constructor(step: string, original: Error) {
super();
this.step = step;
this.original = original;
}
throw() {
console.error(
`[nlk ${packageVersion}] Error occured when installing ${this.step}: ${this.original.message}`,
);
console.error(`[nlk ${packageVersion}] Original error is:`, this.original);
throw this;
}
}
export class LaunchError extends Error {
infos: LaunchErrorInfos;
original: Error;
constructor(
infos: LaunchErrorInfos,
original: Error,
) {
super();
this.infos = infos;
this.original = original;
}
throw() {
console.error(
`[nlk ${packageVersion}] Error occured when launching the game: ${this.original.message}`,
);
console.error(`[nlk ${packageVersion}] Original error is:`, this.original);
console.dir(this.infos);
throw this;
}
}

View file

@ -186,3 +186,13 @@ export type Paths = {
natives?: string
javaRoot?: string
}
export type LaunchErrorInfos = {
version: string;
versionPath: string;
javaLocation: string;
rootPath: string;
auth: Auth;
customGameArgs: string;
customJvmArgs: string;
}