aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author0neGal <mail@0negal.com>2023-03-05 00:25:07 +0100
committer0neGal <mail@0negal.com>2023-03-05 00:38:53 +0100
commit5d86a3daa5f762326055469b6bcd8346e0655056 (patch)
tree110fb1395fac1510dc3bf70f39f073df555445ff
parent4536aad2c4f19d32569d84a7082e8636a2be6985 (diff)
downloadViper-5d86a3daa5f762326055469b6bcd8346e0655056.tar.gz
Viper-5d86a3daa5f762326055469b6bcd8346e0655056.zip
modularized many functions and got rid of utils.js
Notably, winLog() and winAlert() are now win.log() and win.alert() inside modules/window.js. updateViper(), updateNorthstar and handleNorthstarUpdating() are now update.viper(), update.northstar() and update.northstar_autoupdate(), inside modules/update.js isGameRunning() and isOriginRunning() are now is_running.origin() and is_running.game() inside modules/is_running.js, along with a .titanfall() and .northstar() for more specificity. Not used anywhere right now, but may in the future be used. setpath() and gamepathExists() are now gamepath.set() and gamepath.exists() inside modules/gamepath.js killOrigin() are now kill.origin() inside modules/kill.js setlang() is now just inlined into the only event where it's used.
-rw-r--r--src/app/main.js8
-rw-r--r--src/index.js32
-rw-r--r--src/modules/gamepath.js86
-rw-r--r--src/modules/is_running.js66
-rw-r--r--src/modules/kill.js27
-rw-r--r--src/modules/launch.js34
-rw-r--r--src/modules/mods.js20
-rw-r--r--src/modules/update.js239
-rw-r--r--src/modules/window.js15
-rw-r--r--src/utils.js404
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();
- },
-}