Ver código fonte

Beginning work on configuration management, updates to UI to prevent unresponsive behavior, bug fixes..

Daniel Scalzi 8 anos atrás
pai
commit
4c2c46f535

+ 99 - 0
app/assets/js/actionbinder.js

@@ -0,0 +1,99 @@
+const mojang = require('mojang')
+const path = require('path')
+const AssetGuard = require(path.join(__dirname, 'assets', 'js', 'assetguard.js'))
+const ProcessBuilder = require(path.join(__dirname, 'assets', 'js', 'processbuilder.js'))
+const {GAME_DIRECTORY, DEFAULT_CONFIG} = require(path.join(__dirname, 'assets', 'js', 'constants.js'))
+
+document.onreadystatechange = function(){
+    if (document.readyState === 'interactive'){
+
+        // Bind launch button
+        document.getElementById("launch_button").addEventListener('click', function(e){
+            console.log('Launching game..')
+            testdownloads()
+        })
+
+    }
+}
+
+testdownloads = async function(){
+    const content = document.getElementById("launch_content")
+    const details = document.getElementById("launch_details")
+    const progress = document.getElementById("launch_progress")
+    const progress_text = document.getElementById("launch_progress_label")
+    const det_text = document.getElementById("launch_details_text")
+
+    det_text.innerHTML = 'Please wait..'
+    progress.setAttribute('max', '100')
+    details.style.display = 'flex'
+    content.style.display = 'none'
+
+    det_text.innerHTML = 'Loading version information..'
+    const versionData = await AssetGuard.loadVersionData('1.11.2', GAME_DIRECTORY)
+    progress.setAttribute('value', 20)
+    progress_text.innerHTML = '20%'
+
+    det_text.innerHTML = 'Validating asset integrity..'
+    await AssetGuard.validateAssets(versionData, GAME_DIRECTORY)
+    progress.setAttribute('value', 40)
+    progress_text.innerHTML = '40%'
+    console.log('assets done')
+
+    det_text.innerHTML = 'Validating library integrity..'
+    await AssetGuard.validateLibraries(versionData, GAME_DIRECTORY)
+    progress.setAttribute('value', 60)
+    progress_text.innerHTML = '60%'
+    console.log('libs done')
+
+    det_text.innerHTML = 'Validating miscellaneous file integrity..'
+    await AssetGuard.validateMiscellaneous(versionData, GAME_DIRECTORY)
+    progress.setAttribute('value', 80)
+    progress_text.innerHTML = '80%'
+    console.log('files done')
+
+    det_text.innerHTML = 'Validating server distribution files..'
+    const serv = await AssetGuard.validateDistribution('WesterosCraft-1.11.2', GAME_DIRECTORY)
+    progress.setAttribute('value', 100)
+    progress_text.innerHTML = '100%'
+    console.log('forge stuff done')
+
+    det_text.innerHTML = 'Downloading files..'
+    AssetGuard.instance.on('totaldlprogress', function(data){
+        progress.setAttribute('max', data.total)
+        progress.setAttribute('value', data.acc)
+        progress_text.innerHTML = parseInt((data.acc/data.total)*100) + '%'
+    })
+
+    AssetGuard.instance.on('dlcomplete', async function(){
+        det_text.innerHTML = 'Preparing to launch..'
+        const forgeData = await AssetGuard.loadForgeData('WesterosCraft-1.11.2', GAME_DIRECTORY)
+        const authUser = await mojang.auth('EMAIL', 'PASS', DEFAULT_CONFIG.getClientToken(), {
+            name: 'Minecraft',
+            version: 1
+        })
+        let pb = new ProcessBuilder(GAME_DIRECTORY, serv, versionData, forgeData, authUser)
+        det_text.innerHTML = 'Launching game..'
+        let proc;
+        try{
+            proc = pb.build()
+            det_text.innerHTML = 'Done. Enjoy the server!'
+            const tempListener = function(data){
+                if(data.indexOf('[Client thread/INFO]: -- System Details --') > -1){
+                    details.style.display = 'none'
+                    content.style.display = 'inline-flex'
+                    proc.stdout.removeListener('data', tempListener)
+                }
+            }
+            proc.stdout.on('data', tempListener)
+        } catch(err) {
+            //det_text.innerHTML = 'Error: ' + err.message;
+            det_text.innerHTML = 'Error: See log for details..';
+            console.log(err)
+            setTimeout(function(){
+                details.style.display = 'none'
+                content.style.display = 'inline-flex'
+            }, 5000)
+        }
+    })
+    AssetGuard.processDlQueues()
+}

