From d9291d82e57c83741400e4135dfd55cd7afc463e Mon Sep 17 00:00:00 2001 From: Daniel Scalzi Date: Sat, 25 Feb 2023 03:09:22 -0500 Subject: [PATCH] Progress checkin, mostly works. --- app/assets/js/assetguard.js | 42 +--- app/assets/js/processbuilder.js | 70 +++--- app/assets/js/scripts/landing.js | 410 ++++++++++++------------------- 3 files changed, 190 insertions(+), 332 deletions(-) diff --git a/app/assets/js/assetguard.js b/app/assets/js/assetguard.js index 2063cb58..eb13a9a5 100644 --- a/app/assets/js/assetguard.js +++ b/app/assets/js/assetguard.js @@ -1644,43 +1644,6 @@ class AssetGuard extends EventEmitter { } } - // _enqueueMojangJRE(dir){ - // return new Promise((resolve, reject) => { - // // Mojang does not host the JRE for linux. - // if(process.platform === 'linux'){ - // resolve(false) - // } - // AssetGuard.loadMojangLauncherData().then(data => { - // if(data != null) { - - // try { - // const mJRE = data[Library.mojangFriendlyOS()]['64'].jre - // const url = mJRE.url - - // request.head(url, (err, resp, body) => { - // if(err){ - // resolve(false) - // } else { - // const name = url.substring(url.lastIndexOf('/')+1) - // const fDir = path.join(dir, name) - // const jre = new Asset('jre' + mJRE.version, mJRE.sha1, resp.headers['content-length'], url, fDir) - // this.java = new DLTracker([jre], jre.size, a => { - // fs.readFile(a.to, (err, data) => { - // // Data buffer needs to be decompressed from lzma, - // // not really possible using node.js - // }) - // }) - // } - // }) - // } catch (err){ - // resolve(false) - // } - - // } - // }) - // }) - // } - // #endregion @@ -1894,9 +1857,6 @@ class AssetGuard extends EventEmitter { } module.exports = { - Util, AssetGuard, - JavaGuard, - Asset, - Library + JavaGuard } \ No newline at end of file diff --git a/app/assets/js/processbuilder.js b/app/assets/js/processbuilder.js index b9cddac9..50ea1727 100644 --- a/app/assets/js/processbuilder.js +++ b/app/assets/js/processbuilder.js @@ -7,7 +7,6 @@ const { getMojangOS, isLibraryCompatible, mcVersionAtLeast } = require('helios- const { Type } = require('helios-distribution-types') const os = require('os') const path = require('path') -const { URL } = require('url') const ConfigManager = require('./configmanager') @@ -16,7 +15,7 @@ const logger = LoggerUtil.getLogger('ProcessBuilder') class ProcessBuilder { constructor(distroServer, versionData, forgeData, authUser, launcherVersion){ - this.gameDir = path.join(ConfigManager.getInstanceDirectory(), distroServer.getID()) + this.gameDir = path.join(ConfigManager.getInstanceDirectory(), distroServer.rawServer.id) this.commonDir = ConfigManager.getCommonDirectory() this.server = distroServer this.versionData = versionData @@ -41,10 +40,10 @@ class ProcessBuilder { process.throwDeprecation = true this.setupLiteLoader() logger.info('Using liteloader:', this.usingLiteLoader) - const modObj = this.resolveModConfiguration(ConfigManager.getModConfiguration(this.server.getID()).mods, this.server.getModules()) + const modObj = this.resolveModConfiguration(ConfigManager.getModConfiguration(this.server.rawServer.id).mods, this.server.modules) // Mod list below 1.13 - if(!mcVersionAtLeast('1.13', this.server.getMinecraftVersion())){ + if(!mcVersionAtLeast('1.13', this.server.rawServer.minecraftVersion)){ this.constructJSONModList('forge', modObj.fMods, true) if(this.usingLiteLoader){ this.constructJSONModList('liteloader', modObj.lMods, true) @@ -54,14 +53,14 @@ class ProcessBuilder { const uberModArr = modObj.fMods.concat(modObj.lMods) let args = this.constructJVMArguments(uberModArr, tempNativePath) - if(mcVersionAtLeast('1.13', this.server.getMinecraftVersion())){ + if(mcVersionAtLeast('1.13', this.server.rawServer.minecraftVersion)){ //args = args.concat(this.constructModArguments(modObj.fMods)) args = args.concat(this.constructModList(modObj.fMods)) } logger.info('Launch Arguments:', args) - const child = child_process.spawn(ConfigManager.getJavaExecutable(this.server.getID()), args, { + const child = child_process.spawn(ConfigManager.getJavaExecutable(this.server.rawServer.id), args, { cwd: this.gameDir, detached: ConfigManager.getLaunchDetached() }) @@ -137,15 +136,15 @@ class ProcessBuilder { if(!ll.getRequired().value){ const modCfg = ConfigManager.getModConfiguration(this.server.rawServer.id).mods if(ProcessBuilder.isModEnabled(modCfg[ll.getVersionlessMavenIdentifier()], ll.getRequired())){ - if(fs.existsSync(ll.localPath)){ + if(fs.existsSync(ll.getPath())){ this.usingLiteLoader = true - this.llPath = ll.localPath + this.llPath = ll.getPath() } } } else { - if(fs.existsSync(ll.localPath)){ + if(fs.existsSync(ll.getPath())){ this.usingLiteLoader = true - this.llPath = ll.localPath + this.llPath = ll.getPath() } } } @@ -307,14 +306,11 @@ class ProcessBuilder { } _processAutoConnectArg(args){ - if(ConfigManager.getAutoConnect() && this.server.isAutoConnect()){ - const serverURL = new URL('my://' + this.server.getAddress()) + if(ConfigManager.getAutoConnect() && this.server.rawServer.autoconnect){ args.push('--server') - args.push(serverURL.hostname) - if(serverURL.port){ - args.push('--port') - args.push(serverURL.port) - } + args.push(this.server.hostname) + args.push('--port') + args.push(this.server.port) } } @@ -326,7 +322,7 @@ class ProcessBuilder { * @returns {Array.} An array containing the full JVM arguments for this process. */ constructJVMArguments(mods, tempNativePath){ - if(mcVersionAtLeast('1.13', this.server.getMinecraftVersion())){ + if(mcVersionAtLeast('1.13', this.server.rawServer.minecraftVersion)){ return this._constructJVMArguments113(mods, tempNativePath) } else { return this._constructJVMArguments112(mods, tempNativePath) @@ -354,9 +350,9 @@ class ProcessBuilder { args.push('-Xdock:name=HeliosLauncher') args.push('-Xdock:icon=' + path.join(__dirname, '..', 'images', 'minecraft.icns')) } - args.push('-Xmx' + ConfigManager.getMaxRAM(this.server.getID())) - args.push('-Xms' + ConfigManager.getMinRAM(this.server.getID())) - args = args.concat(ConfigManager.getJVMOptions(this.server.getID())) + args.push('-Xmx' + ConfigManager.getMaxRAM(this.server.rawServer.id)) + args.push('-Xms' + ConfigManager.getMinRAM(this.server.rawServer.id)) + args = args.concat(ConfigManager.getJVMOptions(this.server.rawServer.id)) args.push('-Djava.library.path=' + tempNativePath) // Main Java Class @@ -405,9 +401,9 @@ class ProcessBuilder { args.push('-Xdock:name=HeliosLauncher') args.push('-Xdock:icon=' + path.join(__dirname, '..', 'images', 'minecraft.icns')) } - args.push('-Xmx' + ConfigManager.getMaxRAM(this.server.getID())) - args.push('-Xms' + ConfigManager.getMinRAM(this.server.getID())) - args = args.concat(ConfigManager.getJVMOptions(this.server.getID())) + args.push('-Xmx' + ConfigManager.getMaxRAM(this.server.rawServer.id)) + args.push('-Xms' + ConfigManager.getMinRAM(this.server.rawServer.id)) + args = args.concat(ConfigManager.getJVMOptions(this.server.rawServer.id)) // Main Java Class args.push(this.forgeData.mainClass) @@ -471,7 +467,7 @@ class ProcessBuilder { break case 'version_name': //val = versionData.id - val = this.server.getID() + val = this.server.rawServer.id break case 'game_directory': val = this.gameDir @@ -569,7 +565,7 @@ class ProcessBuilder { break case 'version_name': //val = versionData.id - val = this.server.getID() + val = this.server.rawServer.id break case 'game_directory': val = this.gameDir @@ -668,7 +664,7 @@ class ProcessBuilder { classpathArg(mods, tempNativePath){ let cpArgs = [] - if(!mcVersionAtLeast('1.17', this.server.getMinecraftVersion())) { + if(!mcVersionAtLeast('1.17', this.server.rawServer.minecraftVersion)) { // Add the version.jar to the classpath. // Must not be added to the classpath for Forge 1.17+. const version = this.versionData.id @@ -826,15 +822,15 @@ class ProcessBuilder { * @returns {{[id: string]: string}} An object containing the paths of each library this server requires. */ _resolveServerLibraries(mods){ - const mdls = this.server.getModules() + const mdls = this.server.modules let libs = {} // Locate Forge/Libraries for(let mdl of mdls){ - const type = mdl.getType() + const type = mdl.rawModule.type if(type === Type.ForgeHosted || type === Type.Library){ - libs[mdl.getVersionlessID()] = mdl.getArtifact().getPath() - if(mdl.hasSubModules()){ + libs[mdl.getVersionlessMavenIdentifier()] = mdl.getPath() + if(mdl.subModules.length > 0){ const res = this._resolveModuleLibraries(mdl) if(res.length > 0){ libs = {...libs, ...res} @@ -863,20 +859,20 @@ class ProcessBuilder { * @returns {Array.} An array containing the paths of each library this module requires. */ _resolveModuleLibraries(mdl){ - if(!mdl.hasSubModules()){ + if(!mdl.subModules.length > 0){ return [] } let libs = [] - for(let sm of mdl.getSubModules()){ - if(sm.getType() === Type.Library){ + for(let sm of mdl.subModules){ + if(sm.rawModule.type === Type.Library){ - if(sm.getClasspath()) { - libs.push(sm.getArtifact().getPath()) + if(sm.rawModule.classpath ?? true) { + libs.push(sm.getPath()) } } // If this module has submodules, we need to resolve the libraries for those. // To avoid unnecessary recursive calls, base case is checked here. - if(mdl.hasSubModules()){ + if(mdl.subModules.length > 0){ const res = this._resolveModuleLibraries(sm) if(res.length > 0){ libs = libs.concat(res) diff --git a/app/assets/js/scripts/landing.js b/app/assets/js/scripts/landing.js index 6dd31779..22e5d912 100644 --- a/app/assets/js/scripts/landing.js +++ b/app/assets/js/scripts/landing.js @@ -5,13 +5,24 @@ const cp = require('child_process') const crypto = require('crypto') const { URL } = require('url') -const { MojangRestAPI, getServerStatus } = require('helios-core/mojang') +const { + MojangRestAPI, + getServerStatus +} = require('helios-core/mojang') +const { + RestResponseStatus, + isDisplayableError, + mcVersionAtLeast +} = require('helios-core/common') +const { + FullRepair, + DistributionIndexProcessor, + MojangIndexProcessor +} = require('helios-core/dl') // Internal Requirements const DiscordWrapper = require('./assets/js/discordwrapper') const ProcessBuilder = require('./assets/js/processbuilder') -const { RestResponseStatus, isDisplayableError, mcVersionAtLeast } = require('helios-core/common') -const { stdout } = require('process') // Launch Elements const launch_content = document.getElementById('launch_content') @@ -53,26 +64,22 @@ function setLaunchDetails(details){ /** * Set the value of the loading progress bar and display that value. * - * @param {number} value The progress value. - * @param {number} max The total size. - * @param {number|string} percent Optional. The percentage to display on the progress label. + * @param {number} percent Percentage (0-100) */ -function setLaunchPercentage(value, max, percent = ((value/max)*100)){ - launch_progress.setAttribute('max', max) - launch_progress.setAttribute('value', value) +function setLaunchPercentage(percent){ + launch_progress.setAttribute('max', 100) + launch_progress.setAttribute('value', percent) launch_progress_label.innerHTML = percent + '%' } /** * Set the value of the OS progress bar and display that on the UI. * - * @param {number} value The progress value. - * @param {number} max The total download size. - * @param {number|string} percent Optional. The percentage to display on the progress label. + * @param {number} percent Percentage (0-100) */ -function setDownloadPercentage(value, max, percent = ((value/max)*100)){ - remote.getCurrentWindow().setProgressBar(value/max) - setLaunchPercentage(value, max, percent) +function setDownloadPercentage(percent){ + remote.getCurrentWindow().setProgressBar(percent/100) + setLaunchPercentage(percent) } /** @@ -98,10 +105,10 @@ document.getElementById('launch_button').addEventListener('click', async (e) => setLaunchPercentage(0, 100) const jg = new JavaGuard(mcVersion) - jg._validateJavaBinary(jExe).then((v) => { + jg._validateJavaBinary(jExe).then(async v => { loggerLanding.info('Java version meta', v) if(v.valid){ - dlAsync() + await dlAsync() } else { asyncSystemScan(mcVersion) } @@ -369,7 +376,7 @@ function asyncSystemScan(mcVersion, launchAfter = true){ await populateJavaExecDetails(settingsJavaExecVal.value) if(launchAfter){ - dlAsync() + await dlAsync() } sysAEx.disconnect() } @@ -445,7 +452,7 @@ function asyncSystemScan(mcVersion, launchAfter = true){ setLaunchDetails('Java Installed!') if(launchAfter){ - dlAsync() + await dlAsync() } sysAEx.disconnect() @@ -473,18 +480,28 @@ const GAME_JOINED_REGEX = /\[.+\]: Sound engine started/ const GAME_LAUNCH_REGEX = /^\[.+\]: (?:MinecraftForge .+ Initialized|ModLauncher .+ starting: .+)$/ const MIN_LINGER = 5000 -let aEx -let serv -let versionData -let forgeData - -let progressListener - -function dlAsync(login = true){ +async function dlAsync(login = true) { // Login parameter is temporary for debug purposes. Allows testing the validation/downloads without // launching the game. + const loggerLaunchSuite = LoggerUtil.getLogger('LaunchSuite') + + setLaunchDetails('Loading server information..') + + let distro + + try { + distro = await DistroAPI.refreshDistributionOrFallback() + onDistroRefresh(distro) + } catch(err) { + loggerLaunchSuite.error('Unable to refresh distribution index.', err) + showLaunchFailure('Fatal Error', 'Could not load a copy of the distribution index. See the console (CTRL + Shift + i) for more details.') + return + } + + const serv = distro.getServerById(ConfigManager.getSelectedServer()) + if(login) { if(ConfigManager.getSelectedAccount() == null){ loggerLanding.error('You must be logged into an account.') @@ -496,262 +513,147 @@ function dlAsync(login = true){ toggleLaunchArea(true) setLaunchPercentage(0, 100) - const loggerLaunchSuite = LoggerUtil.getLogger('LaunchSuite') - - const forkEnv = JSON.parse(JSON.stringify(process.env)) - forkEnv.CONFIG_DIRECT_PATH = ConfigManager.getLauncherDirectory() - - // Start AssetExec to run validations and downloads in a forked process. - aEx = cp.fork(path.join(__dirname, 'assets', 'js', 'assetexec.js'), [ - 'AssetGuard', + const fullRepairModule = new FullRepair( ConfigManager.getCommonDirectory(), - ConfigManager.getJavaExecutable(ConfigManager.getSelectedServer()) - ], { - env: forkEnv, - stdio: 'pipe' - }) - // Stdout - aEx.stdio[1].setEncoding('utf8') - aEx.stdio[1].on('data', (data) => { - console.log(`\x1b[32m[AEx]\x1b[0m ${data}`) - }) - // Stderr - aEx.stdio[2].setEncoding('utf8') - aEx.stdio[2].on('data', (data) => { - console.log(`\x1b[31m[AEx]\x1b[0m ${data}`) - }) - aEx.on('error', (err) => { + ConfigManager.getLauncherDirectory(), + ConfigManager.getSelectedServer(), + DistroAPI.isDevMode() + ) + + fullRepairModule.spawnReceiver() + + fullRepairModule.childProcess.on('error', (err) => { loggerLaunchSuite.error('Error during launch', err) showLaunchFailure('Error During Launch', err.message || 'See console (CTRL + Shift + i) for more details.') }) - aEx.on('close', (code, signal) => { + fullRepairModule.childProcess.on('close', (code, _signal) => { if(code !== 0){ loggerLaunchSuite.error(`AssetExec exited with code ${code}, assuming error.`) showLaunchFailure('Error During Launch', 'See console (CTRL + Shift + i) for more details.') } }) - // Establish communications between the AssetExec and current process. - aEx.on('message', async (m) => { + loggerLaunchSuite.info('Validating files.') + setLaunchDetails('Validating file integrity..') + const invalidFileCount = await fullRepairModule.verifyFiles(percent => { + setLaunchPercentage(percent) + }) + setLaunchPercentage(100) - if(m.context === 'validate'){ - switch(m.data){ - case 'distribution': - setLaunchPercentage(20, 100) - loggerLaunchSuite.info('Validated distibution index.') - setLaunchDetails('Loading version information..') - break - case 'version': - setLaunchPercentage(40, 100) - loggerLaunchSuite.info('Version data loaded.') - setLaunchDetails('Validating asset integrity..') - break - case 'assets': - setLaunchPercentage(60, 100) - loggerLaunchSuite.info('Asset Validation Complete') - setLaunchDetails('Validating library integrity..') - break - case 'libraries': - setLaunchPercentage(80, 100) - loggerLaunchSuite.info('Library validation complete.') - setLaunchDetails('Validating miscellaneous file integrity..') - break - case 'files': - setLaunchPercentage(100, 100) - loggerLaunchSuite.info('File validation complete.') - setLaunchDetails('Downloading files..') - break + if(invalidFileCount > 0) { + loggerLaunchSuite.info('Downloading files.') + setLaunchDetails('Downloading files..') + await fullRepairModule.download(percent => { + setDownloadPercentage(percent) + }) + setDownloadPercentage(100) + } else { + loggerLaunchSuite.info('No invalid files, skipping download.') + } + + // Remove download bar. + remote.getCurrentWindow().setProgressBar(-1) + + fullRepairModule.destroyReceiver() + + setLaunchDetails('Preparing to launch..') + + const mojangIndexProcessor = new MojangIndexProcessor( + ConfigManager.getCommonDirectory(), + serv.rawServer.minecraftVersion) + const distributionIndexProcessor = new DistributionIndexProcessor( + ConfigManager.getCommonDirectory(), + distro, + serv.rawServer.id + ) + + // TODO need to load these. + const forgeData = await distributionIndexProcessor.loadForgeVersionJson(serv) + const versionData = await mojangIndexProcessor.getVersionJson() + + if(login) { + const authUser = ConfigManager.getSelectedAccount() + loggerLaunchSuite.info(`Sending selected account (${authUser.displayName}) to ProcessBuilder.`) + let pb = new ProcessBuilder(serv, versionData, forgeData, authUser, remote.app.getVersion()) + setLaunchDetails('Launching game..') + + // const SERVER_JOINED_REGEX = /\[.+\]: \[CHAT\] [a-zA-Z0-9_]{1,16} joined the game/ + const SERVER_JOINED_REGEX = new RegExp(`\\[.+\\]: \\[CHAT\\] ${authUser.displayName} joined the game`) + + const onLoadComplete = () => { + toggleLaunchArea(false) + if(hasRPC){ + DiscordWrapper.updateDetails('Loading game..') } - } else if(m.context === 'progress'){ - switch(m.data){ - case 'assets': { - const perc = (m.value/m.total)*20 - setLaunchPercentage(40+perc, 100, parseInt(40+perc)) - break - } - case 'download': - setDownloadPercentage(m.value, m.total, m.percent) - break - case 'extract': { - // Show installing progress bar. - remote.getCurrentWindow().setProgressBar(2) + proc.stdout.on('data', gameStateChange) + proc.stdout.removeListener('data', tempListener) + proc.stderr.removeListener('data', gameErrorListener) + } + const start = Date.now() - // Download done, extracting. - const eLStr = 'Extracting libraries' - let dotStr = '' - setLaunchDetails(eLStr) - progressListener = setInterval(() => { - if(dotStr.length >= 3){ - dotStr = '' - } else { - dotStr += '.' - } - setLaunchDetails(eLStr + dotStr) - }, 750) - break + // Attach a temporary listener to the client output. + // Will wait for a certain bit of text meaning that + // the client application has started, and we can hide + // the progress bar stuff. + const tempListener = function(data){ + if(GAME_LAUNCH_REGEX.test(data.trim())){ + const diff = Date.now()-start + if(diff < MIN_LINGER) { + setTimeout(onLoadComplete, MIN_LINGER-diff) + } else { + onLoadComplete() } } - } else if(m.context === 'complete'){ - switch(m.data){ - case 'download': - // Download and extraction complete, remove the loading from the OS progress bar. - remote.getCurrentWindow().setProgressBar(-1) - if(progressListener != null){ - clearInterval(progressListener) - progressListener = null - } + } - setLaunchDetails('Preparing to launch..') - break + // Listener for Discord RPC. + const gameStateChange = function(data){ + data = data.trim() + if(SERVER_JOINED_REGEX.test(data)){ + DiscordWrapper.updateDetails('Exploring the Realm!') + } else if(GAME_JOINED_REGEX.test(data)){ + DiscordWrapper.updateDetails('Sailing to Westeros!') } - } else if(m.context === 'error'){ - switch(m.data){ - case 'download': - loggerLaunchSuite.error('Error while downloading:', m.error) - - if(m.error.code === 'ENOENT'){ - showLaunchFailure( - 'Download Error', - 'Could not connect to the file server. Ensure that you are connected to the internet and try again.' - ) - } else { - showLaunchFailure( - 'Download Error', - 'Check the console (CTRL + Shift + i) for more details. Please try again.' - ) - } + } - remote.getCurrentWindow().setProgressBar(-1) - - // Disconnect from AssetExec - aEx.disconnect() - break + const gameErrorListener = function(data){ + data = data.trim() + if(data.indexOf('Could not find or load main class net.minecraft.launchwrapper.Launch') > -1){ + loggerLaunchSuite.error('Game launch failed, LaunchWrapper was not downloaded properly.') + showLaunchFailure('Error During Launch', 'The main file, LaunchWrapper, failed to download properly. As a result, the game cannot launch.

