From 15f7560916a16927c6a439de8b3a9c695642c7d6 Mon Sep 17 00:00:00 2001 From: Daniel Scalzi Date: Mon, 13 Mar 2023 02:06:58 -0400 Subject: [PATCH] Replace all javaguard logic, logic in landing.js needs to be rewritten to integrate. --- app/assets/js/assetguard.js | 787 +----------------------------- app/assets/js/scripts/landing.js | 321 ++++++------ app/assets/js/scripts/settings.js | 26 +- package-lock.json | 21 +- package.json | 4 +- 5 files changed, 172 insertions(+), 987 deletions(-) diff --git a/app/assets/js/assetguard.js b/app/assets/js/assetguard.js index c0a9279..592db53 100644 --- a/app/assets/js/assetguard.js +++ b/app/assets/js/assetguard.js @@ -5,18 +5,15 @@ const crypto = require('crypto') const EventEmitter = require('events') const fs = require('fs-extra') const { LoggerUtil } = require('helios-core') -const nodeDiskInfo = require('node-disk-info') +const { javaExecFromRoot, latestOpenJDK } = require('helios-core/java') const StreamZip = require('node-stream-zip') const path = require('path') -const Registry = require('winreg') const request = require('request') const tar = require('tar-fs') const zlib = require('zlib') const isDev = require('./isdev') -const isARM64 = process.arch === 'arm64' - // Classes /** Class representing a base asset. */ @@ -81,781 +78,8 @@ class Util { return true } - static isAutoconnectBroken(forgeVersion) { - - const minWorking = [31, 2, 15] - const verSplit = forgeVersion.split('.').map(v => Number(v)) - - if(verSplit[0] === 31) { - for(let i=0; i minWorking[i]) { - return false - } else if(verSplit[i] < minWorking[i]) { - return true - } - } - } - - return false - } - } - -class JavaGuard extends EventEmitter { - - constructor(mcVersion){ - super() - this.mcVersion = mcVersion - this.logger = LoggerUtil.getLogger('JavaGuard') - } - - /** - * @typedef OpenJDKData - * @property {string} uri The base uri of the JRE. - * @property {number} size The size of the download. - * @property {string} name The name of the artifact. - */ - - /** - * Fetch the last open JDK binary. - * - * HOTFIX: Uses Corretto 8 for macOS. - * See: https://github.com/dscalzi/HeliosLauncher/issues/70 - * See: https://github.com/AdoptOpenJDK/openjdk-support/issues/101 - * - * @param {string} major The major version of Java to fetch. - * - * @returns {Promise.} Promise which resolved to an object containing the JRE download data. - */ - static _latestOpenJDK(major = '8'){ - - if(process.platform === 'darwin') { - return this._latestCorretto(major) - } else { - return this._latestAdoptium(major) - } - } - - static _latestAdoptium(major) { - - const majorNum = Number(major) - const sanitizedOS = process.platform === 'win32' ? 'windows' : (process.platform === 'darwin' ? 'mac' : process.platform) - const url = `https://api.adoptium.net/v3/assets/latest/${major}/hotspot?vendor=eclipse` - - return new Promise((resolve, reject) => { - request({url, json: true}, (err, resp, body) => { - if(!err && body.length > 0){ - - const targetBinary = body.find(entry => { - return entry.version.major === majorNum - && entry.binary.os === sanitizedOS - && entry.binary.image_type === 'jdk' - && entry.binary.architecture === 'x64' - }) - - if(targetBinary != null) { - resolve({ - uri: targetBinary.binary.package.link, - size: targetBinary.binary.package.size, - name: targetBinary.binary.package.name - }) - } else { - resolve(null) - } - } else { - resolve(null) - } - }) - }) - } - - static _latestCorretto(major) { - - let sanitizedOS, ext - - switch(process.platform) { - case 'win32': - sanitizedOS = 'windows' - ext = 'zip' - break - case 'darwin': - sanitizedOS = 'macos' - ext = 'tar.gz' - break - case 'linux': - sanitizedOS = 'linux' - ext = 'tar.gz' - break - default: - sanitizedOS = process.platform - ext = 'tar.gz' - break - } - - const arch = isARM64 ? 'aarch64' : 'x64' - const url = `https://corretto.aws/downloads/latest/amazon-corretto-${major}-${arch}-${sanitizedOS}-jdk.${ext}` - - return new Promise((resolve, reject) => { - request.head({url, json: true}, (err, resp) => { - if(!err && resp.statusCode === 200){ - resolve({ - uri: url, - size: parseInt(resp.headers['content-length']), - name: url.substr(url.lastIndexOf('/')+1) - }) - } else { - resolve(null) - } - }) - }) - - } - - /** - * Returns the path of the OS-specific executable for the given Java - * installation. Supported OS's are win32, darwin, linux. - * - * @param {string} rootDir The root directory of the Java installation. - * @returns {string} The path to the Java executable. - */ - static javaExecFromRoot(rootDir){ - if(process.platform === 'win32'){ - return path.join(rootDir, 'bin', 'javaw.exe') - } else if(process.platform === 'darwin'){ - return path.join(rootDir, 'Contents', 'Home', 'bin', 'java') - } else if(process.platform === 'linux'){ - return path.join(rootDir, 'bin', 'java') - } - return rootDir - } - - /** - * Check to see if the given path points to a Java executable. - * - * @param {string} pth The path to check against. - * @returns {boolean} True if the path points to a Java executable, otherwise false. - */ - static isJavaExecPath(pth){ - if(pth == null) { - return false - } - if(process.platform === 'win32'){ - return pth.endsWith(path.join('bin', 'javaw.exe')) - } else if(process.platform === 'darwin'){ - return pth.endsWith(path.join('bin', 'java')) - } else if(process.platform === 'linux'){ - return pth.endsWith(path.join('bin', 'java')) - } - return false - } - - /** - * Load Mojang's launcher.json file. - * - * @returns {Promise.} Promise which resolves to Mojang's launcher.json object. - */ - static loadMojangLauncherData(){ - return new Promise((resolve, reject) => { - request.get('https://launchermeta.mojang.com/mc/launcher.json', (err, resp, body) => { - if(err){ - resolve(null) - } else { - resolve(JSON.parse(body)) - } - }) - }) - } - - /** - * Parses a **full** Java Runtime version string and resolves - * the version information. Dynamically detects the formatting - * to use. - * - * @param {string} verString Full version string to parse. - * @returns Object containing the version information. - */ - static parseJavaRuntimeVersion(verString){ - const major = verString.split('.')[0] - if(major == 1){ - return JavaGuard._parseJavaRuntimeVersion_8(verString) - } else { - return JavaGuard._parseJavaRuntimeVersion_9(verString) - } - } - - /** - * Parses a **full** Java Runtime version string and resolves - * the version information. Uses Java 8 formatting. - * - * @param {string} verString Full version string to parse. - * @returns Object containing the version information. - */ - static _parseJavaRuntimeVersion_8(verString){ - // 1.{major}.0_{update}-b{build} - // ex. 1.8.0_152-b16 - const ret = {} - let pts = verString.split('-') - ret.build = parseInt(pts[1].substring(1)) - pts = pts[0].split('_') - ret.update = parseInt(pts[1]) - ret.major = parseInt(pts[0].split('.')[1]) - return ret - } - - /** - * Parses a **full** Java Runtime version string and resolves - * the version information. Uses Java 9+ formatting. - * - * @param {string} verString Full version string to parse. - * @returns Object containing the version information. - */ - static _parseJavaRuntimeVersion_9(verString){ - // {major}.{minor}.{revision}+{build} - // ex. 10.0.2+13 - const ret = {} - let pts = verString.split('+') - ret.build = parseInt(pts[1]) - pts = pts[0].split('.') - ret.major = parseInt(pts[0]) - ret.minor = parseInt(pts[1]) - ret.revision = parseInt(pts[2]) - return ret - } - - /** - * Validates the output of a JVM's properties. Currently validates that a JRE is x64 - * and that the major = 8, update > 52. - * - * @param {string} stderr The output to validate. - * - * @returns {Promise.} A promise which resolves to a meta object about the JVM. - * The validity is stored inside the `valid` property. - */ - _validateJVMProperties(stderr){ - const res = stderr - const props = res.split('\n') - - const goal = 2 - let checksum = 0 - - const meta = {} - - for(let i=0; i -1){ - let arch = props[i].split('=')[1].trim() - arch = parseInt(arch) - this.logger.debug(props[i].trim()) - if(arch === 64){ - meta.arch = arch - ++checksum - if(checksum === goal){ - break - } - } - } else if(props[i].indexOf('java.runtime.version') > -1){ - let verString = props[i].split('=')[1].trim() - this.logger.debug(props[i].trim()) - const verOb = JavaGuard.parseJavaRuntimeVersion(verString) - // TODO implement a support matrix eventually. Right now this is good enough - // 1.7-1.16 = Java 8 - // 1.17+ = Java 17 - // Actual support may vary, but we're going with this rule for simplicity. - if(verOb.major < 9){ - // Java 8 - if(!Util.mcVersionAtLeast('1.17', this.mcVersion)){ - if(verOb.major === 8 && verOb.update > 52){ - meta.version = verOb - ++checksum - if(checksum === goal){ - break - } - } - } - } else if(verOb.major >= 17) { - // Java 9+ - if(Util.mcVersionAtLeast('1.17', this.mcVersion)){ - meta.version = verOb - ++checksum - if(checksum === goal){ - break - } - } - } - // Space included so we get only the vendor. - } else if(props[i].lastIndexOf('java.vendor ') > -1) { - let vendorName = props[i].split('=')[1].trim() - this.logger.debug(props[i].trim()) - meta.vendor = vendorName - } else if (props[i].indexOf('os.arch') > -1) { - meta.isARM = props[i].split('=')[1].trim() === 'aarch64' - } - } - - meta.valid = checksum === goal - - return meta - } - - /** - * Validates that a Java binary is at least 64 bit. This makes use of the non-standard - * command line option -XshowSettings:properties. The output of this contains a property, - * sun.arch.data.model = ARCH, in which ARCH is either 32 or 64. This option is supported - * in Java 8 and 9. Since this is a non-standard option. This will resolve to true if - * the function's code throws errors. That would indicate that the option is changed or - * removed. - * - * @param {string} binaryExecPath Path to the java executable we wish to validate. - * - * @returns {Promise.} A promise which resolves to a meta object about the JVM. - * The validity is stored inside the `valid` property. - */ - _validateJavaBinary(binaryExecPath){ - - return new Promise((resolve, reject) => { - if(!JavaGuard.isJavaExecPath(binaryExecPath)){ - resolve({valid: false}) - } else if(fs.existsSync(binaryExecPath)){ - // Workaround (javaw.exe no longer outputs this information.) - this.logger.debug(typeof binaryExecPath) - if(binaryExecPath.indexOf('javaw.exe') > -1) { - binaryExecPath.replace('javaw.exe', 'java.exe') - } - child_process.exec('"' + binaryExecPath + '" -XshowSettings:properties', (err, stdout, stderr) => { - try { - // Output is stored in stderr? - resolve(this._validateJVMProperties(stderr)) - } catch (err){ - // Output format might have changed, validation cannot be completed. - resolve({valid: false}) - } - }) - } else { - resolve({valid: false}) - } - }) - - } - - /** - * Checks for the presence of the environment variable JAVA_HOME. If it exits, we will check - * to see if the value points to a path which exists. If the path exits, the path is returned. - * - * @returns {string} The path defined by JAVA_HOME, if it exists. Otherwise null. - */ - static _scanJavaHome(){ - const jHome = process.env.JAVA_HOME - try { - let res = fs.existsSync(jHome) - return res ? jHome : null - } catch (err) { - // Malformed JAVA_HOME property. - return null - } - } - - /** - * Scans the registry for 64-bit Java entries. The paths of each entry are added to - * a set and returned. Currently, only Java 8 (1.8) is supported. - * - * @returns {Promise.>} A promise which resolves to a set of 64-bit Java root - * paths found in the registry. - */ - static _scanRegistry(){ - - return new Promise((resolve, reject) => { - // Keys for Java v9.0.0 and later: - // 'SOFTWARE\\JavaSoft\\JRE' - // 'SOFTWARE\\JavaSoft\\JDK' - // Forge does not yet support Java 9, therefore we do not. - - // Keys for Java 1.8 and prior: - const regKeys = [ - '\\SOFTWARE\\JavaSoft\\Java Runtime Environment', - '\\SOFTWARE\\JavaSoft\\Java Development Kit' - ] - - let keysDone = 0 - - const candidates = new Set() - - for(let i=0; i { - if(exists) { - key.keys((err, javaVers) => { - if(err){ - keysDone++ - console.error(err) - - // REG KEY DONE - // DUE TO ERROR - if(keysDone === regKeys.length){ - resolve(candidates) - } - } else { - if(javaVers.length === 0){ - // REG KEY DONE - // NO SUBKEYS - keysDone++ - if(keysDone === regKeys.length){ - resolve(candidates) - } - } else { - - let numDone = 0 - - for(let j=0; j { - const jHome = res.value - if(jHome.indexOf('(x86)') === -1){ - candidates.add(jHome) - } - - // SUBKEY DONE - - numDone++ - if(numDone === javaVers.length){ - keysDone++ - if(keysDone === regKeys.length){ - resolve(candidates) - } - } - }) - } else { - - // SUBKEY DONE - // NOT JAVA 8 - - numDone++ - if(numDone === javaVers.length){ - keysDone++ - if(keysDone === regKeys.length){ - resolve(candidates) - } - } - } - } - } - } - }) - } else { - - // REG KEY DONE - // DUE TO NON-EXISTANCE - - keysDone++ - if(keysDone === regKeys.length){ - resolve(candidates) - } - } - }) - } - - }) - - } - - /** - * See if JRE exists in the Internet Plug-Ins folder. - * - * @returns {string} The path of the JRE if found, otherwise null. - */ - static _scanInternetPlugins(){ - // /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java - const pth = '/Library/Internet Plug-Ins/JavaAppletPlugin.plugin' - const res = fs.existsSync(JavaGuard.javaExecFromRoot(pth)) - return res ? pth : null - } - - /** - * Scan a directory for root JVM folders. - * - * @param {string} scanDir The directory to scan. - * @returns {Promise.>} A promise which resolves to a set of the discovered - * root JVM folders. - */ - static async _scanFileSystem(scanDir){ - - let res = new Set() - - if(await fs.pathExists(scanDir)) { - - const files = await fs.readdir(scanDir) - for(let i=0; i} rootSet A set of JVM root strings to validate. - * @returns {Promise.} A promise which resolves to an array of meta objects - * for each valid JVM root directory. - */ - async _validateJavaRootSet(rootSet){ - - const rootArr = Array.from(rootSet) - const validArr = [] - - for(let i=0; i { - - if(a.version.major === b.version.major){ - - if(a.version.major < 9){ - // Java 8 - if(a.version.update === b.version.update){ - if(a.version.build === b.version.build){ - - // Same version, give priority to JRE. - if(a.execPath.toLowerCase().indexOf('jdk') > -1){ - return b.execPath.toLowerCase().indexOf('jdk') > -1 ? 0 : 1 - } else { - return -1 - } - - } else { - return a.version.build > b.version.build ? -1 : 1 - } - } else { - return a.version.update > b.version.update ? -1 : 1 - } - } else { - // Java 9+ - if(a.version.minor === b.version.minor){ - if(a.version.revision === b.version.revision){ - - // Same version, give priority to JRE. - if(a.execPath.toLowerCase().indexOf('jdk') > -1){ - return b.execPath.toLowerCase().indexOf('jdk') > -1 ? 0 : 1 - } else { - return -1 - } - - } else { - return a.version.revision > b.version.revision ? -1 : 1 - } - } else { - return a.version.minor > b.version.minor ? -1 : 1 - } - } - - } else { - return a.version.major > b.version.major ? -1 : 1 - } - }) - - return retArr - } - - /** - * Attempts to find a valid x64 installation of Java on Windows machines. - * Possible paths will be pulled from the registry and the JAVA_HOME environment - * variable. The paths will be sorted with higher versions preceeding lower, and - * JREs preceeding JDKs. The binaries at the sorted paths will then be validated. - * The first validated is returned. - * - * Higher versions > Lower versions - * If versions are equal, JRE > JDK. - * - * @param {string} dataDir The base launcher directory. - * @returns {Promise.} A Promise which resolves to the executable path of a valid - * x64 Java installation. If none are found, null is returned. - */ - async _win32JavaValidate(dataDir){ - - // Get possible paths from the registry. - let pathSet1 = await JavaGuard._scanRegistry() - if(pathSet1.size === 0){ - - // Do a manual file system scan of program files. - // Check all drives - const driveMounts = nodeDiskInfo.getDiskInfoSync().map(({ mounted }) => mounted) - for(const mount of driveMounts) { - pathSet1 = new Set([ - ...pathSet1, - ...(await JavaGuard._scanFileSystem(`${mount}\\Program Files\\Java`)), - ...(await JavaGuard._scanFileSystem(`${mount}\\Program Files\\Eclipse Adoptium`)), - ...(await JavaGuard._scanFileSystem(`${mount}\\Program Files\\Eclipse Foundation`)), - ...(await JavaGuard._scanFileSystem(`${mount}\\Program Files\\AdoptOpenJDK`)) - ]) - } - - } - - // Get possible paths from the data directory. - const pathSet2 = await JavaGuard._scanFileSystem(path.join(dataDir, 'runtime', 'x64')) - - // Merge the results. - const uberSet = new Set([...pathSet1, ...pathSet2]) - - // Validate JAVA_HOME. - const jHome = JavaGuard._scanJavaHome() - if(jHome != null && jHome.indexOf('(x86)') === -1){ - uberSet.add(jHome) - } - - let pathArr = await this._validateJavaRootSet(uberSet) - pathArr = JavaGuard._sortValidJavaArray(pathArr) - - if(pathArr.length > 0){ - return pathArr[0].execPath - } else { - return null - } - - } - - /** - * Attempts to find a valid x64 installation of Java on MacOS. - * The system JVM directory is scanned for possible installations. - * The JAVA_HOME enviroment variable and internet plugins directory - * are also scanned and validated. - * - * Higher versions > Lower versions - * If versions are equal, JRE > JDK. - * - * @param {string} dataDir The base launcher directory. - * @returns {Promise.} A Promise which resolves to the executable path of a valid - * x64 Java installation. If none are found, null is returned. - * - * Added: On the system with ARM architecture attempts to find aarch64 Java. - * - */ - async _darwinJavaValidate(dataDir){ - - const pathSet1 = await JavaGuard._scanFileSystem('/Library/Java/JavaVirtualMachines') - const pathSet2 = await JavaGuard._scanFileSystem(path.join(dataDir, 'runtime', 'x64')) - - const uberSet = new Set([...pathSet1, ...pathSet2]) - - // Check Internet Plugins folder. - const iPPath = JavaGuard._scanInternetPlugins() - if(iPPath != null){ - uberSet.add(iPPath) - } - - // Check the JAVA_HOME environment variable. - let jHome = JavaGuard._scanJavaHome() - if(jHome != null){ - // Ensure we are at the absolute root. - if(jHome.contains('/Contents/Home')){ - jHome = jHome.substring(0, jHome.indexOf('/Contents/Home')) - } - uberSet.add(jHome) - } - - let pathArr = await this._validateJavaRootSet(uberSet) - pathArr = JavaGuard._sortValidJavaArray(pathArr) - - if(pathArr.length > 0){ - - // TODO Revise this a bit, seems to work for now. Discovery logic should - // probably just filter out the invalid architectures before it even - // gets to this point. - if (isARM64) { - return pathArr.find(({ isARM }) => isARM)?.execPath ?? null - } else { - return pathArr.find(({ isARM }) => !isARM)?.execPath ?? null - } - - } else { - return null - } - } - - /** - * Attempts to find a valid x64 installation of Java on Linux. - * The system JVM directory is scanned for possible installations. - * The JAVA_HOME enviroment variable is also scanned and validated. - * - * Higher versions > Lower versions - * If versions are equal, JRE > JDK. - * - * @param {string} dataDir The base launcher directory. - * @returns {Promise.} A Promise which resolves to the executable path of a valid - * x64 Java installation. If none are found, null is returned. - */ - async _linuxJavaValidate(dataDir){ - - const pathSet1 = await JavaGuard._scanFileSystem('/usr/lib/jvm') - const pathSet2 = await JavaGuard._scanFileSystem(path.join(dataDir, 'runtime', 'x64')) - - const uberSet = new Set([...pathSet1, ...pathSet2]) - - // Validate JAVA_HOME - const jHome = JavaGuard._scanJavaHome() - if(jHome != null){ - uberSet.add(jHome) - } - - let pathArr = await this._validateJavaRootSet(uberSet) - pathArr = JavaGuard._sortValidJavaArray(pathArr) - - if(pathArr.length > 0){ - return pathArr[0].execPath - } else { - return null - } - } - - /** - * Retrieve the path of a valid x64 Java installation. - * - * @param {string} dataDir The base launcher directory. - * @returns {string} A path to a valid x64 Java installation, null if none found. - */ - async validateJava(dataDir){ - return await this['_' + process.platform + 'JavaValidate'](dataDir) - } - -} - - - - /** * Central object class used for control flow. This object stores data about * categories of downloads. Each category is assigned an identifier with a @@ -981,7 +205,7 @@ class AssetGuard extends EventEmitter { _enqueueOpenJDK(dataDir, mcVersion){ return new Promise((resolve, reject) => { const major = Util.mcVersionAtLeast('1.17', mcVersion) ? '17' : '8' - JavaGuard._latestOpenJDK(major).then(verData => { + latestOpenJDK(major).then(verData => { if(verData != null){ dataDir = path.join(dataDir, 'runtime', 'x64') @@ -1016,7 +240,7 @@ class AssetGuard extends EventEmitter { h = h.substring(0, h.indexOf('/')) } const pos = path.join(dataDir, h) - self.emit('complete', 'java', JavaGuard.javaExecFromRoot(pos)) + self.emit('complete', 'java', javaExecFromRoot(pos)) }) }) } @@ -1053,7 +277,7 @@ class AssetGuard extends EventEmitter { AssetGuard.logger.error(err) } finally { zip.close() - self.emit('complete', 'java', JavaGuard.javaExecFromRoot(pos)) + self.emit('complete', 'java', javaExecFromRoot(pos)) } } @@ -1221,6 +445,5 @@ class AssetGuard extends EventEmitter { } module.exports = { - AssetGuard, - JavaGuard + AssetGuard } diff --git a/app/assets/js/scripts/landing.js b/app/assets/js/scripts/landing.js index a36f870..5c096d2 100644 --- a/app/assets/js/scripts/landing.js +++ b/app/assets/js/scripts/landing.js @@ -19,6 +19,13 @@ const { DistributionIndexProcessor, MojangIndexProcessor } = require('helios-core/dl') +const { + getDefaultSemverRange, + validateSelectedJvm, + ensureJavaDirIsRoot, + javaExecFromRoot, + discoverBestJvmInstallation +} = require('helios-core/java') // Internal Requirements const DiscordWrapper = require('./assets/js/discordwrapper') @@ -97,22 +104,21 @@ document.getElementById('launch_button').addEventListener('click', async (e) => const mcVersion = (await DistroAPI.getDistribution()).getServerById(ConfigManager.getSelectedServer()).rawServer.minecraftVersion const jExe = ConfigManager.getJavaExecutable(ConfigManager.getSelectedServer()) if(jExe == null){ - asyncSystemScan(mcVersion) + await asyncSystemScan(mcVersion) } else { setLaunchDetails(Lang.queryJS('landing.launch.pleaseWait')) toggleLaunchArea(true) setLaunchPercentage(0, 100) - const jg = new JavaGuard(mcVersion) - jg._validateJavaBinary(jExe).then(async v => { - loggerLanding.info('Java version meta', v) - if(v.valid){ - await dlAsync() - } else { - asyncSystemScan(mcVersion) - } - }) + // TODO Update to use semver range + const details = await validateSelectedJvm(ensureJavaDirIsRoot(execPath), getDefaultSemverRange(mcVer)) + if(details != null){ + loggerLanding.info('Jvm Details', details) + await dlAsync() + } else { + await asyncSystemScan(mcVersion) + } } }) @@ -284,9 +290,6 @@ function showLaunchFailure(title, desc){ /* System (Java) Scan */ -let sysAEx -let scanAt - let extractListener /** @@ -295,178 +298,159 @@ let extractListener * @param {string} mcVersion The Minecraft version we are scanning for. * @param {boolean} launchAfter Whether we should begin to launch after scanning. */ -function asyncSystemScan(mcVersion, launchAfter = true){ +async function asyncSystemScan(mcVersion, launchAfter = true){ - setLaunchDetails('Please wait..') + setLaunchDetails('Checking system info..') toggleLaunchArea(true) setLaunchPercentage(0, 100) - const forkEnv = JSON.parse(JSON.stringify(process.env)) - forkEnv.CONFIG_DIRECT_PATH = ConfigManager.getLauncherDirectory() - - // Fork a process to run validations. - sysAEx = cp.fork(path.join(__dirname, 'assets', 'js', 'assetexec.js'), [ - 'JavaGuard', - mcVersion - ], { - env: forkEnv, - stdio: 'pipe' - }) - // Stdout - sysAEx.stdio[1].setEncoding('utf8') - sysAEx.stdio[1].on('data', (data) => { - console.log(`\x1b[32m[SysAEx]\x1b[0m ${data}`) - }) - // Stderr - sysAEx.stdio[2].setEncoding('utf8') - sysAEx.stdio[2].on('data', (data) => { - console.log(`\x1b[31m[SysAEx]\x1b[0m ${data}`) - }) - const javaVer = mcVersionAtLeast('1.17', mcVersion) ? '17' : '8' - - sysAEx.on('message', async (m) => { - if(m.context === 'validateJava'){ - if(m.result == null){ - // If the result is null, no valid Java installation was found. - // Show this information to the user. + const jvmDetails = await discoverBestJvmInstallation( + ConfigManager.getDataDirectory(), + getDefaultSemverRange(mcVersion) + ) + + if(jvmDetails == null) { + // If the result is null, no valid Java installation was found. + // Show this information to the user. + setOverlayContent( + 'No Compatible
Java Installation Found', + `In order to join WesterosCraft, you need a 64-bit installation of Java ${javaVer}. Would you like us to install a copy?`, + 'Install Java', + 'Install Manually' + ) + setOverlayHandler(() => { + setLaunchDetails('Preparing Java Download..') + + // TODO Kick off JDK download. + + toggleOverlay(false) + }) + setDismissHandler(() => { + $('#overlayContent').fadeOut(250, () => { + //$('#overlayDismiss').toggle(false) setOverlayContent( - 'No Compatible
Java Installation Found', - `In order to join WesterosCraft, you need a 64-bit installation of Java ${javaVer}. Would you like us to install a copy?`, - 'Install Java', - 'Install Manually' + 'Java is Required
to Launch', + `A valid x64 installation of Java ${javaVer} is required to launch.