+ 10 - 9
app/assets/js/assetguard.js

@@ -22,21 +22,22 @@
  * @module assetguard
  */
 // Requirements
-const fs = require('fs')
-const request = require('request')
-const path = require('path')
-const mkpath = require('mkdirp');
-const async = require('async')
-const crypto = require('crypto')
 const AdmZip = require('adm-zip')
+const async = require('async')
 const child_process = require('child_process')
+const crypto = require('crypto')
+const {DEFAULT_CONFIG} = require('./constants')
 const EventEmitter = require('events')
+const fs = require('fs')
+const mkpath = require('mkdirp');
+const path = require('path')
+const request = require('request')
 const {remote} = require('electron')
 
 // Classes
 
 /** Class representing a base asset. */
-class Asset{
+class Asset {
     /**
      * Create an asset.
      * 
@@ -56,7 +57,7 @@ class Asset{
 }
 
 /** Class representing a mojang library. */
-class Library extends Asset{
+class Library extends Asset {
 
     /**
      * Converts the process.platform OS names to match mojang's OS names.
@@ -349,7 +350,7 @@ function _extractPackXZ(filePaths){
     return new Promise(function(fulfill, reject){
         const libPath = path.join(__dirname, '..', 'libraries', 'java', 'PackXZExtract.jar')
         const filePath = filePaths.join(',')
-        const child = child_process.spawn('C:\\Program Files\\Java\\jdk1.8.0_152\\bin\\javaw.exe', ['-jar', libPath, '-packxz', filePath])
+        const child = child_process.spawn(DEFAULT_CONFIG.getJavaExecutable(), ['-jar', libPath, '-packxz', filePath])
         child.stdout.on('data', (data) => {
             //console.log('PackXZExtract:', data.toString('utf8'))
         })

+ 127 - 2
app/assets/js/configmanager.js

@@ -1,5 +1,6 @@
 const fs = require('fs')
 const mkpath = require('mkdirp')
+const os = require('os')
 const path = require('path')
 const uuidV4 = require('uuid/v4')
 
@@ -11,6 +12,13 @@ class ConfigManager {
         this.load()
     }
 
+    /* Private functions to resolve default settings based on system specs. */
+
+    static _resolveMaxRAM(){
+        const mem = os.totalmem()
+        return mem >= 8000000000 ? '4G' : (mem >= 6000000000 ? '3G' : '2G')
+    }
+
     /**
      * Generates a default configuration object and saves it.
      * 
@@ -18,15 +26,45 @@ class ConfigManager {
      */
     _generateDefault(save = true){
         this.config = {
-            settings: {},
+            settings: {
+                java: {
+                    minRAM: '2G',
+                    maxRAM: ConfigManager._resolveMaxRAM(),
+                    executable: 'C:\\Program Files\\Java\\jdk1.8.0_152\\bin\\javaw.exe', //TODO Resolve
+                    jvmOptions: [
+                        '-XX:+UseConcMarkSweepGC',
+                        '-XX:+CMSIncrementalMode',
+                        '-XX:-UseAdaptiveSizePolicy',
+                        '-Xmn128M'
+                    ],
+                },
+                game: {
+                    resWidth: 1280,
+                    resHeight: 720,
+                    fullscreen: false,
+                    autoConnect: true
+                },
+                launcher: {
+
+                }
+            },
             clientToken: uuidV4(),
-            authenticationDatabase: []
+            selectedServer: null,
+            selectedAccount: null,
+            authenticationDatabase: [],
+            discord: {
+                clientID: 385581240906022916
+            }
         }
         if(save){
             this.save()
         }
     }
 
+    /**
+     * Load the launcher configuration into memory. If the specified file does
+     * not exist, a default configuration will be generated and saved.
+     */
     load(){
         if(!fs.existsSync(this.path)){
             mkpath.sync(path.join(this.path, '..'))
@@ -36,14 +74,101 @@ class ConfigManager {
         }
     }
 
+    /**
+     * Save the launcher configuration to the specified file.
+     */
     save(){
         fs.writeFileSync(this.path, JSON.stringify(this.config, null, 4), 'UTF-8')
     }
 
+    /**
+     * Retrieve the launcher's Client Token.
+     */
     getClientToken(){
         return this.config.clientToken
     }
 
+    /**
+     * Retrieve the selected server configuration value.
+     */
+    getSelectedServer(){
+        return this.config.selectedServer
+    }
+
+    /**
+     * Set the selected server configuration value.
+     * 
+     * @param {String} serverID - the id of the new selected server.
+     */
+    setSelectedServer(serverID){
+        this.config.selectedServer = serverID
+        this.save()
+    }
+
+    /**
+     * Retrieve the launcher's Discord Client ID.
+     */
+    getDiscordClientID(){
+        return this.config.discord.clientID
+    }
+
+    /**
+     * Retrieve the minimum amount of memory for JVM initialization.
+     */
+    getMinRAM(){
+        return this.config.settings.java.minRAM
+    }
+
+    /**
+     * Retrieve the maximum amount of memory for JVM initialization.
+     */
+    getMaxRAM(){
+        return this.config.settings.java.maxRAM
+    }
+
+    /**
+     * Retrieve the path of the java executable.
+     */
+    getJavaExecutable(){
+        return this.config.settings.java.executable
+    }
+
+    /**
+     * Retrieve the additional arguments for JVM initialization. Required arguments,
+     * such as memory allocation, will be dynamically resolved.
+     */
+    getJVMOptions(){
+        return this.config.settings.java.jvmOptions
+    }
+
+    /**
+     * Retrieve the width of the game window.
+     */
+    getGameWidth(){
+        return this.config.settings.game.resWidth
+    }
+
+    /**
+     * Retrieve the height of the game window.
+     */
+    getGameHeight(){
+        return this.config.settings.game.resHeight
+    }
+
+    /**
+     * Check if the game should be launched in fullscreen mode.
+     */
+    isFullscreen(){
+        return this.config.settings.game.fullscreen
+    }
+
+    /**
+     * Check if auto connect is enabled.
+     */
+    isAutoConnect(){
+        return this.config.settings.game.autoConnect
+    }
+
 }
 
 module.exports = ConfigManager

+ 1 - 1
app/assets/js/constants.js

@@ -3,4 +3,4 @@ const ConfigManager = require('./configmanager')
 
 //TODO: Resolve game directory based on windows, linux, or mac..
 exports.GAME_DIRECTORY = path.join(__dirname, '..', '..', '..', 'target', 'test', 'mcfiles')
-exports.DEFAULT_CONFIG = new ConfigManager(path.join(exports.GAME_DIRECTORY, 'config.yml'))
+exports.DEFAULT_CONFIG = new ConfigManager(path.join(exports.GAME_DIRECTORY, 'config.json'))

+ 22 - 0
app/assets/js/discordwrapper.js

@@ -0,0 +1,22 @@
+// Work in progress
+const Client = require('discord-rpc')
+const {DEFAULT_CONFIG} = require('./constants')
+
+let rpc
+
+function initRPC(){
+    rpc = new Client({ transport: 'ipc' });
+
+    rpc.login(DEFAULT_CONFIG.getDiscordClientID()).catch(error => {
+        if(error.message.includes('ENOENT')) {
+            console.log('Unable to initialize Discord Rich Presence, no client detected.')
+        } else {
+            console.log('Unable to initialize Discord Rich Presence: ' + error.message)
+        }
+    })
+}
+
+function shutdownRPC(){
+    rpc.destroy()
+    rpc = null
+}

+ 39 - 8
app/assets/js/processbuilder.js

@@ -8,9 +8,11 @@
 const AdmZip = require('adm-zip')
 const ag = require('./assetguard.js')
 const child_process = require('child_process')
+const {DEFAULT_CONFIG} = require('./constants')
 const fs = require('fs')
 const mkpath = require('mkdirp')
 const path = require('path')
+const {URL} = require('url')
 
 class ProcessBuilder {
 
@@ -33,13 +35,14 @@ class ProcessBuilder {
      * Convienence method to run the functions typically used to build a process.
      */
     build(){
+        process.throwDeprecation = true
         const mods = this.resolveDefaultMods()
         this.constructFMLModList(mods, true)
         const args = this.constructJVMArguments(mods)
 
-        //console.log(args)
+        console.log(args)
 
-        const child = child_process.spawn('C:\\Program Files\\Java\\jdk1.8.0_152\\bin\\javaw.exe', args)
+        const child = child_process.spawn(DEFAULT_CONFIG.getJavaExecutable(), args)
 
         child.stdout.on('data', (data) => {
             console.log('Minecraft:', data.toString('utf8'))
@@ -95,16 +98,18 @@ class ProcessBuilder {
      */
     constructJVMArguments(mods){
         
-        let args = ['-Xmx4G',
-        '-XX:+UseConcMarkSweepGC',
-        '-XX:+CMSIncrementalMode',
-        '-XX:-UseAdaptiveSizePolicy',
-        '-Xmn128M',
+        let args = ['-Xmx' + DEFAULT_CONFIG.getMaxRAM(),
+        '-Xms' + DEFAULT_CONFIG.getMinRAM(),,
         '-Djava.library.path=' + path.join(this.dir, 'natives'),
         '-cp',
         this.classpathArg(mods).join(';'),
         this.forgeData.mainClass]
 
+        // For some reason this will add an undefined value unless
+        // the delete count is 1. I suspect this is unintended behavior
+        // by the function.. need to keep an eye on this.
+        args.splice(2, 1, ...DEFAULT_CONFIG.getJVMOptions())
+
         args = args.concat(this._resolveForgeArgs())
 
         return args
@@ -161,6 +166,28 @@ class ProcessBuilder {
         }
         mcArgs.push('--modListFile')
         mcArgs.push('absolute:' + this.fmlDir)
+
+        // Prepare game resolution
+        if(DEFAULT_CONFIG.isFullscreen()){
+            mcArgs.unshift('--fullscreen')
+        } else {
+            mcArgs.unshift(DEFAULT_CONFIG.getGameWidth())
+            mcArgs.unshift('--width')
+            mcArgs.unshift(DEFAULT_CONFIG.getGameHeight())
+            mcArgs.unshift('--height')
+        }
+
+        // Prepare autoconnect
+        if(DEFAULT_CONFIG.isAutoConnect() && this.server.autoconnect){
+            const serverURL = new URL('my://' + this.server.server_ip)
+            mcArgs.unshift(serverURL.hostname)
+            mcArgs.unshift('--server')
+            if(serverURL.port){
+                mcArgs.unshift(serverURL.port)
+                mcArgs.unshift('--port')
+            }
+        }
+
         return mcArgs
     }
 
@@ -244,7 +271,11 @@ class ProcessBuilder {
                         // Extract the file.
                         if(!shouldExclude){
                             mkpath.sync(path.join(nativePath, fileName, '..'))
-                            fs.writeFile(path.join(nativePath, fileName), zipEntries[i].getData())
+                            fs.writeFile(path.join(nativePath, fileName), zipEntries[i].getData(), (err) => {
+                                if(err){
+                                    console.error('Error while extracting native library:', err)
+                                }
+                            })
                         }
     
                     }

+ 0 - 170
app/assets/js/script.js

@@ -1,170 +0,0 @@
-const $ = require('jquery');
-const remote = require('electron').remote
-const shell = require('electron').shell
-const path = require('path')
-const os = require('os');
-const ag = require(path.join(__dirname, 'assets', 'js', 'assetguard.js'))
-const ProcessBuilder = require(path.join(__dirname, 'assets', 'js', 'processbuilder.js'))
-const mojang = require('mojang')
-const {GAME_DIRECTORY, DEFAULT_CONFIG} = require(path.join(__dirname, 'assets', 'js', 'constants.js'))
-
-$(document).on('ready', function(){
-    console.log('okay');
-})
-
-document.onreadystatechange = function () {
-    if (document.readyState == "complete") {
-
-        // Bind close button.
-        document.getElementById("frame_btn_close").addEventListener("click", function (e) {
-            const window = remote.getCurrentWindow()
-            window.close()
-        })
-
-        // Bind restore down button.
-        document.getElementById("frame_btn_restoredown").addEventListener("click", function (e) {
-            const window = remote.getCurrentWindow()
-            if(window.isMaximized()){
-                window.unmaximize();
-            } else {
-                window.maximize()
-            }
-        })
-
-        // Bind minimize button.
-        document.getElementById("frame_btn_minimize").addEventListener("click", function (e) {
-            const window = remote.getCurrentWindow()
-            window.minimize()
-        })
-
-        // Bind launch button
-        document.getElementById("launch_button").addEventListener('click', function(e){
-            console.log('Launching game..')
-            testdownloads()
-        })
-
-        // 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
-    }
-}
-
-// Open web links in the user's default browser.
-$(document).on('click', 'a[href^="http"]', function(event) {
-    event.preventDefault();
-    //console.log(os.homedir())
-    shell.openExternal(this.href)
-})
-
-testdownloads = async function(){
-    const content = document.getElementById("launch_content")
-    const details = document.getElementById("launch_details")
-    const progress = document.getElementById("launch_progress")
-    const progress_text = document.getElementById("launch_progress_label")
-    const det_text = document.getElementById("launch_details_text")
-
-    det_text.innerHTML = 'Please wait..'
-    progress.setAttribute('max', '100')
-    details.style.display = 'flex'
-    content.style.display = 'none'
-
-    det_text.innerHTML = 'Loading version information..'
-    const versionData = await ag.loadVersionData('1.11.2', GAME_DIRECTORY)
-    progress.setAttribute('value', 20)
-    progress_text.innerHTML = '20%'
-
-    det_text.innerHTML = 'Validating asset integrity..'
-    await ag.validateAssets(versionData, GAME_DIRECTORY)
-    progress.setAttribute('value', 40)
-    progress_text.innerHTML = '40%'
-    console.log('assets done')
-
-    det_text.innerHTML = 'Validating library integrity..'
-    await ag.validateLibraries(versionData, GAME_DIRECTORY)
-    progress.setAttribute('value', 60)
-    progress_text.innerHTML = '60%'
-    console.log('libs done')
-
-    det_text.innerHTML = 'Validating miscellaneous file integrity..'
-    await ag.validateMiscellaneous(versionData, GAME_DIRECTORY)
-    progress.setAttribute('value', 80)
-    progress_text.innerHTML = '80%'
-    console.log('files done')
-
-    det_text.innerHTML = 'Validating server distribution files..'
-    const serv = await ag.validateDistribution('WesterosCraft-1.11.2', GAME_DIRECTORY)
-    progress.setAttribute('value', 100)
-    progress_text.innerHTML = '100%'
-    console.log('forge stuff done')
-
-    det_text.innerHTML = 'Downloading files..'
-    ag.instance.on('totaldlprogress', function(data){
-        progress.setAttribute('max', data.total)
-        progress.setAttribute('value', data.acc)
-        progress_text.innerHTML = parseInt((data.acc/data.total)*100) + '%'
-    })
-
-    ag.instance.on('dlcomplete', async function(){
-        det_text.innerHTML = 'Preparing to launch..'
-        const forgeData = await ag.loadForgeData('WesterosCraft-1.11.2', GAME_DIRECTORY)
-        const authUser = await mojang.auth('EMAIL', 'PASS', DEFAULT_CONFIG.getClientToken(), {
-            name: 'Minecraft',
-            version: 1
-        })
-        let pb = new ProcessBuilder(GAME_DIRECTORY, serv, versionData, forgeData, authUser)
-        det_text.innerHTML = 'Launching game..'
-        let proc;
-        try{
-            proc = pb.build()
-            det_text.innerHTML = 'Done. Enjoy the server!'
-        } catch(err) {
-            //det_text.innerHTML = 'Error: ' + err.message;
-            det_text.innerHTML = 'Error: See log for details..';
-        }
-        setTimeout(function(){
-            details.style.display = 'none'
-            content.style.display = 'inline-flex'
-        }, 5000)
-    })
-    ag.processDlQueues()
-}
-
-/**
- * Opens DevTools window if you type "wcdev" in sequence.
- * This will crash the program if you are using multiple
- * DevTools, for example the chrome debugger in VS Code. 
- */
-const match = [87, 67, 68, 69, 86]
-let at = 0;
-
-document.addEventListener('keydown', function (e) {
-    switch(e.keyCode){
-        case match[0]:
-            if(at === 0) ++at
-            break
-        case match[1]:
-            if(at === 1) ++at
-            break
-        case match[2]:
-            if(at === 2) ++at
-            break
-        case match[3]:
-            if(at === 3) ++at
-            break
-        case match[4]:
-            if(at === 4) ++at
-            break
-        default:
-            at = 0
-    }
-    if(at === 5) {
-        var window = remote.getCurrentWindow()
-        window.toggleDevTools()
-        at = 0
-    }
-})

+ 93 - 0
app/assets/js/uicore.js

@@ -0,0 +1,93 @@
+/**
+ * Core UI functions are initialized in this file. This prevents
+ * unexpected errors from breaking the core features.
+ */
+const $ = require('jquery');
+const {remote, shell} = require('electron')
+
+/* jQuery Example
+$(function(){
+    console.log('UICore Initialized');
+})*/
+
+document.onreadystatechange = function () {
+    if (document.readyState === "interactive") {
+
+        console.log('UICore Initializing..');
+
+        // Bind close button.
+        document.getElementById("frame_btn_close").addEventListener("click", function (e) {
+            const window = remote.getCurrentWindow()
+            window.close()
+        })
+
+        // Bind restore down button.
+        document.getElementById("frame_btn_restoredown").addEventListener("click", function (e) {
+            const window = remote.getCurrentWindow()
+            if(window.isMaximized()){
+                window.unmaximize();
+            } else {
+                window.maximize()
+            }
+        })
+
+        // Bind minimize button.
+        document.getElementById("frame_btn_minimize").addEventListener("click", function (e) {
+            const window = remote.getCurrentWindow()
+            window.minimize()
+        })
+
+        // 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
+    }
+}
+
+/**
+ * Open web links in the user's default browser.
+ */
+$(document).on('click', 'a[href^="http"]', function(event) {
+    event.preventDefault();
+    //console.log(os.homedir())
+    shell.openExternal(this.href)
+})
+
+/**
+ * Opens DevTools window if you type "wcdev" in sequence.
+ * This will crash the program if you are using multiple
+ * DevTools, for example the chrome debugger in VS Code. 
+ */
+const match = [87, 67, 68, 69, 86]
+let at = 0;
+
+document.addEventListener('keydown', function (e) {
+    switch(e.keyCode){
+        case match[0]:
+            if(at === 0) ++at
+            break
+        case match[1]:
+            if(at === 1) ++at
+            break
+        case match[2]:
+            if(at === 2) ++at
+            break
+        case match[3]:
+            if(at === 3) ++at
+            break
+        case match[4]:
+            if(at === 4) ++at
+            break
+        default:
+            at = 0
+    }
+    if(at === 5) {
+        var window = remote.getCurrentWindow()
+        window.toggleDevTools()
+        at = 0
+    }
+})

+ 1 - 1
app/assets/westeroscraft.json

@@ -7,7 +7,7 @@
             "news_feed": "http://www.westeroscraft.com/api/rss.php?preset_id=12700544",
             "icon_url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/server-prod.png",
             "revision": "0.0.1",
-            "server_ip": "mc.westeroscraft.com:4444",
+            "server_ip": "mc.westeroscraft.com",
             "mc_version": "1.11.2",
             "autoconnect": true,
             "modules": [

+ 2 - 1
app/index.ejs

@@ -2,7 +2,8 @@
 <head>
     <meta charset="utf-8" />
     <title>Westeroscraft Launcher</title>
-    <script src="./assets/js/script.js"></script>
+    <script src="./assets/js/uicore.js"></script>
+    <script src="./assets/js/actionbinder.js"></script>
     <link type="text/css" rel="stylesheet" href="./assets/css/launcher.css">
     <style>
         body {

+ 83 - 2
package-lock.json

@@ -95,6 +95,11 @@
         "lodash": "4.17.4"
       }
     },
+    "async-limiter": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
+    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -151,6 +156,12 @@
         "chainsaw": "0.1.0"
       }
     },
+    "bindings": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz",
+      "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==",
+      "optional": true
+    },
     "bluebird": {
       "version": "3.5.1",
       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
@@ -405,6 +416,33 @@
       "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
       "dev": true
     },
+    "discord-rpc": {
+      "version": "3.0.0-beta.4",
+      "resolved": "https://registry.npmjs.org/discord-rpc/-/discord-rpc-3.0.0-beta.4.tgz",
+      "integrity": "sha512-IYSH6jYN3oiNZidWxQ1Z+fCw18W6u0iNN2SvPsq4bRiPwEgKgCa11cCw0aQp60IC/1zhS+eNfjpNkoSAjzJvEQ==",
+      "requires": {
+        "discord.js": "github:hydrabolt/discord.js#efd1c4c51625fd3c16ce3535f4bd340551dbd8ae",
+        "register-scheme": "github:devsnek/node-register-scheme#17333a6a6cdf6ef261f32e5563ba446f0ba5a63f",
+        "snekfetch": "3.5.8"
+      }
+    },
+    "discord.js": {
+      "version": "github:hydrabolt/discord.js#efd1c4c51625fd3c16ce3535f4bd340551dbd8ae",
+      "requires": {
+        "pako": "1.0.6",
+        "prism-media": "0.0.2",
+        "snekfetch": "3.5.8",
+        "tweetnacl": "1.0.0",
+        "ws": "3.3.2"
+      },
+      "dependencies": {
+        "tweetnacl": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.0.tgz",
+          "integrity": "sha1-cT2LgY2kIGh0C/aDhtBHnmb8ins="
+        }
+      }
+    },
     "ecc-jsbn": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
@@ -1209,6 +1247,12 @@
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
       "dev": true
     },
+    "node-addon-api": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.1.0.tgz",
+      "integrity": "sha512-WSGZ/xdGcyhNY2neCHS/JKQXdbOU5qZp8EGwxpEWSqOmI9+sCwO93npSH93ipa4z13+KZhGUgA5g/EAKH/F+Wg==",
+      "optional": true
+    },
     "nopt": {
       "version": "3.0.6",
       "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -1299,6 +1343,11 @@
         "p-limit": "1.1.0"
       }
     },
+    "pako": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
+      "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg=="
+    },
     "parse-author": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz",
@@ -1403,6 +1452,11 @@
         "meow": "3.7.0"
       }
     },
+    "prism-media": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-0.0.2.tgz",
+      "integrity": "sha512-L6yc8P5NVG35ivzvfI7bcTYzqFV+K8gTfX9YaJbmIFfMXTs71RMnAupvTQPTCteGsiOy9QcNLkQyWjAafY/hCQ=="
+    },
     "process-nextick-args": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
