minecraft-java-core
Version:
A library starting minecraft game NW.js and Electron.js
270 lines • 12.9 kB
JavaScript
/**
* This code is distributed under the CC-BY-NC 4.0 license:
* https://creativecommons.org/licenses/by-nc/4.0/
*
* Original author: Luuxis
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const semver_1 = __importDefault(require("semver"));
const Index_js_1 = require("../utils/Index.js");
/**
* Maps the Node.js process.platform values to Mojang's library folders.
*/
const MOJANG_LIBRARY_MAP = {
win32: 'windows',
darwin: 'osx',
linux: 'linux'
};
/**
* Builds and organizes JVM and game arguments required to launch Minecraft,
* including optional loader (e.g., Forge) arguments.
*/
class MinecraftArguments {
constructor(options) {
this.options = options;
this.authenticator = options.authenticator;
}
/**
* Gathers all arguments (game, JVM, classpath) and returns them for launching.
* @param versionJson The Minecraft version JSON.
* @param loaderJson An optional loader JSON (Forge, Fabric, etc.).
*/
async GetArguments(versionJson, loaderJson) {
const gameArguments = await this.GetGameArguments(versionJson, loaderJson);
const jvmArguments = await this.GetJVMArguments(versionJson);
const classpathData = await this.GetClassPath(versionJson, loaderJson);
return {
game: gameArguments,
jvm: jvmArguments,
classpath: classpathData.classpath,
mainClass: classpathData.mainClass
};
}
/**
* Builds the Minecraft game arguments, injecting authentication tokens,
* user info, and any loader arguments if present.
* @param versionJson The Minecraft version JSON.
* @param loaderJson The loader JSON (e.g., Forge) if applicable.
*/
async GetGameArguments(versionJson, loaderJson) {
// For older MC versions, arguments may be in `minecraftArguments` instead of `arguments.game`
let gameArgs = versionJson.minecraftArguments
? versionJson.minecraftArguments.split(' ')
: versionJson.arguments?.game ?? [];
// Merge loader's Minecraft arguments if provided
if (loaderJson) {
const loaderGameArgs = loaderJson.minecraftArguments ? loaderJson.minecraftArguments.split(' ') : [];
gameArgs = gameArgs.concat(loaderGameArgs);
// Remove duplicate arguments
gameArgs = gameArgs.filter((item, index, self) => index === self.findIndex(arg => arg === item));
}
// Determine user type (e.g. 'msa' or 'Xbox') depending on version and authenticator
let userType = 'msa';
if (versionJson.id.startsWith('1.16')) {
userType = 'Xbox';
}
else {
userType = this.authenticator.meta.type === 'Xbox' ? 'msa' : this.authenticator.meta.type;
}
// Map of placeholders to actual values
const placeholderMap = {
'${auth_access_token}': this.authenticator.access_token,
'${auth_session}': this.authenticator.access_token,
'${auth_player_name}': this.authenticator.name,
'${auth_uuid}': this.authenticator.uuid,
'${auth_xuid}': this.authenticator?.xboxAccount?.xuid || this.authenticator.access_token,
'${user_properties}': this.authenticator.user_properties,
'${user_type}': userType,
'${version_name}': loaderJson ? loaderJson.id || versionJson.id : versionJson.id,
'${assets_index_name}': versionJson.assetIndex.id,
'${game_directory}': this.options.instance
? `${this.options.path}/instances/${this.options.instance}`
: this.options.path,
'${assets_root}': (0, Index_js_1.isold)(versionJson)
? `${this.options.path}/resources`
: `${this.options.path}/assets`,
'${game_assets}': (0, Index_js_1.isold)(versionJson)
? `${this.options.path}/resources`
: `${this.options.path}/assets`,
'${version_type}': versionJson.type,
'${clientid}': this.authenticator.clientId
|| this.authenticator.client_token
|| this.authenticator.access_token
};
// Replace placeholders in the game arguments
for (let i = 0; i < gameArgs.length; i++) {
if (typeof gameArgs[i] === 'object') {
// If it's an unexpected object, remove it
gameArgs.splice(i, 1);
i--;
continue;
}
if (placeholderMap[gameArgs[i]]) {
gameArgs[i] = placeholderMap[gameArgs[i]];
}
}
// If screen options are provided, add them
if (this.options.screen) {
const { width, height } = this.options.screen;
if (width && height) {
gameArgs.push('--width', String(width), '--height', String(height));
}
}
// Add any extra game arguments from user config
gameArgs.push(...this.options.GAME_ARGS);
// Filter out any remaining unexpected objects
return gameArgs.filter(item => typeof item === 'string');
}
/**
* Builds the JVM arguments needed by Minecraft. This includes memory settings,
* OS-specific options, and any additional arguments supplied by the user.
* @param versionJson The Minecraft version JSON.
*/
async GetJVMArguments(versionJson) {
// Some OS-specific defaults
const osSpecificOpts = {
win32: '-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump',
darwin: '-XstartOnFirstThread',
linux: '-Xss1M'
};
// Core JVM arguments
const jvmArgs = [
`-Xms${this.options.memory.min}`,
`-Xmx${this.options.memory.max}`,
'-XX:+UnlockExperimentalVMOptions',
'-XX:G1NewSizePercent=20',
'-XX:G1ReservePercent=20',
'-XX:MaxGCPauseMillis=50',
'-XX:G1HeapRegionSize=32M',
'-Dfml.ignoreInvalidMinecraftCertificates=true',
`-Djna.tmpdir=${this.options.path}/versions/${versionJson.id}/natives`,
`-Dorg.lwjgl.system.SharedLibraryExtractPath=${this.options.path}/versions/${versionJson.id}/natives`,
`-Dio.netty.native.workdir=${this.options.path}/versions/${versionJson.id}/natives`
];
// For newer MC versions that use "arguments.game" instead of "minecraftArguments",
// we add OS-specific arguments (e.g., Mac uses -XstartOnFirstThread).
if (!versionJson.minecraftArguments) {
const opt = osSpecificOpts[process.platform];
if (opt) {
jvmArgs.push(opt);
}
}
// bypass offline mode multiplayer
if (this.options?.bypassOffline) {
jvmArgs.push('-Dminecraft.api.auth.host=https://nope.invalid/');
jvmArgs.push('-Dminecraft.api.account.host=https://nope.invalid/');
jvmArgs.push('-Dminecraft.api.session.host=https://nope.invalid/');
jvmArgs.push('-Dminecraft.api.services.host=https://nope.invalid/');
}
// If natives are specified, add the native library path
if (versionJson.nativesList) {
jvmArgs.push(`-Djava.library.path=${this.options.path}/versions/${versionJson.id}/natives`);
}
// Special handling for macOS (setting dock icon)
if (os_1.default.platform() === 'darwin') {
const assetsPath = `${this.options.path}/assets/indexes/${versionJson.assets}.json`;
const assetsContent = fs_1.default.readFileSync(assetsPath, 'utf-8');
const assetsJson = JSON.parse(assetsContent);
// Retrieve the hash of the minecraft.icns file
const iconHash = assetsJson.objects['icons/minecraft.icns']?.hash;
if (iconHash) {
jvmArgs.push('-Xdock:name=Minecraft');
jvmArgs.push(`-Xdock:icon=${this.options.path}/assets/objects/${iconHash.substring(0, 2)}/${iconHash}`);
}
}
// Append any user-supplied JVM arguments
jvmArgs.push(...this.options.JVM_ARGS);
return jvmArgs;
}
/**
* Constructs the classpath (including libraries) that Minecraft requires
* to launch, and identifies the main class. Optionally merges loader libraries.
* @param versionJson The Minecraft version JSON.
* @param loaderJson The loader JSON (e.g., Forge, Fabric) if applicable.
*/
async GetClassPath(versionJson, loaderJson) {
let combinedLibraries = versionJson.libraries ?? [];
// If a loader JSON is provided, merge its libraries with the base MC version
if (loaderJson?.libraries) {
combinedLibraries = loaderJson.libraries.concat(combinedLibraries);
}
const map = new Map();
for (const dep of combinedLibraries) {
const parts = (0, Index_js_1.getPathLibraries)(dep.name);
const version = semver_1.default.valid(semver_1.default.coerce(parts.version));
if (!version)
continue;
const pathParts = parts.path.split('/');
const basePath = pathParts.slice(0, -1).join('/');
const key = `${basePath}/${parts.name.replace(`-${parts.version}`, '')}`;
const current = map.get(key);
const isSupportedVersion = semver_1.default.satisfies(semver_1.default.valid(semver_1.default.coerce(this.options.version)), '1.14.4 - 1.18.2');
const isWindows = process.platform === 'win32';
if (!current || semver_1.default.gt(version, current.version) && (isSupportedVersion && isWindows)) {
map.set(key, { ...dep, version });
}
}
const latest = Object.fromEntries(Array.from(map.entries()).map(([key, value]) => [key, value]));
// Prepare to accumulate all library paths
const librariesList = [];
for (const lib of Object.values(latest)) {
// Skip certain logging libraries if flagged (e.g., in Forge's "loader" property)
if (lib.loader && lib.name.startsWith('org.apache.logging.log4j:log4j-slf4j2-impl'))
continue;
// Check if the library has native bindings
if (lib.natives) {
const nativeName = lib.natives[MOJANG_LIBRARY_MAP[process.platform]] || lib.natives[process.platform];
if (!nativeName)
continue;
}
else if (lib.rules && lib.rules[0].os) {
// Some libraries only apply to specific OS platforms
if (lib.rules[0].os.name !== MOJANG_LIBRARY_MAP[process.platform])
continue;
}
// Build the path for this library
const libPath = (0, Index_js_1.getPathLibraries)(lib.name);
if (lib.loader) {
// If the loader uses a specific library path
librariesList.push(`${lib.loader}/libraries/${libPath.path}/${libPath.name}`);
}
else {
librariesList.push(`${this.options.path}/libraries/${libPath.path}/${libPath.name}`);
}
}
// Add the main Minecraft JAR (or special jar if using old Forge or MCP)
if (loaderJson?.isOldForge && loaderJson.jarPath) {
librariesList.push(loaderJson.jarPath);
}
else if (this.options.mcp) {
librariesList.push(this.options.mcp);
}
else {
librariesList.push(`${this.options.path}/versions/${versionJson.id}/${versionJson.id}.jar`);
}
// Filter out duplicates in the final library paths
const uniquePaths = [];
for (const libPath of librariesList) {
// We only check if we've already used the exact file name
const fileName = libPath.split('/').pop();
if (fileName && !uniquePaths.includes(fileName)) {
uniquePaths.push(libPath);
}
}
// The final classpath argument is OS-dependent (':' on Unix, ';' on Windows)
const cpSeparator = process.platform === 'win32' ? ';' : ':';
const cpArgument = uniquePaths.length > 0 ? uniquePaths.join(cpSeparator) : '';
return {
classpath: ['-cp', cpArgument],
mainClass: loaderJson ? loaderJson.mainClass : versionJson.mainClass
};
}
}
exports.default = MinecraftArguments;
//# sourceMappingURL=Minecraft-Arguments.js.map
;