diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app/main.js | 8 | ||||
-rw-r--r-- | src/index.js | 32 | ||||
-rw-r--r-- | src/modules/gamepath.js | 86 | ||||
-rw-r--r-- | src/modules/is_running.js | 66 | ||||
-rw-r--r-- | src/modules/kill.js | 27 | ||||
-rw-r--r-- | src/modules/launch.js | 34 | ||||
-rw-r--r-- | src/modules/mods.js | 20 | ||||
-rw-r--r-- | src/modules/update.js | 239 | ||||
-rw-r--r-- | src/modules/window.js | 15 | ||||
-rw-r--r-- | src/utils.js | 404 |
10 files changed, 498 insertions, 433 deletions
diff --git a/src/app/main.js b/src/app/main.js index a9329fa..1c846b4 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -1,6 +1,6 @@ const fs = require("fs"); const path = require("path"); -const { ipcRenderer, shell, ipcMain } = require("electron"); +const { ipcRenderer, shell } = require("electron"); const lang = require("../lang"); var modsobj = {}; @@ -90,11 +90,7 @@ function launch() { // Tells the main process to launch the vanilla game function launchVanilla() {ipcRenderer.send("launch-vanilla")} -// In conjunction with utils.js' winLog(), it'll send log messages in -// the devTools from utils.js -function log(msg) { - console.log(msg); -} +let log = console.log; // Disables or enables certain buttons when for example // updating/installing Northstar. diff --git a/src/index.js b/src/index.js index 991717b..88e3ed9 100644 --- a/src/index.js +++ b/src/index.js @@ -6,11 +6,14 @@ const { app, ipcMain, BrowserWindow, dialog } = require("electron"); // ensures PWD/CWD is the config folder where viper.json is located process.chdir(app.getPath("appData")); -const utils = require("./utils"); const cli = require("./cli"); const json = require("./modules/json"); +const kill = require("./modules/kill"); const mods = require("./modules/mods"); +const update = require("./modules/update"); +const launch = require("./modules/launch"); const version = require("./modules/version"); +const gamepath = require("./modules/gamepath"); const settings = require("./modules/settings"); const requests = require("./modules/requests"); @@ -61,7 +64,7 @@ function start() { if (settings.originkill) { utils.isOriginRunning().then((running) => { if (running) { - utils.killOrigin().then(process.exit(0)) + kill.origin().then(process.exit(0)) } else { process.exit(0) } @@ -120,12 +123,12 @@ function start() { // start auto-update process if (settings.autoupdate) { if (cli.hasParam("no-vp-updates")) { - utils.handleNorthstarUpdating(); + update.northstar_autoupdate(); } else { - utils.updateViper(false) + update.viper(false) } } else { - utils.handleNorthstarUpdating(); + update.northstar_autoupdate(); } autoUpdater.on("update-downloaded", () => { @@ -158,19 +161,22 @@ ipcMain.on("install-mod", () => { ipcMain.on("remove-mod", (event, mod) => {mods.remove(mod)}); ipcMain.on("toggle-mod", (event, mod) => {mods.toggle(mod)}); -ipcMain.on("launch-ns", () => {utils.launch()}); -ipcMain.on("launch-vanilla", () => {utils.launch("vanilla")}); +ipcMain.on("launch-ns", () => {launch()}); +ipcMain.on("launch-vanilla", () => {launch("vanilla")}); -ipcMain.on("setlang", (event, lang) => {utils.setlang(lang)}); +ipcMain.on("setlang", (event, lang) => { + settings.lang = lang; + settings.save(); +}); -ipcMain.on("update-northstar", () => {utils.updateNorthstar()}) -ipcMain.on("setpath-cli", () => {utils.setpath()}); +ipcMain.on("update-northstar", () => {update.northstar()}) +ipcMain.on("setpath-cli", () => {gamepath.set()}); ipcMain.on("setpath", (event, value) => { if (! value) { if (! win.isVisible()) { - utils.setpath(win); + gamepath.set(win); } else { - utils.setpath(win, true); + gamepath.set(win, true); } } else if (! win.isVisible()) { win.show(); @@ -240,7 +246,7 @@ ipcMain.on("newpath", (event, newpath) => { // starts the GUI or CLI if (cli.hasArgs()) { if (cli.hasParam("update-viper")) { - utils.updateViper(true); + update.viper(true); } else { cli.init(); } diff --git a/src/modules/gamepath.js b/src/modules/gamepath.js new file mode 100644 index 0000000..93290ec --- /dev/null +++ b/src/modules/gamepath.js @@ -0,0 +1,86 @@ +const path = require("path"); +const fs = require("fs-extra"); + +const { dialog, ipcMain } = require("electron"); + +const cli = require("../cli"); + +const settings = require("./settings"); + +let gamepath = {}; + +// Returns true/false depending on if the gamepath currently exists/is +// mounted, used to avoid issues... +gamepath.exists = () => { + return fs.existsSync(settings.gamepath); +} + +// Requests to set the game path +// +// If running with CLI it takes in the --setpath argument otherwise it +// open the systems file browser for the user to select a path. +gamepath.set = async (win, forcedialog) => { + function set_gamepath(folder) { + settings.gamepath = folder; + settings.zip = path.join(settings.gamepath + "/northstar.zip"); + settings.save(); + win.webContents.send("newpath", settings.gamepath); + ipcMain.emit("newpath", null, settings.gamepath); + + modpath = path.join(settings.gamepath, "R2Northstar/mods"); + } + + if (! win) { // CLI + set_gamepath(cli.param("setpath")); + } else { // GUI + if (! forcedialog) { + function set_gamepath(folder, forcedialog) { + settings.gamepath = folder; + settings.zip = path.join(settings.gamepath + "/northstar.zip"); + settings.save(); + win.webContents.send("newpath", settings.gamepath); + ipcMain.emit("newpath", null, settings.gamepath); + } + + let gamepath = await findgame(); + if (gamepath) { + set_gamepath(gamepath); + return; + } + + win.alert(lang("general.missingpath")); + } + + // Fallback to manual selection + dialog.showOpenDialog({properties: ["openDirectory"]}).then(res => { + if (res.canceled) { + ipcMain.emit("newpath", null, false); + return; + } + if (! fs.existsSync(path.join(res.filePaths[0], "Titanfall2.exe"))) { + ipcMain.emit("wrong-path"); + return; + } + + set_gamepath(res.filePaths[0]); + + cli.exit(); + return; + }).catch(err => {console.error(err)}) + } +} + +// periodically check for the gamepath still existing +setInterval(() => { + if (gamepath.exists()) { + ipcMain.emit("gui-getmods"); + } else { + if (fs.existsSync("viper.json")) { + if (settings.gamepath != "") { + ipcMain.emit("gamepath-lost"); + } + } + } +}, 1500) + +module.exports = gamepath; diff --git a/src/modules/is_running.js b/src/modules/is_running.js new file mode 100644 index 0000000..cc91ded --- /dev/null +++ b/src/modules/is_running.js @@ -0,0 +1,66 @@ +const exec = require("child_process").exec; + +let is_running = {}; + +// a simple function that checks whether any of a given set of process +// names are running, you can either input a string or an Array of +// strings +async function check_processes(processes) { + if (typeof processes == "string") { + processes = [processes]; + } + + return new Promise(resolve => { + if (! Array.isArray(processes)) { + reject(false); + } + + // While we could use a Node module to do this instead, I + // decided not to do so. As this achieves exactly the same + // thing. And it's not much more clunky. + let cmd = (() => { + switch (process.platform) { + case "linux": return "ps -A"; + case "win32": return "tasklist"; + } + })(); + + exec(cmd, (err, stdout) => { + for (let i = 0; i < processes.length; i++) { + if (stdout.includes(processes[i])) { + resolve(true); + break + } + + if (i == processes.length - 1) {resolve(false)} + } + }); + }); +} + +is_running.game = async () => { + return await check_processes([ + "NorthstarLauncher.exe", + "Titanfall2.exe", "Titanfall2-unpacked.exe" + ]) +} + +is_running.origin = async () => { + return await check_processes([ + "Origin.exe", + ]) +} + +is_running.titanfall = async () => { + return await check_processes([ + "Titanfall2.exe", "Titanfall2-unpacked.exe" + ]) +} + +is_running.northstar = async () => { + return await check_processes([ + "NorthstarLauncher.exe", + ]) +} + +module.exports = is_running; diff --git a/src/modules/kill.js b/src/modules/kill.js new file mode 100644 index 0000000..7482bed --- /dev/null +++ b/src/modules/kill.js @@ -0,0 +1,27 @@ +const exec = require("child_process").exec; + +// a simple function to kill processes with a certain name +async function kill(process_name) { + return new Promise(resolve => { + let proc = process_name; + let cmd = (() => { + switch (process.platform) { + case "linux": return "killall " + proc; + case "win32": return "taskkill /IM " + proc + " /F"; + } + })(); + + exec(cmd, (err, stdout) => { + // just try and fail silently if we don't find it w/e + resolve(true); + }); + }); +} + +kill.process = kill; + +kill.origin = () => { + return kill("Origin.exe"); +} + +module.exports = kill; diff --git a/src/modules/launch.js b/src/modules/launch.js new file mode 100644 index 0000000..765c348 --- /dev/null +++ b/src/modules/launch.js @@ -0,0 +1,34 @@ +const exec = require("child_process").exec; + +const cli = require("../cli"); +const lang = require("../lang"); + +const win = require("./window"); +const settings = require("./settings"); + +// Launches the game +// +// Either Northstar or Vanilla. Linux support is not currently a thing, +// however it'll be added at some point. +function launch(game_version) { + if (process.platform == "linux") { + win.alert(lang("cli.launch.linuxerror")); + console.error("error:", lang("cli.launch.linuxerror")); + cli.exit(1); + return; + } + + process.chdir(settings.gamepath); + switch(game_version) { + case "vanilla": + console.log(lang("general.launching"), "Vanilla..."); + exec("Titanfall2.exe", {cwd: settings.gamepath}); + break; + default: + console.log(lang("general.launching"), "Northstar..."); + exec("NorthstarLauncher.exe", {cwd: settings.gamepath}); + break; + } +} + +module.exports = launch; diff --git a/src/modules/mods.js b/src/modules/mods.js index 22865c2..6abed96 100644 --- a/src/modules/mods.js +++ b/src/modules/mods.js @@ -6,12 +6,12 @@ const { app, ipcMain } = require("electron"); const { https } = require("follow-redirects"); const json = require("./json"); +const win = require("./window"); const version = require("./version"); const settings = require("./settings"); const cli = require("../cli"); const lang = require("../lang"); -const utils = require("../utils"); var mods = { installing: [], @@ -31,7 +31,7 @@ mods.list = () => { update_path(); if (version.northstar() == "unknown") { - utils.winLog(lang("general.notinstalled")); + win.log(lang("general.notinstalled")); console.log("error: " + lang("general.notinstalled")); cli.exit(1); return false; @@ -108,7 +108,7 @@ mods.get = (mod) => { update_path(); if (version.northstar() == "unknown") { - utils.winLog(lang("general.notinstalled")); + win.log(lang("general.notinstalled")); console.log("error: " + lang("general.notinstalled")); cli.exit(1); return false; @@ -223,14 +223,14 @@ mods.install = (mod, opts) => { } if (version.northstar() == "unknown") { - utils.winLog(lang("general.notinstalled")); + win.log(lang("general.notinstalled")); console.log("error: " + lang("general.notinstalled")); cli.exit(1); return false; } let notamod = () => { - utils.winLog(lang("gui.mods.notamod")); + win.log(lang("gui.mods.notamod")); console.log("error: " + lang("cli.mods.notamod")); cli.exit(1); return false; @@ -240,7 +240,7 @@ mods.install = (mod, opts) => { console.log(lang("cli.mods.installed")); cli.exit(); - utils.winLog(lang("gui.mods.installedmod")); + win.log(lang("gui.mods.installedmod")); if (modname == "mods") { let manifest = path.join(app.getPath("userData"), "Archives/manifest.json"); @@ -262,7 +262,7 @@ mods.install = (mod, opts) => { if (! fs.existsSync(mod)) {return notamod()} if (fs.statSync(mod).isDirectory()) { - utils.winLog(lang("gui.mods.installing")); + win.log(lang("gui.mods.installing")); files = fs.readdirSync(mod); if (fs.existsSync(path.join(mod, "mod.json")) && fs.statSync(path.join(mod, "mod.json")).isFile()) { @@ -351,7 +351,7 @@ mods.install = (mod, opts) => { return notamod(); } } else { - utils.winLog(lang("gui.mods.extracting")); + win.log(lang("gui.mods.extracting")); let cache = path.join(app.getPath("userData"), "Archives"); if (fs.existsSync(cache)) { fs.rmSync(cache, {recursive: true}); @@ -464,7 +464,7 @@ mods.remove = (mod) => { update_path(); if (version.northstar() == "unknown") { - utils.winLog(lang("general.notinstalled")); + win.log(lang("general.notinstalled")); console.log("error: " + lang("general.notinstalled")); cli.exit(1); return false; @@ -525,7 +525,7 @@ mods.toggle = (mod, fork) => { update_path(); if (version.northstar() == "unknown") { - utils.winLog(lang("general.notinstalled")); + win.log(lang("general.notinstalled")); console.log("error: " + lang("general.notinstalled")); cli.exit(1); return false; diff --git a/src/modules/update.js b/src/modules/update.js new file mode 100644 index 0000000..cfdb42b --- /dev/null +++ b/src/modules/update.js @@ -0,0 +1,239 @@ +const path = require("path"); +const fs = require("fs-extra"); +const { ipcMain, Notification } = require("electron"); + +const cli = require("../cli"); +const lang = require("../lang"); + +const win = require("./window"); +const version = require("./version"); +const settings = require("./settings"); +const requests = require("./requests"); +const gamepath = require("./gamepath"); +const is_running = require("./is_running"); + +const unzip = require("unzipper"); +const { https } = require("follow-redirects"); + +let update = {}; + +// renames excluded files to their original name +function restore_excluded_files() { + if (! gamepath.exists()) {return} + + for (let i = 0; i < settings.excludes.length; i++) { + let exclude = path.join(settings.gamepath + "/" + settings.excludes[i]); + if (fs.existsSync(exclude + ".excluded")) { + fs.renameSync(exclude + ".excluded", exclude); + } + } +}; restore_excluded_files(); + +// renames excluded files to <file>.excluded, the list of files to be +// exluded is set in the settings (settings.excludes) +function exclude_files() { + for (let i = 0; i < settings.excludes.length; i++) { + let exclude = path.join(settings.gamepath + "/" + settings.excludes[i]); + if (fs.existsSync(exclude)) { + fs.renameSync(exclude, exclude + ".excluded"); + } + } +} + +// whether update.northstar_auto_update() has already been run before +let is_auto_updating = false; + +// Handles auto updating Northstar. +// +// It uses isGameRunning() to ensure it doesn't run while the game is +// running, as that may have all kinds of issues. +update.northstar_autoupdate = () => { + if (! settings.nsupdate || ! fs.existsSync("viper.json") || settings.gamepath.length === 0) { + return; + } + + if (is_auto_updating) {return} + + async function _checkForUpdates() { + is_auto_updating = true; + + console.log(lang("cli.autoupdates.checking")); + + // Checks if NS is outdated + if (await northstar_update_available()) { + console.log(lang("cli.autoupdates.available")); + if (await is_running.game()) { + console.log(lang("cli.autoupdates.gamerunning")); + new Notification({ + title: lang("gui.nsupdate.gaming.title"), + body: lang("gui.nsupdate.gaming.body") + }).show(); + } else { + console.log(lang("cli.autoupdates.updatingns")); + update.northstar(); + } + } else { + console.log(lang("cli.autoupdates.noupdate")); + } + + setTimeout( + _checkForUpdates, + 15 * 60 * 1000 + // interval in between each update check + // by default 15 minutes. + ); + } + + _checkForUpdates(); +} + +// returns whether an update is available for Northstar +async function northstar_update_available() { + let local = version.northstar(); + let distant = await requests.getLatestNsVersion(); + + if (distant == false) { + return false; + } + + // checks if NS is outdated + if (local !== distant) { + return true; + } else { + return false; + } +} + +// Updates Viper itself +// +// This uses electron updater to easily update and publish releases, it +// simply fetches it from GitHub and updates if it's outdated, very +// useful. Not much we have to do on our side. +update.viper = (autoinstall) => { + const { autoUpdater } = require("electron-updater"); + + if (! autoUpdater.isUpdaterActive()) { + if (settings.nsupdate) { + update.northstar_autoupdate(); + } + + return cli.exit(); + } + + if (autoinstall) { + autoUpdater.on("update-downloaded", (info) => { + autoUpdater.quitAndInstall(); + }); + } + + autoUpdater.on("error", (info) => {cli.exit(1)}); + autoUpdater.on("update-not-available", (info) => { + // only check for NS updates if Viper itself has no updates and + // if NS auto updates is enabled. + if (settings.nsupdate || cli.hasArgs()) { + update.northstar_autoupdate(); + } + cli.exit(); + }); + + autoUpdater.checkForUpdatesAndNotify(); +} + +// Installs/Updates Northstar +// +// If Northstar is already installed it'll be an update, otherwise it'll +// install it. It simply downloads the Northstar archive from GitHub, if +// it's outdated, then extracts it into the game path. +// +// As to handle not overwriting files we rename certain files to +// <file>.excluded, then rename them back after the extraction. The +// unzip module does not support excluding files directly. +update.northstar = async () => { + if (await is_running.game()) { + console.log(lang("cli.autoupdates.gamerunning")); + return false; + } + + if (! gamepath.exists()) {return} + + ipcMain.emit("ns-update-event", "cli.update.checking"); + console.log(lang("cli.update.checking")); + let ns_version = version.northstar(); + + const latest_version = await requests.getLatestNsVersion(); + console.log(latest_version) + if (latest_version == false) { + ipcMain.emit("ns-update-event", "cli.update.noInternet"); + return; + } + + // Makes sure it is not already the latest version + if (! await northstar_update_available()) { + ipcMain.emit("ns-update-event", "cli.update.uptodate.short"); + console.log(lang("cli.update.uptodate"), ns_version); + + win.log(lang("gui.update.uptodate")); + cli.exit(); + return; + } else { + if (ns_version != "unknown") { + console.log(lang("cli.update.current"), ns_version); + }; + + console.log(lang("cli.update.downloading") + ":", latest_version); + ipcMain.emit("ns-update-event", "cli.update.downloading"); + } + + exclude_files(); + + // Start the download of the zip + https.get(requests.getLatestNsVersionLink(), (res) => { + let stream = fs.createWriteStream(settings.zip); + res.pipe(stream); + + let received = 0; + // Progress messages, we should probably switch this to + // percentage instead of how much is downloaded. + res.on("data", (chunk) => { + received += chunk.length; + ipcMain.emit("ns-update-event", lang("gui.update.downloading") + " " + (received / 1024 / 1024).toFixed(1) + "mb"); + }) + + stream.on("finish", () => { + stream.close(); + let extract = fs.createReadStream(settings.zip); + + win.log(lang("gui.update.extracting")); + ipcMain.emit("ns-update-event", "gui.update.extracting"); + console.log(lang("cli.update.downloaddone")); + // Extracts the zip, this is the part where we're actually + // installing Northstar. + extract.pipe(unzip.Extract({path: settings.gamepath})) + + let max = received; + received = 0; + + extract.on("data", (chunk) => { + received += chunk.length; + let percent = Math.floor(received / max * 100); + ipcMain.emit("ns-update-event", lang("gui.update.extracting") + " " + percent + "%"); + }) + + extract.on("end", () => { + extract.close(); + ipcMain.emit("getversion"); + + restore_excluded_files(); + + ipcMain.emit("gui-getmods"); + ipcMain.emit("get-version"); + ipcMain.emit("ns-update-event", "cli.update.uptodate.short"); + win.log(lang("gui.update.finished")); + console.log(lang("cli.update.finished")); + cli.exit(); + }) + }) + }) +} + +module.exports = update; diff --git a/src/modules/window.js b/src/modules/window.js new file mode 100644 index 0000000..3b5c76f --- /dev/null +++ b/src/modules/window.js @@ -0,0 +1,15 @@ +const ipcMain = require("electron").ipcMain; + +let win = {}; + +// logs into the dev tools of the renderer +win.log = (msg) => { + ipcMain.emit("win-log", msg, msg); +} + +// sends an alert to the renderer +win.alert = (msg) => { + ipcMain.emit("win-alert", msg, msg); +} + +module.exports = win; diff --git a/src/utils.js b/src/utils.js deleted file mode 100644 index 5ba98e6..0000000 --- a/src/utils.js +++ /dev/null @@ -1,404 +0,0 @@ -const path = require("path"); -const fs = require("fs-extra"); -const { dialog, ipcMain, Notification } = require("electron"); - -const Emitter = require("events"); -const events = new Emitter(); - -const cli = require("./cli"); -const lang = require("./lang"); -const json = require("./modules/json"); -const version = require("./modules/version"); -const settings = require("./modules/settings"); -const requests = require("./modules/requests"); -const findgame = require("./modules/findgame"); - -const unzip = require("unzipper"); -const exec = require("child_process").exec; -const { https } = require("follow-redirects"); - -// Logs into the dev tools of the renderer -function winLog(msg) { - ipcMain.emit("win-log", msg, msg); -} - -// Sends an alert to the renderer -function winAlert(msg) { - ipcMain.emit("win-alert", msg, msg); -} - -// A simple function that checks if the game is running, which we use to -// not update Northstar when it is running. -async function isGameRunning() { - return new Promise(resolve => { - let procs = ["Titanfall2.exe", "Titanfall2-unpacked.exe", "NorthstarLauncher.exe"]; - // While we could use a Node module to do this instead, I - // decided not to do so. As this achieves exactly the same - // thing. And it's not much more clunky. - let cmd = (() => { - switch (process.platform) { - case "linux": return "ps -A"; - case "win32": return "tasklist"; - } - })(); - - exec(cmd, (err, stdout) => { - for (let i = 0; i < procs.length; i++) { - if (stdout.includes(procs[i])) { - resolve(true); - break - } - - if (i == procs.length - 1) {resolve(false)} - } - }); - }); -} - -// checks if any origin processes are running -async function isOriginRunning() { - return new Promise(resolve => { - let procs = ["Origin.exe", "OriginClientService.exe"]; - let cmd = (() => { - switch (process.platform) { - case "linux": return "ps -A"; - case "win32": return "tasklist"; - } - })(); - - exec(cmd, (err, stdout) => { - procs.forEach(proc => { - if (stdout.includes(proc)) { - resolve(true); - return; - } - resolve(false); - }); - }); - }); -} - -// kill origin processes -async function killOrigin() { - return new Promise(resolve => { - let proc = "Origin.exe"; //I'm pretty sure we only have to kill this one - let cmd = (() => { - switch (process.platform) { - case "linux": return "killall " + proc; - case "win32": return "taskkill /IM " + proc + " /F"; - } - })(); - - exec(cmd, (err, stdout) => { - // just try and fail silently if we don't find it w/e - resolve(true); - }); - }); -} - -// Handles auto updating Northstar. -// -// It uses isGameRunning() to ensure it doesn't run while the game is -// running, as that may have all kinds of issues. -function handleNorthstarUpdating() { - if (! settings.nsupdate || ! fs.existsSync("viper.json") || settings.gamepath.length === 0) { - return; - } - - async function _checkForUpdates() { - let localVersion = version.northstar(); - let distantVersion = await requests.getLatestNsVersion(); - if (distantVersion == false) { return; } - console.log(lang("cli.autoupdates.checking")); - - // Checks if NS is outdated - if (localVersion !== distantVersion) { - console.log(lang("cli.autoupdates.available")); - if (await isGameRunning()) { - console.log(lang("cli.autoupdates.gamerunning")); - new Notification({ - title: lang("gui.nsupdate.gaming.title"), - body: lang("gui.nsupdate.gaming.body") - }).show(); - } else { - console.log(lang("cli.autoupdates.updatingns")); - updateNorthstar(); - } - } else { - console.log(lang("cli.autoupdates.noupdate")); - } - - setTimeout( - _checkForUpdates, - 15 * 60 * 1000 - // interval in between each update check - // by default 15 minutes. - ); - } - - _checkForUpdates(); -} - - -// Requests to set the game path -// -// If running with CLI it takes in the --setpath argument otherwise it -// open the systems file browser for the user to select a path. -async function setpath(win, forcedialog) { - function setGamepath(folder) { - settings.gamepath = folder; - settings.zip = path.join(settings.gamepath + "/northstar.zip"); - settings.save(); - win.webContents.send("newpath", settings.gamepath); - ipcMain.emit("newpath", null, settings.gamepath); - - modpath = path.join(settings.gamepath, "R2Northstar/mods"); - } - - if (! win) { // CLI - setGamepath(cli.param("setpath")); - } else { // GUI - if (! forcedialog) { - function setGamepath(folder, forcedialog) { - settings.gamepath = folder; - settings.zip = path.join(settings.gamepath + "/northstar.zip"); - settings.save(); - win.webContents.send("newpath", settings.gamepath); - ipcMain.emit("newpath", null, settings.gamepath); - } - - let gamepath = await findgame(); - if (gamepath) { - setGamepath(gamepath); - return; - } - - winAlert(lang("general.missingpath")); - } - - // Fallback to manual selection - dialog.showOpenDialog({properties: ["openDirectory"]}).then(res => { - if (res.canceled) { - ipcMain.emit("newpath", null, false); - return; - } - if (! fs.existsSync(path.join(res.filePaths[0], "Titanfall2.exe"))) { - ipcMain.emit("wrong-path"); - return; - } - - setGamepath(res.filePaths[0]); - - cli.exit(); - return; - }).catch(err => {console.error(err)}) - } -} - -// Renames excluded files to their original name -function restoreExcludedFiles() { - for (let i = 0; i < settings.excludes.length; i++) { - let exclude = path.join(settings.gamepath + "/" + settings.excludes[i]); - if (fs.existsSync(exclude + ".excluded")) { - fs.renameSync(exclude + ".excluded", exclude); - } - } -} -// At start, restore excluded files who might have been created by an incomplete update process. -restoreExcludedFiles(); - -// Installs/Updates Northstar -// -// If Northstar is already installed it'll be an update, otherwise it'll -// install it. It simply downloads the Northstar archive from GitHub, if -// it's outdated, then extracts it into the game path. -// -// As to handle not overwriting files we rename certain files to -// <file>.excluded, then rename them back after the extraction. The -// unzip module does not support excluding files directly. -async function updateNorthstar() { - if (! gamepathExists()) {return} - - ipcMain.emit("ns-update-event", "cli.update.checking"); - console.log(lang("cli.update.checking")); - var ns_version = version.northstar(); - - const latestAvailableVersion = await requests.getLatestNsVersion(); - console.log(latestAvailableVersion) - if (latestAvailableVersion == false) { - ipcMain.emit("ns-update-event", "cli.update.noInternet"); - return; - } - - // Makes sure it is not already the latest version - if (ns_version === latestAvailableVersion) { - ipcMain.emit("ns-update-event", "cli.update.uptodate.short"); - console.log(lang("cli.update.uptodate"), ns_version); - - winLog(lang("gui.update.uptodate")); - cli.exit(); - return; - } else { - if (ns_version != "unknown") { - console.log(lang("cli.update.current"), ns_version); - }; - console.log(lang("cli.update.downloading") + ":", latestAvailableVersion); - ipcMain.emit("ns-update-event", "cli.update.downloading"); - } - - // Renames excluded files to <file>.excluded - for (let i = 0; i < settings.excludes.length; i++) { - let exclude = path.join(settings.gamepath + "/" + settings.excludes[i]); - if (fs.existsSync(exclude)) { - fs.renameSync(exclude, exclude + ".excluded"); - } - } - - // Start the download of the zip - https.get(requests.getLatestNsVersionLink(), (res) => { - let stream = fs.createWriteStream(settings.zip); - res.pipe(stream); - - let received = 0; - // Progress messages, we should probably switch this to - // percentage instead of how much is downloaded. - res.on("data", (chunk) => { - received += chunk.length; - ipcMain.emit("ns-update-event", lang("gui.update.downloading") + " " + (received / 1024 / 1024).toFixed(1) + "mb"); - }) - - stream.on("finish", () => { - stream.close(); - let extract = fs.createReadStream(settings.zip); - - winLog(lang("gui.update.extracting")); - ipcMain.emit("ns-update-event", "gui.update.extracting"); - console.log(lang("cli.update.downloaddone")); - // Extracts the zip, this is the part where we're actually - // installing Northstar. - extract.pipe(unzip.Extract({path: settings.gamepath})) - - let max = received; - received = 0; - - extract.on("data", (chunk) => { - received += chunk.length; - let percent = Math.floor(received / max * 100); - ipcMain.emit("ns-update-event", lang("gui.update.extracting") + " " + percent + "%"); - }) - - extract.on("end", () => { - extract.close(); - ipcMain.emit("getversion"); - - restoreExcludedFiles(); - - ipcMain.emit("gui-getmods"); - ipcMain.emit("get-version"); - ipcMain.emit("ns-update-event", "cli.update.uptodate.short"); - winLog(lang("gui.update.finished")); - console.log(lang("cli.update.finished")); - cli.exit(); - }) - }) - }) -} - -// Updates Viper itself -// -// This uses electron updater to easily update and publish releases, it -// simply fetches it from GitHub and updates if it's outdated, very -// useful. Not much we have to do on our side. -function updateViper(autoinstall) { - const { autoUpdater } = require("electron-updater"); - - if (! autoUpdater.isUpdaterActive()) { - if (settings.nsupdate) { - handleNorthstarUpdating(); - } - return cli.exit(); - } - - if (autoinstall) { - autoUpdater.on("update-downloaded", (info) => { - autoUpdater.quitAndInstall(); - }); - } - - autoUpdater.on("error", (info) => {cli.exit(1)}); - autoUpdater.on("update-not-available", (info) => { - // only check for NS updates if Viper itself has no updates and - // if NS auto updates is enabled. - if (settings.nsupdate || cli.hasArgs()) { - handleNorthstarUpdating(); - } - cli.exit(); - }); - - autoUpdater.checkForUpdatesAndNotify(); -} - -// Launches the game -// -// Either Northstar or Vanilla. Linux support is not currently a thing, -// however it'll be added at some point. -function launch(game_version) { - if (process.platform == "linux") { - winAlert(lang("cli.launch.linuxerror")); - console.error("error:", lang("cli.launch.linuxerror")); - cli.exit(1); - return; - } - - process.chdir(settings.gamepath); - switch(game_version) { - case "vanilla": - console.log(lang("general.launching"), "Vanilla..."); - exec("Titanfall2.exe", {cwd: settings.gamepath}); - break; - default: - console.log(lang("general.launching"), "Northstar..."); - exec("NorthstarLauncher.exe", {cwd: settings.gamepath}); - break; - } -} - -// Returns true/false depending on if the gamepath currently exists/is -// mounted, used to avoid issues... -function gamepathExists() { - return fs.existsSync(settings.gamepath); -} - -setInterval(() => { - if (gamepathExists()) { - ipcMain.emit("gui-getmods"); - } else { - if (fs.existsSync("viper.json")) { - if (settings.gamepath != "") { - ipcMain.emit("gamepath-lost"); - } - } - } -}, 1500) - -module.exports = { - winLog, - - updateViper, - updateNorthstar, - handleNorthstarUpdating, - - launch, - killOrigin, - isGameRunning, - isOriginRunning, - - setpath, - gamepathExists, - - lang, - setlang: (lang) => { - settings.lang = lang; - settings.save(); - }, -} |