@@ -1528,6 +1582,14 @@
         "strip-indent": "1.0.1"
       }
     },
+    "register-scheme": {
+      "version": "github:devsnek/node-register-scheme#17333a6a6cdf6ef261f32e5563ba446f0ba5a63f",
+      "optional": true,
+      "requires": {
+        "bindings": "1.3.0",
+        "node-addon-api": "1.1.0"
+      }
+    },
     "repeating": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
@@ -1612,8 +1674,7 @@
     "safe-buffer": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
-      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
-      "dev": true
+      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
     },
     "sanitize-filename": {
       "version": "1.6.1",
@@ -1645,6 +1706,11 @@
         "string-width": "1.0.2"
       }
     },
+    "snekfetch": {
+      "version": "3.5.8",
+      "resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.5.8.tgz",
+      "integrity": "sha512-osq7soqKBObV4u/WE9tGQT/m5JdqTU1PWVPcT0We3sKZ99h9QA7wSj7ZWrwEwgRbELeO5BrVCanYjDYtVYcwrQ=="
+    },
     "sntp": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
@@ -1864,6 +1930,11 @@
       "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
       "dev": true
     },
+    "ultron": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+      "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
+    },
     "universalify": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
@@ -1921,6 +1992,16 @@
       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
       "dev": true
     },
