2018-04-25 17:11:10 -07:00
/ * *
* Script for landing . ejs
* /
2018-04-25 14:40:46 -07:00
// Requirements
2018-04-25 23:39:47 -07:00
const cp = require ( 'child_process' )
2018-06-04 20:08:03 -07:00
const crypto = require ( 'crypto' )
2018-04-25 23:39:47 -07:00
const { URL } = require ( 'url' )
2018-04-25 14:40:46 -07:00
// Internal Requirements
2018-07-22 08:40:15 -07:00
const DiscordWrapper = require ( './assets/js/discordwrapper' )
const Mojang = require ( './assets/js/mojang' )
const ProcessBuilder = require ( './assets/js/processbuilder' )
const ServerStatus = require ( './assets/js/serverstatus' )
2018-04-25 14:40:46 -07:00
// Launch Elements
2018-04-25 23:39:47 -07:00
const launch _content = document . getElementById ( 'launch_content' )
const launch _details = document . getElementById ( 'launch_details' )
const launch _progress = document . getElementById ( 'launch_progress' )
const launch _progress _label = document . getElementById ( 'launch_progress_label' )
const launch _details _text = document . getElementById ( 'launch_details_text' )
const server _selection _button = document . getElementById ( 'server_selection_button' )
2018-04-29 15:05:59 -07:00
const user _text = document . getElementById ( 'user_text' )
2018-04-25 14:40:46 -07:00
2018-04-25 17:11:10 -07:00
/* Launch Progress Wrapper Functions */
/ * *
* Show / hide the loading area .
*
* @ param { boolean } loading True if the loading area should be shown , otherwise false .
* /
function toggleLaunchArea ( loading ) {
if ( loading ) {
launch _details . style . display = 'flex'
launch _content . style . display = 'none'
} else {
launch _details . style . display = 'none'
launch _content . style . display = 'inline-flex'
}
}
/ * *
* Set the details text of the loading area .
*
* @ param { string } details The new text for the loading details .
* /
function setLaunchDetails ( details ) {
launch _details _text . innerHTML = 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 .
* /
function setLaunchPercentage ( value , max , percent = ( ( value / max ) * 100 ) ) {
launch _progress . setAttribute ( 'max' , max )
launch _progress . setAttribute ( 'value' , value )
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 .
* /
function setDownloadPercentage ( value , max , percent = ( ( value / max ) * 100 ) ) {
remote . getCurrentWindow ( ) . setProgressBar ( value / max )
setLaunchPercentage ( value , max , percent )
}
2018-04-26 15:41:26 -07:00
/ * *
* Enable or disable the launch button .
*
* @ param { boolean } val True to enable , false to disable .
* /
function setLaunchEnabled ( val ) {
document . getElementById ( 'launch_button' ) . disabled = ! val
}
2018-04-25 14:40:46 -07:00
// Bind launch button
document . getElementById ( 'launch_button' ) . addEventListener ( 'click' , function ( e ) {
console . log ( 'Launching game..' )
const jExe = ConfigManager . getJavaExecutable ( )
if ( jExe == null ) {
asyncSystemScan ( )
} else {
setLaunchDetails ( 'Please wait..' )
toggleLaunchArea ( true )
setLaunchPercentage ( 0 , 100 )
AssetGuard . _validateJavaBinary ( jExe ) . then ( ( v ) => {
2018-05-15 03:07:28 -07:00
console . log ( v )
if ( v . valid ) {
2018-04-25 14:40:46 -07:00
dlAsync ( )
} else {
asyncSystemScan ( )
}
} )
}
} )
2018-05-30 13:00:07 -07:00
// Bind settings button
document . getElementById ( 'settingsMediaButton' ) . onclick = ( e ) => {
2018-05-30 19:22:17 -07:00
prepareSettings ( )
2018-05-30 13:00:07 -07:00
switchView ( getCurrentView ( ) , VIEWS . settings )
}
2018-06-21 09:38:21 -07:00
// Bind avatar overlay button.
document . getElementById ( 'avatarOverlay' ) . onclick = ( e ) => {
prepareSettings ( )
switchView ( getCurrentView ( ) , VIEWS . settings , 500 , 500 , ( ) => {
settingsNavItemListener ( document . getElementById ( 'settingsNavAccount' ) , false )
} )
}
2018-04-29 15:05:59 -07:00
// Bind selected account
function updateSelectedAccount ( authUser ) {
let username = 'No Account Selected'
2018-07-01 23:01:28 -07:00
if ( authUser != null ) {
if ( authUser . displayName != null ) {
username = authUser . displayName
}
if ( authUser . uuid != null ) {
document . getElementById ( 'avatarContainer' ) . style . backgroundImage = ` url('https://crafatar.com/renders/body/ ${ authUser . uuid } ') `
}
2018-04-29 15:05:59 -07:00
}
user _text . innerHTML = username
}
updateSelectedAccount ( ConfigManager . getSelectedAccount ( ) )
2018-04-25 14:40:46 -07:00
// Bind selected server
2018-04-26 15:41:26 -07:00
function updateSelectedServer ( serverName ) {
if ( serverName == null ) {
serverName = 'No Server Selected'
}
server _selection _button . innerHTML = '\u2022 ' + serverName
}
2018-05-06 22:34:57 -07:00
// Real text is set in uibinder.js on distributionIndexDone.
updateSelectedServer ( 'Loading..' )
2018-04-25 23:39:47 -07:00
server _selection _button . addEventListener ( 'click' , ( e ) => {
e . target . blur ( )
2018-04-26 21:04:09 -07:00
toggleServerSelection ( true )
2018-04-25 23:39:47 -07:00
} )
2018-04-25 14:40:46 -07:00
// Update Mojang Status Color
const refreshMojangStatuses = async function ( ) {
console . log ( 'Refreshing Mojang Statuses..' )
2018-05-17 00:11:44 -07:00
2018-04-25 14:40:46 -07:00
let status = 'grey'
2018-07-22 10:31:15 -07:00
let tooltipEssentialHTML = ''
let tooltipNonEssentialHTML = ''
2018-05-17 00:11:44 -07:00
2018-04-25 14:40:46 -07:00
try {
const statuses = await Mojang . status ( )
greenCount = 0
2018-05-22 05:41:22 -07:00
greyCount = 0
2018-05-17 00:11:44 -07:00
2018-04-25 14:40:46 -07:00
for ( let i = 0 ; i < statuses . length ; i ++ ) {
2018-05-17 00:11:44 -07:00
const service = statuses [ i ]
if ( service . essential ) {
tooltipEssentialHTML += ` <div class="mojangStatusContainer">
< span class = "mojangStatusIcon" style = "color: ${Mojang.statusToHex(service.status)};" > & # 8226 ; < / s p a n >
< span class = "mojangStatusName" > $ { service . name } < / s p a n >
< / d i v > `
} else {
tooltipNonEssentialHTML += ` <div class="mojangStatusContainer">
< span class = "mojangStatusIcon" style = "color: ${Mojang.statusToHex(service.status)};" > & # 8226 ; < / s p a n >
< span class = "mojangStatusName" > $ { service . name } < / s p a n >
< / d i v > `
}
if ( service . status === 'yellow' && status !== 'red' ) {
2018-04-25 14:40:46 -07:00
status = 'yellow'
2018-05-17 00:11:44 -07:00
} else if ( service . status === 'red' ) {
2018-04-25 14:40:46 -07:00
status = 'red'
2018-05-22 05:41:22 -07:00
} else {
if ( service . status === 'grey' ) {
++ greyCount
}
++ greenCount
2018-04-25 14:40:46 -07:00
}
2018-05-17 00:11:44 -07:00
2018-04-25 14:40:46 -07:00
}
2018-05-17 00:11:44 -07:00
2018-05-22 05:41:22 -07:00
if ( greenCount === statuses . length ) {
if ( greyCount === statuses . length ) {
status = 'grey'
} else {
status = 'green'
}
2018-04-25 14:40:46 -07:00
}
2018-05-17 00:11:44 -07:00
2018-04-25 14:40:46 -07:00
} catch ( err ) {
console . warn ( 'Unable to refresh Mojang service status.' )
console . debug ( err )
}
2018-05-17 00:11:44 -07:00
document . getElementById ( 'mojangStatusEssentialContainer' ) . innerHTML = tooltipEssentialHTML
document . getElementById ( 'mojangStatusNonEssentialContainer' ) . innerHTML = tooltipNonEssentialHTML
2018-04-25 14:40:46 -07:00
document . getElementById ( 'mojang_status_icon' ) . style . color = Mojang . statusToHex ( status )
}
2018-04-26 15:41:26 -07:00
const refreshServerStatus = async function ( fade = false ) {
2018-04-25 14:40:46 -07:00
console . log ( 'Refreshing Server Status' )
2018-07-22 08:40:15 -07:00
const serv = DistroManager . getDistribution ( ) . getServer ( ConfigManager . getSelectedServer ( ) )
2018-04-25 14:40:46 -07:00
let pLabel = 'SERVER'
let pVal = 'OFFLINE'
try {
2018-07-22 08:40:15 -07:00
const serverURL = new URL ( 'my://' + serv . getAddress ( ) )
2018-04-25 14:40:46 -07:00
const servStat = await ServerStatus . getStatus ( serverURL . hostname , serverURL . port )
if ( servStat . online ) {
pLabel = 'PLAYERS'
pVal = servStat . onlinePlayers + '/' + servStat . maxPlayers
}
} catch ( err ) {
console . warn ( 'Unable to refresh server status, assuming offline.' )
console . debug ( err )
}
2018-04-26 15:41:26 -07:00
if ( fade ) {
$ ( '#server_status_wrapper' ) . fadeOut ( 250 , ( ) => {
document . getElementById ( 'landingPlayerLabel' ) . innerHTML = pLabel
document . getElementById ( 'player_count' ) . innerHTML = pVal
$ ( '#server_status_wrapper' ) . fadeIn ( 500 )
} )
} else {
document . getElementById ( 'landingPlayerLabel' ) . innerHTML = pLabel
document . getElementById ( 'player_count' ) . innerHTML = pVal
}
2018-04-25 14:40:46 -07:00
}
refreshMojangStatuses ( )
2018-05-06 22:34:57 -07:00
// Server Status is refreshed in uibinder.js on distributionIndexDone.
2018-04-25 14:40:46 -07:00
// Set refresh rate to once every 5 minutes.
2018-04-26 15:41:26 -07:00
let mojangStatusListener = setInterval ( ( ) => refreshMojangStatuses ( true ) , 300000 )
let serverStatusListener = setInterval ( ( ) => refreshServerStatus ( true ) , 300000 )
2018-04-25 14:40:46 -07:00
/* System (Java) Scan */
let sysAEx
let scanAt
2018-04-28 20:34:23 -07:00
let extractListener
2018-04-25 14:40:46 -07:00
function asyncSystemScan ( launchAfter = true ) {
setLaunchDetails ( 'Please wait..' )
toggleLaunchArea ( true )
setLaunchPercentage ( 0 , 100 )
// Fork a process to run validations.
sysAEx = cp . fork ( path . join ( _ _dirname , 'assets' , 'js' , 'assetexec.js' ) , [
2018-06-03 21:17:20 -07:00
ConfigManager . getCommonDirectory ( ) ,
2018-07-22 08:40:15 -07:00
ConfigManager . getJavaExecutable ( )
2018-05-07 15:15:59 -07:00
] , {
stdio : 'pipe'
} )
// Stdout
sysAEx . stdio [ 1 ] . on ( 'data' , ( data ) => {
console . log ( '%c[SysAEx]' , 'color: #353232; font-weight: bold' , data . toString ( 'utf-8' ) )
} )
// Stderr
sysAEx . stdio [ 2 ] . on ( 'data' , ( data ) => {
console . log ( '%c[SysAEx]' , 'color: #353232; font-weight: bold' , data . toString ( 'utf-8' ) )
} )
2018-04-25 14:40:46 -07:00
sysAEx . on ( 'message' , ( m ) => {
2018-07-22 08:40:15 -07:00
if ( m . context === 'validateJava' ) {
2018-04-25 14:40:46 -07:00
if ( m . result == null ) {
// If the result is null, no valid Java installation was found.
// Show this information to the user.
setOverlayContent (
'No Compatible<br>Java Installation Found' ,
'In order to join WesterosCraft, you need a 64-bit installation of Java 8. Would you like us to install a copy? By installing, you accept <a href="http://www.oracle.com/technetwork/java/javase/terms/license/index.html">Oracle\'s license agreement</a>.' ,
'Install Java' ,
'Install Manually'
)
setOverlayHandler ( ( ) => {
setLaunchDetails ( 'Preparing Java Download..' )
2018-07-22 08:40:15 -07:00
sysAEx . send ( { task : 'execute' , function : '_enqueueOracleJRE' , argsArr : [ ConfigManager . getLauncherDirectory ( ) ] } )
2018-04-25 14:40:46 -07:00
toggleOverlay ( false )
} )
setDismissHandler ( ( ) => {
$ ( '#overlayContent' ) . fadeOut ( 250 , ( ) => {
//$('#overlayDismiss').toggle(false)
setOverlayContent (
'Don\'t Forget!<br>Java is Required' ,
'A valid x64 installation of Java 8 is required to launch. Downloads can be found on <a href="http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html">Oracle\'s website</a>. Once installed, you will be able to connect to the server.<br><br>Please refer to our <a href="http://westeroscraft.wikia.com/wiki/Troubleshooting_Guide">Troubleshooting Guide</a> if you have any difficulty.' ,
'I Understand' ,
'Go Back'
)
setOverlayHandler ( ( ) => {
toggleLaunchArea ( false )
toggleOverlay ( false )
} )
setDismissHandler ( ( ) => {
toggleOverlay ( false , true )
asyncSystemScan ( )
} )
$ ( '#overlayContent' ) . fadeIn ( 250 )
} )
} )
toggleOverlay ( true , true )
} else {
// Java installation found, use this to launch the game.
ConfigManager . setJavaExecutable ( m . result )
ConfigManager . save ( )
2018-06-14 00:49:55 -07:00
// 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
populateJavaExecDetails ( settingsJavaExecVal . value )
2018-04-25 14:40:46 -07:00
if ( launchAfter ) {
dlAsync ( )
}
sysAEx . disconnect ( )
}
2018-07-22 08:40:15 -07:00
} else if ( m . context === '_enqueueOracleJRE' ) {
2018-04-25 14:40:46 -07:00
if ( m . result === true ) {
// Oracle JRE enqueued successfully, begin download.
setLaunchDetails ( 'Downloading Java..' )
2018-07-22 08:40:15 -07:00
sysAEx . send ( { task : 'execute' , function : 'processDlQueues' , argsArr : [ [ { id : 'java' , limit : 1 } ] ] } )
2018-04-25 14:40:46 -07:00
} 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:<br>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 <a href="http://westeroscraft.wikia.com/wiki/Troubleshooting_Guide">Troubleshooting Guide</a> for more details and instructions.' ,
'I Understand'
)
setOverlayHandler ( ( ) => {
toggleOverlay ( false )
toggleLaunchArea ( false )
} )
toggleOverlay ( true )
sysAEx . disconnect ( )
}
2018-07-22 08:40:15 -07:00
} else if ( m . context === 'progress' ) {
2018-04-25 14:40:46 -07:00
2018-07-22 08:40:15 -07:00
switch ( m . data ) {
case 'download' :
2018-07-22 10:31:15 -07:00
// Downloading..
2018-07-22 08:40:15 -07:00
setDownloadPercentage ( m . value , m . total , m . percent )
break
}
2018-04-25 14:40:46 -07:00
2018-07-22 08:40:15 -07:00
} else if ( m . context === 'complete' ) {
2018-07-01 23:24:13 -07:00
2018-07-22 08:40:15 -07:00
switch ( m . data ) {
2018-07-22 10:31:15 -07:00
case 'download' : {
2018-07-22 08:40:15 -07:00
// Show installing progress bar.
remote . getCurrentWindow ( ) . setProgressBar ( 2 )
2018-04-25 14:40:46 -07:00
2018-07-22 08:40:15 -07:00
// 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
2018-07-22 10:31:15 -07:00
}
2018-07-22 08:40:15 -07:00
case 'java' :
2018-07-22 10:31:15 -07:00
// Download & extraction complete, remove the loading from the OS progress bar.
2018-07-22 08:40:15 -07:00
remote . getCurrentWindow ( ) . setProgressBar ( - 1 )
// Extraction completed successfully.
ConfigManager . setJavaExecutable ( m . args [ 0 ] )
ConfigManager . save ( )
2018-04-28 20:34:23 -07:00
2018-07-22 08:40:15 -07:00
if ( extractListener != null ) {
clearInterval ( extractListener )
extractListener = null
}
2018-04-25 14:40:46 -07:00
2018-07-22 08:40:15 -07:00
setLaunchDetails ( 'Java Installed!' )
2018-04-25 14:40:46 -07:00
2018-07-22 08:40:15 -07:00
if ( launchAfter ) {
dlAsync ( )
}
sysAEx . disconnect ( )
break
2018-04-25 14:40:46 -07:00
}
2018-07-22 08:40:15 -07:00
2018-04-25 14:40:46 -07:00
}
} )
// Begin system Java scan.
setLaunchDetails ( 'Checking system info..' )
2018-07-22 08:40:15 -07:00
sysAEx . send ( { task : 'execute' , function : 'validateJava' , argsArr : [ ConfigManager . getLauncherDirectory ( ) ] } )
2018-04-25 14:40:46 -07:00
}
// Keep reference to Minecraft Process
let proc
// Is DiscordRPC enabled
let hasRPC = false
// Joined server regex
const servJoined = /[[0-2][0-9]:[0-6][0-9]:[0-6][0-9]\] \[Client thread\/INFO\]: \[CHAT\] [a-zA-Z0-9_]{1,16} joined the game/g
const gameJoined = /\[[0-2][0-9]:[0-6][0-9]:[0-6][0-9]\] \[Client thread\/WARN\]: Skipping bad option: lastServer:/g
const gameJoined2 = /\[[0-2][0-9]:[0-6][0-9]:[0-6][0-9]\] \[Client thread\/INFO\]: Created: \d+x\d+ textures-atlas/g
let aEx
let serv
let versionData
let forgeData
2018-04-28 20:34:23 -07:00
let progressListener
2018-04-25 14:40:46 -07:00
function dlAsync ( login = true ) {
// Login parameter is temporary for debug purposes. Allows testing the validation/downloads without
// launching the game.
if ( login ) {
if ( ConfigManager . getSelectedAccount ( ) == null ) {
console . error ( 'login first.' )
//in devtools AuthManager.addAccount(username, pass)
return
}
}
setLaunchDetails ( 'Please wait..' )
toggleLaunchArea ( true )
setLaunchPercentage ( 0 , 100 )
// Start AssetExec to run validations and downloads in a forked process.
aEx = cp . fork ( path . join ( _ _dirname , 'assets' , 'js' , 'assetexec.js' ) , [
2018-06-03 21:17:20 -07:00
ConfigManager . getCommonDirectory ( ) ,
2018-07-22 08:40:15 -07:00
ConfigManager . getJavaExecutable ( )
2018-05-07 15:15:59 -07:00
] , {
stdio : 'pipe'
} )
// Stdout
aEx . stdio [ 1 ] . on ( 'data' , ( data ) => {
console . log ( '%c[AEx]' , 'color: #353232; font-weight: bold' , data . toString ( 'utf-8' ) )
} )
// Stderr
aEx . stdio [ 2 ] . on ( 'data' , ( data ) => {
console . log ( '%c[AEx]' , 'color: #353232; font-weight: bold' , data . toString ( 'utf-8' ) )
} )
2018-04-25 14:40:46 -07:00
// Establish communications between the AssetExec and current process.
aEx . on ( 'message' , ( m ) => {
2018-07-22 08:40:15 -07:00
if ( m . context === 'validate' ) {
switch ( m . data ) {
case 'distribution' :
setLaunchPercentage ( 20 , 100 )
console . log ( 'Validated distibution index.' )
setLaunchDetails ( 'Loading version information..' )
break
case 'version' :
setLaunchPercentage ( 40 , 100 )
console . log ( 'Version data loaded.' )
setLaunchDetails ( 'Validating asset integrity..' )
break
case 'assets' :
setLaunchPercentage ( 60 , 100 )
console . log ( 'Asset Validation Complete' )
setLaunchDetails ( 'Validating library integrity..' )
break
case 'libraries' :
setLaunchPercentage ( 80 , 100 )
console . log ( 'Library validation complete.' )
setLaunchDetails ( 'Validating miscellaneous file integrity..' )
break
case 'files' :
setLaunchPercentage ( 100 , 100 )
console . log ( 'File validation complete.' )
setLaunchDetails ( 'Downloading files..' )
break
2018-04-25 14:40:46 -07:00
}
2018-07-22 08:40:15 -07:00
} else if ( m . context === 'progress' ) {
switch ( m . data ) {
2018-07-22 10:31:15 -07:00
case 'assets' : {
2018-07-22 08:40:15 -07:00
const perc = ( m . value / m . total ) * 20
setLaunchPercentage ( 40 + perc , 100 , parseInt ( 40 + perc ) )
break
2018-07-22 10:31:15 -07:00
}
2018-07-22 08:40:15 -07:00
case 'download' :
setDownloadPercentage ( m . value , m . total , m . percent )
break
2018-07-22 10:31:15 -07:00
case 'extract' : {
2018-07-22 08:40:15 -07:00
// Show installing progress bar.
remote . getCurrentWindow ( ) . setProgressBar ( 2 )
// 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
2018-07-22 10:31:15 -07:00
}
2018-07-22 08:40:15 -07:00
}
} 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
}
2018-04-25 14:40:46 -07:00
2018-07-22 08:40:15 -07:00
setLaunchDetails ( 'Preparing to launch..' )
break
}
} else if ( m . context === 'error' ) {
switch ( m . data ) {
case 'download' :
console . error ( m . error )
if ( m . error . code === 'ENOENT' ) {
setOverlayContent (
'Download Error' ,
'Could not connect to the file server. Ensure that you are connected to the internet and try again.' ,
'Okay'
)
setOverlayHandler ( null )
2018-04-28 20:34:23 -07:00
} else {
2018-07-22 08:40:15 -07:00
setOverlayContent (
'Download Error' ,
'Check the console for more details. Please try again.' ,
'Okay'
)
setOverlayHandler ( null )
2018-04-28 20:34:23 -07:00
}
2018-05-08 03:34:16 -07:00
2018-07-22 08:40:15 -07:00
remote . getCurrentWindow ( ) . setProgressBar ( - 1 )
toggleOverlay ( true )
toggleLaunchArea ( false )
2018-04-25 14:40:46 -07:00
2018-07-22 08:40:15 -07:00
// Disconnect from AssetExec
aEx . disconnect ( )
break
2018-04-25 14:40:46 -07:00
}
2018-07-22 08:40:15 -07:00
} else if ( m . context === 'validateEverything' ) {
2018-04-25 14:40:46 -07:00
2018-07-22 08:40:15 -07:00
forgeData = m . result . forgeData
versionData = m . result . versionData
2018-04-25 14:40:46 -07:00
if ( login ) {
const authUser = ConfigManager . getSelectedAccount ( )
console . log ( 'authu' , authUser )
2018-06-03 21:17:20 -07:00
let pb = new ProcessBuilder ( serv , versionData , forgeData , authUser )
2018-04-25 14:40:46 -07:00
setLaunchDetails ( 'Launching game..' )
try {
// Build Minecraft process.
proc = pb . build ( )
setLaunchDetails ( 'Done. Enjoy the server!' )
// 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 ( data . indexOf ( '[Client thread/INFO]: -- System Details --' ) > - 1 ) {
toggleLaunchArea ( false )
if ( hasRPC ) {
DiscordWrapper . updateDetails ( 'Loading game..' )
}
proc . stdout . removeListener ( 'data' , tempListener )
}
}
// Listener for Discord RPC.
const gameStateChange = function ( data ) {
if ( servJoined . test ( data ) ) {
DiscordWrapper . updateDetails ( 'Exploring the Realm!' )
} else if ( gameJoined . test ( data ) ) {
2018-07-22 08:40:15 -07:00
DiscordWrapper . updateDetails ( 'Sailing to Westeros!' )
2018-04-25 14:40:46 -07:00
}
}
// Bind listeners to stdout.
proc . stdout . on ( 'data' , tempListener )
proc . stdout . on ( 'data' , gameStateChange )
// Init Discord Hook
2018-07-22 08:40:15 -07:00
const distro = DistroManager . getDistribution ( )
2018-04-25 14:40:46 -07:00
if ( distro . discord != null && serv . discord != null ) {
DiscordWrapper . initRPC ( distro . discord , serv . discord )
hasRPC = true
proc . on ( 'close' , ( code , signal ) => {
console . log ( 'Shutting down Discord Rich Presence..' )
DiscordWrapper . shutdownRPC ( )
hasRPC = false
proc = null
} )
}
} catch ( err ) {
2018-05-08 03:34:16 -07:00
console . error ( 'Error during launch' , err )
setOverlayContent (
'Error During Launch' ,
'Please check the console for more details.' ,
'Okay'
)
setOverlayHandler ( null )
toggleOverlay ( true )
toggleLaunchArea ( false )
2018-04-25 14:40:46 -07:00
}
}
// Disconnect from AssetExec
aEx . disconnect ( )
}
} )
// Begin Validations
// Validate Forge files.
setLaunchDetails ( 'Loading server information..' )
2018-05-08 17:10:46 -07:00
2018-07-22 08:40:15 -07:00
refreshDistributionIndex ( true , ( data ) => {
onDistroRefresh ( data )
serv = data . getServer ( ConfigManager . getSelectedServer ( ) )
aEx . send ( { task : 'execute' , function : 'validateEverything' , argsArr : [ ConfigManager . getSelectedServer ( ) , DistroManager . isDevMode ( ) ] } )
} , ( err ) => {
console . log ( err )
2018-05-08 17:10:46 -07:00
refreshDistributionIndex ( false , ( data ) => {
onDistroRefresh ( data )
2018-07-22 08:40:15 -07:00
serv = data . getServer ( ConfigManager . getSelectedServer ( ) )
aEx . send ( { task : 'execute' , function : 'validateEverything' , argsArr : [ ConfigManager . getSelectedServer ( ) , DistroManager . isDevMode ( ) ] } )
2018-05-08 17:10:46 -07:00
} , ( err ) => {
console . error ( 'Unable to refresh distribution index.' , err )
2018-07-22 08:40:15 -07:00
if ( DistroManager . getDistribution ( ) == null ) {
2018-05-08 17:10:46 -07:00
setOverlayContent (
'Fatal Error' ,
'Could not load a copy of the distribution index. See the console for more details.' ,
'Okay'
)
setOverlayHandler ( null )
toggleOverlay ( true )
toggleLaunchArea ( false )
// Disconnect from AssetExec
aEx . disconnect ( )
} else {
2018-07-22 08:40:15 -07:00
serv = data . getServer ( ConfigManager . getSelectedServer ( ) )
aEx . send ( { task : 'execute' , function : 'validateEverything' , argsArr : [ ConfigManager . getSelectedServer ( ) , DistroManager . isDevMode ( ) ] } )
2018-05-08 17:10:46 -07:00
}
} )
2018-07-22 08:40:15 -07:00
} )
2018-05-06 18:45:20 -07:00
}
/ * *
* News Loading Functions
* /
2018-06-20 06:12:44 -07:00
// DOM Cache
const newsContent = document . getElementById ( 'newsContent' )
const newsArticleTitle = document . getElementById ( 'newsArticleTitle' )
const newsArticleDate = document . getElementById ( 'newsArticleDate' )
const newsArticleAuthor = document . getElementById ( 'newsArticleAuthor' )
const newsArticleComments = document . getElementById ( 'newsArticleComments' )
const newsNavigationStatus = document . getElementById ( 'newsNavigationStatus' )
const newsArticleContentScrollable = document . getElementById ( 'newsArticleContentScrollable' )
const nELoadSpan = document . getElementById ( 'nELoadSpan' )
2018-05-06 18:45:20 -07:00
// News slide caches.
let newsActive = false
let newsGlideCount = 0
/ * *
* Show the news UI via a slide animation .
*
* @ param { boolean } up True to slide up , otherwise false .
* /
function slide _ ( up ) {
const lCUpper = document . querySelector ( '#landingContainer > #upper' )
const lCLLeft = document . querySelector ( '#landingContainer > #lower > #left' )
const lCLCenter = document . querySelector ( '#landingContainer > #lower > #center' )
const lCLRight = document . querySelector ( '#landingContainer > #lower > #right' )
const newsBtn = document . querySelector ( '#landingContainer > #lower > #center #content' )
const landingContainer = document . getElementById ( 'landingContainer' )
const newsContainer = document . querySelector ( '#landingContainer > #newsContainer' )
newsGlideCount ++
if ( up ) {
lCUpper . style . top = '-200vh'
lCLLeft . style . top = '-200vh'
lCLCenter . style . top = '-200vh'
lCLRight . style . top = '-200vh'
newsBtn . style . top = '130vh'
newsContainer . style . top = '0px'
//date.toLocaleDateString('en-US', {month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric'})
//landingContainer.style.background = 'rgba(29, 29, 29, 0.55)'
landingContainer . style . background = 'rgba(0, 0, 0, 0.50)'
setTimeout ( ( ) => {
if ( newsGlideCount === 1 ) {
lCLCenter . style . transition = 'none'
newsBtn . style . transition = 'none'
}
newsGlideCount --
} , 2000 )
} else {
setTimeout ( ( ) => {
newsGlideCount --
} , 2000 )
landingContainer . style . background = null
lCLCenter . style . transition = null
newsBtn . style . transition = null
newsContainer . style . top = '100%'
lCUpper . style . top = '0px'
lCLLeft . style . top = '0px'
lCLCenter . style . top = '0px'
lCLRight . style . top = '0px'
newsBtn . style . top = '10px'
}
}
// Bind news button.
document . getElementById ( 'newsButton' ) . onclick = ( ) => {
2018-05-10 02:48:55 -07:00
// Toggle tabbing.
if ( newsActive ) {
2018-06-04 20:08:03 -07:00
$ ( '#landingContainer *' ) . removeAttr ( 'tabindex' )
$ ( '#newsContainer *' ) . attr ( 'tabindex' , '-1' )
2018-05-10 02:48:55 -07:00
} else {
2018-06-04 20:08:03 -07:00
$ ( '#landingContainer *' ) . attr ( 'tabindex' , '-1' )
$ ( '#newsContainer, #newsContainer *, #lower, #lower #center *' ) . removeAttr ( 'tabindex' )
if ( newsAlertShown ) {
$ ( '#newsButtonAlert' ) . fadeOut ( 2000 )
newsAlertShown = false
ConfigManager . setNewsCacheDismissed ( true )
ConfigManager . save ( )
}
2018-05-10 02:48:55 -07:00
}
2018-05-06 18:45:20 -07:00
slide _ ( ! newsActive )
newsActive = ! newsActive
}
// Array to store article meta.
let newsArr = null
// News load animation listener.
let newsLoadingListener = null
/ * *
* Set the news loading animation .
*
* @ param { boolean } val True to set loading animation , otherwise false .
* /
function setNewsLoading ( val ) {
if ( val ) {
const nLStr = 'Checking for News'
let dotStr = '..'
nELoadSpan . innerHTML = nLStr + dotStr
newsLoadingListener = setInterval ( ( ) => {
if ( dotStr . length >= 3 ) {
dotStr = ''
} else {
dotStr += '.'
}
nELoadSpan . innerHTML = nLStr + dotStr
} , 750 )
} else {
if ( newsLoadingListener != null ) {
clearInterval ( newsLoadingListener )
newsLoadingListener = null
}
}
}
// Bind retry button.
newsErrorRetry . onclick = ( ) => {
$ ( '#newsErrorFailed' ) . fadeOut ( 250 , ( ) => {
initNews ( )
$ ( '#newsErrorLoading' ) . fadeIn ( 250 )
} )
}
2018-06-20 06:12:44 -07:00
newsArticleContentScrollable . onscroll = ( e ) => {
if ( e . target . scrollTop > Number . parseFloat ( $ ( '.newsArticleSpacerTop' ) . css ( 'height' ) ) ) {
newsContent . setAttribute ( 'scrolled' , '' )
} else {
newsContent . removeAttribute ( 'scrolled' )
}
}
2018-05-06 18:45:20 -07:00
/ * *
* Reload the news without restarting .
2018-05-10 02:48:55 -07:00
*
* @ returns { Promise . < void > } A promise which resolves when the news
* content has finished loading and transitioning .
2018-05-06 18:45:20 -07:00
* /
2018-05-10 02:48:55 -07:00
function reloadNews ( ) {
return new Promise ( ( resolve , reject ) => {
$ ( '#newsContent' ) . fadeOut ( 250 , ( ) => {
$ ( '#newsErrorLoading' ) . fadeIn ( 250 )
initNews ( ) . then ( ( ) => {
resolve ( )
} )
} )
2018-05-06 18:45:20 -07:00
} )
}
2018-06-04 20:08:03 -07:00
let newsAlertShown = false
/ * *
* Show the news alert indicating there is new news .
* /
function showNewsAlert ( ) {
newsAlertShown = true
$ ( newsButtonAlert ) . fadeIn ( 250 )
}
2018-05-06 18:45:20 -07:00
/ * *
* Initialize News UI . This will load the news and prepare
* the UI accordingly .
2018-05-10 02:48:55 -07:00
*
* @ returns { Promise . < void > } A promise which resolves when the news
* content has finished loading and transitioning .
2018-05-06 18:45:20 -07:00
* /
2018-05-10 02:48:55 -07:00
function initNews ( ) {
2018-05-06 18:45:20 -07:00
2018-05-10 02:48:55 -07:00
return new Promise ( ( resolve , reject ) => {
setNewsLoading ( true )
2018-05-06 18:45:20 -07:00
2018-05-10 02:48:55 -07:00
let news = { }
loadNews ( ) . then ( news => {
2018-05-06 18:45:20 -07:00
2018-05-10 02:48:55 -07:00
newsArr = news . articles || null
2018-05-06 18:45:20 -07:00
2018-05-10 02:48:55 -07:00
if ( newsArr == null ) {
// News Loading Failed
setNewsLoading ( false )
2018-05-06 18:45:20 -07:00
2018-05-10 02:48:55 -07:00
$ ( '#newsErrorLoading' ) . fadeOut ( 250 , ( ) => {
$ ( '#newsErrorFailed' ) . fadeIn ( 250 , ( ) => {
resolve ( )
} )
} )
} else if ( newsArr . length === 0 ) {
// No News Articles
setNewsLoading ( false )
2018-05-06 18:45:20 -07:00
2018-06-04 20:08:03 -07:00
ConfigManager . setNewsCache ( {
date : null ,
content : null ,
dismissed : false
} )
ConfigManager . save ( )
2018-05-10 02:48:55 -07:00
$ ( '#newsErrorLoading' ) . fadeOut ( 250 , ( ) => {
$ ( '#newsErrorNone' ) . fadeIn ( 250 , ( ) => {
resolve ( )
} )
} )
} else {
// Success
setNewsLoading ( false )
2018-06-04 20:08:03 -07:00
const lN = newsArr [ 0 ]
const cached = ConfigManager . getNewsCache ( )
let newHash = crypto . createHash ( 'sha1' ) . update ( lN . content ) . digest ( 'hex' )
let newDate = new Date ( lN . date )
let isNew = false
if ( cached . date != null && cached . content != null ) {
if ( new Date ( cached . date ) >= newDate ) {
// Compare Content
if ( cached . content !== newHash ) {
isNew = true
showNewsAlert ( )
} else {
if ( ! cached . dismissed ) {
isNew = true
showNewsAlert ( )
}
}
} else {
isNew = true
showNewsAlert ( )
}
} else {
isNew = true
showNewsAlert ( )
}
if ( isNew ) {
ConfigManager . setNewsCache ( {
date : newDate . getTime ( ) ,
content : newHash ,
dismissed : false
} )
ConfigManager . save ( )
}
2018-05-10 02:48:55 -07:00
const switchHandler = ( forward ) => {
let cArt = parseInt ( newsContent . getAttribute ( 'article' ) )
let nxtArt = forward ? ( cArt >= newsArr . length - 1 ? 0 : cArt + 1 ) : ( cArt <= 0 ? newsArr . length - 1 : cArt - 1 )
displayArticle ( newsArr [ nxtArt ] , nxtArt + 1 )
}
2018-06-30 12:16:31 -07:00
2018-05-10 02:48:55 -07:00
document . getElementById ( 'newsNavigateRight' ) . onclick = ( ) => { switchHandler ( true ) }
document . getElementById ( 'newsNavigateLeft' ) . onclick = ( ) => { switchHandler ( false ) }
$ ( '#newsErrorContainer' ) . fadeOut ( 250 , ( ) => {
displayArticle ( newsArr [ 0 ] , 1 )
$ ( '#newsContent' ) . fadeIn ( 250 , ( ) => {
resolve ( )
} )
} )
}
2018-05-06 18:45:20 -07:00
} )
2018-05-10 02:48:55 -07:00
} )
2018-05-06 18:45:20 -07:00
}
2018-06-30 12:16:31 -07:00
/ * *
* Add keyboard controls to the news UI . Left and right arrows toggle
* between articles . If you are on the landing page , the up arrow will
* open the news UI .
* /
document . addEventListener ( 'keydown' , ( e ) => {
if ( newsActive ) {
if ( e . key === 'ArrowRight' || e . key === 'ArrowLeft' ) {
document . getElementById ( e . key === 'ArrowRight' ? 'newsNavigateRight' : 'newsNavigateLeft' ) . click ( )
}
// Interferes with scrolling an article using the down arrow.
// Not sure of a straight forward solution at this point.
// if(e.key === 'ArrowDown'){
// document.getElementById('newsButton').click()
// }
} else {
if ( getCurrentView ( ) === VIEWS . landing ) {
if ( e . key === 'ArrowUp' ) {
document . getElementById ( 'newsButton' ) . click ( )
}
}
}
} )
2018-05-06 18:45:20 -07:00
/ * *
* Display a news article on the UI .
*
* @ param { Object } articleObject The article meta object .
* @ param { number } index The article index .
* /
function displayArticle ( articleObject , index ) {
newsArticleTitle . innerHTML = articleObject . title
newsArticleTitle . href = articleObject . link
newsArticleAuthor . innerHTML = 'by ' + articleObject . author
newsArticleDate . innerHTML = articleObject . date
newsArticleComments . innerHTML = articleObject . comments
newsArticleComments . href = articleObject . commentsLink
2018-06-20 06:12:44 -07:00
newsArticleContentScrollable . innerHTML = '<div id="newsArticleContentWrapper"><div class="newsArticleSpacerTop"></div>' + articleObject . content + '<div class="newsArticleSpacerBot"></div></div>'
2018-05-06 18:45:20 -07:00
newsNavigationStatus . innerHTML = index + ' of ' + newsArr . length
newsContent . setAttribute ( 'article' , index - 1 )
}
/ * *
* Load news information from the RSS feed specified in the
* distribution index .
* /
function loadNews ( ) {
return new Promise ( ( resolve , reject ) => {
2018-07-22 08:40:15 -07:00
const distroData = DistroManager . getDistribution ( )
const newsFeed = distroData . getRSS ( )
2018-05-08 17:10:46 -07:00
const newsHost = new URL ( newsFeed ) . origin + '/'
2018-05-22 05:41:22 -07:00
$ . ajax (
2018-07-22 10:31:15 -07:00
{
url : newsFeed ,
success : ( data ) => {
const items = $ ( data ) . find ( 'item' )
const articles = [ ]
2018-05-22 05:41:22 -07:00
2018-07-22 10:31:15 -07:00
for ( let i = 0 ; i < items . length ; i ++ ) {
2018-05-22 05:41:22 -07:00
// JQuery Element
2018-07-22 10:31:15 -07:00
const el = $ ( items [ i ] )
2018-05-22 05:41:22 -07:00
2018-07-22 10:31:15 -07:00
// Resolve date.
const date = new Date ( el . find ( 'pubDate' ) . text ( ) ) . toLocaleDateString ( 'en-US' , { month : 'short' , day : 'numeric' , year : 'numeric' , hour : 'numeric' , minute : 'numeric' } )
2018-05-22 05:41:22 -07:00
2018-07-22 10:31:15 -07:00
// Resolve comments.
let comments = el . find ( 'slash\\:comments' ) . text ( ) || '0'
comments = comments + ' Comment' + ( comments === '1' ? '' : 's' )
2018-05-22 05:41:22 -07:00
2018-07-22 10:31:15 -07:00
// Fix relative links in content.
let content = el . find ( 'content\\:encoded' ) . text ( )
let regex = /src="(?!http:\/\/|https:\/\/)(.+)"/g
let matches
while ( ( matches = regex . exec ( content ) ) ) {
content = content . replace ( matches [ 1 ] , newsHost + matches [ 1 ] )
2018-05-22 05:41:22 -07:00
}
2018-07-22 10:31:15 -07:00
let link = el . find ( 'link' ) . text ( )
let title = el . find ( 'title' ) . text ( )
let author = el . find ( 'dc\\:creator' ) . text ( )
// Generate article.
articles . push (
{
link ,
title ,
date ,
author ,
content ,
comments ,
commentsLink : link + '#comments'
}
)
}
resolve ( {
articles
} )
} ,
timeout : 2500
} ) . catch ( err => {
2018-05-08 17:10:46 -07:00
resolve ( {
2018-05-22 05:41:22 -07:00
articles : null
2018-05-06 18:45:20 -07:00
} )
} )
} )
2018-05-08 03:34:16 -07:00
}