Эх сурвалжийг харах

Various additions and fixes to the launch controller.
Fixed an issue with concurrency when there are no downloads queued on launch. Added support for static function execution in AssetExec. Fixed a binding issue in uicore.js caused by delayed div loading. For now the solution is just hard coding in the value, will probably switch these two a css file later on. Included the launcher's x64 runtime directory in Java scans.

Daniel Scalzi 7 жил өмнө
parent
commit
5c7e0c3c8a

+ 143 - 63
app/assets/js/actionbinder.js

@@ -35,8 +35,18 @@ document.addEventListener('readystatechange', function(){
         // Bind launch button
         // Bind launch button
         document.getElementById('launch_button').addEventListener('click', function(e){
         document.getElementById('launch_button').addEventListener('click', function(e){
             console.log('Launching game..')
             console.log('Launching game..')
-            //testdownloads()
-            dlAsync()
+            const jExe = ConfigManager.getJavaExecutable()
+            if(jExe == null){
+                asyncSystemScan()
+            } else {
+                AssetGuard._validateJavaBinary(jExe).then((v) => {
+                    if(v){
+                        dlAsync()
+                    } else {
+                        asyncSystemScan()
+                    }
+                })
+            }
         })
         })
 
 
         // TODO convert this to dropdown menu.
         // TODO convert this to dropdown menu.
@@ -79,6 +89,64 @@ document.addEventListener('readystatechange', function(){
     }
     }
 }, false)
 }, false)
 
 
+/* Launch Progress Wrapper Functions */
+
+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'
+    }
+}
+
+function setLaunchDetails(details){
+    launch_details_text.innerHTML = details
+}
+
+function setLaunchPercentage(value, max, percent = ((value/max)*100)){
+    launch_progress.setAttribute('max', max)
+    launch_progress.setAttribute('value', value)
+    launch_progress_label.innerHTML = percent + '%'
+}
+
+function setDownloadPercentage(value, max, percent = ((value/max)*100)){
+    remote.getCurrentWindow().setProgressBar(value/max)
+    setLaunchPercentage(value, max, percent)
+}
+
+let sysAEx
+let scanAt
+
+function asyncSystemScan(){
+
+    setLaunchDetails('Please wait..')
+    toggleLaunchArea(true)
+    setLaunchPercentage(0, 100)
+
+    sysAEx = cp.fork(path.join(__dirname, 'assets', 'js', 'assetexec.js'), [
+        ConfigManager.getGameDirectory(),
+        ConfigManager.getJavaExecutable()
+    ])
+    
+    sysAEx.on('message', (m) => {
+        if(m.content === 'validateJava'){
+            jPath = m.result
+            console.log(m.result)
+            sysAEx.disconnect()
+        }
+    })
+
+    setLaunchDetails('Checking system info..')
+    sysAEx.send({task: 0, content: 'validateJava', argsArr: [ConfigManager.getLauncherDirectory()]})
+
+}
+
+function overlayError(){
+
+}
+
 // Keep reference to Minecraft Process
 // Keep reference to Minecraft Process
 let proc
 let proc
 // Is DiscordRPC enabled
 // Is DiscordRPC enabled
@@ -89,7 +157,6 @@ const gameJoined = /\[[0-2][0-9]:[0-6][0-9]:[0-6][0-9]\] \[Client thread\/WARN\]
 const gameJoined2 = /\[[0-2][0-9]:[0-6][0-9]:[0-6][0-9]\] \[Client thread\/INFO\]: Created: \d+x\d+ textures-atlas/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 aEx
-let currentProc
 let serv
 let serv
 let versionData
 let versionData
 let forgeData
 let forgeData
@@ -107,89 +174,86 @@ function dlAsync(login = true){
         }
         }
     }
     }
 
 
-    launch_details_text.innerHTML = 'Please wait..'
-    launch_progress.setAttribute('max', '100')
-    launch_details.style.display = 'flex'
-    launch_content.style.display = 'none'
+    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'), [
     aEx = cp.fork(path.join(__dirname, 'assets', 'js', 'assetexec.js'), [
         ConfigManager.getGameDirectory(),
         ConfigManager.getGameDirectory(),
         ConfigManager.getJavaExecutable()
         ConfigManager.getJavaExecutable()
     ])
     ])
 
 