+    "ws": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.2.tgz",
+      "integrity": "sha512-t+WGpsNxhMR4v6EClXS8r8km5ZljKJzyGhJf7goJz9k5Ye3+b5Bvno5rjqPuIBn5mnn5GBb7o8IrIWHxX1qOLQ==",
+      "requires": {
+        "async-limiter": "1.0.0",
+        "safe-buffer": "5.1.1",
+        "ultron": "1.1.1"
+      }
+    },
     "xmlbuilder": {
       "version": "8.2.2",
       "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",

+ 5 - 4
package.json

@@ -6,10 +6,10 @@
   "main": "index.js",
   "scripts": {
     "start": "electron index.js",
-    "buildwin": "electron-packager . WesteroscraftLauncher --overwrite --asar --platform=win32 --arch=x64 --ignore=\"\\.git(ignore|modules)|package\\.bat|node_modules|docs|target\" --out=\"./target\" --icon=\"app/assets/images/WesterosSealSquare.ico\"",
-    "builddarwin": "electron-packager . WesteroscraftLauncher --overwrite --asar --platform=darwin --ignore=\"\\.git(ignore|modules)|package\\.bat|node_modules|docs|target\" --out=\"./target\" --icon=\"app/assets/images/WesterosSealSquare.ico\"",
-    "buildlinux": "electron-packager . WesteroscraftLauncher --overwrite --asar --platform=linux --arch=x64 --ignore=\"\\.git(ignore|modules)|package\\.bat|node_modules|docs|target\" --out=\"./target\" --icon=\"app/assets/images/WesterosSealSquare.ico\"",
-    "buildlinuxarm": "electron-packager . WesteroscraftLauncher --overwrite --asar --platform=linux --arch=arm64 --ignore=\"\\.git(ignore|modules)|package\\.bat|node_modules|docs|target\" --out=\"./target\" --icon=\"app/assets/images/WesterosSealSquare.ico\""
+    "buildwin": "electron-packager . WesteroscraftLauncher --overwrite --asar --platform=win32 --arch=x64 --ignore=\"\\.git(ignore|modules)|logs|README.md|.vscode|docs|target\" --out=\"./target\" --icon=\"app/assets/images/WesterosSealSquare.ico\"",
+    "builddarwin": "electron-packager . WesteroscraftLauncher --overwrite --asar --platform=darwin --ignore=\"\\.git(ignore|modules)|logs|README.md|.vscode|docs|target\" --out=\"./target\" --icon=\"app/assets/images/WesterosSealSquare.ico\"",
+    "buildlinux": "electron-packager . WesteroscraftLauncher --overwrite --asar --platform=linux --arch=x64 --ignore=\"\\.git(ignore|modules)|logs|README.md|.vscode|docs|target\" --out=\"./target\" --icon=\"app/assets/images/WesterosSealSquare.ico\"",
+    "buildlinuxarm": "electron-packager . WesteroscraftLauncher --overwrite --asar --platform=linux --arch=arm64 --ignore=\"\\.git(ignore|modules)|logs|README.md|.vscode|docs|target\" --out=\"./target\" --icon=\"app/assets/images/WesterosSealSquare.ico\""
   },
   "engines": {
     "node": "8.9.x"
@@ -27,6 +27,7 @@
   "dependencies": {
     "adm-zip": "^0.4.7",
     "async": "^2.6.0",
+    "discord-rpc": "^3.0.0-beta.4",
     "ejs": "^2.5.7",
     "ejs-electron": "^2.0.1",
     "find-java-home": "^0.2.0",