diff options
author | 0neGal <mail@0negal.com> | 2022-02-13 23:32:44 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-13 23:32:44 +0100 |
commit | 9f2f77558238c28ceb8ff4fca2096602671779e5 (patch) | |
tree | c15586b60de31de094c2f811832ef9ad6df2f50a /src | |
parent | 057b60f4843798441ad441370381b87299d5ad7f (diff) | |
parent | b2f826a39e8ecf77f85ca56906758edaa3f1dac3 (diff) | |
download | Viper-9f2f77558238c28ceb8ff4fca2096602671779e5.tar.gz Viper-9f2f77558238c28ceb8ff4fca2096602671779e5.zip |
Merge branch 'main' into enabledmods
Diffstat (limited to 'src')
-rw-r--r-- | src/app/browser.js | 264 | ||||
-rw-r--r-- | src/app/icons/no-image.png | bin | 0 -> 1959 bytes | |||
-rw-r--r-- | src/app/icons/viper.png | bin | 137107 -> 59028 bytes | |||
-rw-r--r-- | src/app/index.html | 18 | ||||
-rw-r--r-- | src/app/main.css | 298 | ||||
-rw-r--r-- | src/app/main.js | 23 | ||||
-rw-r--r-- | src/app/toast.js | 59 | ||||
-rw-r--r-- | src/assets/icons/512x512.png | bin | 137107 -> 59028 bytes | |||
-rw-r--r-- | src/assets/icons/icon.ico | bin | 201799 -> 194797 bytes | |||
-rw-r--r-- | src/extras/findgame.js | 67 | ||||
-rw-r--r-- | src/extras/requests.js (renamed from src/requests.js) | 0 | ||||
-rw-r--r-- | src/index.js | 16 | ||||
-rw-r--r-- | src/lang/en.json | 21 | ||||
-rw-r--r-- | src/lang/es.json | 119 | ||||
-rw-r--r-- | src/lang/fr.json | 21 | ||||
-rw-r--r-- | src/lang/maintainers.json | 13 | ||||
-rw-r--r-- | src/utils.js | 240 |
17 files changed, 1071 insertions, 88 deletions
diff --git a/src/app/browser.js b/src/app/browser.js new file mode 100644 index 0000000..ded12fa --- /dev/null +++ b/src/app/browser.js @@ -0,0 +1,264 @@ +const Fuse = require("fuse.js"); +var fuse; +var packages = []; + +var Browser = { + maxentries: 50, + toggle: (state) => { + if (state) { + browser.scrollTo(0, 0); + overlay.classList.add("shown") + browser.classList.add("shown") + + if (browserEntries.querySelectorAll(".el").length == 0) { + Browser.loadfront(); + } + return + } else if (! state) { + if (state != undefined) { + overlay.classList.remove("shown") + browser.classList.remove("shown") + return + } + } + + browser.scrollTo(0, 0); + overlay.classList.toggle("shown") + browser.classList.toggle("shown") + }, + loadfront: async () => { + Browser.loading(); + + if (packages.length < 1) { + packages = await (await fetch("https://northstar.thunderstore.io/api/v1/package/")).json(); + + fuse = new Fuse(packages, { + keys: ["full_name"] + }) + } + + for (let i in packages) { + if (i == Browser.maxentries) {Browser.endoflist();break} + new BrowserElFromObj(packages[i]); + } + }, + loading: (string) => { + if (string) { + browserEntries.innerHTML = `<div class="loading">${string}</div>`; + } + + if (! browserEntries.querySelector(".loading")) { + browserEntries.innerHTML = `<div class="loading">${lang('gui.browser.loading')}</div>`; + } + }, + endoflist: () => { + browserEntries.innerHTML += `<div class="message">${lang('gui.browser.endoflist')}</div>` + }, + search: (string) => { + Browser.loading(); + let res = fuse.search(string); + + if (res.length < 1) { + Browser.loading("No results...") + return + } + + for (let i = 0; i < res.length; i++) { + if (i == Browser.maxentries) {Browser.endoflist();break} + new BrowserElFromObj(res[i].item); + } + }, + setbutton: (mod, string) => { + mod = normalize(mod); + if (document.getElementById(mod)) { + let elems = document.querySelectorAll(`#${mod}`); + + for (let i = 0; i < elems.length; i++) { + elems[i].querySelector(".text button").innerHTML = string; + } + } else { + let make = (str) => { + if (document.getElementById(str)) { + return Browser.setbutton(str, string); + } else { + return false; + } + } + + setTimeout(() => { + for (let i = 0; i < modsobj.all.length; i++) { + let modname = normalize(modsobj.all[i].Name); + let modfolder = normalize(modsobj.all[i].FolderName); + + if (mod.includes(modname)) { + if (! make(modname)) { + if (modsobj.all[i].ManifestName) { + make(normalize(modsobj.all[i].ManifestName)); + } + } + } + else if (mod.includes(modfolder)) {make(modfolder);break} + } + }, 1501) + } + } +} + +document.body.addEventListener("keyup", (e) => { + if (e.key == "Escape") {Browser.toggle(false)} +}) + +function BrowserElFromObj(obj) { + let pkg = {...obj, ...obj.versions[0]}; + + new BrowserEl({ + title: pkg.name, + image: pkg.icon, + author: pkg.owner, + url: pkg.package_url, + download: pkg.download_url, + version: pkg.version_number, + description: pkg.description + }) +} + +function BrowserEl(properties) { + properties = { + title: "No name", + version: "1.0.0", + image: "icons/no-image.png", + author: "Unnamed Pilot", + description: "No description", + ...properties + } + + if (properties.version[0] != "v") { + properties.version = "v" + properties.version; + } + + if (browserEntries.querySelector(".loading")) { + browserEntries.innerHTML = ""; + } + + let installstr = lang("gui.browser.install"); + + if (normalize(modsdiv.innerText.split("\n")).includes(normalize(properties.title))) { + installstr = lang("gui.browser.reinstall"); + + for (let i = 0; i < modsobj.all.length; i++) { + if (normalize(modsobj.all[i].Name) == normalize(properties.title) + && "v" + modsobj.all[i].Version != properties.version) { + + installstr = lang("gui.browser.update"); + } + } + } else { + for (let i = 0; i < modsobj.all.length; i++) { + let title = normalize(properties.title); + let folder = normalize(modsobj.all[i].FolderName); + let manifestname = null; + if (modsobj.all[i].ManifestName) { + manifestname = normalize(modsobj.all[i].ManifestName); + } + + if (title.includes(folder) || title.includes(manifestname)) { + installstr = lang("gui.browser.reinstall"); + + if (folder == title + && "v" + modsobj.all[i].Version != properties.version) { + + installstr = lang("gui.browser.update"); + } + } + } + } + + browserEntries.innerHTML += ` + <div class="el" id="${normalize(properties.title)}"> + <div class="image"> + <img src="${properties.image}"> + </div> + <div class="text"> + <div class="title">${properties.title}</div> + <div class="description">${properties.description}</div> + <button onclick="installFromURL('${properties.download}')">${installstr}</button> + <button onclick="require('electron').shell.openExternal('${properties.url}')">${lang('gui.browser.info')}</button> + <button class="visual">${properties.version}</button> + <button class="visual">${lang("gui.browser.madeby")} ${properties.author}</button> + </div> + </div> + ` +} + +ipcRenderer.on("removedmod", (event, mod) => { + setButtons(true); + Browser.setbutton(mod.name, lang("gui.browser.install")); + if (mod.manifestname) { + Browser.setbutton(mod.manifestname, lang("gui.browser.install")); + } +}) + +ipcRenderer.on("failedmod", (event, modname) => { + setButtons(true); + new Toast({ + timeout: 10000, + scheme: "error", + title: lang("gui.toast.title.failed"), + description: lang("gui.toast.desc.failed") + }) +}) + +ipcRenderer.on("installedmod", (event, mod) => { + setButtons(true); + Browser.setbutton(mod.name, lang("gui.browser.reinstall")); + + if (mod.malformed) { + new Toast({ + timeout: 8000, + scheme: "warning", + title: lang("gui.toast.title.malformed"), + description: mod.name + " " + lang("gui.toast.desc.malformed") + }) + } + + new Toast({ + scheme: "success", + title: lang("gui.toast.title.installed"), + description: mod.name + " " + lang("gui.toast.desc.installed") + }) +}) + +function normalize(items) { + let main = (string) => { + return string.replaceAll(" ", "").replaceAll(".", "").replaceAll("-", "").replaceAll("_", "").toLowerCase() + } + if (typeof items == "string") { + return main(items) + } else { + let newArray = []; + for (let i = 0; i < items.length; i++) { + newArray.push(main(items[i])); + } + + return newArray; + } +} + +let searchtimeout; +let searchstr = ""; +search.addEventListener("keyup", () => { + clearTimeout(searchtimeout); + + if (searchstr != search.value) { + if (search.value.replaceAll(" ", "") == "") { + searchstr = ""; + Browser.loadfront(); + return + } + + searchtimeout = setTimeout(() => { + Browser.search(search.value); + searchstr = search.value; + }, 500) + } +}) diff --git a/src/app/icons/no-image.png b/src/app/icons/no-image.png Binary files differnew file mode 100644 index 0000000..43265d1 --- /dev/null +++ b/src/app/icons/no-image.png diff --git a/src/app/icons/viper.png b/src/app/icons/viper.png Binary files differindex 281f3dd..69b1cce 100644 --- a/src/app/icons/viper.png +++ b/src/app/icons/viper.png diff --git a/src/app/index.html b/src/app/index.html index fe383a0..8222416 100644 --- a/src/app/index.html +++ b/src/app/index.html @@ -7,11 +7,26 @@ </head> <body> <div id="bgHolder"></div> + <div id="toasts"></div> <div id="winbtns"> <div id="minimize" onclick="ipcRenderer.send('minimize')"></div> <div id="close" onclick="ipcRenderer.send('exit')"></div> </div> + + + <div id="overlay" onclick="Browser.toggle(false)"></div> + <div id="browser"> + <div id="misc"> + <input id="search" placeholder="%%gui.browser.search%%"> + <button id="close" onclick="Browser.toggle(false)"> + <img src="icons/close.png"> + </button> + </div> + <div id="browserEntries"> + <div class="loading">%%gui.browser.loading%%</div> + </div> + </div> <nav class="gamesContainer"> <button id="vpBtn" onclick="page(0)"></button> @@ -79,6 +94,7 @@ <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> + <button id="installmod" onclick="Browser.toggle(true)">%%gui.mods.find%%</button> </div> </div> </div> @@ -102,6 +118,8 @@ <script src="lang.js"></script> <script src="main.js"></script> + <script src="toast.js"></script> + <script src="browser.js"></script> <script src="launcher.js"></script> </body> </html> diff --git a/src/app/main.css b/src/app/main.css index de0db46..a532490 100644 --- a/src/app/main.css +++ b/src/app/main.css @@ -9,7 +9,11 @@ --selbg: rgba(80, 80, 80, 0.5); --redbg: linear-gradient(45deg, var(--red), #FA4343); --bluebg: linear-gradient(45deg, var(--blue), #7380ED); +} +#browser, #modsdiv { + outline: 1px solid #444444; + border: 3px solid var(--bg); } ::-webkit-scrollbar { @@ -26,12 +30,191 @@ background: var(--red); } +::selection { + color: black; + background: var(--red); +} + +body { + margin: 0; + overflow: hidden; + user-select: none; +} + +body, button, input {font-family: "Roboto", sans-serif} + +button {outline: none} +b, strong {font-weight: 700} +body, input, button {font-weight: 500} + +button { + border: none; + color: white; + outline: none; + cursor: pointer; + font-weight: 700; + padding: 5px 10px; + border-radius: 5px; + transition: 0.2s ease-in-out; +} + .playBtn, .gamesContainer button, #winbtns div { cursor: pointer; } -#winbtns { +#browser { + --spacing: var(--padding); + + z-index: 2; + opacity: 0.0; + position: fixed; + overflow-y: scroll; + top: var(--spacing); + pointer-events: none; + left: var(--spacing); + background: var(--bg); + right: var(--spacing); + bottom: var(--spacing); + transform: scale(0.98); + backdrop-filter: blur(15px); + border-radius: calc(var(--padding) / 3); + transition: opacity 0.15s ease-in-out, transform 0.15s ease-in-out; +} + +#browser.shown { + opacity: 1.0; + pointer-events: all; + transform: scale(1.0); +} + +#overlay { + top: 0; + left: 0; + right: 0; + bottom: 0; z-index: 1; + opacity: 0.0; + position: fixed; + background: var(--bg); + pointer-events: none; + transition: opacity 0.15s ease-in-out; +} + +#overlay.shown { + opacity: 0.8; + pointer-events: all; +} + +@keyframes fadein { + 0% {opacity: 0.0} + 100% {opacity: 1.0} +} + +#browser .el, #browser #misc, #browser .loading { + --spacing: calc(var(--padding) / 2); + --height: calc(var(--padding) * 3); + --mischeight: calc(var(--padding) * 1.5); + + animation-duration: 0.15s; + animation-iteration-count: 1; + animation-name: fadein; + animation-fill-mode: forwards; + animation-timing-function: ease-in-out; + + opacity: 0.0; + transition: 0.15s ease-in-out; +} + +#browser .el, #browser #search, #browser #close { + color: white; + display: flex; + align-items: center; + height: var(--height); + margin: var(--spacing); + padding: var(--spacing); + background: var(--selbg); + border-radius: var(--spacing); + width: calc(100% - var(--spacing) * 4); +} + +#browser #misc, #browser #search { + --height: var(--mischeight); +} + +#browser #misc { + display: flex; +} + +#browser #search { + border: none; + outline: none; + transition: filter 0.15s ease-in-out; + width: calc(100% - var(--spacing) * 2); +} + +#browser #search:focus { + filter: brightness(1.5); +} + +#browser #close { + --height: calc(var(--padding) * 1.5); + + padding: 0px; + margin-left: 0px; + width: var(--height); +} + +#browser #close img { + opacity: 0.6; + width: var(--height); + height: var(--height); + transform: scale(0.6); +} + +#browser .loading { + width: 100%; + color: white; + display: flex; + text-align: center; + align-items: center; + justify-content: center; + height: calc(100% - var(--mischeight) - var(--height)); +} + +#browser .message { + color: white; + text-align: center; + margin: var(--padding); + width: calc(100% - var(--padding)); +} + +#browser .el .image, #browser .el .image img { + width: var(--height); + height: var(--height); + margin-right: var(--spacing); + border-radius: var(--spacing); +} + +#browser .el .text { + overflow: hidden; +} + +#browser .el .title, #browser .el .description { + height: 1.2em; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +#browser .el .title {font-size: 1.2em} +#browser .el .description {font-size: 0.8em} +#browser .el button { + background: var(--blue); + margin-top: var(--spacing); +} + +#winbtns { + z-index: 2; display: flex; position: fixed; top: var(--padding); @@ -54,40 +237,18 @@ #winbtns div:hover {opacity: 1.0} #winbtns div:active {transform: scale(0.98)} -body { - margin: 0; - overflow: hidden; - user-select: none; - font-family: "Roboto", sans-serif; -} - -button {outline: none} -b, strong {font-weight: 700} -body, input, button {font-weight: 500} - -button { - border: none; - color: white; - outline: none; - font-weight: 700; - padding: 5px 10px; - border-radius: 5px; - transition: 0.2s ease-in-out; -} - button:hover {filter: brightness(110%)} button:active {filter: brightness(90%)} img {pointer-events: none} #bgHolder { - top: 0; - left: 0; - right: 0; - bottom: 0; - pointer-events: none; - position: fixed; - transform: scale(1.1); + top: -5px; + left: -5px; + right: -5px; + bottom: -5px; + z-index: -1; + position: absolute; background-size: cover; background-position: center; background-repeat: no-repeat; @@ -162,7 +323,7 @@ img {pointer-events: none} } .contentContainer { - width: 90%; + width: 85%; color: white; flex-grow: 1; opacity: 1.0; @@ -346,6 +507,12 @@ button:disabled { pointer-events: none; } +button.visual { + opacity: 1.0; + pointer-events: none; + background: transparent !important; +} + code { font-size: 16px; padding: 2px 5px; @@ -385,16 +552,81 @@ code { } .modbtns button { - margin-left: calc(var(--padding) / 3); + margin-left: var(--spacing); + --spacing: calc(var(--padding) / 3); + margin-top: calc(var(--spacing) / 2); + margin-bottom: calc(var(--spacing) / 2); +} + +#toasts { + position: fixed; + z-index: 100000; + right: calc(var(--padding) * 1.5); + bottom: calc(var(--padding) * 1.5); +} + +@keyframes bodyfadeaway { + 0% {opacity: 0.0; transform: scale(0.95)} + 100% {opacity: 1.0; transform: scale(1.0)} +} + +#toasts .toast { + width: 300px; + opacity: 0.0; + cursor: pointer; + overflow: hidden; + max-height: 100vh; + background: #FFFFFF; + transform: scale(0.95); + transition: 0.2s ease-in-out; + padding: calc(var(--padding) / 2); + margin-top: calc(var(--padding) / 2); + border-radius: calc(var(--padding) / 2.5); + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.2); + + animation-duration: 0.2s; + animation-iteration-count: 1; + animation-name: bodyfadeaway; + animation-fill-mode: forwards; + animation-timing-function: ease-in-out; +} + +#toasts .toast.hidden { + margin-top: 0px; + max-height: 0px; + padding-top: 0px; + padding-bottom: 0px; + filter: opacity(0.0); + transform: scale(0.95); +} + +#toasts .toast:not(.hidden):hover {filter: opacity(0.9)} +#toasts .toast:not(.hidden):active {filter: opacity(0.8)} + +.toast .title { +} + +.toast .description { + opacity: 0.8; + font-size: 0.8em; + font-weight: 600; } /* drag control */ -#bgHolder { +#bgHolder, +.contentContainer, +.gamesContainer { user-select: none; -webkit-app-region: drag; } -a, button, .contentMenu, #close, #nsRelease, #vpReleaseNotes, .mod { +#overlay.shown ~ #bgHolder, +#overlay.shown ~ .contentContainer, +#overlay.shown ~ .gamesContainer { + -webkit-app-region: no-drag; +} + +a, button, #close, #nsRelease, #vpReleaseNotes, .mod, #overlay, #modsdiv, #winbtns, .contentMenu { -webkit-app-region: no-drag; } diff --git a/src/app/main.js b/src/app/main.js index 5f0cc9a..169f86f 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -3,6 +3,7 @@ const path = require("path"); const { ipcRenderer, shell } = require("electron"); const lang = require("../lang"); +var modsobj = {}; let shouldInstallNorthstar = false; // Base settings @@ -26,13 +27,11 @@ if (fs.existsSync("viper.json")) { settings.zip = path.join(settings.gamepath + "/northstar.zip"); if (settings.gamepath.length === 0) { - alert(lang("general.missingpath")); setpath(false); } else { setpath(true); } } else { - alert(lang("general.missingpath")); setpath(); } @@ -68,6 +67,15 @@ function log(msg) { // updating/installing Northstar. function setButtons(state) { playNsBtn.disabled = !state; + + let disablearray = (array) => { + for (let i = 0; i < array.length; i++) { + array[i].disabled = !state; + } + } + + disablearray(document.querySelectorAll("#nsMods .buttons.modbtns button")) + disablearray(document.querySelectorAll("#browser #browserEntries .text button")) } // Frontend part of updating Northstar @@ -150,12 +158,20 @@ function selected(all) { // Tells the main process to install a mod function installmod() { + setButtons(false); ipcRenderer.send("installmod") } +// Tells the main process to install a mod from a URL +function installFromURL(url) { + setButtons(false); + ipcRenderer.send("installfromurl", url) +} + // Frontend part of settings a new game path ipcRenderer.on("newpath", (event, newpath) => { settings.gamepath = newpath; + ipcRenderer.send("guigetmods"); }) // Continuation of log() @@ -164,6 +180,9 @@ ipcRenderer.on("alert", (event, msg) => {alert(msg)}) // Updates the installed mods ipcRenderer.on("mods", (event, mods) => { + modsobj = mods; + if (! mods) {return} + modcount.innerHTML = `${lang("gui.mods.count")} ${mods.all.length}`; modsdiv.innerHTML = ""; diff --git a/src/app/toast.js b/src/app/toast.js new file mode 100644 index 0000000..2a8555e --- /dev/null +++ b/src/app/toast.js @@ -0,0 +1,59 @@ +function Toast(properties) { + let toast = { + fg: "#000000", + bg: "#FFFFFF", + timeout: 3000, + title: "Untitled Toast", + description: "No description provided for toast", + ...properties + } + + switch(toast.scheme) { + case "error": + toast.fg = "#FFFFFF"; + toast.bg = "var(--red)"; + break + case "success": + toast.fg = "#FFFFFF"; + toast.bg = "#60D394"; + break + case "warning": + toast.fg = "#FFFFFF"; + toast.bg = "#FF9B85"; + break + } + + + let id = Date.now(); + if (document.getElementById(id)) {id = id + 1} + let el = document.createElement("div"); + + el.classList.add("toast"); + + el.style.color = toast.fg; + el.style.background = toast.bg; + + el.id = id; + el.setAttribute("onclick", `dismissToast(${id})`); + + el.innerHTML = ` + <div class="title">${toast.title}</div> + <div class="description">${toast.description}</div> + ` + + toasts.appendChild(el); + + setTimeout(() => { + dismissToast(id); + }, toast.timeout) +} + +function dismissToast(id) { + id = document.getElementById(id); + if (id) { + id.classList.add("hidden"); + setTimeout(() => { + id.remove(); + }, 500) + } +} diff --git a/src/assets/icons/512x512.png b/src/assets/icons/512x512.png Binary files differindex 281f3dd..69b1cce 100644 --- a/src/assets/icons/512x512.png +++ b/src/assets/icons/512x512.png diff --git a/src/assets/icons/icon.ico b/src/assets/icons/icon.ico Binary files differindex 32df0c5..06702df 100644 --- a/src/assets/icons/icon.ico +++ b/src/assets/icons/icon.ico diff --git a/src/extras/findgame.js b/src/extras/findgame.js new file mode 100644 index 0000000..42c9b85 --- /dev/null +++ b/src/extras/findgame.js @@ -0,0 +1,67 @@ +const fs = require("fs"); +const path = require("path"); +const vdf = require("simple-vdf"); +const { app } = require("electron"); + +const util = require("util"); +const exec = util.promisify(require("child_process").exec); + +module.exports = async () => { + let gamepath = ""; + + // Autodetect path + // Windows only using powershell and windows registery + // Get-Item -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Respawn\Titanfall2\ + if (process.platform == "win32") { + try { + const {stdout} = await exec("Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\Respawn\\Titanfall2\\ -Name \"Install Dir\"", {"shell":"powershell.exe"}); + + const gamepath = stdout.split('\n') + .filter(r => r.indexOf("Install Dir") !== -1)[0] + .replace(/\s+/g,' ') + .trim() + .replace("Install Dir : ",""); + + if (gamepath) {return gamepath} + } catch (err) {} + } + + // Detect using Steam VDF + function readvdf(data) { + // Parse read_data + data = vdf.parse(data); + + // `.length - 1` This is because the last value is `contentstatsid` + for (let pathIterator = 0; pathIterator < Object.values(data["libraryfolders"]).length - 1; pathIterator++) { + let data_array = Object.values(data["libraryfolders"][pathIterator]) + + if (fs.existsSync(data_array[0] + "/steamapps/common/Titanfall2/Titanfall2.exe")) { + return data_array[0] + "/steamapps/common/Titanfall2"; + } + } + } + + let folder = null; + switch (process.platform) { + case "win32": + folder = "C:\\Program Files (x86)\\Steam\\steamapps\\libraryfolders.vdf"; + break + case "linux": + case "openbsd": + case "freebsd": + folder = path.join(app.getPath("home"), "/.steam/steam/steamapps/libraryfolders.vdf"); + break + } + + if (fs.existsSync(folder) && folder) { + let data = fs.readFileSync(folder) + let read_vdf = readvdf(data.toString()) + if (read_vdf ) {return read_vdf} + } + + if (gamepath) { + return gamepath; + } else { + return false; + } +} diff --git a/src/requests.js b/src/extras/requests.js index 16b1330..16b1330 100644 --- a/src/requests.js +++ b/src/extras/requests.js diff --git a/src/index.js b/src/index.js index 3f50840..a06cf69 100644 --- a/src/index.js +++ b/src/index.js @@ -8,7 +8,7 @@ const events = new Emitter(); const utils = require("./utils"); const cli = require("./cli"); -const requests = require("./requests"); +const requests = require("./extras/requests"); // Starts the actual BrowserWindow, which is only run when using the // GUI, for the CLI this function is never called. @@ -43,9 +43,13 @@ function start() { ipcMain.on("exit", () => {process.exit(0)}) ipcMain.on("minimize", () => {win.minimize()}) + ipcMain.on("installfromurl", (event, url) => {utils.mods.installFromURL(url)}) ipcMain.on("winLog", (event, ...args) => {win.webContents.send("log", ...args)}); ipcMain.on("winAlert", (event, ...args) => {win.webContents.send("alert", ...args)}); ipcMain.on("ns-update-event", (event) => win.webContents.send("ns-update-event", event)); + ipcMain.on("failedmod", (event, modname) => {win.webContents.send("failedmod", modname)}); + ipcMain.on("removedmod", (event, modname) => {win.webContents.send("removedmod", modname)}); + ipcMain.on("installedmod", (event, modname) => {win.webContents.send("installedmod", modname)}); ipcMain.on("guigetmods", (event, ...args) => {win.webContents.send("mods", utils.mods.list())}); win.webContents.on("dom-ready", () => { @@ -87,9 +91,13 @@ ipcMain.on("launchVanilla", (event) => {utils.launch("vanilla")}) ipcMain.on("update", (event) => {utils.update()}) ipcMain.on("setpathcli", (event) => {utils.setpath()}); ipcMain.on("setpath", (event, value) => { - if (!value) { - utils.setpath(win); - } else if (!win.isVisible()) { + if (! value) { + if (! win.isVisible()) { + utils.setpath(win); + } else { + utils.setpath(win, true); + } + } else if (! win.isVisible()) { win.show(); } }); diff --git a/src/lang/en.json b/src/lang/en.json index c2b6f98..27630ec 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -42,13 +42,14 @@ "gui.versions.northstar": "Northstar version", "gui.exit": "Exit", "gui.update": "Update", - "gui.setpath": "Game Path", + "gui.setpath": "Change Game Path", "gui.update.check": "Check for updates", "gui.mods": "Mods", "gui.mods.count": "Mods Installed:", "gui.mods.disabledtag": "Disabled", "gui.mods.install": "Install Mod", + "gui.mods.find": "Find Mods", "gui.mods.toggle": "Toggle Mod", "gui.mods.toggleall": "Toggle All", "gui.mods.remove": "Remove Mod", @@ -62,6 +63,15 @@ "gui.mods.installing": "Installing mod...", "gui.mods.installedmod": "Installed mod!", + "gui.browser.info": "Info", + "gui.browser.madeby": "by", + "gui.browser.search": "Search...", + "gui.browser.update": "Update", + "gui.browser.install": "Install", + "gui.browser.reinstall": "Re-Install", + "gui.browser.loading": "Loading mods...", + "gui.browser.endoflist": "Maximum packages has been loaded.<br>Use the search for finding other packages!", + "gui.update.downloading": "Downloading...", "gui.update.extracting": "Extracting update...", "gui.update.finished": "Done! Ready to play!", @@ -80,6 +90,13 @@ "gui.gamepath.must": "The game path must be set to start Viper.", "gui.gamepath.wrong": "This folder is not a valid game path.", + "gui.toast.title.installed": "Mod installed!", + "gui.toast.title.failed": "Failed to install", + "gui.toast.title.malformed": "Incorrect folder structure!", + "gui.toast.desc.installed": "has been installed successfully!", + "gui.toast.desc.malformed": "has an incorrect folder structure, if you're the developer, you should fix this.", + "gui.toast.desc.failed": "An unknown error occurred while trying to install the mod. This may be the author's fault, and it may also be Viper's fault.", + "viper.menu.main": "Viper", "viper.menu.release": "Release Notes", "viper.menu.info": "Extras", @@ -96,7 +113,7 @@ "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 location could not be found automatically! Please select it manually!", "general.notinstalled": "Northstar is not installed!", "general.launching": "Launching" } diff --git a/src/lang/es.json b/src/lang/es.json new file mode 100644 index 0000000..8beabef --- /dev/null +++ b/src/lang/es.json @@ -0,0 +1,119 @@ +{ + "cli.help.help": "muestra este mensaje de ayuda", + "cli.help.debug": "habre las herramientas de desarrollador/depuración ", + "cli.help.version": "muestra la información de la versión", + "cli.help.cli": "obliga la linea de comandos a habilitarse", + "cli.help.update": "actualiza Northstar desde la ruta de juego establecida", + "cli.help.setpath": "establece la ruta del juego", + "cli.help.updatevp": "actualiza Viper si es soportado", + "cli.help.installmod": "instala una modificación, desde una carpeta o zip", + "cli.help.removemod": "remueve una modificación", + "cli.help.togglemod": "alterna el estado de la modificación", + + "cli.setpath.noarg": "No se ha proporcionado ningún argumento para --setpath", + + "cli.update.current": "Versión actual:", + "cli.update.downloading": "Descargando...", + "cli.update.checking": "Buscando actualizaciones...", + "cli.update.downloaddone": "¡Descarga completa! Extrayendo...", + "cli.update.finished": "Instalación/Actualización completada!", + "cli.update.uptodate": "La ultima versión (%s) ya está instalada, omitiendo actualización.", + "cli.update.uptodate.short": "Está actualizado", + + "cli.autoupdates.checking": "Buscando actualizaciones de Northstar...", + "cli.autoupdates.available": "¡Actualización de Northsar disponible!", + "cli.autoupdates.gamerunning": "El juego se está ejecutando, no se puede actualizar Northstar", + "cli.autoupdates.updatingns": "Lanzando proceso de actualización...", + "cli.autoupdates.noupdate": "No hay actualizaciones de Northstar disponibles.", + + "cli.launch.linuxerror": "La ejecución del juego en Linux aun no está implementada", + + "cli.mods.failed": "¡Fallo al instalar el mod!", + "cli.mods.removed": "¡Modificación removida exitosamente!", + "cli.mods.toggled": "¡El estado de la modificación ha cambiado exitosamente!", + "cli.mods.installed": "¡Modificiación instalada exitosamente!", + "cli.mods.cantfind": "¡No se encuentra una modificación con ese nombre!", + "cli.mods.notamod": "La carpeta o el archivo seleccionado no es una modificación", + "cli.mods.toggledall": "¡Estado de todas las modiciaciones ha sido cambiado exitosamente!", + "cli.mods.improperjson": "%s's mod.json tiene errores de formato", + + "gui.welcome": "Bienvenido a Viper!", + "gui.versions.viper": "Versión de Viper", + "gui.versions.northstar": "Versión de Northstar", + "gui.exit": "Salir", + "gui.update": "Actualizar", + "gui.setpath": "Cambiar la ruta del juego", + + "gui.update.check": "Buscar actualizaciones", + "gui.mods": "Modificaciones (Mods)", + "gui.mods.count": "Modificaciones instaladas:", + "gui.mods.disabledtag": "Deshabilitado", + "gui.mods.install": "Instalar modificación", + "gui.mods.find": "Encontrar modificaciones", + "gui.mods.toggle": "Alternar modificación", + "gui.mods.toggleall": "Alternar todo", + "gui.mods.remove": "Remover modificación", + "gui.mods.removeall": "Remover todo", + "gui.mods.nothingselected": "No has seleccionado una modificación.", + "gui.mods.toggleall.confirm": "Alternar todo podría deshabilitar las modificaciones requeridas para que Northstar funcione. ¿Está seguro?", + "gui.mods.removeall.confirm": "Eliminar todas las modificaciones generalmente requerirá que reinstales Northstar. ¿Está seguro?", + "gui.mods.required.confirm": "Ha seleccionado un mod esencial, es posible que Northstar no funcione sin él. ¿Está seguro?", + "gui.mods.notamod": "¡No es una modificación!", + "gui.mods.extracting": "Extrayendo modificación...", + "gui.mods.installing": "Instalando modificación...", + "gui.mods.installedmod": "¡Modificación instalada!", + + "gui.browser.info": "Información", + "gui.browser.madeby": "por", + "gui.browser.search": "Buscar...", + "gui.browser.update": "Actualizar", + "gui.browser.install": "Instalar", + "gui.browser.reinstall": "Re-Instalar", + "gui.browser.loading": "Cargando modificaciones...", + "gui.browser.endoflist": "Se ha cargado el máximo de paquetes.<br>¡Usa la búsqueda para encontrar otros paquetes!", + + "gui.update.downloading": "Descargando...", + "gui.update.extracting": "Extrayendo actualización...", + "gui.update.finished": "¡Hecho! ¡Está listo para jugar!", + "gui.update.uptodate": "¡Ya está actualizado!", + "gui.update.available": "Hay una actualización disponible para Viper, ¿desea reiniciar y aplicarla?", + + "gui.nsupdate.gaming.title": "¡Actualización de Northstar disponible!", + "gui.nsupdate.gaming.body": "Una actualización de northstar está disponible.\nPuedes forzar su instalación despues de cerrar el juego.", + + "gui.launch": "Ejecutar", + "gui.launchvanilla": "Vainilla", + "gui.launchnorthstar": "Northstar", + "gui.installnorthstar": "Instalar", + + "gui.selectpath": "Por favor, ¡elija la ruta del juego!", + "gui.gamepath.must": "La ruta del juego debe establecerse para ejecutar Viper.", + "gui.gamepath.wrong": "Esta carpeta no es una ruta válida para el juego.", + + "gui.toast.title.installed": "¡Modificación instalada!", + "gui.toast.title.failed": "¡Falló al instalar!", + "gui.toast.title.malformed": "¡Estructura de las carpetas incorrecta!", + "gui.toast.desc.installed": "¡Ha sido instalado exitosamente!", + "gui.toast.desc.malformed": "tiene una estructura de carpetas incorrecta, si usted es el desarrollador, debe corregir esto.", + "gui.toast.desc.failed": "Se produjo un error desconocido al intentar instalar la modificación. Esto puede ser culpa del autor de la modificación, y también puede ser culpa de Viper.", + + "viper.menu.main": "Viper", + "viper.menu.release": "Notas de la versión", + "viper.menu.info": "Extras", + "viper.menu.info.links": "Links", + "viper.menu.info.credits": "Creditos", + + "viper.info.discord": "Unete al Discord:", + "viper.info.issues": "Reporta problemas de Viper:", + + "ns.menu.main": "Northstar Launcher", + "ns.menu.mods": "Modificiaciones", + "ns.menu.release": "Notas de actualización", + + "general.mods.enabled": "Modificaciones habilitadas:", + "general.mods.disabled": "Modificaciones deshabilitadas:", + "general.mods.installed": "Modificiaciones intaladas:", + "general.missingpath": "¡La ruta del jueno no se ha podido encontrar automaticamente! ¡Por favor, elige la ruta manualmente!", + "general.notinstalled": "¡Northstar no se ha instalado!", + "general.launching": "Ejecutando" +} diff --git a/src/lang/fr.json b/src/lang/fr.json index 59a3090..3bdeec6 100644 --- a/src/lang/fr.json +++ b/src/lang/fr.json @@ -42,13 +42,14 @@ "gui.versions.northstar": "Version de Northstar", "gui.exit": "Fermer", "gui.update": "Mise à jour", - "gui.setpath": "Chemin du jeu", + "gui.setpath": "Mettre à jour le chemin du jeu", "gui.update.check": "Vérifier les mises à jour", "gui.mods": "Mods", "gui.mods.count": "Mods installés :", "gui.mods.disabledtag": "Désactivé", "gui.mods.install": "Installer le mod", + "gui.mods.find": "Chercher des mods", "gui.mods.toggle": "Activer/désactiver le mod", "gui.mods.toggleall": "Activer/désactiver tous les mods", "gui.mods.remove": "Supprimer le mod", @@ -62,6 +63,15 @@ "gui.mods.installing": "Installation du mod...", "gui.mods.installedmod": "Mod installé !", + "gui.browser.info": "Info", + "gui.browser.madeby": "par", + "gui.browser.search": "Rechercher", + "gui.browser.update": "Mise à jour", + "gui.browser.install": "Installer", + "gui.browser.reinstall": "Réinstaller", + "gui.browser.loading": "Chargement des mods...", + "gui.browser.endoflist": "Fin de la liste de mods.<br>Utilisez la barre de recherche pour en trouver davantage !", + "gui.update.downloading": "Téléchargement de la mise à jour...", "gui.update.extracting": "Extraction des fichiers...", "gui.update.finished": "Terminé, vous pouvez jouer !", @@ -80,6 +90,13 @@ "gui.gamepath.must": "Vous devez sélectionner le chemin du dossier du jeu Titanfall 2 pour pouvoir lancer Viper.", "gui.gamepath.wrong": "Ce dossier ne contient pas le jeu Titanfall 2, et n'est donc pas valide.", + "gui.toast.title.installed": "Mod installé !", + "gui.toast.title.failed": "L'installation a échoué", + "gui.toast.title.malformed": "La structure du dossier du mod est incorrecte.", + "gui.toast.desc.installed": "a été installé avec succès !", + "gui.toast.desc.malformed": "a une structure de dossier incorrecte ; si vous êtes son développeur, vous devriez réparer ça.", + "gui.toast.desc.failed": "Une erreur inconnue est survenue lors de l'installation du mod. Cela peut être du ressort de l'auteur du mod ou de Viper.", + "viper.menu.main": "Viper", "viper.menu.release": "Notes de mises à jour", "viper.menu.info": "Informations", @@ -96,7 +113,7 @@ "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.missingpath": "Le chemin du client n'a pu être trouvé automatiquement, merci de le sélectionner manuellement.", "general.notinstalled": "Northstar n'est pas installé !", "general.launching": "Lancement" } diff --git a/src/lang/maintainers.json b/src/lang/maintainers.json new file mode 100644 index 0000000..094814a --- /dev/null +++ b/src/lang/maintainers.json @@ -0,0 +1,13 @@ +{ + "explanation": "This file is for storing contact information for the various maintainers of various languages/localizations", + + "list": { + "es": [ + "https://github.com/AA-Delta" + ], + + "fr": [ + "https://github.com/Alystrasz" + ] + } +} diff --git a/src/utils.js b/src/utils.js index a41def4..a0c88ef 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,6 @@ const path = require("path"); const fs = require("fs-extra"); -const copy = require("copy-dir"); +const copy = require("recursive-copy"); const { app, dialog, ipcMain, Notification } = require("electron"); const Emitter = require("events"); @@ -8,7 +8,8 @@ const events = new Emitter(); const cli = require("./cli"); const lang = require("./lang"); -const requests = require("./requests"); +const requests = require("./extras/requests"); +const findgame = require("./extras/findgame"); const unzip = require("unzipper"); const run = require("child_process").spawn; @@ -115,10 +116,39 @@ northstar_auto_updates: { // // If running with CLI it takes in the --setpath argument otherwise it // open the systems file browser for the user to select a path. -function setpath(win) { +async function setpath(win, forcedialog) { + function setGamepath(folder) { + settings.gamepath = folder; + settings.zip = path.join(settings.gamepath + "/northstar.zip"); + saveSettings(); + win.webContents.send("newpath", settings.gamepath); + ipcMain.emit("newpath", null, settings.gamepath); + + modpath = path.join(settings.gamepath, "R2Northstar/mods"); + } + if (! win) { // CLI - settings.gamepath = cli.param("setpath"); + setGamepath(cli.param("setpath")); } else { // GUI + if (! forcedialog) { + function setGamepath(folder, forcedialog) { + settings.gamepath = folder; + settings.zip = path.join(settings.gamepath + "/northstar.zip"); + saveSettings(); + 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); @@ -129,16 +159,12 @@ function setpath(win) { return; } - settings.gamepath = res.filePaths[0]; - settings.zip = path.join(settings.gamepath + "/northstar.zip"); - saveSettings(); - win.webContents.send("newpath", settings.gamepath); - ipcMain.emit("newpath", null, settings.gamepath); + setGamepath(res.filePaths[0]) + + cli.exit(); + return; }).catch(err => {console.error(err)}) } - - saveSettings(); - cli.exit(); } // As to not have to do the same one liner a million times, this @@ -176,6 +202,19 @@ function getTF2Version() { } } + +// 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 @@ -186,14 +225,6 @@ function getTF2Version() { // <file>.excluded, then rename them back after the extraction. The // unzip module does not support excluding files directly. async function update() { - // 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") - } - } - ipcMain.emit("ns-update-event", "cli.update.checking"); console.log(lang("cli.update.checking")); var version = getNSVersion(); @@ -215,6 +246,14 @@ async function update() { 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); @@ -237,16 +276,10 @@ async function update() { // installing Northstar. fs.createReadStream(settings.zip).pipe(unzip.Extract({path: settings.gamepath})) .on("finish", () => { - fs.writeFileSync(path.join(settings.gamepath, "ns_version.txt"), latestAvailableVersion); - ipcMain.emit("getversion"); - - // Renames excluded files to their original name - 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) - } - } + fs.writeFileSync(path.join(settings.gamepath, "ns_version.txt"), latestAvailableVersion); + ipcMain.emit("getversion"); + + restoreExcludedFiles(); ipcMain.emit("guigetmods"); ipcMain.emit("ns-update-event", "cli.update.uptodate.short"); @@ -323,6 +356,8 @@ const mods = { // combination of the other two, enabled being enabled mods, and you // guessed it, disabled being disabled mods. list: () => { + let modpath = path.join(settings.gamepath, "R2Northstar/mods"); + if (getNSVersion() == "unknown") { winLog(lang("general.notinstalled")) console.log("error: " + lang("general.notinstalled")) @@ -349,9 +384,14 @@ const mods = { try { mods.push({...require(path.join(modpath, file, "mod.json")), FolderName: file, Disabled: false}) }catch(err) { - console.log("error: " + lang("cli.mods.improperjson"), file) + if (cli.hasArgs()) {console.log("error: " + lang("cli.mods.improperjson"), file)} mods.push({Name: file, FolderName: file, Version: "unknown", Disabled: false}) } + + let manifest = path.join(modpath, file, "manifest.json"); + if (fs.existsSync(manifest)) { + try {mods[mods.length - 1].ManifestName = require(manifest).name}catch(err){} + } } } }) @@ -368,10 +408,15 @@ const mods = { try { disabled.push({...require(path.join(disabledPath, file, "mod.json")), FolderName: file, Disabled: true}) }catch(err) { - console.log("error: " + lang("cli.mods.improperjson"), file) + if (cli.hasArgs()) {console.log("error: " + lang("cli.mods.improperjson"), file)} disabled.push({Name: file, FolderName: file, Version: "unknown", Disabled: true}) } } + + let manifest = path.join(modpath, file, "manifest.json"); + if (fs.existsSync(manifest)) { + try {mods[mods.length - 1].ManifestName = require(manifest).name}catch(err){} + } } }) @@ -389,6 +434,8 @@ const mods = { // the absolute basics will be provided and we can't know the // version or similar. get: (mod) => { + let modpath = path.join(settings.gamepath, "R2Northstar/mods"); + if (getNSVersion() == "unknown") { winLog(lang("general.notinstalled")) console.log("error: " + lang("general.notinstalled")) @@ -462,7 +509,9 @@ const mods = { // Either a zip or folder is supported, we'll also try to search // inside the zip or folder to see if buried in another folder or // not, as sometimes that's the case. - install: (mod) => { + install: (mod, destname, manifestfile, malformed = false) => { + let modname = mod.replace(/^.*(\\|\/|\:)/, ""); + if (getNSVersion() == "unknown") { winLog(lang("general.notinstalled")) console.log("error: " + lang("general.notinstalled")) @@ -482,6 +531,19 @@ const mods = { cli.exit(); winLog(lang("gui.mods.installedmod")) + + if (modname == "mods") { + let manifest = path.join(app.getPath("userData"), "Archives/manifest.json") + + if (fs.existsSync(manifest)) { + modname = require(manifest).name; + } + } + + ipcMain.emit("installedmod", "", { + name: modname, + malformed: malformed, + }); ipcMain.emit("guigetmods"); return true; } @@ -490,14 +552,17 @@ const mods = { if (fs.statSync(mod).isDirectory()) { winLog(lang("gui.mods.installing")) + files = fs.readdirSync(mod); 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, - }); + if (fs.existsSync(path.join(modpath, modname))) { + fs.rmSync(path.join(modpath, modname), {recursive: true}); + } + let copydest = path.join(modpath, modname); + if (typeof destname == "string") {copydest = path.join(modpath, destname)} + copy(mod, copydest) + copy(manifestfile, path.join(copydest, "manifest.json")) return installed(); } else { @@ -508,39 +573,110 @@ const mods = { if (fs.existsSync(path.join(mod, files[i], "mod.json")) && fs.statSync(path.join(mod, files[i], "mod.json")).isFile()) { + mods.install(path.join(mod, files[i])) if (mods.install(path.join(mod, files[i]))) {return true}; } } } - notamod(); - return false; + return notamod(); } + + return notamod(); } 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); + fs.mkdirSync(path.join(cache, "mods"), {recursive: true}); } else { - fs.mkdirSync(cache); + fs.mkdirSync(path.join(cache, "mods"), {recursive: true}); } try { fs.createReadStream(mod).pipe(unzip.Extract({path: cache})) .on("finish", () => { - if (mods.install(cache)) { - installed(); - } else {return notamod()} + setTimeout(() => { + let manifest = path.join(cache, "manifest.json"); + if (fs.existsSync(manifest)) { + files = fs.readdirSync(path.join(cache, "mods")); + if (fs.existsSync(path.join(cache, "mods/mod.json"))) { + if (mods.install(path.join(cache, "mods"), require(manifest).name, manifest, true)) { + return true; + } + } else { + for (let i = 0; i < files.length; i++) { + let mod = path.join(cache, "mods", files[i]); + if (fs.statSync(mod).isDirectory()) { + setTimeout(() => { + if (mods.install(mod, false, manifest)) {return true}; + }, 1000) + } + } + + if (files.length == 0) { + ipcMain.emit("failedmod"); + return notamod(); + } + } + + return notamod(); + } + + if (mods.install(cache)) { + installed(); + } else {return notamod()} + }, 1000) }); }catch(err) {return notamod()} } }, + + // Installs mods from URL's + // + // This'll simply download the file that the URL points to and then + // install it with mods.install() + installFromURL: (url) => { + https.get(url, (res) => { + let tmp = path.join(app.getPath("cache"), "vipertmp"); + let modlocation = path.join(tmp, "/mod.zip"); + + if (fs.existsSync(tmp)) { + if (! fs.statSync(tmp).isDirectory()) { + fs.rmSync(tmp) + } + } else { + fs.mkdirSync(tmp) + if (fs.existsSync(modlocation)) { + fs.rmSync(modlocation) + } + } + + let stream = fs.createWriteStream(modlocation); + 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(); + mods.install(modlocation); + }) + }) + }, + // Removes mods // // Takes in the names of the mod then removes it, no confirmation, // that'd be up to the GUI. remove: (mod) => { + let modpath = path.join(settings.gamepath, "R2Northstar/mods"); + if (getNSVersion() == "unknown") { winLog(lang("general.notinstalled")) console.log("error: " + lang("general.notinstalled")) @@ -575,10 +711,19 @@ const mods = { } if (fs.statSync(modPath).isDirectory()) { + let manifestname = null; + if (fs.existsSync(path.join(modPath, "manifest.json"))) { + manifestname = require(path.join(modPath, "manifest.json")).name; + } + fs.rmSync(modPath, {recursive: true}); console.log(lang("cli.mods.removed")); cli.exit(); ipcMain.emit("guigetmods"); + ipcMain.emit("removedmod", "", { + name: mod.replace(/^.*(\\|\/|\:)/, ""), + manifestname: manifestname + }); } else { cli.exit(1); } @@ -591,6 +736,8 @@ const mods = { // you checked for if a mod is already disable and if not run the // function. However we currently have no need for that. toggle: (mod, fork) => { + let modpath = path.join(settings.gamepath, "R2Northstar/mods"); + if (getNSVersion() == "unknown") { winLog(lang("general.notinstalled")) console.log("error: " + lang("general.notinstalled")) @@ -639,6 +786,9 @@ const mods = { }; console.log(mods.modfile().get()) +setInterval(() => { + ipcMain.emit("guigetmods"); +}, 1500) module.exports = { mods, |