From 86d42246f1902606b9a09439aaced6d0b86a9589 Mon Sep 17 00:00:00 2001 From: Pierce Date: Tue, 1 Mar 2022 18:52:06 -0800 Subject: [PATCH] OptiFine & UUID fix Allows OptiFine properly install and for forge clients to launch in offline mode. Addresses #84 --- components/authenticator.js | 54 +++++--- components/handler.js | 2 +- components/launcher.js | 259 +++++++++++++++++++----------------- package.json | 2 +- 4 files changed, 169 insertions(+), 148 deletions(-) diff --git a/components/authenticator.js b/components/authenticator.js index 2781ba3..d3e7b52 100644 --- a/components/authenticator.js +++ b/components/authenticator.js @@ -1,14 +1,17 @@ const request = require('request') -const uuid = require('uuid').v1 +const { v3 } = require('uuid') + +let uuid let api_url = 'https://authserver.mojang.com' module.exports.getAuth = function (username, password, client_token = null) { return new Promise((resolve, reject) => { + getUUID(username) if (!password) { const user = { - access_token: uuid(), - client_token: client_token || uuid(), - uuid: uuid(), + access_token: uuid, + client_token: client_token || uuid, + uuid, name: username, user_properties: '{}' } @@ -23,9 +26,9 @@ module.exports.getAuth = function (username, password, client_token = null) { name: 'Minecraft', version: 1 }, - username: username, - password: password, - clientToken: uuid(), + username, + password, + clientToken: uuid, requestUser: true } } @@ -50,13 +53,13 @@ module.exports.getAuth = function (username, password, client_token = null) { }) } -module.exports.validate = function (access_token, client_token) { +module.exports.validate = function (accessToken, clientToken) { return new Promise((resolve, reject) => { const requestObject = { url: api_url + '/validate', json: { - accessToken: access_token, - clientToken: client_token + accessToken, + clientToken } } @@ -74,8 +77,8 @@ module.exports.refreshAuth = function (accessToken, clientToken) { const requestObject = { url: api_url + '/refresh', json: { - accessToken: accessToken, - clientToken: clientToken, + accessToken, + clientToken, requestUser: true } } @@ -88,13 +91,13 @@ module.exports.refreshAuth = function (accessToken, clientToken) { const userProfile = { access_token: body.accessToken, - client_token: uuid(), + client_token: getUUID(body.selectedProfile.name), uuid: body.selectedProfile.id, name: body.selectedProfile.name, user_properties: parsePropts(body.user.properties) } - resolve(userProfile) + return resolve(userProfile) }) }) } @@ -104,16 +107,16 @@ module.exports.invalidate = function (accessToken, clientToken) { const requestObject = { url: api_url + '/invalidate', json: { - accessToken: accessToken, - clientToken: clientToken + accessToken, + clientToken } } request.post(requestObject, function (error, response, body) { if (error) return reject(error) - if (!body) resolve(true) - else reject(body) + if (!body) return resolve(true) + else return reject(body) }) }) } @@ -123,16 +126,16 @@ module.exports.signOut = function (username, password) { const requestObject = { url: api_url + '/signout', json: { - username: username, - password: password + username, + password } } request.post(requestObject, function (error, response, body) { if (error) return reject(error) - if (!body) resolve(true) - else reject(body) + if (!body) return resolve(true) + else return reject(body) }) }) } @@ -156,3 +159,10 @@ function parsePropts (array) { return '{}' } } + +function getUUID (value) { + if (!uuid) { + uuid = v3(value, v3.DNS) + } + return uuid +} diff --git a/components/handler.js b/components/handler.js index aaf8257..c3e2358 100644 --- a/components/handler.js +++ b/components/handler.js @@ -458,7 +458,7 @@ class Handler { runInstaller (path) { return new Promise(resolve => { const installer = child.exec(path) - installer.on('close', (code) => resolve()) + installer.on('close', (code) => resolve(code)) }) } diff --git a/components/launcher.js b/components/launcher.js index c87b4b1..27b72be 100644 --- a/components/launcher.js +++ b/components/launcher.js @@ -6,133 +6,144 @@ const EventEmitter = require('events').EventEmitter class MCLCore extends EventEmitter { async launch (options) { - this.options = { ...options } - this.options.root = path.resolve(this.options.root) - this.options.overrides = { - detached: true, - ...this.options.overrides, - url: { - meta: 'https://launchermeta.mojang.com', - resource: 'https://resources.download.minecraft.net', - mavenForge: 'http://files.minecraftforge.net/maven/', - defaultRepoForge: 'https://libraries.minecraft.net/', - fallbackMaven: 'https://search.maven.org/remotecontent?filepath=', - ...this.options.overrides - ? this.options.overrides.url - : undefined - }, - fw: { - baseUrl: 'https://github.com/ZekerZhayard/ForgeWrapper/releases/download/', - version: '1.5.5', - sh1: '566dfd60aacffaa02884614835f1151d36f1f985', - size: 34331, - ...this.options.overrides - ? this.options.overrides.fw - : undefined - } - } - - this.handler = new Handler(this) - - this.printVersion() - - const java = await this.handler.checkJava(this.options.javaPath || 'java') - if (!java.run) { - this.emit('debug', `[MCLC]: Couldn't start Minecraft due to: ${java.message}`) - this.emit('close', 1) - return null - } - - this.createRootDirectory() - this.createGameDirectory() - - await this.extractPackage() - - if (this.options.installer) { - // So installers that create a profile in launcher_profiles.json can run without breaking. - const profilePath = path.join(this.options.root, 'launcher_profiles.json') - if (!fs.existsSync(profilePath)) { fs.writeFileSync(profilePath, JSON.stringify({}, null, 4)) } - await this.handler.runInstaller(this.options.installer) - } - - const directory = this.options.overrides.directory || path.join(this.options.root, 'versions', this.options.version.custom ? this.options.version.custom : this.options.version.number) - this.options.directory = directory - - const versionFile = await this.handler.getVersion() - const mcPath = this.options.overrides.minecraftJar || (this.options.version.custom - ? path.join(this.options.root, 'versions', this.options.version.custom, `${this.options.version.custom}.jar`) - : path.join(directory, `${this.options.version.number}.jar`)) - this.options.mcPath = mcPath - const nativePath = await this.handler.getNatives() - - if (!fs.existsSync(mcPath)) { - this.emit('debug', '[MCLC]: Attempting to download Minecraft version jar') - await this.handler.getJar() - } - - const modifyJson = await this.getModifyJson() - - const args = [] - - let jvm = [ - '-XX:-UseAdaptiveSizePolicy', - '-XX:-OmitStackTraceInFastThrow', - '-Dfml.ignorePatchDiscrepancies=true', - '-Dfml.ignoreInvalidMinecraftCertificates=true', - `-Djava.library.path=${nativePath}`, - `-Xmx${this.handler.getMemory()[0]}`, - `-Xms${this.handler.getMemory()[1]}` - ] - if (this.handler.getOS() === 'osx') { - if (parseInt(versionFile.id.split('.')[1]) > 12) jvm.push(await this.handler.getJVM()) - } else jvm.push(await this.handler.getJVM()) - - if (this.options.customArgs) jvm = jvm.concat(this.options.customArgs) - if (this.options.overrides.logj4ConfigurationFile) { - jvm.push(`-Dlog4j.configurationFile=${path.resolve(this.options.overrides.logj4ConfigurationFile)}`) - } - // https://help.minecraft.net/hc/en-us/articles/4416199399693-Security-Vulnerability-in-Minecraft-Java-Edition - if (parseInt(versionFile.id.split('.')[1]) === 17) jvm.push('-Dlog4j2.formatMsgNoLookups=true') - if (parseInt(versionFile.id.split('.')[1]) < 17) { - if (!jvm.find(arg => arg.includes('Dlog4j.configurationFile'))) { - const configPath = path.resolve(this.options.overrides.cwd || this.options.root) - const intVersion = parseInt(versionFile.id.split('.')[1]) - if (intVersion >= 12) { - await this.handler.downloadAsync('https://launcher.mojang.com/v1/objects/02937d122c86ce73319ef9975b58896fc1b491d1/log4j2_112-116.xml', - configPath, 'log4j2_112-116.xml', true, 'log4j') - jvm.push('-Dlog4j.configurationFile=log4j2_112-116.xml') - } else if (intVersion >= 7) { - await this.handler.downloadAsync('https://launcher.mojang.com/v1/objects/dd2b723346a8dcd48e7f4d245f6bf09e98db9696/log4j2_17-111.xml', - configPath, 'log4j2_17-111.xml', true, 'log4j') - jvm.push('-Dlog4j.configurationFile=log4j2_17-111.xml') + try { + this.options = { ...options } + this.options.root = path.resolve(this.options.root) + this.options.overrides = { + detached: true, + ...this.options.overrides, + url: { + meta: 'https://launchermeta.mojang.com', + resource: 'https://resources.download.minecraft.net', + mavenForge: 'http://files.minecraftforge.net/maven/', + defaultRepoForge: 'https://libraries.minecraft.net/', + fallbackMaven: 'https://search.maven.org/remotecontent?filepath=', + ...this.options.overrides + ? this.options.overrides.url + : undefined + }, + fw: { + baseUrl: 'https://github.com/ZekerZhayard/ForgeWrapper/releases/download/', + version: '1.5.5', + sh1: '566dfd60aacffaa02884614835f1151d36f1f985', + size: 34331, + ...this.options.overrides + ? this.options.overrides.fw + : undefined } } + + this.handler = new Handler(this) + + this.printVersion() + + const java = await this.handler.checkJava(this.options.javaPath || 'java') + if (!java.run) { + this.emit('debug', `[MCLC]: Couldn't start Minecraft due to: ${java.message}`) + this.emit('close', 1) + return null + } + + this.createRootDirectory() + this.createGameDirectory() + + await this.extractPackage() + + if (this.options.installer) { + // So installers that create a profile in launcher_profiles.json can run without breaking. + const profilePath = path.join(this.options.root, 'launcher_profiles.json') + if (!fs.existsSync(profilePath) || !JSON.parse(fs.readFileSync(profilePath)).profiles) { + fs.writeFileSync(profilePath, JSON.stringify({ profiles: {} }, null, 4)) + } + const code = await this.handler.runInstaller(this.options.installer) + if (!this.options.version.custom && code === 0) { + this.emit('debug', '[MCLC]: Installer successfully ran, but no custom version was provided') + } + this.emit('debug', `[MCLC]: Installer closed with code ${code}`) + } + + const directory = this.options.overrides.directory || path.join(this.options.root, 'versions', this.options.version.custom ? this.options.version.custom : this.options.version.number) + this.options.directory = directory + + const versionFile = await this.handler.getVersion() + const mcPath = this.options.overrides.minecraftJar || (this.options.version.custom + ? path.join(this.options.root, 'versions', this.options.version.custom, `${this.options.version.custom}.jar`) + : path.join(directory, `${this.options.version.number}.jar`)) + this.options.mcPath = mcPath + const nativePath = await this.handler.getNatives() + + if (!fs.existsSync(mcPath)) { + this.emit('debug', '[MCLC]: Attempting to download Minecraft version jar') + await this.handler.getJar() + } + + const modifyJson = await this.getModifyJson() + + const args = [] + + let jvm = [ + '-XX:-UseAdaptiveSizePolicy', + '-XX:-OmitStackTraceInFastThrow', + '-Dfml.ignorePatchDiscrepancies=true', + '-Dfml.ignoreInvalidMinecraftCertificates=true', + `-Djava.library.path=${nativePath}`, + `-Xmx${this.handler.getMemory()[0]}`, + `-Xms${this.handler.getMemory()[1]}` + ] + if (this.handler.getOS() === 'osx') { + if (parseInt(versionFile.id.split('.')[1]) > 12) jvm.push(await this.handler.getJVM()) + } else jvm.push(await this.handler.getJVM()) + + if (this.options.customArgs) jvm = jvm.concat(this.options.customArgs) + if (this.options.overrides.logj4ConfigurationFile) { + jvm.push(`-Dlog4j.configurationFile=${path.resolve(this.options.overrides.logj4ConfigurationFile)}`) + } + // https://help.minecraft.net/hc/en-us/articles/4416199399693-Security-Vulnerability-in-Minecraft-Java-Edition + if (parseInt(versionFile.id.split('.')[1]) === 17) jvm.push('-Dlog4j2.formatMsgNoLookups=true') + if (parseInt(versionFile.id.split('.')[1]) < 17) { + if (!jvm.find(arg => arg.includes('Dlog4j.configurationFile'))) { + const configPath = path.resolve(this.options.overrides.cwd || this.options.root) + const intVersion = parseInt(versionFile.id.split('.')[1]) + if (intVersion >= 12) { + await this.handler.downloadAsync('https://launcher.mojang.com/v1/objects/02937d122c86ce73319ef9975b58896fc1b491d1/log4j2_112-116.xml', + configPath, 'log4j2_112-116.xml', true, 'log4j') + jvm.push('-Dlog4j.configurationFile=log4j2_112-116.xml') + } else if (intVersion >= 7) { + await this.handler.downloadAsync('https://launcher.mojang.com/v1/objects/dd2b723346a8dcd48e7f4d245f6bf09e98db9696/log4j2_17-111.xml', + configPath, 'log4j2_17-111.xml', true, 'log4j') + jvm.push('-Dlog4j.configurationFile=log4j2_17-111.xml') + } + } + } + + const classes = this.options.overrides.classes || this.handler.cleanUp(await this.handler.getClasses(modifyJson)) + const classPaths = ['-cp'] + const separator = this.handler.getOS() === 'windows' ? ';' : ':' + this.emit('debug', `[MCLC]: Using ${separator} to separate class paths`) + // Handling launch arguments. + const file = modifyJson || versionFile + // So mods like fabric work. + const jar = fs.existsSync(mcPath) + ? `${separator}${mcPath}` + : `${separator}${path.join(directory, `${this.options.version.number}.jar`)}` + classPaths.push(`${this.options.forge ? this.options.forge + separator : ''}${classes.join(separator)}${jar}`) + classPaths.push(file.mainClass) + + this.emit('debug', '[MCLC]: Attempting to download assets') + await this.handler.getAssets() + + // Forge -> Custom -> Vanilla + const launchOptions = await this.handler.getLaunchOptions(modifyJson) + + const launchArguments = args.concat(jvm, classPaths, launchOptions) + this.emit('arguments', launchArguments) + this.emit('debug', `[MCLC]: Launching with arguments ${launchArguments.join(' ')}`) + + return this.startMinecraft(launchArguments) + } catch (e) { + this.emit('debug', `[MCLC]: Failed to start due to ${e}, closing...`) + return null } - - const classes = this.options.overrides.classes || this.handler.cleanUp(await this.handler.getClasses(modifyJson)) - const classPaths = ['-cp'] - const separator = this.handler.getOS() === 'windows' ? ';' : ':' - this.emit('debug', `[MCLC]: Using ${separator} to separate class paths`) - // Handling launch arguments. - const file = modifyJson || versionFile - // So mods like fabric work. - const jar = fs.existsSync(mcPath) - ? `${separator}${mcPath}` - : `${separator}${path.join(directory, `${this.options.version.number}.jar`)}` - classPaths.push(`${this.options.forge ? this.options.forge + separator : ''}${classes.join(separator)}${jar}`) - classPaths.push(file.mainClass) - - this.emit('debug', '[MCLC]: Attempting to download assets') - await this.handler.getAssets() - - // Forge -> Custom -> Vanilla - const launchOptions = await this.handler.getLaunchOptions(modifyJson) - - const launchArguments = args.concat(jvm, classPaths, launchOptions) - this.emit('arguments', launchArguments) - this.emit('debug', `[MCLC]: Launching with arguments ${launchArguments.join(' ')}`) - - return this.startMinecraft(launchArguments) } printVersion () { diff --git a/package.json b/package.json index 4f7ecda..82a5cf4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minecraft-launcher-core", - "version": "3.16.11", + "version": "3.16.12", "description": "Lightweight module that downloads and runs Minecraft using javascript / NodeJS", "main": "index.js", "dependencies": {