diff --git a/README.md b/README.md
index 9e8eab4f..161b2ae0 100644
--- a/README.md
+++ b/README.md
@@ -104,4 +104,6 @@ Run either of the build scrips noted below. Note that each platform can only be
If you run into any issue which cannot be resolved via a quick google search, create an issue using the tab above.
-Much of the discussion regarding this launcher is done on Discord, feel free to join us there [![Discord](https://discordapp.com/api/guilds/98469309352775680/widget.png)](https://discord.gg/hqdjs3m)
\ No newline at end of file
+Much of the discussion regarding this launcher is done on #launcherdev in Discord, feel free to join us there.
+
+[![Discord](https://discordapp.com/api/guilds/98469309352775680/embed.png?style=banner2)](https://discord.gg/hqdjs3m)
\ No newline at end of file
diff --git a/app/assets/css/launcher.css b/app/assets/css/launcher.css
index 4f0a97af..246c3731 100644
--- a/app/assets/css/launcher.css
+++ b/app/assets/css/launcher.css
@@ -1210,15 +1210,38 @@ p {
display: flex;
flex-direction: column;
align-items: center;
- justify-content: space-between;
+ /*justify-content: space-between;*/
width: 300px;
- height: 35%;
+ /*height: 35%;*/
box-sizing: border-box;
padding: 15px 0px;
/* background-color: #424242; */
text-align: center;
}
+#overlayContent a {
+ color: rgba(202, 202, 202, 0.75);
+ transition: 0.25s ease;
+}
+#overlayContent a:hover {
+ color: rgba(255, 255, 255, 0.75);
+}
+#overlayContent a:active {
+ color: rgba(165, 165, 165, 0.75);
+}
+
+#overlayContent > *:first-child {
+ margin-top: 0px !important;
+}
+
+#overlayContent > *:last-child {
+ margin-bottom: 0px !important;
+}
+
+#overlayContent > * {
+ margin: 8px 0px;
+}
+
#overlayTitle {
font-family: 'Avenir Medium';
font-size: 20px;
diff --git a/app/assets/js/actionbinder.js b/app/assets/js/actionbinder.js
index 5872813c..22dffe25 100644
--- a/app/assets/js/actionbinder.js
+++ b/app/assets/js/actionbinder.js
@@ -39,6 +39,11 @@ document.addEventListener('readystatechange', function(){
if(jExe == null){
asyncSystemScan()
} else {
+
+ setLaunchDetails('Please wait..')
+ toggleLaunchArea(true)
+ setLaunchPercentage(0, 100)
+
AssetGuard._validateJavaBinary(jExe).then((v) => {
if(v){
dlAsync()
@@ -194,12 +199,13 @@ function setDownloadPercentage(value, max, percent = ((value/max)*100)){
let sysAEx
let scanAt
-function asyncSystemScan(){
+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'), [
ConfigManager.getGameDirectory(),
ConfigManager.getJavaExecutable()
@@ -207,21 +213,96 @@ function asyncSystemScan(){
sysAEx.on('message', (m) => {
if(m.content === 'validateJava'){
- jPath = m.result
- console.log(m.result)
- sysAEx.disconnect()
+
+ //m.result = null
+
+ if(m.result == 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 8. Would you like us to install a copy? By installing, you accept Oracle\'s license agreement.',
+ 'Install Java'
+ )
+ setOverlayHandler(() => {
+ setLaunchDetails('Preparing Java Download..')
+ sysAEx.send({task: 0, content: '_enqueueOracleJRE', argsArr: [ConfigManager.getLauncherDirectory()]})
+ toggleOverlay(false)
+ })
+ toggleOverlay(true)
+
+ // TODO Add option to not install Java x64.
+
+ } else {
+ // Java installation found, use this to launch the game.
+ ConfigManager.setJavaExecutable(m.result)
+ ConfigManager.save()
+ if(launchAfter){
+ dlAsync()
+ }
+ sysAEx.disconnect()
+ }
+
+ } else if(m.content === '_enqueueOracleJRE'){
+
+ if(m.result === true){
+
+ // Oracle JRE enqueued successfully, begin download.
+ setLaunchDetails('Downloading Java..')
+ sysAEx.send({task: 0, content: '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(
+ 'Yikes!
Java download failed.',
+ 'Unfortunately we\'ve encountered an issue while attempting to install Java. You will need to install a copy yourself. Please check out this guide for more details and instructions.',
+ 'Got it'
+ )
+ setOverlayHandler(null)
+ toggleOverlay(true)
+ sysAEx.disconnect()
+
+ }
+
+ } else if(m.content === 'dl'){
+
+ if(m.task === 0){
+ // Downloading..
+ setDownloadPercentage(m.value, m.total, m.percent)
+ } else if(m.task === 1){
+ // Download will be at 100%, remove the loading from the OS progress bar.
+ remote.getCurrentWindow().setProgressBar(-1)
+
+ // Wait for extration to complete.
+ setLaunchDetails('Extracting..')
+
+ } else if(m.task === 2){
+
+ // Extraction completed successfully.
+ ConfigManager.setJavaExecutable(m.jPath)
+ ConfigManager.save()
+
+ setLaunchDetails('Java Installed!')
+
+ if(launchAfter){
+ dlAsync()
+ }
+
+ sysAEx.disconnect()
+ } else {
+ console.error('Unknown download data type.', m)
+ }
}
})
+ // Begin system Java scan.
setLaunchDetails('Checking system info..')
sysAEx.send({task: 0, content: 'validateJava', argsArr: [ConfigManager.getLauncherDirectory()]})
}
-function overlayError(){
-
-}
-
// Keep reference to Minecraft Process
let proc
// Is DiscordRPC enabled
@@ -283,12 +364,18 @@ function dlAsync(login = true){
} else if(m.content === 'validateAssets'){
- setLaunchPercentage(60, 100)
- console.log('Asset Validation Complete')
+ // Asset validation can *potentially* take longer, so let's track progress.
+ if(m.task === 0){
+ const perc = (m.value/m.total)*20
+ setLaunchPercentage(40+perc, 100, parseInt(40+perc))
+ } else {
+ setLaunchPercentage(60, 100)
+ console.log('Asset Validation Complete')
- // Begin library validation.
- setLaunchDetails('Validating library integrity..')
- aEx.send({task: 0, content: 'validateLibraries', argsArr: [versionData]})
+ // Begin library validation.
+ setLaunchDetails('Validating library integrity..')
+ aEx.send({task: 0, content: 'validateLibraries', argsArr: [versionData]})
+ }
} else if(m.content === 'validateLibraries'){
diff --git a/app/assets/js/assetexec.js b/app/assets/js/assetexec.js
index 5d41fed9..7a75d90b 100644
--- a/app/assets/js/assetexec.js
+++ b/app/assets/js/assetexec.js
@@ -6,6 +6,10 @@ console.log('AssetExec Started')
// Temporary for debug purposes.
process.on('unhandledRejection', r => console.log(r))
+tracker.on('assetVal', (data) => {
+ process.send({task: 0, total: data.total, value: data.acc, content: 'validateAssets'})
+})
+
tracker.on('totaldlprogress', (data) => {
process.send({task: 0, total: data.total, value: data.acc, percent: parseInt((data.acc/data.total)*100), content: 'dl'})
})
@@ -14,6 +18,10 @@ tracker.on('dlcomplete', () => {
process.send({task: 1, content: 'dl'})
})
+tracker.on('jExtracted', (jPath) => {
+ process.send({task: 2, content: 'dl', jPath})
+})
+
process.on('message', (msg) => {
if(msg.task === 0){
const func = msg.content
diff --git a/app/assets/js/assetguard.js b/app/assets/js/assetguard.js
index 5a713eb9..f8c50633 100644
--- a/app/assets/js/assetguard.js
+++ b/app/assets/js/assetguard.js
@@ -32,7 +32,8 @@ const mkpath = require('mkdirp');
const path = require('path')
const Registry = require('winreg')
const request = require('request')
-const targz = require('targz')
+const tar = require('tar-fs')
+const zlib = require('zlib')
// Constants
const PLATFORM_MAP = {
@@ -558,6 +559,23 @@ class AssetGuard extends EventEmitter {
})
}
+ /**
+ * 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.
+ */
+ 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
+ }
+
/**
* Load Mojang's launcher.json file.
*
@@ -583,20 +601,16 @@ class AssetGuard extends EventEmitter {
* the function's code throws errors. That would indicate that the option is changed or
* removed.
*
- * @param {string} binaryPath Path to the root of the java binary we wish to validate.
+ * @param {string} binaryExecPath Path to the java executable we wish to validate.
*
* @returns {Promise.} Resolves to false only if the test is successful and the result
* is less than 64.
*/
- static _validateJavaBinary(binaryPath){
+ static _validateJavaBinary(binaryExecPath){
return new Promise((resolve, reject) => {
- let fBp = binaryPath
- if(!fBp.endsWith('.exe')){
- fBp = path.join(binaryPath, 'bin', 'java.exe')
- }
- if(fs.existsSync(fBp)){
- child_process.exec('"' + fBp + '" -XshowSettings:properties', (err, stdout, stderr) => {
+ if(fs.existsSync(binaryExecPath)){
+ child_process.exec('"' + binaryExecPath + '" -XshowSettings:properties', (err, stdout, stderr) => {
try {
// Output is stored in stderr?
@@ -605,7 +619,7 @@ class AssetGuard extends EventEmitter {
for(let i=0; i -1){
let arch = props[i].split('=')[1].trim()
- console.log(props[i].trim() + ' for ' + binaryPath)
+ console.log(props[i].trim() + ' for ' + binaryExecPath)
resolve(parseInt(arch) >= 64)
}
}
@@ -770,7 +784,7 @@ class AssetGuard extends EventEmitter {
* If versions are equal, JRE > JDK.
*
* @param {string} dataDir The base launcher directory.
- * @returns {Promise.} A Promise which resolves to the root path of a valid
+ * @returns {Promise.} A Promise which resolves to the executable path of a valid
* x64 Java installation. If none are found, null is returned.
*/
static async _win32JavaValidate(dataDir){
@@ -813,9 +827,10 @@ class AssetGuard extends EventEmitter {
// Validate that the binary is actually x64.
for(let i=0; i {
AssetGuard._latestJREOracle().then(verData => {
if(verData != null){
@@ -1269,24 +1288,37 @@ class AssetGuard extends EventEmitter {
if(err){
resolve(false)
} else {
+ dataDir = path.join(dataDir, 'runtime', 'x64')
const name = combined.substring(combined.lastIndexOf('/')+1)
- const fDir = path.join(dir, name)
+ const fDir = path.join(dataDir, name)
const jre = new Asset(name, null, resp.headers['content-length'], opts, fDir)
- this.java = new DLTracker([jre], jre.size, a => {
- targz.decompress({
- src: a.to,
- dest: dir
- }, err => {
- if(err){
- console.log(err)
- } else {
+ this.java = new DLTracker([jre], jre.size, (a, self) => {
+ let h = null
+ fs.createReadStream(a.to)
+ .on('error', err => console.log(err))
+ .pipe(zlib.createGunzip())
+ .on('error', err => console.log(err))
+ .pipe(tar.extract(dataDir, {
+ map: (header) => {
+ if(h == null){
+ h = header.name
+ }
+ }
+ }))
+ .on('error', err => console.log(err))
+ .on('finish', () => {
fs.unlink(a.to, err => {
if(err){
console.log(err)
}
+ if(h.indexOf('/') > -1){
+ h = h.substring(0, h.indexOf('/'))
+ }
+ const pos = path.join(dataDir, h)
+ self.emit('jExtracted', AssetGuard.javaExecFromRoot(pos))
})
- }
- })
+ })
+
})
resolve(true)
}
@@ -1371,7 +1403,7 @@ class AssetGuard extends EventEmitter {
writeStream.on('close', () => {
//console.log('DLResults ' + asset.size + ' ' + count + ' ', asset.size === count)
if(concurrentDlTracker.callback != null){
- concurrentDlTracker.callback.apply(concurrentDlTracker, [asset])
+ concurrentDlTracker.callback.apply(concurrentDlTracker, [asset, self])
}
cb()
})
diff --git a/app/assets/js/configmanager.js b/app/assets/js/configmanager.js
index 8899d134..4ce6dc8f 100644
--- a/app/assets/js/configmanager.js
+++ b/app/assets/js/configmanager.js
@@ -25,7 +25,7 @@ const DEFAULT_CONFIG = {
java: {
minRAM: '2G',
maxRAM: resolveMaxRAM(), // Dynamic
- executable: 'C:\\Program Files\\Java\\jdk1.8.0_152\\bin\\javaw.exe', // TODO Resolve
+ executable: null,
jvmOptions: [
'-XX:+UseConcMarkSweepGC',
'-XX:+CMSIncrementalMode',
diff --git a/package-lock.json b/package-lock.json
index 407b9951..54267aee 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -246,7 +246,7 @@
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"requires": {
- "readable-stream": "2.3.5",
+ "readable-stream": "2.3.6",
"safe-buffer": "5.1.1"
},
"dependencies": {
@@ -256,23 +256,23 @@
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "2.0.0",
"safe-buffer": "5.1.1",
- "string_decoder": "1.0.3",
+ "string_decoder": "1.1.1",
"util-deprecate": "1.0.2"
}
},
"string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "5.1.1"
}
@@ -2750,7 +2750,7 @@
"requires": {
"bl": "1.2.2",
"end-of-stream": "1.4.1",
- "readable-stream": "2.3.5",
+ "readable-stream": "2.3.6",
"xtend": "4.0.1"
},
"dependencies": {
@@ -2760,23 +2760,23 @@
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "2.0.0",
"safe-buffer": "5.1.1",
- "string_decoder": "1.0.3",
+ "string_decoder": "1.1.1",
"util-deprecate": "1.0.2"
}
},
"string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "5.1.1"
}
@@ -2788,14 +2788,6 @@
}
}
},
- "targz": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/targz/-/targz-1.0.1.tgz",
- "integrity": "sha1-j3alI2lM3t+7XWCkB2/27uzFOY8=",
- "requires": {
- "tar-fs": "1.16.0"
- }
- },
"temp-file": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.1.1.tgz",
diff --git a/package.json b/package.json
index 646ea0cd..03183698 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
"ejs-electron": "^2.0.1",
"jquery": "^3.3.1",
"request-promise-native": "^1.0.5",
- "targz": "^1.0.1",
+ "tar-fs": "^1.16.0",
"uuid": "^3.2.1",
"winreg": "^1.2.4"
},