To fix this issue, temporarily turn off your antivirus software and launch the game again.

If you have time, please submit an issue and let us know what antivirus software you use. We\'ll contact them and try to straighten things out.') } - } else if(m.context === 'validateEverything'){ + } - let allGood = true + try { + // Build Minecraft process. + proc = pb.build() - // If these properties are not defined it's likely an error. - if(m.result.forgeData == null || m.result.versionData == null){ - loggerLaunchSuite.error('Error during validation:', m.result) + // Bind listeners to stdout. + proc.stdout.on('data', tempListener) + proc.stderr.on('data', gameErrorListener) - loggerLaunchSuite.error('Error during launch', m.result.error) - showLaunchFailure('Error During Launch', 'Please check the console (CTRL + Shift + i) for more details.') + setLaunchDetails('Done. Enjoy the server!') - allGood = false + // Init Discord Hook + if(distro.rawDistribution.discord != null && serv.rawServerdiscord != null){ + DiscordWrapper.initRPC(distro.rawDistribution.discord, serv.rawServer.discord) + hasRPC = true + proc.on('close', (code, signal) => { + loggerLaunchSuite.info('Shutting down Discord Rich Presence..') + DiscordWrapper.shutdownRPC() + hasRPC = false + proc = null + }) } - forgeData = m.result.forgeData - versionData = m.result.versionData + } catch(err) { - if(login && allGood) { - const authUser = ConfigManager.getSelectedAccount() - loggerLaunchSuite.info(`Sending selected account (${authUser.displayName}) to ProcessBuilder.`) - let pb = new ProcessBuilder(serv, versionData, forgeData, authUser, remote.app.getVersion()) - setLaunchDetails('Launching game..') - - // const SERVER_JOINED_REGEX = /\[.+\]: \[CHAT\] [a-zA-Z0-9_]{1,16} joined the game/ - const SERVER_JOINED_REGEX = new RegExp(`\\[.+\\]: \\[CHAT\\] ${authUser.displayName} joined the game`) - - const onLoadComplete = () => { - toggleLaunchArea(false) - if(hasRPC){ - DiscordWrapper.updateDetails('Loading game..') - } - proc.stdout.on('data', gameStateChange) - proc.stdout.removeListener('data', tempListener) - proc.stderr.removeListener('data', gameErrorListener) - } - const start = Date.now() - - // Attach a temporary listener to the client output. - // Will wait for a certain bit of text meaning that - // the client application has started, and we can hide - // the progress bar stuff. - const tempListener = function(data){ - if(GAME_LAUNCH_REGEX.test(data.trim())){ - const diff = Date.now()-start - if(diff < MIN_LINGER) { - setTimeout(onLoadComplete, MIN_LINGER-diff) - } else { - onLoadComplete() - } - } - } - - // Listener for Discord RPC. - const gameStateChange = function(data){ - data = data.trim() - if(SERVER_JOINED_REGEX.test(data)){ - DiscordWrapper.updateDetails('Exploring the Realm!') - } else if(GAME_JOINED_REGEX.test(data)){ - DiscordWrapper.updateDetails('Sailing to Westeros!') - } - } - - const gameErrorListener = function(data){ - data = data.trim() - if(data.indexOf('Could not find or load main class net.minecraft.launchwrapper.Launch') > -1){ - loggerLaunchSuite.error('Game launch failed, LaunchWrapper was not downloaded properly.') - showLaunchFailure('Error During Launch', 'The main file, LaunchWrapper, failed to download properly. As a result, the game cannot launch.

To fix this issue, temporarily turn off your antivirus software and launch the game again.

If you have time, please submit an issue and let us know what antivirus software you use. We\'ll contact them and try to straighten things out.') - } - } - - try { - // Build Minecraft process. - proc = pb.build() - - // Bind listeners to stdout. - proc.stdout.on('data', tempListener) - proc.stderr.on('data', gameErrorListener) - - setLaunchDetails('Done. Enjoy the server!') - - // Init Discord Hook - const distro = await DistroAPI.getDistribution() - if(distro.rawDistribution.discord != null && serv.rawServerdiscord != null){ - DiscordWrapper.initRPC(distro.rawDistribution.discord, serv.rawServer.discord) - hasRPC = true - proc.on('close', (code, signal) => { - loggerLaunchSuite.info('Shutting down Discord Rich Presence..') - DiscordWrapper.shutdownRPC() - hasRPC = false - proc = null - }) - } - - } catch(err) { - - loggerLaunchSuite.error('Error during launch', err) - showLaunchFailure('Error During Launch', 'Please check the console (CTRL + Shift + i) for more details.') - - } - } - - // Disconnect from AssetExec - aEx.disconnect() + loggerLaunchSuite.error('Error during launch', err) + showLaunchFailure('Error During Launch', 'Please check the console (CTRL + Shift + i) for more details.') } - }) + } - // Begin Validations - - // Validate Forge files. - setLaunchDetails('Loading server information..') - - DistroAPI.refreshDistributionOrFallback() - .then(data => { - onDistroRefresh(data) - serv = data.getServerById(ConfigManager.getSelectedServer()) - aEx.send({task: 'execute', function: 'validateEverything', argsArr: [ConfigManager.getSelectedServer(), DistroAPI.isDevMode()]}) - }) - .catch(err => { - loggerLaunchSuite.error('Unable to refresh distribution index.', err) - showLaunchFailure('Fatal Error', 'Could not load a copy of the distribution index. See the console (CTRL + Shift + i) for more details.') - - // Disconnect from AssetExec - aEx.disconnect() - }) } /**