From ee885beeed57d95ffb58922cfc6b826b7ebf9a31 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Thu, 30 Dec 2021 22:05:54 +0100 Subject: first-ish draft of the mod UI and utils --- src/lang/en.json | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/lang/en.json') diff --git a/src/lang/en.json b/src/lang/en.json index e30d576..0aa7ef2 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -22,6 +22,12 @@ "gui.update": "Update", "gui.setpath": "Game Path", + "gui.mods": "Mods", + "gui.mods.count": "Mods Installed:", + "gui.mods.install": "Install Mod", + "gui.mods.removemod": "Remove Mod", + "gui.mods.removeall": "Remove All", + "gui.update.downloading": "Downloading...", "gui.update.extracting": "Extracting update...", "gui.update.finished": "Done! Ready to play!", @@ -35,5 +41,6 @@ "general.launching": "Launching", + "general.mods.installed": "Installed mods:", "general.missingpath": "Game path is not set!" } -- cgit v1.2.3 From c5c63d6c8a81596d0dcf3e14e072ea14f4d0610c Mon Sep 17 00:00:00 2001 From: 0neGal Date: Fri, 31 Dec 2021 15:41:51 +0100 Subject: added remove and toggle mod/all mods --- src/app/index.html | 4 +++- src/app/main.css | 3 ++- src/lang/en.json | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src/lang/en.json') diff --git a/src/app/index.html b/src/app/index.html index edf7fbe..9b72ebb 100644 --- a/src/app/index.html +++ b/src/app/index.html @@ -25,8 +25,10 @@
%%gui.mods%%
- + + +
diff --git a/src/app/main.css b/src/app/main.css index 08c82fa..929483a 100644 --- a/src/app/main.css +++ b/src/app/main.css @@ -77,8 +77,9 @@ button:active { } -#update, #installmod {background: #81A1C1} #setpath {background: #5E81AC} #vanilla, #exit {background: #656E7F} +#update, #installmod {background: #81A1C1} +#togglemod, #toggleall {background: #ECD19A} #northstar, #removeall, #removemod {background: #C7777F} button:disabled {background: var(--disabled) !important; opacity: 0.5} diff --git a/src/lang/en.json b/src/lang/en.json index 0aa7ef2..523c6fa 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -25,7 +25,9 @@ "gui.mods": "Mods", "gui.mods.count": "Mods Installed:", "gui.mods.install": "Install Mod", - "gui.mods.removemod": "Remove Mod", + "gui.mods.toggle": "Toggle Mod", + "gui.mods.toggleall": "Toggle All", + "gui.mods.remove": "Remove Mod", "gui.mods.removeall": "Remove All", "gui.update.downloading": "Downloading...", -- cgit v1.2.3 From 9c4b2aa7bfe2a96354f968c23a98213995242380 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Sun, 2 Jan 2022 03:36:39 +0100 Subject: toggling, removing and installing mods works Mostly, the installing part needs a bit more look at, to support archives and different layouts for the mod. Such as searching through an archive to find the right folder because some mods don't use a proper layout. I also somewhat mitigated the whole issue of JSON files not being formatted properly by the mod developer (please just fix your formatting, I beg you.) by simply assigning the absolute basics, however we can't know the versions of the mods. I am not going to go out of my way to write code which can parse a file that wasn't made to be parsed because whoever wrote it doesn't know what a JSON file is made of. Simply not happening. I also added a few locatiolization related things, along with more info for --mods, so besides the normal counter for "Installed mods" you also have "Enabled mods" and "Disabled mods", very useful. The GUI also has a new added "Disabled" tag to mods that are disabled, however this is a temporary, it looks bad and I don't want it in release, I just needed a way to distinquish when testing. Because you can now also enable and disable mods, mods.list() gives back an Object that goes more or less something like: {all: ..., enabled: ..., disabled: ... }, take your guesses as to what everything means, you might even get it in the first try. --- package-lock.json | 11 +++++++ package.json | 3 +- src/app/main.css | 8 +++++ src/app/main.js | 22 +++++++++++-- src/index.js | 16 ++++++--- src/lang/en.json | 5 +++ src/utils.js | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 150 insertions(+), 14 deletions(-) (limited to 'src/lang/en.json') diff --git a/package-lock.json b/package-lock.json index 96d23eb..8bb3e29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.9.0", "license": "GPL-3.0-or-later", "dependencies": { + "copy-dir": "^1.3.0", "electron-updater": "^4.6.1", "follow-redirects": "^1.14.6", "marked-man": "^0.7.0", @@ -1085,6 +1086,11 @@ "node": ">=8" } }, + "node_modules/copy-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/copy-dir/-/copy-dir-1.3.0.tgz", + "integrity": "sha512-Q4+qBFnN4bwGwvtXXzbp4P/4iNk0MaiGAzvQ8OiMtlLjkIKjmNN689uVzShSM0908q7GoFHXIPx4zi75ocoaHw==" + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -4540,6 +4546,11 @@ "xdg-basedir": "^4.0.0" } }, + "copy-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/copy-dir/-/copy-dir-1.3.0.tgz", + "integrity": "sha512-Q4+qBFnN4bwGwvtXXzbp4P/4iNk0MaiGAzvQ8OiMtlLjkIKjmNN689uVzShSM0908q7GoFHXIPx4zi75ocoaHw==" + }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", diff --git a/package.json b/package.json index 449adb7..45eb7fc 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,9 @@ "start": "npx electron src/index.js", "debug": "npx electron src/index.js --debug", "man": "npx marked-man docs/viper.1.md > docs/viper.1", - "build": "npx electron-builder --win nsis --win portable --linux", "build:windows": "npx electron-builder --win nsis --win portable", "build:linux": "npx electron-builder --linux", - "publish": "npx electron-builder --win nsis --win portable --linux -p always", "publish:windows": "npx electron-builder --win nsis --win portable -p always", "publish:linux": "npx electron-builder --linux -p always" @@ -49,6 +47,7 @@ }, "homepage": "https://github.com/0neGal/viper#readme", "dependencies": { + "copy-dir": "^1.3.0", "electron-updater": "^4.6.1", "follow-redirects": "^1.14.6", "marked-man": "^0.7.0", diff --git a/src/app/main.css b/src/app/main.css index c6c9bb9..8776ec9 100644 --- a/src/app/main.css +++ b/src/app/main.css @@ -47,11 +47,19 @@ nobr {white-space: nowrap} } #modsdiv { + padding: 1px; border-radius: var(--padding); background: var(--boxbackground); margin: calc(var(--padding) / 3) var(--padding); } +.mod { + margin: calc(var(--padding) / 3); + border-radius: calc(var(--padding) / 2) !important; +} + +.mod.selected {background: var(--background)} + .buttons { text-align: right; margin-left: auto; diff --git a/src/app/main.js b/src/app/main.js index ab80ba9..c500ba3 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -45,6 +45,18 @@ function setButtons(state) { } } +function select(entry) { + let entries = document.querySelectorAll("#modsdiv .mod"); + + for (let i = 0; i < entries.length; i++) { + if (entries[i].innerHTML == entry) { + entries[i].classList.add("selected"); + } else { + entries[i].classList.remove("selected"); + } + } +} + ipcRenderer.on("ns-updated", () => {setButtons(true)}) ipcRenderer.on("ns-updating", () => {setButtons(false)}) @@ -55,10 +67,14 @@ ipcRenderer.on("newpath", (event, newpath) => { ipcRenderer.on("log", (event, msg) => {log(msg)}) ipcRenderer.on("mods", (event, mods) => { - modcount.innerHTML = `${lang("gui.mods.count")} ${mods.length}`; + modcount.innerHTML = `${lang("gui.mods.count")} ${mods.all.length}`; modsdiv.innerHTML = ""; - for (let i = 0; i < mods.length; i++) { - modsdiv.innerHTML += `
${mods[i].Name}
`; + for (let i = 0; i < mods.enabled.length; i++) { + modsdiv.innerHTML += `
${mods.enabled[i].Name}
`; + } + + for (let i = 0; i < mods.disabled.length; i++) { + modsdiv.innerHTML += `
${mods.disabled[i].Name} - Disabled
`; } }) diff --git a/src/index.js b/src/index.js index 511aadb..161092a 100644 --- a/src/index.js +++ b/src/index.js @@ -83,10 +83,18 @@ ipcMain.on("versioncli", () => { ipcMain.on("getmods", (event) => { let mods = utils.mods.list(); - if (mods.length > 0) { - console.log(`${utils.lang("general.mods.installed")} ${mods.length}`) - for (let i = 0; i < mods.length; i++) { - console.log(` ${mods[i].Name} ${mods[i].Version}`) + if (mods.all.length > 0) { + console.log(`${utils.lang("general.mods.installed")} ${mods.all.length}`) + console.log(`${utils.lang("general.mods.enabled")} ${mods.enabled.length}`) + for (let i = 0; i < mods.enabled.length; i++) { + console.log(` ${mods.enabled[i].Name} ${mods.enabled[i].Version}`) + } + + if (mods.disabled.length > 0) { + console.log(`${utils.lang("general.mods.disabled")} ${mods.disabled.length}`) + for (let i = 0; i < mods.disabled.length; i++) { + console.log(` ${mods.disabled[i].Name} ${mods.disabled[i].Version}`) + } } cli.exit(0); } else { diff --git a/src/lang/en.json b/src/lang/en.json index 4221edb..c5e0d77 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -18,6 +18,9 @@ "cli.launch.linuxerror": "Launching the game is not currently supported on Linux", + "cli.mods.notamod": "Selected folder/file is not a mod", + "cli.mods.improperjson": "%s's mod.json has formatting errors", + "gui.welcome": "Welcome to Viper!", "gui.versions.viper": "Viper version", "gui.versions.northstar": "Northstar version", @@ -47,6 +50,8 @@ "general.launching": "Launching", + "general.mods.enabled": "Enabled mods:", + "general.mods.disabled": "Disabled mods:", "general.mods.installed": "Installed mods:", "general.missingpath": "Game path is not set!" } diff --git a/src/utils.js b/src/utils.js index c89a033..88e5f3a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,6 @@ -const fs = require("fs"); const path = require("path"); +const fs = require("fs-extra"); +const copy = require("copy-dir"); const { app, dialog, ipcMain } = require("electron"); const Emitter = require("events"); @@ -164,23 +165,111 @@ function winLog(msg) { ipcMain.emit("winLog", msg, msg); } +let modpath = path.join(settings.gamepath, "R2Northstar/mods"); const mods = { list: () => { let mods = []; - let modpath = path.join(settings.gamepath, "R2Northstar/mods"); + let disabled = []; files = fs.readdirSync(modpath) files.forEach((file) => { if (fs.statSync(path.join(modpath, file)).isDirectory()) { if (fs.existsSync(path.join(modpath, file, "mod.json"))) { - mods.push(require(path.join(modpath, file, "mod.json"))) + try { + mods.push({...require(path.join(modpath, file, "mod.json")), FolderName: file, Disabled: false}) + }catch(err) { + console.log("error: " + lang("cli.mods.improperjson"), file) + mods.push({Name: file, FolderName: file, Version: "unknown", Disabled: false}) + } + } + } + }) + + let disabledPath = path.join(modpath, "disabled") + files = fs.readdirSync(disabledPath) + files.forEach((file) => { + if (fs.statSync(path.join(disabledPath, file)).isDirectory()) { + if (fs.existsSync(path.join(disabledPath, file, "mod.json"))) { + try { + disabled.push({...require(path.join(disabledPath, file, "mod.json")), FolderName: file, Disabled: true}) + }catch(err) { + console.log("error: " + lang("cli.mods.improperjson"), file) + disabled.push({Name: file, FolderName: file, Version: "unknown", Disabled: false}) + } } } }) - return mods; + return { + enabled: mods, + disabled: disabled, + all: [...mods, ...disabled] + }; }, -} + get: (mod) => { + let list = mods.list().all; + + for (let i = 0; i < list.length; i++) { + if (list[i].Name == mod) { + return list[i]; + } else {continue} + } + + return false; + }, + install: (mod) => { + if (fs.statSync(mod).isDirectory()) { + if (fs.statSync(path.join(mod, "mod.json"))) { + copy(mod, path.join(modpath, mod.replace(/^.*(\\|\/|\:)/, "")), { + mode: true, + cover: true, + utimes: true, + }, (err) => { + if(err) {console.log("error:", err)}; + console.log(); + }); + cli.exit(); + return + } else { + console.log("error: " + lang("cli.mods.notamod")) + cli.exit(1); + return; + } + } + + fs.createReadStream(mod).pipe(unzip.Extract({path: modpath})) + .on("finish", () => { + cli.exit(); + }); + }, + remove: (mod) => { + let modName = mods.get(mod).FolderName; + let modPath = path.join(modpath, modName); + if (fs.statSync(modPath).isDirectory()) { + fs.rmSync(modPath, {recursive: true}); + cli.exit(); + } else { + cli.exit(1); + } + }, + toggle: (mod) => { + let disabled = path.join(modpath, "disabled"); + if (! fs.existsSync(disabled)) { + fs.mkdirSync(disabled) + } + + let modName = mods.get(mod).FolderName; + let modPath = path.join(modpath, modName); + let dest = path.join(disabled, modName); + + if (mods.get(mod).Disabled) { + modPath = path.join(disabled, modName); + dest = path.join(modpath, modName); + } + + fs.moveSync(modPath, dest) + } +}; module.exports = { mods, -- cgit v1.2.3 From bf7bd0993714f20fd0ab360de956378113e1d9c2 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Mon, 3 Jan 2022 00:43:45 +0100 Subject: fully working GUI functionality Smoothly updates and works flawlessly, the only thing that really needs improvements is the design and on top of that installing mods from a Zip file over folder. --- src/app/index.html | 10 ++++----- src/app/main.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++-------- src/index.js | 8 ++++++++ src/lang/en.json | 2 ++ src/utils.js | 38 ++++++++++++++++++++++++++++++----- 5 files changed, 99 insertions(+), 18 deletions(-) (limited to 'src/lang/en.json') diff --git a/src/app/index.html b/src/app/index.html index ffc31a6..88cfbb5 100644 --- a/src/app/index.html +++ b/src/app/index.html @@ -31,11 +31,11 @@
%%gui.mods%%
- - - - - + + + + +
diff --git a/src/app/main.js b/src/app/main.js index c500ba3..f903c29 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -45,18 +45,57 @@ function setButtons(state) { } } +let lastselected = ""; function select(entry) { - let entries = document.querySelectorAll("#modsdiv .mod"); + let entries = document.querySelectorAll("#modsdiv .mod span"); for (let i = 0; i < entries.length; i++) { if (entries[i].innerHTML == entry) { - entries[i].classList.add("selected"); + lastselected = entry; + entries[i].parentElement.classList.add("selected"); } else { - entries[i].classList.remove("selected"); + entries[i].parentElement.classList.remove("selected"); } } } +function selected(all) { + let selected = ""; + if (all) { + selected = "allmods" + } else { + selected = document.querySelector(".mod.selected span"); + if (selected != null) { + selected = selected.innerHTML; + } else { + alert(lang("gui.mods.nothingselected")); + return { + remove: () => {}, + toggle: () => {}, + } + } + } + + return { + remove: () => { + if (selected == "allmods") { + if (! confirm(lang("gui.mods.removeall.confirm"))) { + return; + } + } + + ipcRenderer.send("removemod", selected) + }, + toggle: () => { + ipcRenderer.send("togglemod", selected) + } + } +} + +function installmod() { + ipcRenderer.send("installmod") +} + ipcRenderer.on("ns-updated", () => {setButtons(true)}) ipcRenderer.on("ns-updating", () => {setButtons(false)}) @@ -67,15 +106,19 @@ ipcRenderer.on("newpath", (event, newpath) => { ipcRenderer.on("log", (event, msg) => {log(msg)}) ipcRenderer.on("mods", (event, mods) => { + console.log("refreshed mods") modcount.innerHTML = `${lang("gui.mods.count")} ${mods.all.length}`; modsdiv.innerHTML = ""; - for (let i = 0; i < mods.enabled.length; i++) { - modsdiv.innerHTML += `
${mods.enabled[i].Name}
`; + + let newmod = (name, extra) => { + if (! extra) {extra = ""} + modsdiv.innerHTML += `
${name}${extra}
`; } - for (let i = 0; i < mods.disabled.length; i++) { - modsdiv.innerHTML += `
${mods.disabled[i].Name} - Disabled
`; - } + for (let i = 0; i < mods.enabled.length; i++) {newmod(mods.enabled[i].Name)} + for (let i = 0; i < mods.disabled.length; i++) {newmod(mods.disabled[i].Name, " - Disabled")} + + select(lastselected); }) ipcRenderer.on("version", (event, versions) => { diff --git a/src/index.js b/src/index.js index 161092a..7ef9321 100644 --- a/src/index.js +++ b/src/index.js @@ -57,6 +57,14 @@ function start() { ipcMain.on("updatenow", () => { autoUpdater.quitAndInstall(); }) + + ipcMain.on("removemod", (event, mod) => {utils.mods.remove(mod)}) + ipcMain.on("togglemod", (event, mod) => {utils.mods.toggle(mod)}) + ipcMain.on("installmod", () => { + dialog.showOpenDialog({properties: ["openDirectory"]}).then(res => { + utils.mods.install(res.filePaths[0]); + }).catch(err => {console.error(err)}) + }) } ipcMain.on("launch", (event) => {utils.launch()}) diff --git a/src/lang/en.json b/src/lang/en.json index c5e0d77..20f97f2 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -35,6 +35,8 @@ "gui.mods.toggleall": "Toggle All", "gui.mods.remove": "Remove Mod", "gui.mods.removeall": "Remove All", + "gui.mods.nothingselected": "You've not selected a mod.", + "gui.mods.removeall.confirm": "Removing all mods will usually require you to reinstall Northstar, are you sure?", "gui.update.downloading": "Downloading...", "gui.update.extracting": "Extracting update...", diff --git a/src/utils.js b/src/utils.js index 88e5f3a..ef8b586 100644 --- a/src/utils.js +++ b/src/utils.js @@ -117,6 +117,7 @@ function update() { } } + ipcMain.emit("guigetmods"); ipcMain.emit("ns-updated"); winLog(lang("gui.update.finished")); console.log(lang("cli.update.finished")); @@ -194,7 +195,7 @@ const mods = { disabled.push({...require(path.join(disabledPath, file, "mod.json")), FolderName: file, Disabled: true}) }catch(err) { console.log("error: " + lang("cli.mods.improperjson"), file) - disabled.push({Name: file, FolderName: file, Version: "unknown", Disabled: false}) + disabled.push({Name: file, FolderName: file, Version: "unknown", Disabled: true}) } } } @@ -220,15 +221,13 @@ const mods = { install: (mod) => { if (fs.statSync(mod).isDirectory()) { if (fs.statSync(path.join(mod, "mod.json"))) { - copy(mod, path.join(modpath, mod.replace(/^.*(\\|\/|\:)/, "")), { + copy.sync(mod, path.join(modpath, mod.replace(/^.*(\\|\/|\:)/, "")), { mode: true, cover: true, utimes: true, - }, (err) => { - if(err) {console.log("error:", err)}; - console.log(); }); cli.exit(); + ipcMain.emit("guigetmods"); return } else { console.log("error: " + lang("cli.mods.notamod")) @@ -240,19 +239,47 @@ const mods = { fs.createReadStream(mod).pipe(unzip.Extract({path: modpath})) .on("finish", () => { cli.exit(); + ipcMain.emit("guigetmods"); }); }, remove: (mod) => { + if (mod == "allmods") { + let modlist = mods.list().all; + for (let i = 0; i < modlist.length; i++) { + mods.remove(modlist[i].Name) + } + return + } + + let disabled = path.join(modpath, "disabled"); + if (! fs.existsSync(disabled)) { + fs.mkdirSync(disabled) + } + let modName = mods.get(mod).FolderName; let modPath = path.join(modpath, modName); + + if (mods.get(mod).Disabled) { + modPath = path.join(disabled, modName); + } + if (fs.statSync(modPath).isDirectory()) { fs.rmSync(modPath, {recursive: true}); cli.exit(); + ipcMain.emit("guigetmods"); } else { cli.exit(1); } }, toggle: (mod) => { + if (mod == "allmods") { + let modlist = mods.list().all; + for (let i = 0; i < modlist.length; i++) { + mods.toggle(modlist[i].Name) + } + return + } + let disabled = path.join(modpath, "disabled"); if (! fs.existsSync(disabled)) { fs.mkdirSync(disabled) @@ -268,6 +295,7 @@ const mods = { } fs.moveSync(modPath, dest) + ipcMain.emit("guigetmods"); } }; -- cgit v1.2.3 From a210128d806bb1479c25198fa2399df99ee92bd0 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Mon, 3 Jan 2022 00:54:03 +0100 Subject: improved visuals of disabled tag --- src/app/main.css | 16 +++++++++++++--- src/app/main.js | 15 ++++++++++----- src/lang/en.json | 1 + 3 files changed, 24 insertions(+), 8 deletions(-) (limited to 'src/lang/en.json') diff --git a/src/app/main.css b/src/app/main.css index 8776ec9..60a05c6 100644 --- a/src/app/main.css +++ b/src/app/main.css @@ -6,6 +6,10 @@ --boxbackground: #666E7F; --subforeground: #AFAFAF; --btnforeground: var(--foreground); + + --red: #C7777F; + --blue: #81A1C1; + --yellow: #ECD19A; } @media (prefers-color-scheme: light) { @@ -60,6 +64,12 @@ nobr {white-space: nowrap} .mod.selected {background: var(--background)} +.mod .disabled { + color: var(--red); + position: absolute; + right: calc(var(--padding) * 2.5); +} + .buttons { text-align: right; margin-left: auto; @@ -96,7 +106,7 @@ button:active { #setpath {background: #5E81AC} #vanilla, #exit {background: #656E7F} -#update, #installmod {background: #81A1C1} -#togglemod, #toggleall {background: #ECD19A} -#northstar, #removeall, #removemod {background: #C7777F} +#update, #installmod {background: var(--blue)} +#togglemod, #toggleall {background: var(--yellow)} +#northstar, #removeall, #removemod {background: var(--red)} button:disabled {background: var(--disabled) !important; opacity: 0.5} diff --git a/src/app/main.js b/src/app/main.js index f903c29..897c82c 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -47,7 +47,7 @@ function setButtons(state) { let lastselected = ""; function select(entry) { - let entries = document.querySelectorAll("#modsdiv .mod span"); + let entries = document.querySelectorAll("#modsdiv .mod .modtext"); for (let i = 0; i < entries.length; i++) { if (entries[i].innerHTML == entry) { @@ -64,7 +64,7 @@ function selected(all) { if (all) { selected = "allmods" } else { - selected = document.querySelector(".mod.selected span"); + selected = document.querySelector(".mod.selected .modtext"); if (selected != null) { selected = selected.innerHTML; } else { @@ -110,9 +110,14 @@ ipcRenderer.on("mods", (event, mods) => { modcount.innerHTML = `${lang("gui.mods.count")} ${mods.all.length}`; modsdiv.innerHTML = ""; - let newmod = (name, extra) => { - if (! extra) {extra = ""} - modsdiv.innerHTML += `
${name}${extra}
`; + let newmod = (name, disabled) => { + if (disabled) { + disabled = `${lang("gui.mods.disabledtag")}` + } else { + disabled = "" + } + + modsdiv.innerHTML += `
${name}${disabled}
`; } for (let i = 0; i < mods.enabled.length; i++) {newmod(mods.enabled[i].Name)} diff --git a/src/lang/en.json b/src/lang/en.json index 20f97f2..7c68542 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -30,6 +30,7 @@ "gui.mods": "Mods", "gui.mods.count": "Mods Installed:", + "gui.mods.disabledtag": "Disabled", "gui.mods.install": "Install Mod", "gui.mods.toggle": "Toggle Mod", "gui.mods.toggleall": "Toggle All", -- cgit v1.2.3 From 34c93e9e7714322aa67fc427e3b9f03fea827229 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Mon, 3 Jan 2022 01:53:05 +0100 Subject: we're now able to install archived mods Lovely, only issue is for some reason both on Windows and Linux file dialogs can't select both directories and files, so either it's a folder or it's an archive, not both. So I guess we need to make some way to select it... --- src/index.js | 2 +- src/lang/en.json | 4 ++++ src/utils.js | 48 ++++++++++++++++++++++++++++++++++++------------ 3 files changed, 41 insertions(+), 13 deletions(-) (limited to 'src/lang/en.json') diff --git a/src/index.js b/src/index.js index 45400a5..a723622 100644 --- a/src/index.js +++ b/src/index.js @@ -62,7 +62,7 @@ function start() { ipcMain.on("removemod", (event, mod) => {utils.mods.remove(mod)}) ipcMain.on("togglemod", (event, mod) => {utils.mods.toggle(mod)}) ipcMain.on("installmod", () => { - dialog.showOpenDialog({properties: ["openDirectory"]}).then(res => { + dialog.showOpenDialog({properties: ["openFile"]}).then(res => { utils.mods.install(res.filePaths[0]); }).catch(err => {console.error(err)}) }) diff --git a/src/lang/en.json b/src/lang/en.json index 7c68542..401e98f 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -38,6 +38,10 @@ "gui.mods.removeall": "Remove All", "gui.mods.nothingselected": "You've not selected a mod.", "gui.mods.removeall.confirm": "Removing all mods will usually require you to reinstall Northstar, are you sure?", + "gui.mods.notamod": "Not a mod!", + "gui.mods.extracting": "Extracting mod...", + "gui.mods.installing": "Installing mod...", + "gui.mods.installedmod": "Installed mod!", "gui.update.downloading": "Downloading...", "gui.update.extracting": "Extracting update...", diff --git a/src/utils.js b/src/utils.js index a34f91f..e54a2c1 100644 --- a/src/utils.js +++ b/src/utils.js @@ -223,7 +223,20 @@ const mods = { return false; }, install: (mod) => { + let notamod = () => { + winLog(lang("gui.mods.notamod")) + console.log("error: " + lang("cli.mods.notamod")) + cli.exit(1); + } + + let installed = () => { + cli.exit(); + winLog(lang("gui.mods.installedmod")) + ipcMain.emit("guigetmods"); + } + if (fs.statSync(mod).isDirectory()) { + winLog(lang("gui.mods.installing")) if (fs.existsSync(path.join(mod, "mod.json")) && fs.statSync(path.join(mod, "mod.json")).isFile()) { @@ -232,8 +245,8 @@ const mods = { cover: true, utimes: true, }); - cli.exit(); - ipcMain.emit("guigetmods"); + + installed(); return true; } else { files = fs.readdirSync(mod); @@ -243,22 +256,33 @@ const mods = { if (fs.existsSync(path.join(mod, files[i], "mod.json")) && fs.statSync(path.join(mod, files[i], "mod.json")).isFile()) { - if (mods.install(path.join(mod, files[i]))) {return}; + if (mods.install(path.join(mod, files[i]))) {return true}; } } } - winAlert(lang("cli.mods.notamod")) - console.log("error: " + lang("cli.mods.notamod")) - cli.exit(1); + + notamod(); return false; } - } + } else { + winLog(lang("gui.mods.extracting")) + let cache = path.join(app.getPath("userData"), "Archives"); + if (fs.existsSync(cache)) { + fs.rmSync(cache, {recursive: true}); + fs.mkdirSync(cache); + } else { + fs.mkdirSync(cache); + } - fs.createReadStream(mod).pipe(unzip.Extract({path: modpath})) - .on("finish", () => { - cli.exit(); - ipcMain.emit("guigetmods"); - }); + try { + fs.createReadStream(mod).pipe(unzip.Extract({path: cache})) + .on("finish", () => { + if (mods.install(cache)) { + installed(); + } else {notamod();return false} + }); + }catch(err) {notamod();return false} + } }, remove: (mod) => { if (mod == "allmods") { -- cgit v1.2.3 From 1c12fb2e90d26e25be6206076ad6762fa92a5962 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Sat, 8 Jan 2022 15:05:35 +0100 Subject: added cli arguments for mods This adds both the arguments themselves, but also their entries in the man page and help page. --- docs/viper.1 | 15 +++++++++++++++ docs/viper.1.md | 16 ++++++++++++++++ src/cli.js | 27 +++++++++++++++++++-------- src/index.js | 16 +++++++++++----- src/lang/en.json | 9 +++++++++ src/utils.js | 26 ++++++++++++++++++++++++-- 6 files changed, 94 insertions(+), 15 deletions(-) (limited to 'src/lang/en.json') diff --git a/docs/viper.1 b/docs/viper.1 index 5b5b913..52ec4c5 100644 --- a/docs/viper.1 +++ b/docs/viper.1 @@ -31,6 +31,18 @@ Viper is a program made to make updating and launching Northstar a lot easier\. .P \fB\-\-setpath\fP= Sets the game path, this'll change the \fBgamepath\fP variable in your \fBviper\.json\fP, note that it only takes in absolute paths and not relative ones\. +.P +\fB\-\-mods\fP + Lists out all installed mods, and gives a count on how many are installed, enabled and or disabled\. +.P +\fB\-\-installmod\fP= + Installs a mod, this supports both Zip files and folders, just that the \fBmod\.json\fP file is easily found is all that matters\. +.P +\fB\-\-removemod\fP= + Removes a mod, the mod name should be taken from \fB\-\-mods\fP as it provides accurate names\. Putting in \fBallmods\fP will remove all mods, no confirmation\. +.P +\fB\-\-togglemod\fP= + Toggles a mod, the mod name should be taken from \fB\-\-mods\fP as it provides accurate names\. Putting in \fBallmods\fP will toggle all mods\. Keep in mind, if a mod is already disabled and some mods are enabled, the states will be flipped, so 3 enabled and 1 disabled, goes to 1 enabled and 3 disabled\. .SH CONFIGURATION .P All configuration takes place in your \fBviper\.json\fP file, this file may be in various locations depending on your platform, for Linux you're likely to find it at: @@ -45,6 +57,9 @@ All configuration takes place in your \fBviper\.json\fP file, this file may be i On Windows it's likely to be in \fB%APPDATA%\\viper\.json\fP .P All configuration is done by Viper itself, the locale is auto set when the GUI launches through your systems locale, the gamepath is selected with \fB\-\-setpath\fP or in the GUI\. +.SH MOD SUPPORT +.P +To toggle mods since Northstar itself has no filter as to what mods it loads, we have to move the mods into a separate folder, that folder being \fBdisabled\fP inside \fBR2Northstar/mods\fP, so you can also just manually move these if you want\. .SH BUGS .P Report bugs on the GitHub issues page, and feel free to make a pull request if you also have the fix to the bug\. diff --git a/docs/viper.1.md b/docs/viper.1.md index e271f4f..daa9263 100644 --- a/docs/viper.1.md +++ b/docs/viper.1.md @@ -34,6 +34,18 @@ Viper is a program made to make updating and launching Northstar a lot easier. I `--setpath`= Sets the game path, this'll change the `gamepath` variable in your `viper.json`, note that it only takes in absolute paths and not relative ones. +`--mods` + Lists out all installed mods, and gives a count on how many are installed, enabled and or disabled. + +`--installmod`= + Installs a mod, this supports both Zip files and folders, just that the `mod.json` file is easily found is all that matters. + +`--removemod`= + Removes a mod, the mod name should be taken from `--mods` as it provides accurate names. Putting in `allmods` will remove all mods, no confirmation. + +`--togglemod`= + Toggles a mod, the mod name should be taken from `--mods` as it provides accurate names. Putting in `allmods` will toggle all mods. Keep in mind, if a mod is already disabled and some mods are enabled, the states will be flipped, so 3 enabled and 1 disabled, goes to 1 enabled and 3 disabled. + ## CONFIGURATION All configuration takes place in your `viper.json` file, this file may be in various locations depending on your platform, for Linux you're likely to find it at: @@ -45,6 +57,10 @@ On Windows it's likely to be in `%APPDATA%\viper.json` All configuration is done by Viper itself, the locale is auto set when the GUI launches through your systems locale, the gamepath is selected with `--setpath` or in the GUI. +## MOD SUPPORT + +To toggle mods since Northstar itself has no filter as to what mods it loads, we have to move the mods into a separate folder, that folder being `disabled` inside `R2Northstar/mods`, so you can also just manually move these if you want. + ## BUGS Report bugs on the GitHub issues page, and feel free to make a pull request if you also have the fix to the bug. diff --git a/src/cli.js b/src/cli.js index 55cee0e..d5ee3ad 100644 --- a/src/cli.js +++ b/src/cli.js @@ -16,7 +16,10 @@ function hasArgs() { cli.hasSwitch("setpath") || cli.hasSwitch("version") || cli.hasSwitch("updatevp") || - cli.hasSwitch("gamepath")) { + cli.hasSwitch("gamepath") || + cli.hasSwitch("togglemod") || + cli.hasSwitch("removemod") || + cli.hasSwitch("installmod")) { return true; } else {return false} } @@ -28,14 +31,18 @@ function exit(code) { async function init() { if (cli.hasSwitch("help")) { console.log(`options: - --help ${lang("cli.help.help")} - --debug ${lang("cli.help.debug")} - --version ${lang("cli.help.version")} + --help ${lang("cli.help.help")} + --debug ${lang("cli.help.debug")} + --version ${lang("cli.help.version")} - --cli ${lang("cli.help.cli")} - --update ${lang("cli.help.update")} - --updatevp ${lang("cli.help.updatevp")} - --setpath ${lang("cli.help.setpath")}`) + --cli ${lang("cli.help.cli")} + --update ${lang("cli.help.update")} + --updatevp ${lang("cli.help.updatevp")} + --setpath ${lang("cli.help.setpath")} + + --installmod ${lang("cli.help.installmod")} + --removemod ${lang("cli.help.removemod")} + --togglemod ${lang("cli.help.togglemod")}`) // In the future --setpath should be able to understand // relative paths, instead of just absolute ones. exit(); @@ -64,6 +71,10 @@ async function init() { } } + if (cli.hasSwitch("installmod")) {ipcMain.emit("installmod")} + if (cli.hasSwitch("removemod")) {ipcMain.emit("removemod", "", cli.getSwitchValue("removemod"))} + if (cli.hasSwitch("togglemod")) {ipcMain.emit("togglemod", "", cli.getSwitchValue("togglemod"))} + if (cli.hasSwitch("mods")) {ipcMain.emit("getmods")} } diff --git a/src/index.js b/src/index.js index c03326c..09aa626 100644 --- a/src/index.js +++ b/src/index.js @@ -54,15 +54,20 @@ function start() { ipcMain.on("updatenow", () => { autoUpdater.quitAndInstall(); }) +} - ipcMain.on("removemod", (event, mod) => {utils.mods.remove(mod)}) - ipcMain.on("togglemod", (event, mod) => {utils.mods.toggle(mod)}) - ipcMain.on("installmod", () => { +ipcMain.on("installmod", () => { + if (cli.hasArgs()) { + utils.mods.install(cli.param("installmod")) + } else { dialog.showOpenDialog({properties: ["openFile"]}).then(res => { utils.mods.install(res.filePaths[0]); }).catch(err => {console.error(err)}) - }) -} + } +}) + +ipcMain.on("removemod", (event, mod) => {utils.mods.remove(mod)}) +ipcMain.on("togglemod", (event, mod) => {utils.mods.toggle(mod)}) ipcMain.on("launch", (event) => {utils.launch()}) ipcMain.on("setlang", (event, lang) => {utils.setlang(lang)}) @@ -77,6 +82,7 @@ ipcMain.on("setpath", (event, value) => { win.show(); } }); + ipcMain.on("newpath", (event, newpath) => { if (newpath === false && !win.isVisible()) { win.webContents.send("nopathselected"); diff --git a/src/lang/en.json b/src/lang/en.json index 3f025a8..aa76cdf 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -6,6 +6,9 @@ "cli.help.update": "updates Northstar from your set game path", "cli.help.setpath": "sets your game path", "cli.help.updatevp": "updates Viper itself, if supported.", + "cli.help.installmod": "installs a mod, folder or zip", + "cli.help.removemod": "removes a mod", + "cli.help.togglemod": "toggles a mod", "cli.setpath.noarg": "No argument provided for --setpath", @@ -18,7 +21,13 @@ "cli.launch.linuxerror": "Launching the game is not currently supported on Linux", + "cli.mods.failed": "Failed to install mod!", + "cli.mods.removed": "Successfully removed mod!", + "cli.mods.toggled": "Successfully toggled mod", + "cli.mods.installed": "Successfully installed mod!", + "cli.mods.cantfind": "Can't find a mod with that name!", "cli.mods.notamod": "Selected folder/file is not a mod", + "cli.mods.toggledall": "Successfully toggled all mods", "cli.mods.improperjson": "%s's mod.json has formatting errors", "gui.welcome": "Welcome to Viper!", diff --git a/src/utils.js b/src/utils.js index 18c52da..1e05fed 100644 --- a/src/utils.js +++ b/src/utils.js @@ -245,7 +245,9 @@ const mods = { } let installed = () => { + console.log(lang("cli.mods.installed")); cli.exit(); + winLog(lang("gui.mods.installedmod")) ipcMain.emit("guigetmods"); } @@ -314,6 +316,12 @@ const mods = { } let modName = mods.get(mod).FolderName; + if (! modName) { + console.log("error: " + lang("cli.mods.cantfind")) + cli.exit(1); + return; + } + let modPath = path.join(modpath, modName); if (mods.get(mod).Disabled) { @@ -322,18 +330,22 @@ const mods = { if (fs.statSync(modPath).isDirectory()) { fs.rmSync(modPath, {recursive: true}); + console.log(lang("cli.mods.removed")); cli.exit(); ipcMain.emit("guigetmods"); } else { cli.exit(1); } }, - toggle: (mod) => { + toggle: (mod, fork) => { if (mod == "allmods") { let modlist = mods.list().all; for (let i = 0; i < modlist.length; i++) { - mods.toggle(modlist[i].Name) + mods.toggle(modlist[i].Name, true) } + + console.log(lang("cli.mods.toggledall")); + cli.exit(0); return } @@ -343,6 +355,12 @@ const mods = { } let modName = mods.get(mod).FolderName; + if (! modName) { + console.log("error: " + lang("cli.mods.cantfind")) + cli.exit(1); + return; + } + let modPath = path.join(modpath, modName); let dest = path.join(disabled, modName); @@ -352,6 +370,10 @@ const mods = { } fs.moveSync(modPath, dest) + if (! fork) { + console.log(lang("cli.mods.toggled")); + cli.exit(); + } ipcMain.emit("guigetmods"); } }; -- cgit v1.2.3 From 215f37c6fa16c81020c7256133b0058b1b0a98d8 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Sat, 8 Jan 2022 15:35:48 +0100 Subject: mod support is disables if NS is not installed --- src/lang/en.json | 3 ++- src/utils.js | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'src/lang/en.json') diff --git a/src/lang/en.json b/src/lang/en.json index aa76cdf..61a8545 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -71,5 +71,6 @@ "general.mods.enabled": "Enabled mods:", "general.mods.disabled": "Disabled mods:", "general.mods.installed": "Installed mods:", - "general.missingpath": "Game path is not set!" + "general.missingpath": "Game path is not set!", + "general.notinstalled": "Northstar is not installed!" } diff --git a/src/utils.js b/src/utils.js index 67aee02..68cd6c1 100644 --- a/src/utils.js +++ b/src/utils.js @@ -184,6 +184,13 @@ function winAlert(msg) { let modpath = path.join(settings.gamepath, "R2Northstar/mods"); const mods = { list: () => { + if (getNSVersion() == "unknown") { + winLog(lang("general.notinstalled")) + console.log("error: " + lang("general.notinstalled")) + cli.exit(1); + return false; + } + let mods = []; let disabled = []; @@ -227,6 +234,13 @@ const mods = { }; }, get: (mod) => { + if (getNSVersion() == "unknown") { + winLog(lang("general.notinstalled")) + console.log("error: " + lang("general.notinstalled")) + cli.exit(1); + return false; + } + let list = mods.list().all; for (let i = 0; i < list.length; i++) { @@ -238,6 +252,13 @@ const mods = { return false; }, install: (mod) => { + if (getNSVersion() == "unknown") { + winLog(lang("general.notinstalled")) + console.log("error: " + lang("general.notinstalled")) + cli.exit(1); + return false; + } + let notamod = () => { winLog(lang("gui.mods.notamod")) console.log("error: " + lang("cli.mods.notamod")) @@ -305,6 +326,13 @@ const mods = { } }, remove: (mod) => { + if (getNSVersion() == "unknown") { + winLog(lang("general.notinstalled")) + console.log("error: " + lang("general.notinstalled")) + cli.exit(1); + return false; + } + if (mod == "allmods") { let modlist = mods.list().all; for (let i = 0; i < modlist.length; i++) { @@ -341,6 +369,13 @@ const mods = { } }, toggle: (mod, fork) => { + if (getNSVersion() == "unknown") { + winLog(lang("general.notinstalled")) + console.log("error: " + lang("general.notinstalled")) + cli.exit(1); + return false; + } + if (mod == "allmods") { let modlist = mods.list().all; for (let i = 0; i < modlist.length; i++) { -- cgit v1.2.3