+    // Establish communications between the AssetExec and current process.
     aEx.on('message', (m) => {
     aEx.on('message', (m) => {
-        if(currentProc === 'validateDistribution'){
+        if(m.content === 'validateDistribution'){
 
 
-            launch_progress.setAttribute('value', 20)
-            launch_progress_label.innerHTML = '20%'
+            setLaunchPercentage(20, 100)
             serv = m.result
             serv = m.result
-            console.log('forge stuff done')
+            console.log('Forge Validation Complete.')
 
 
             // Begin version load.
             // Begin version load.
-            launch_details_text.innerHTML = 'Loading version information..'
-            currentProc = 'loadVersionData'
-            aEx.send({task: 0, content: currentProc, argsArr: [serv.mc_version]})
+            setLaunchDetails('Loading version information..')
+            aEx.send({task: 0, content: 'loadVersionData', argsArr: [serv.mc_version]})
 
 
-        } else if(currentProc === 'loadVersionData'){
+        } else if(m.content === 'loadVersionData'){
 
 
-            launch_progress.setAttribute('value', 40)
-            launch_progress_label.innerHTML = '40%'
+            setLaunchPercentage(40, 100)
             versionData = m.result
             versionData = m.result
+            console.log('Version data loaded.')
 
 
             // Begin asset validation.
             // Begin asset validation.
-            launch_details_text.innerHTML = 'Validating asset integrity..'
-            currentProc = 'validateAssets'
-            aEx.send({task: 0, content: currentProc, argsArr: [versionData]})
+            setLaunchDetails('Validating asset integrity..')
+            aEx.send({task: 0, content: 'validateAssets', argsArr: [versionData]})
 
 
-        } else if(currentProc === 'validateAssets'){
+        } else if(m.content === 'validateAssets'){
 
 
-            launch_progress.setAttribute('value', 60)
-            launch_progress_label.innerHTML = '60%'
-            console.log('assets done')
+            setLaunchPercentage(60, 100)
+            console.log('Asset Validation Complete')
 
 
             // Begin library validation.
             // Begin library validation.
-            launch_details_text.innerHTML = 'Validating library integrity..'
-            currentProc = 'validateLibraries'
-            aEx.send({task: 0, content: currentProc, argsArr: [versionData]})
+            setLaunchDetails('Validating library integrity..')
+            aEx.send({task: 0, content: 'validateLibraries', argsArr: [versionData]})
 
 
-        } else if(currentProc === 'validateLibraries'){
+        } else if(m.content === 'validateLibraries'){
 
 
-            launch_progress.setAttribute('value', 80)
-            launch_progress_label.innerHTML = '80%'
-            console.log('libs done')
+            setLaunchPercentage(80, 100)
+            console.log('Library validation complete.')
 
 
             // Begin miscellaneous validation.
             // Begin miscellaneous validation.
-            launch_details_text.innerHTML = 'Validating miscellaneous file integrity..'
-            currentProc = 'validateMiscellaneous'
-            aEx.send({task: 0, content: currentProc, argsArr: [versionData]})
+            setLaunchDetails('Validating miscellaneous file integrity..')
+            aEx.send({task: 0, content: 'validateMiscellaneous', argsArr: [versionData]})
 
 
-        } else if(currentProc === 'validateMiscellaneous'){
+        } else if(m.content === 'validateMiscellaneous'){
 
 
-            launch_progress.setAttribute('value', 100)
-            launch_progress_label.innerHTML = '100%'
-            console.log('files done')
+            setLaunchPercentage(100, 100)
+            console.log('File validation complete.')
 
 
-            launch_details_text.innerHTML = 'Downloading files..'
-            currentProc = 'processDlQueues'
-            aEx.send({task: 0, content: currentProc})
+            // Download queued files.
+            setLaunchDetails('Downloading files..')
+            aEx.send({task: 0, content: 'processDlQueues'})
+
+        } else if(m.content === 'dl'){
 
 
-        } else if(currentProc === 'processDlQueues'){
             if(m.task === 0){
             if(m.task === 0){
-                remote.getCurrentWindow().setProgressBar(m.value/m.total)
-                launch_progress.setAttribute('max', m.total)
-                launch_progress.setAttribute('value', m.value)
-                launch_progress_label.innerHTML = m.percent + '%'
+
+                setDownloadPercentage(m.value, m.total, m.percent)
+
             } else if(m.task === 1){
             } else if(m.task === 1){
+
+                // Download will be at 100%, remove the loading from the OS progress bar.
                 remote.getCurrentWindow().setProgressBar(-1)
                 remote.getCurrentWindow().setProgressBar(-1)
 
 
-                launch_details_text.innerHTML = 'Preparing to launch..'
-                currentProc = 'loadForgeData'
-                aEx.send({task: 0, content: currentProc, argsArr: [serv.id]})
+                setLaunchDetails('Preparing to launch..')
+                aEx.send({task: 0, content: 'loadForgeData', argsArr: [serv.id]})
 
 
             } else {
             } else {
+
                 console.error('Unknown download data type.', m)
                 console.error('Unknown download data type.', m)
+
             }
             }
-        } else if(currentProc === 'loadForgeData'){
+
+        } else if(m.content === 'loadForgeData'){
 
 
             forgeData = m.result
             forgeData = m.result
 
 
@@ -200,20 +264,27 @@ function dlAsync(login = true){
                 const authUser = ConfigManager.getSelectedAccount();
                 const authUser = ConfigManager.getSelectedAccount();
                 console.log('authu', authUser)
                 console.log('authu', authUser)
                 let pb = new ProcessBuilder(ConfigManager.getGameDirectory(), serv, versionData, forgeData, authUser)
                 let pb = new ProcessBuilder(ConfigManager.getGameDirectory(), serv, versionData, forgeData, authUser)
-                launch_details_text.innerHTML = 'Launching game..'
-                try{
+                setLaunchDetails('Launching game..')
+                try {
+                    // Build Minecraft process.
                     proc = pb.build()
                     proc = pb.build()
-                    launch_details_text.innerHTML = 'Done. Enjoy the server!'
+                    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){
                     const tempListener = function(data){
                         if(data.indexOf('[Client thread/INFO]: -- System Details --') > -1){
                         if(data.indexOf('[Client thread/INFO]: -- System Details --') > -1){
-                            launch_details.style.display = 'none'
-                            launch_content.style.display = 'inline-flex'
+                            toggleLaunchArea(false)
                             if(hasRPC){
                             if(hasRPC){
                                 DiscordWrapper.updateDetails('Loading game..')
                                 DiscordWrapper.updateDetails('Loading game..')
                             }
                             }
                             proc.stdout.removeListener('data', tempListener)
                             proc.stdout.removeListener('data', tempListener)
                         }
                         }
                     }
                     }
+
+                    // Listener for Discord RPC.
                     const gameStateChange = function(data){
                     const gameStateChange = function(data){
                         if(servJoined.test(data)){
                         if(servJoined.test(data)){
                             DiscordWrapper.updateDetails('Exploring the Realm!')
                             DiscordWrapper.updateDetails('Exploring the Realm!')
@@ -221,9 +292,12 @@ function dlAsync(login = true){
                             DiscordWrapper.updateDetails('Idling on Main Menu')
                             DiscordWrapper.updateDetails('Idling on Main Menu')
                         }
                         }
                     }
                     }
+
+                    // Bind listeners to stdout.
                     proc.stdout.on('data', tempListener)
                     proc.stdout.on('data', tempListener)
                     proc.stdout.on('data', gameStateChange)
                     proc.stdout.on('data', gameStateChange)
-                    // Init Discord Hook (Untested)
+
+                    // Init Discord Hook
                     const distro = AssetGuard.retrieveDistributionDataSync(ConfigManager.getGameDirectory)
                     const distro = AssetGuard.retrieveDistributionDataSync(ConfigManager.getGameDirectory)
                     if(distro.discord != null && serv.discord != null){
                     if(distro.discord != null && serv.discord != null){
                         DiscordWrapper.initRPC(distro.discord, serv.discord)
                         DiscordWrapper.initRPC(distro.discord, serv.discord)
@@ -235,14 +309,18 @@ function dlAsync(login = true){
                             proc = null
                             proc = null
                         })
                         })
                     }
                     }
+
                 } catch(err) {
                 } catch(err) {
-                    //launch_details_text.innerHTML = 'Error: ' + err.message;
-                    launch_details_text.innerHTML = 'Error: See log for details..';
+
+                    // Show that there was an error then hide the
+                    // progress area. Maybe switch this to an error
+                    // alert in the future. TODO
+                    setLaunchDetails('Error: See log for details..')
                     console.log(err)
                     console.log(err)
                     setTimeout(function(){
                     setTimeout(function(){
-                        launch_details.style.display = 'none'
-                        launch_content.style.display = 'inline-flex'
+                        toggleLaunchArea(false)
                     }, 5000)
                     }, 5000)
+
                 }
                 }
             }
             }
 
 
@@ -252,7 +330,9 @@ function dlAsync(login = true){
         }
         }
     })
     })
 
 