Please refer to our Java Management Guide for instructions on how to manually install Java.`, + 'I Understand', + 'Go Back' ) setOverlayHandler(() => { - setLaunchDetails('Preparing Java Download..') - sysAEx.send({task: 'changeContext', class: 'AssetGuard', args: [ConfigManager.getCommonDirectory(),ConfigManager.getJavaExecutable(ConfigManager.getSelectedServer())]}) - sysAEx.send({task: 'execute', function: '_enqueueOpenJDK', argsArr: [ConfigManager.getDataDirectory(), mcVersion]}) + toggleLaunchArea(false) toggleOverlay(false) }) setDismissHandler(() => { - $('#overlayContent').fadeOut(250, () => { - //$('#overlayDismiss').toggle(false) - setOverlayContent( - 'Java is Required
to Launch', - `A valid x64 installation of Java ${javaVer} is required to launch.

Please refer to our Java Management Guide for instructions on how to manually install Java.`, - 'I Understand', - 'Go Back' - ) - setOverlayHandler(() => { - toggleLaunchArea(false) - toggleOverlay(false) - }) - setDismissHandler(() => { - toggleOverlay(false, true) - asyncSystemScan() - }) - $('#overlayContent').fadeIn(250) - }) + toggleOverlay(false, true) + + // TODO Change this flow + // Should be a separate function probably. + asyncSystemScan() }) - toggleOverlay(true, true) + $('#overlayContent').fadeIn(250) + }) + }) + toggleOverlay(true, true) + } else { + // Java installation found, use this to launch the game. + const javaExec = javaExecFromRoot(jvmDetails.path) + ConfigManager.setJavaExecutable(ConfigManager.getSelectedServer(), javaExec) + ConfigManager.save() - } else { - // Java installation found, use this to launch the game. - ConfigManager.setJavaExecutable(ConfigManager.getSelectedServer(), m.result) - ConfigManager.save() + // We need to make sure that the updated value is on the settings UI. + // Just incase the settings UI is already open. + settingsJavaExecVal.value = javaExec + await populateJavaExecDetails(settingsJavaExecVal.value) - // We need to make sure that the updated value is on the settings UI. - // Just incase the settings UI is already open. - settingsJavaExecVal.value = m.result - await populateJavaExecDetails(settingsJavaExecVal.value) - - if(launchAfter){ - await dlAsync() - } - sysAEx.disconnect() - } - } else if(m.context === '_enqueueOpenJDK'){ - - if(m.result === true){ - - // Oracle JRE enqueued successfully, begin download. - setLaunchDetails('Downloading Java..') - sysAEx.send({task: 'execute', function: 'processDlQueues', argsArr: [[{id:'java', limit:1}]]}) - - } else { - - // Oracle JRE enqueue failed. Probably due to a change in their website format. - // User will have to follow the guide to install Java. - setOverlayContent( - 'Unexpected Issue:
Java Download Failed', - 'Unfortunately we\'ve encountered an issue while attempting to install Java. You will need to manually install a copy. Please check out our Troubleshooting Guide for more details and instructions.', - 'I Understand' - ) - setOverlayHandler(() => { - toggleOverlay(false) - toggleLaunchArea(false) - }) - toggleOverlay(true) - sysAEx.disconnect() - - } - - } else if(m.context === 'progress'){ - - switch(m.data){ - case 'download': - // Downloading.. - setDownloadPercentage(m.value, m.total, m.percent) - break - } - - } else if(m.context === 'complete'){ - - switch(m.data){ - case 'download': { - // Show installing progress bar. - remote.getCurrentWindow().setProgressBar(2) - - // Wait for extration to complete. - const eLStr = 'Extracting' - let dotStr = '' - setLaunchDetails(eLStr) - extractListener = setInterval(() => { - if(dotStr.length >= 3){ - dotStr = '' - } else { - dotStr += '.' - } - setLaunchDetails(eLStr + dotStr) - }, 750) - break - } - case 'java': - // Download & extraction complete, remove the loading from the OS progress bar. - remote.getCurrentWindow().setProgressBar(-1) - - // Extraction completed successfully. - ConfigManager.setJavaExecutable(ConfigManager.getSelectedServer(), m.args[0]) - ConfigManager.save() - - if(extractListener != null){ - clearInterval(extractListener) - extractListener = null - } - - setLaunchDetails('Java Installed!') - - if(launchAfter){ - await dlAsync() - } - - sysAEx.disconnect() - break - } - - } else if(m.context === 'error'){ - console.log(m.error) + // TODO Move this out, separate concerns. + if(launchAfter){ + await dlAsync() } - }) + } + + // TODO Integrate into assetguard 2. + // if(m.context === '_enqueueOpenJDK'){ - // Begin system Java scan. - setLaunchDetails('Checking system info..') - sysAEx.send({task: 'execute', function: 'validateJava', argsArr: [ConfigManager.getDataDirectory()]}) + // if(m.result === true){ + + // // Oracle JRE enqueued successfully, begin download. + // setLaunchDetails('Downloading Java..') + // sysAEx.send({task: 'execute', function: 'processDlQueues', argsArr: [[{id:'java', limit:1}]]}) + + // } else { + + // // Oracle JRE enqueue failed. Probably due to a change in their website format. + // // User will have to follow the guide to install Java. + // setOverlayContent( + // 'Unexpected Issue:
Java Download Failed', + // 'Unfortunately we\'ve encountered an issue while attempting to install Java. You will need to manually install a copy. Please check out our Troubleshooting Guide for more details and instructions.', + // 'I Understand' + // ) + // setOverlayHandler(() => { + // toggleOverlay(false) + // toggleLaunchArea(false) + // }) + // toggleOverlay(true) + // sysAEx.disconnect() + + // } + + // } else if(m.context === 'progress'){ + + // switch(m.data){ + // case 'download': + // // Downloading.. + // setDownloadPercentage(m.value, m.total, m.percent) + // break + // } + + // } else if(m.context === 'complete'){ + + // switch(m.data){ + // case 'download': { + // // Show installing progress bar. + // remote.getCurrentWindow().setProgressBar(2) + + // // Wait for extration to complete. + // const eLStr = 'Extracting' + // let dotStr = '' + // setLaunchDetails(eLStr) + // extractListener = setInterval(() => { + // if(dotStr.length >= 3){ + // dotStr = '' + // } else { + // dotStr += '.' + // } + // setLaunchDetails(eLStr + dotStr) + // }, 750) + // break + // } + // case 'java': + // // Download & extraction complete, remove the loading from the OS progress bar. + // remote.getCurrentWindow().setProgressBar(-1) + + // // Extraction completed successfully. + // ConfigManager.setJavaExecutable(ConfigManager.getSelectedServer(), m.args[0]) + // ConfigManager.save() + + // if(extractListener != null){ + // clearInterval(extractListener) + // extractListener = null + // } + + // setLaunchDetails('Java Installed!') + + // if(launchAfter){ + // await dlAsync() + // } + + // sysAEx.disconnect() + // break + // } + + // } else if(m.context === 'error'){ + // console.log(m.error) + // } } @@ -568,7 +552,6 @@ async function dlAsync(login = true) { serv.rawServer.id ) - // TODO need to load these. const forgeData = await distributionIndexProcessor.loadForgeVersionJson(serv) const versionData = await mojangIndexProcessor.getVersionJson() diff --git a/app/assets/js/scripts/settings.js b/app/assets/js/scripts/settings.js index 425c8a6..3a4beb9 100644 --- a/app/assets/js/scripts/settings.js +++ b/app/assets/js/scripts/settings.js @@ -2,7 +2,6 @@ const os = require('os') const semver = require('semver') -const { JavaGuard } = require('./assets/js/assetguard') const DropinModUtil = require('./assets/js/dropinmodutil') const { MSFT_OPCODE, MSFT_REPLY_TYPE, MSFT_ERROR } = require('./assets/js/ipcconstants') @@ -1350,21 +1349,19 @@ function populateMemoryStatus(){ * @param {string} execPath The executable path to populate against. */ async function populateJavaExecDetails(execPath){ - const jg = new JavaGuard((await DistroAPI.getDistribution()).getServerById(ConfigManager.getSelectedServer()).rawServer.minecraftVersion) - jg._validateJavaBinary(execPath).then(v => { - if(v.valid){ - const vendor = v.vendor != null ? ` (${v.vendor})` : '' - if(v.version.major < 9) { - settingsJavaExecDetails.innerHTML = `Selected: Java ${v.version.major} Update ${v.version.update} (x${v.arch})${vendor}` - } else { - settingsJavaExecDetails.innerHTML = `Selected: Java ${v.version.major}.${v.version.minor}.${v.version.revision} (x${v.arch})${vendor}` - } - } else { - settingsJavaExecDetails.innerHTML = 'Invalid Selection' - } - }) + const mcVer = (await DistroAPI.getDistribution()).getServerById(ConfigManager.getSelectedServer()).rawServer.minecraftVersion + + // TODO Update to use semver range + const details = await validateSelectedJvm(ensureJavaDirIsRoot(execPath), getDefaultSemverRange(mcVer)) + + if(details != null) { + settingsJavaExecDetails.innerHTML = `Selected: Java ${details.semverStr} (${vendor})` + } else { + settingsJavaExecDetails.innerHTML = 'Invalid Selection' + } } +// TODO Update to use semver range async function populateJavaReqDesc() { const mcVer = (await DistroAPI.getDistribution()).getServerById(ConfigManager.getSelectedServer()).rawServer.minecraftVersion if(mcVersionAtLeast('1.17', mcVer)) { @@ -1374,6 +1371,7 @@ async function populateJavaReqDesc() { } } +// TODO Update to use semver range async function populateJvmOptsLink() { const mcVer = (await DistroAPI.getDistribution()).getServerById(ConfigManager.getSelectedServer()).rawServer.minecraftVersion if(mcVersionAtLeast('1.17', mcVer)) { diff --git a/package-lock.json b/package-lock.json index 6cf9776..2645c0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,12 +21,10 @@ "got": "^11.8.5", "helios-distribution-types": "^1.1.0", "jquery": "^3.6.1", - "node-disk-info": "^1.3.0", "node-stream-zip": "^1.15.0", "request": "^2.88.2", "semver": "^7.3.8", - "tar-fs": "^2.1.1", - "winreg": "^1.2.4" + "tar-fs": "^2.1.1" }, "devDependencies": { "electron": "^23.0.0", @@ -2286,6 +2284,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -2785,17 +2784,6 @@ "dev": true, "optional": true }, - "node_modules/node-disk-info": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-disk-info/-/node-disk-info-1.3.0.tgz", - "integrity": "sha512-NEx858vJZ0AoBtmD/ChBIHLjFTF28xCsDIgmFl4jtGKsvlUx9DU/OrMDjvj3qp/E4hzLN0HvTg7eJx5XFQvbeg==", - "dependencies": { - "iconv-lite": "^0.6.2" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -3789,11 +3777,6 @@ "node": ">= 8" } }, - "node_modules/winreg": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.4.tgz", - "integrity": "sha512-IHpzORub7kYlb8A43Iig3reOvlcBJGX9gZ0WycHhghHtA65X0LYnMRuJs+aH1abVnMJztQkvQNlltnbPi5aGIA==" - }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index 94a7f66..e490bb1 100644 --- a/package.json +++ b/package.json @@ -36,12 +36,10 @@ "helios-core": "~0.2.0-pre.1", "helios-distribution-types": "^1.1.0", "jquery": "^3.6.1", - "node-disk-info": "^1.3.0", "node-stream-zip": "^1.15.0", "request": "^2.88.2", "semver": "^7.3.8", - "tar-fs": "^2.1.1", - "winreg": "^1.2.4" + "tar-fs": "^2.1.1" }, "devDependencies": { "electron": "^23.0.0",