aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author0neGal <mail@0negal.com>2022-01-08 16:49:57 +0100
committerGitHub <noreply@github.com>2022-01-08 16:49:57 +0100
commit61384d15d1fb34511cdb78690b5ec4e2f7d7a7c4 (patch)
treeba786f87cdefd22cd54413517fcfb2359f7f3bae
parent5206e9e4051b5c90e11b3aa331b0f6ee5546d25e (diff)
parent8ad3b0522aee4afc0d7494aaf3927c388752b24c (diff)
downloadViper-61384d15d1fb34511cdb78690b5ec4e2f7d7a7c4.tar.gz
Viper-61384d15d1fb34511cdb78690b5ec4e2f7d7a7c4.zip
Merge pull request #19 from 0neGal/mod-support
Adds mod support, removing, installing and toggling mods, both CLI and GUI
-rw-r--r--docs/viper.115
-rw-r--r--docs/viper.1.md16
-rw-r--r--package-lock.json11
-rw-r--r--package.json3
-rw-r--r--src/app/index.html12
-rw-r--r--src/app/main.css48
-rw-r--r--src/app/main.js80
-rw-r--r--src/cli.js30
-rw-r--r--src/index.js43
-rw-r--r--src/lang/en.json33
-rw-r--r--src/lang/fr.json33
-rw-r--r--src/utils.js255
12 files changed, 552 insertions, 27 deletions
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=<absolute\-path>
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=<absolute\-path>
+ 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=<mod name>
+ 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=<mod name>
+ 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`=<absolute-path>
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`=<absolute-path>
+ Installs a mod, this supports both Zip files and folders, just that the `mod.json` file is easily found is all that matters.
+
+`--removemod`=<mod name>
+ 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`=<mod name>
+ 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/package-lock.json b/package-lock.json
index f337f3a..d02e4c7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "0.9.1",
"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",
@@ -1055,6 +1056,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",
@@ -4266,6 +4272,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 efb65eb..942efd6 100644
--- a/package.json
+++ b/package.json
@@ -30,11 +30,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"
@@ -50,6 +48,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/index.html b/src/app/index.html
index f5d92e0..88d5a17 100644
--- a/src/app/index.html
+++ b/src/app/index.html
@@ -26,6 +26,18 @@
<button id="northstar" onclick="launch()">%%gui.launchnorthstar%%</button>
</div>
</div>
+ <div id="modsdiv">
+ </div>
+ <div class="line">
+ <div class="text" id="modcount">%%gui.mods%%</div>
+ <div class="buttons modbtns">
+ <button id="removemod" onclick="selected().remove()">%%gui.mods.remove%%</button>
+ <button id="removeall" onclick="selected(true).remove()">%%gui.mods.removeall%%</button>
+ <button id="togglemod" onclick="selected().toggle()">%%gui.mods.toggle%%</button>
+ <button id="toggleall" onclick="selected(true).toggle(true)">%%gui.mods.toggleall%%</button>
+ <button id="installmod" onclick="installmod()">%%gui.mods.install%%</button>
+ </div>
+ </div>
</div>
<script src="lang.js"></script>
diff --git a/src/app/main.css b/src/app/main.css
index c2ca627..56f17b2 100644
--- a/src/app/main.css
+++ b/src/app/main.css
@@ -1,15 +1,22 @@
:root {
+ --padding: 15px;
--disabled: #656E7F;
- --background: #4C515B;
--foreground: #DDE2EB;
+ --background: #4C515B;
+ --boxbackground: #666E7F;
--subforeground: #AFAFAF;
--btnforeground: var(--foreground);
+
+ --red: #C7777F;
+ --blue: #81A1C1;
+ --yellow: #ECD19A;
}
@media (prefers-color-scheme: light) {
:root {
--background: #FFFFFF;
--foreground: #4C566A;
+ --boxbackground: #EEF0F4;
--btnforeground: var(--background);
}
}
@@ -19,6 +26,8 @@
src: url("roboto.ttf");
}
+::-webkit-scrollbar {display: none}
+
body, button, input {
font-size: 18px;
font-weight: 700;
@@ -40,34 +49,56 @@ nobr {white-space: nowrap}
.line {
display: flex;
- margin-top: 15px;
+ margin-top: var(--padding);
+}
+
+#modsdiv {
+ padding: 1px;
+ height: 125px;
+ overflow-y: scroll;
+ 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)}
+
+.mod .disabled {
+ color: var(--red);
+ position: relative;
+ left: var(--padding);
}
.buttons {
text-align: right;
margin-left: auto;
user-select: none;
- margin-right: 7px;
+ margin-right: calc(var(--padding) / 1.9);
}
.text {max-width: 38vw}
.buttons {max-width: 55vw}
-button, .text {
+button, .text, .mod {
border: none;
outline: none;
- padding: 5px 15px;
user-select: none;
border-radius: 50px;
transition: 0.2s ease-in-out;
+ padding: calc(var(--padding) / 3) var(--padding);
}
#welcome {padding: 0px}
button {
- margin-bottom: 10px;
color: var(--btnforeground);
-webkit-app-region: no-drag;
+ margin-bottom: calc(var(--padding) / 1.5);
}
button:hover {opacity: 0.9}
@@ -77,8 +108,9 @@ button:active {
}
-#update {background: #81A1C1}
#setpath {background: #5E81AC}
-#northstar {background: #C7777F}
#vanilla, #exit {background: #656E7F}
+#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 00d3551..1678b86 100644
--- a/src/app/main.js
+++ b/src/app/main.js
@@ -59,6 +59,57 @@ function setButtons(state) {
}
}
+let lastselected = "";
+function select(entry) {
+ let entries = document.querySelectorAll("#modsdiv .mod .modtext");
+
+ for (let i = 0; i < entries.length; i++) {
+ if (entries[i].innerHTML == entry) {
+ lastselected = entry;
+ entries[i].parentElement.classList.add("selected");
+ } else {
+ entries[i].parentElement.classList.remove("selected");
+ }
+ }
+}
+
+function selected(all) {
+ let selected = "";
+ if (all) {
+ selected = "allmods"
+ } else {
+ selected = document.querySelector(".mod.selected .modtext");
+ 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,10 +118,39 @@ ipcRenderer.on("newpath", (event, newpath) => {
})
ipcRenderer.on("log", (event, msg) => {log(msg)})
+ipcRenderer.on("alert", (event, msg) => {alert(msg)})
+
+ipcRenderer.on("mods", (event, mods) => {
+ modcount.innerHTML = `${lang("gui.mods.count")} ${mods.all.length}`;
+ modsdiv.innerHTML = "";
+
+ let newmod = (name, disabled) => {
+ if (disabled) {
+ disabled = `<span class="disabled">${lang("gui.mods.disabledtag")}</span>`
+ } else {
+ disabled = ""
+ }
+
+ modsdiv.innerHTML += `<div onclick="select('${name}')" class="mod"><span class="modtext">${name}</span>${disabled}</div>`;
+ }
+
+ 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) => {
vpversion.innerText = lang("gui.versions.viper") + ": " + versions.vp;
nsversion.innerText = lang("gui.versions.northstar") + ": " + versions.ns;
+
+ if (versions.ns == "unknown") {
+ let buttons = document.querySelectorAll(".modbtns button");
+
+ for (let i = 0; i < buttons.length; i++) {
+ buttons[i].disabled = true;
+ }
+ }
}); ipcRenderer.send("getversion");
ipcRenderer.on("updateavailable", () => {
diff --git a/src/cli.js b/src/cli.js
index b6594a5..d5ee3ad 100644
--- a/src/cli.js
+++ b/src/cli.js
@@ -10,12 +10,16 @@ const lang = require("./lang");
function hasArgs() {
if (cli.hasSwitch("cli") ||
cli.hasSwitch("help") ||
+ cli.hasSwitch("mods") ||
cli.hasSwitch("update") ||
cli.hasSwitch("launch") ||
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}
}
@@ -27,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();
@@ -62,6 +70,12 @@ async function init() {
break;
}
}
+
+ 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")}
}
module.exports = {
diff --git a/src/index.js b/src/index.js
index 53781dd..09aa626 100644
--- a/src/index.js
+++ b/src/index.js
@@ -38,6 +38,12 @@ function start() {
ipcMain.on("ns-updated", () => {win.webContents.send("ns-updated")})
ipcMain.on("ns-updating", () => {win.webContents.send("ns-updating")})
ipcMain.on("winLog", (event, ...args) => {win.webContents.send("log", ...args)})
+ ipcMain.on("winAlert", (event, ...args) => {win.webContents.send("alert", ...args)})
+ ipcMain.on("guigetmods", (event, ...args) => {win.webContents.send("mods", utils.mods.list())})
+
+ win.webContents.once("dom-ready", () => {
+ win.webContents.send("mods", utils.mods.list());
+ });
if (utils.settings.autoupdate) {utils.updatevp(false)}
@@ -48,9 +54,21 @@ function start() {
ipcMain.on("updatenow", () => {
autoUpdater.quitAndInstall();
})
-
}
+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)})
ipcMain.on("launchVanilla", (event) => {utils.launch("vanilla")})
@@ -64,6 +82,7 @@ ipcMain.on("setpath", (event, value) => {
win.show();
}
});
+
ipcMain.on("newpath", (event, newpath) => {
if (newpath === false && !win.isVisible()) {
win.webContents.send("nopathselected");
@@ -91,6 +110,28 @@ ipcMain.on("versioncli", () => {
cli.exit();
})
+ipcMain.on("getmods", (event) => {
+ let mods = utils.mods.list();
+ 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 {
+ console.log("No mods installed");
+ cli.exit(0);
+ }
+})
+
process.chdir(app.getPath("appData"));
if (cli.hasArgs()) {
diff --git a/src/lang/en.json b/src/lang/en.json
index 10f03cd..61a8545 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,6 +21,15 @@
"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!",
"gui.versions.viper": "Viper version",
"gui.versions.northstar": "Northstar version",
@@ -25,6 +37,21 @@
"gui.update": "Update",
"gui.setpath": "Game Path",
+ "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",
+ "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.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...",
"gui.update.finished": "Done! Ready to play!",
@@ -41,5 +68,9 @@
"general.launching": "Launching",
- "general.missingpath": "Game path is not set!"
+ "general.mods.enabled": "Enabled mods:",
+ "general.mods.disabled": "Disabled mods:",
+ "general.mods.installed": "Installed mods:",
+ "general.missingpath": "Game path is not set!",
+ "general.notinstalled": "Northstar is not installed!"
}
diff --git a/src/lang/fr.json b/src/lang/fr.json
index 0cd9bfb..58302e9 100644
--- a/src/lang/fr.json
+++ b/src/lang/fr.json
@@ -6,6 +6,9 @@
"cli.help.update": "met à jour Northstar sur le chemin du jeu précisé",
"cli.help.setpath": "enregistre le chemin du client de jeu",
"cli.help.updatevp": "met à jour le client Viper, si le format actuel le permet.",
+ "cli.help.installmod": "Installe un mod, dossier ou zip",
+ "cli.help.removemod": "Supprime un mod",
+ "cli.help.togglemod": "Active/désactive un mod",
"cli.setpath.noarg": "Aucun argument donné à --setpath",
@@ -18,6 +21,15 @@
"cli.launch.linuxerror": "Le support du jeu sur Linux n'est pas encore implémenté.",
+ "cli.mods.failed": "L'installation du mod a échoué.",
+ "cli.mods.removed": "Le mod a bien été supprimé.",
+ "cli.mods.toggled": "Le mod a bien été activé/désactivé.",
+ "cli.mods.installed": "Mod installé !",
+ "cli.mods.cantfind": "Aucun mod avec ce nom n'a pu être trouvé.",
+ "cli.mods.notamod": "Le fichier/dossier sélectionné n'est pas un mod.",
+ "cli.mods.toggledall": "Tous les mods ont bien été activés/désactivés.",
+ "cli.mods.improperjson": "Le mod.json de %s présente des erreurs de formatage.",
+
"gui.welcome": "Bienvenue sur Viper !",
"gui.versions.viper": "Version de Viper",
"gui.versions.northstar": "Version de Northstar",
@@ -25,6 +37,21 @@
"gui.update": "Mise à jour",
"gui.setpath": "Chemin du jeu",
+ "gui.mods": "Mods",
+ "gui.mods.count": "Mods installés :",
+ "gui.mods.disabledtag": "Désactivés",
+ "gui.mods.install": "Installer le mod",
+ "gui.mods.toggle": "Activer/désactiver le mod",
+ "gui.mods.toggleall": "Activer/désactiver tous les mods",
+ "gui.mods.remove": "Supprimer le mod",
+ "gui.mods.removeall": "Tout supprimer",
+ "gui.mods.nothingselected": "Aucun mod n'est sélectionné.",
+ "gui.mods.removeall.confirm": "Supprimer tous les mods vous forcera à réinstaller Northstar, souhaitez-vous faire cela ?",
+ "gui.mods.notamod": "Ceci n'est pas un mod !",
+ "gui.mods.extracting": "Extraction du mod...",
+ "gui.mods.installing": "Installation du mod...",
+ "gui.mods.installedmod": "Mod installé !",
+
"gui.update.downloading": "Téléchargement de la mise à jour...",
"gui.update.extracting": "Extraction des fichiers...",
"gui.update.finished": "Terminé, vous pouvez jouer !",
@@ -41,5 +68,9 @@
"general.launching": "Lancement",
- "general.missingpath": "Le chemin du client n'est pas défini !"
+ "general.mods.enabled": "Mods activés :",
+ "general.mods.disabled": "Mods désactivés :",
+ "general.mods.installed": "Mods installés :",
+ "general.missingpath": "Le chemin du client n'est pas défini !",
+ "general.notinstalled": "Northstar n'est pas installé !"
}
diff --git a/src/utils.js b/src/utils.js
index 225de47..68cd6c1 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");
@@ -128,11 +129,12 @@ async function update() {
}
}
- ipcMain.emit("ns-updated");
- winLog(lang("gui.update.finished"));
- console.log(lang("cli.update.finished"));
- cli.exit();
- });
+ ipcMain.emit("guigetmods");
+ ipcMain.emit("ns-updated");
+ winLog(lang("gui.update.finished"));
+ console.log(lang("cli.update.finished"));
+ cli.exit();
+ })
})
})
}
@@ -175,7 +177,248 @@ function winLog(msg) {
ipcMain.emit("winLog", msg, msg);
}
+function winAlert(msg) {
+ ipcMain.emit("winAlert", msg, 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 = [];
+
+ files = fs.readdirSync(modpath)
+ files.forEach((file) => {
+ if (fs.statSync(path.join(modpath, file)).isDirectory()) {
+ if (fs.existsSync(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")
+ if (! fs.existsSync(disabledPath)) {
+ fs.mkdirSync(disabledPath)
+ }
+
+ 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: true})
+ }
+ }
+ }
+ })
+
+ return {
+ enabled: mods,
+ disabled: disabled,
+ all: [...mods, ...disabled]
+ };
+ },
+ 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++) {
+ if (list[i].Name == mod) {
+ return list[i];
+ } else {continue}
+ }
+
+ 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"))
+ cli.exit(1);
+ return false;
+ }
+
+ let installed = () => {
+ console.log(lang("cli.mods.installed"));
+ cli.exit();
+
+ winLog(lang("gui.mods.installedmod"))
+ ipcMain.emit("guigetmods");
+ return true;
+ }
+
+ if (! fs.existsSync(mod)) {return notamod()}
+
+ 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()) {
+
+ copy.sync(mod, path.join(modpath, mod.replace(/^.*(\\|\/|\:)/, "")), {
+ mode: true,
+ cover: true,
+ utimes: true,
+ });
+
+ return installed();
+ } else {
+ files = fs.readdirSync(mod);
+
+ for (let i = 0; i < files.length; i++) {
+ if (fs.statSync(path.join(mod, files[i])).isDirectory()) {
+ 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 true};
+ }
+ }
+ }
+
+ 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);
+ }
+
+ try {
+ fs.createReadStream(mod).pipe(unzip.Extract({path: cache}))
+ .on("finish", () => {
+ if (mods.install(cache)) {
+ installed();
+ } else {return notamod()}
+ });
+ }catch(err) {return notamod()}
+ }
+ },
+ 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++) {
+ 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;
+ if (! modName) {
+ console.log("error: " + lang("cli.mods.cantfind"))
+ cli.exit(1);
+ return;
+ }
+
+ 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});
+ console.log(lang("cli.mods.removed"));
+ cli.exit();
+ ipcMain.emit("guigetmods");
+ } else {
+ cli.exit(1);
+ }
+ },
+ 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++) {
+ mods.toggle(modlist[i].Name, true)
+ }
+
+ console.log(lang("cli.mods.toggledall"));
+ cli.exit(0);
+ return
+ }
+
+ let disabled = path.join(modpath, "disabled");
+ if (! fs.existsSync(disabled)) {
+ fs.mkdirSync(disabled)
+ }
+
+ 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);
+
+ if (mods.get(mod).Disabled) {
+ modPath = path.join(disabled, modName);
+ dest = path.join(modpath, modName);
+ }
+
+ fs.moveSync(modPath, dest)
+ if (! fork) {
+ console.log(lang("cli.mods.toggled"));
+ cli.exit();
+ }
+ ipcMain.emit("guigetmods");
+ }
+};
+
module.exports = {
+ mods,
+ lang,
winLog,
launch,
update,