-    launch_details_text.innerHTML = 'Loading server information..'
-    currentProc = 'validateDistribution'
-    aEx.send({task: 0, content: currentProc, argsArr: [ConfigManager.getSelectedServer()]})
+    // Begin Validations
+
+    // Validate Forge files.
+    setLaunchDetails('Loading server information..')
+    aEx.send({task: 0, content: 'validateDistribution', argsArr: [ConfigManager.getSelectedServer()]})
 }
 }

+ 9 - 7
app/assets/js/assetexec.js

@@ -7,25 +7,27 @@ console.log('AssetExec Started')
 process.on('unhandledRejection', r => console.log(r))
 process.on('unhandledRejection', r => console.log(r))
 
 
 tracker.on('totaldlprogress', (data) => {
 tracker.on('totaldlprogress', (data) => {
-    process.send({task: 0, total: data.total, value: data.acc, percent: parseInt((data.acc/data.total)*100)})
+    process.send({task: 0, total: data.total, value: data.acc, percent: parseInt((data.acc/data.total)*100), content: 'dl'})
 })
 })
 
 
 tracker.on('dlcomplete', () => {
 tracker.on('dlcomplete', () => {
-    process.send({task: 1})
+    process.send({task: 1, content: 'dl'})
 })
 })
 
 
 process.on('message', (msg) => {
 process.on('message', (msg) => {
     if(msg.task === 0){
     if(msg.task === 0){
         const func = msg.content
         const func = msg.content
-        if(typeof tracker[func] === 'function'){
-            const f = tracker[func]
-            const res = f.apply(tracker, msg.argsArr)
+        let nS = tracker[func]
+        let iS = AssetGuard[func]
+        if(typeof nS === 'function' || typeof iS === 'function'){
+            const f = typeof nS === 'function' ? nS : iS
+            const res = f.apply(f === nS ? tracker : null, msg.argsArr)
             if(res instanceof Promise){
             if(res instanceof Promise){
                 res.then((v) => {
                 res.then((v) => {
-                    process.send({result: v})
+                    process.send({result: v, content: msg.content})
                 })
                 })
             } else {
             } else {
-                process.send({result: res})
+                process.send({result: res, content: msg.content})
             }
             }
         }
         }
     }
     }

+ 48 - 7
app/assets/js/assetguard.js

@@ -591,7 +591,10 @@ class AssetGuard extends EventEmitter {
     static _validateJavaBinary(binaryPath){
     static _validateJavaBinary(binaryPath){
 
 
         return new Promise((resolve, reject) => {
         return new Promise((resolve, reject) => {
-            const fBp = path.join(binaryPath, 'bin', 'java.exe')
+            let fBp = binaryPath
+            if(!fBp.endsWith('.exe')){
+                fBp = path.join(binaryPath, 'bin', 'java.exe')
+            }
             if(fs.existsSync(fBp)){
             if(fs.existsSync(fBp)){
                 child_process.exec('"' + fBp + '" -XshowSettings:properties', (err, stdout, stderr) => {
                 child_process.exec('"' + fBp + '" -XshowSettings:properties', (err, stdout, stderr) => {
 
 
@@ -642,6 +645,37 @@ class AssetGuard extends EventEmitter {
         }
         }
     }
     }
 
 
+    /**
+     * Scans the data folder's runtime directory for suitable JRE candidates.
+     * 
+     * @param {string} dataDir The base launcher directory.
+     * @returns {Promise.<Set.<string>>} A set containing suitable JRE candidates found
+     * in the runtime directory.
+     */
+    static _scanDataFolder(dataDir){
+        return new Promise((resolve, reject) => {
+            const x64RuntimeDir = path.join(dataDir, 'runtime', 'x64')
+            fs.exists(x64RuntimeDir, (e) => {
+                let res = new Set()
+                if(e){
+                    fs.readdir(x64RuntimeDir, (err, files) => {
+                        if(err){
+                            resolve(res)
+                            console.log(err)
+                        } else {
+                            for(let i=0; i<files.length; i++){
+                               res.add(path.join(x64RuntimeDir, files[i]))
+                            }
+                            resolve(res)
+                        }
+                    })
+                } else {
+                    resolve(res)
+                }
+            })
+        })
+    }
+
     /**
     /**
      * Scans the registry for 64-bit Java entries. The paths of each entry are added to
      * 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.
      * a set and returned. Currently, only Java 8 (1.8) is supported.
@@ -735,24 +769,30 @@ class AssetGuard extends EventEmitter {
      * Higher versions > Lower versions
      * Higher versions > Lower versions
      * If versions are equal, JRE > JDK.
      * If versions are equal, JRE > JDK.
      * 
      * 
+     * @param {string} dataDir The base launcher directory.
      * @returns {Promise.<string>} A Promise which resolves to the root path of a valid 
      * @returns {Promise.<string>} A Promise which resolves to the root path of a valid 
      * x64 Java installation. If none are found, null is returned.
      * x64 Java installation. If none are found, null is returned.
      */
      */
-    static async _win32JavaValidate(){
+    static async _win32JavaValidate(dataDir){
 
 
         // Get possible paths from the registry.
         // Get possible paths from the registry.
         const pathSet = await AssetGuard._scanRegistry()
         const pathSet = await AssetGuard._scanRegistry()
 
 
         //console.log(Array.from(pathSet)) // DEBUGGING
         //console.log(Array.from(pathSet)) // DEBUGGING
 
 
+        // Get possible paths from the data directory.
+        const pathSet2 = await AssetGuard._scanDataFolder(dataDir)
+
         // Validate JAVA_HOME
         // Validate JAVA_HOME
         const jHome = AssetGuard._scanJavaHome()
         const jHome = AssetGuard._scanJavaHome()
         if(jHome != null && jHome.indexOf('(x86)') === -1){
         if(jHome != null && jHome.indexOf('(x86)') === -1){
             pathSet.add(jHome)
             pathSet.add(jHome)
         }
         }
 
 
+        const mergedSet = new Set([...pathSet, ...pathSet2])
+
         // Convert path set to an array for processing.
         // Convert path set to an array for processing.
-        let pathArr = Array.from(pathSet)
+        let pathArr = Array.from(mergedSet)
 
 
         //console.log(pathArr) // DEBUGGING
         //console.log(pathArr) // DEBUGGING
 
 
@@ -787,24 +827,25 @@ class AssetGuard extends EventEmitter {
     /**
     /**
      * WIP ->  get a valid x64 Java path on macOS.
      * WIP ->  get a valid x64 Java path on macOS.
      */
      */
-    static async _darwinJavaValidate(){
+    static async _darwinJavaValidate(dataDir){
         return null
         return null
     }
     }
 
 
     /**
     /**
      * WIP ->  get a valid x64 Java path on linux.
      * WIP ->  get a valid x64 Java path on linux.
      */
      */
-    static async _linuxJavaValidate(){
+    static async _linuxJavaValidate(dataDir){
         return null
         return null
     }
     }
 
 
     /**
     /**
      * Retrieve the path of a valid x64 Java installation.
      * 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.
      * @returns {string} A path to a valid x64 Java installation, null if none found.
      */
      */
-    static async validateJava(){
-        return await AssetGuard['_' + process.platform + 'JavaValidate']()
+    static async validateJava(dataDir){
+        return await AssetGuard['_' + process.platform + 'JavaValidate'](dataDir)
     }
     }
 
 
     // #endregion
     // #endregion

+ 11 - 7
app/assets/js/uicore.js

@@ -40,14 +40,18 @@ document.addEventListener('readystatechange', function () {
 
 
     } else if(document.readyState === 'complete'){
     } else if(document.readyState === 'complete'){
 
 
+        //266.01
+        //170.8
+        //53.21
         // Bind progress bar length to length of bot wrapper
         // Bind progress bar length to length of bot wrapper
-        const targetWidth = document.getElementById("launch_content").getBoundingClientRect().width
-        const targetWidth2 = document.getElementById("server_selection").getBoundingClientRect().width
-        const targetWidth3 = document.getElementById("launch_button").getBoundingClientRect().width
-        document.getElementById("launch_details").style.maxWidth = targetWidth
-        document.getElementById("launch_progress").style.width = targetWidth2
-        document.getElementById("launch_details_right").style.maxWidth = targetWidth2
-        document.getElementById("launch_progress_label").style.width = targetWidth3
+        //const targetWidth = document.getElementById("launch_content").getBoundingClientRect().width
+        //const targetWidth2 = document.getElementById("server_selection").getBoundingClientRect().width
+        //const targetWidth3 = document.getElementById("launch_button").getBoundingClientRect().width
+
+        document.getElementById("launch_details").style.maxWidth = 266.01
+        document.getElementById("launch_progress").style.width = 170.8
+        document.getElementById("launch_details_right").style.maxWidth = 170.8
+        document.getElementById("launch_progress_label").style.width = 53.21
         
         
     }
     }