diff --git a/manager/ejs/managerinstallations.ejs b/manager/ejs/managerinstallations.ejs new file mode 100644 index 00000000..5cdb8b0e --- /dev/null +++ b/manager/ejs/managerinstallations.ejs @@ -0,0 +1,49 @@ + +
+ +
+ + Select the copies of DCS you want to install Olympus to. + + + For most people, this is your main DCS installation. + + + If you are running a dedicated server, you would also install Olympus to this DCS version. + +
+ <%= instances[0] %> + <% for (let i = 0; i < instances.length; i++) {%> +
+ <%= instances[i] %> +
+ <% } %> +
\ No newline at end of file diff --git a/manager/ejs/managermenu.ejs b/manager/ejs/managermenu.ejs new file mode 100644 index 00000000..21dc76ac --- /dev/null +++ b/manager/ejs/managermenu.ejs @@ -0,0 +1,40 @@ + +
+ +
+ Install Olympus +
+
+ Update/remove Olympus +
+
+ View and manage instances +
+
\ No newline at end of file diff --git a/manager/index.html b/manager/index.html index cb0091b1..8ff23a37 100644 --- a/manager/index.html +++ b/manager/index.html @@ -16,8 +16,8 @@
DCS Olympus manager
- - + +
-
- These are the DCS instances that the Olympus Manager has automatically detected in your system.
- You can install Olympus and manage your instances from here.
- Click on "Install Olympus to instance" to install Olympus. -
@@ -45,13 +40,13 @@ window.ipcRender.send('window:minimize'); }); - document.querySelector('.restore').addEventListener('click', () => { + /*document.querySelector('.restore').addEventListener('click', () => { window.ipcRender.send('window:restore'); }); document.querySelector('.maximize').addEventListener('click', () => { window.ipcRender.send('window:maximize'); - }); + });*/ document.querySelector('.close').addEventListener('click', () => { window.ipcRender.send('window:close'); @@ -66,7 +61,6 @@ document.querySelector('.restore').classList.add("hide"); document.querySelector('.maximize').classList.remove("hide"); }) - \ No newline at end of file diff --git a/manager/javascripts/filesystem.js b/manager/javascripts/filesystem.js new file mode 100644 index 00000000..0c0de091 --- /dev/null +++ b/manager/javascripts/filesystem.js @@ -0,0 +1,115 @@ +const sha256 = require('sha256') +const createShortcut = require('create-desktop-shortcuts'); + +export function installOlympus(folder) { + console.log(`Installing Olympus in ${folder}`); + try { + fs.cpSync(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true }); + fs.cpSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hooks", "OlympusHook.lua")); + fs.cpSync(path.join("..", "olympus.json"), path.join(folder, "Config", "olympus.json")); + if (createShortcut({ + windows: { + filePath: path.resolve(__dirname, '..', '..', 'client', 'client.vbs'), + outputPath: folder, + name: "DCS Olympus Client", + arguments: `"${path.join(folder, "Config", "olympus.json")}"`, + icon: path.resolve(__dirname, '..', '..', 'img', 'olympus.ico'), + workingDirectory: path.resolve(__dirname, '..', '..', 'client') + } + }) && + createShortcut({ + windows: { + filePath: path.resolve(__dirname, '..', '..', 'client', 'server.vbs'), + outputPath: folder, + name: "DCS Olympus Server", + arguments: `"${path.join(folder, "Config", "olympus.json")}"`, + icon: path.resolve(__dirname, '..', '..', 'img', 'olympus_server.ico'), + workingDirectory: path.resolve(__dirname, '..', '..', 'client') + } + })) { + console.log("Shorcuts created succesfully") + } else { + return false; + } + } catch (e) { + console.error(e); + return false; + } + loadDivs(); + return true; +} + +export function uninstallOlympus(folder) { + console.log(`Uninstalling Olympus from ${folder}`); + try { + fs.rmSync(path.join(folder, "Mods", "Services", "Olympus"), { recursive: true, force: true }); + fs.rmSync(path.join(folder, "Config", "olympus.json"), {force: true}); + loadDivs(); + } catch (e) { + console.error(e); + return false; + } + return true; +} + +export function applyConfiguration(folder, data) { + console.log(`Applying configuration to Olympus from ${folder}`); + + if (fs.existsSync(path.join(folder, "Config", "olympus.json"))) { + var config = JSON.parse(fs.readFileSync(path.join(folder, "Config", "olympus.json"))); + + config["client"]["port"] = data["clientPort"]; + config["server"]["port"] = data["backendPort"]; + config["server"]["address"] = data["backendAddress"]; + config["authentication"]["gameMasterPassword"] = sha256(data["gameMasterPassword"]); + config["authentication"]["blueCommanderPassword"] = sha256(data["blueCommanderPassword"]); + config["authentication"]["redCommanderPassword"] = sha256(data["redCommanderPassword"]); + + try { + fs.writeFileSync(path.join(folder, "Config", "olympus.json"), JSON.stringify(config, null, 4)); + } catch (e) { + console.error(e); + return false; + } + } else { + return false; + } + return true; +} + +export function updateOlympus(folder) { + console.log(`Updating Olympus in ${folder}`); + try { + fs.cpSync(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true }); + fs.cpSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hook", "OlympusHook.lua")); + loadDivs(); + } catch (e) { + console.error(e); + return false; + } + return true; +} + +export function createDesktopShortcuts(folder) { + if (createShortcut({ + windows: { + filePath: path.resolve(__dirname, '..', '..', 'client', 'client.vbs'), + name: "DCS Olympus Client", + arguments: `"${path.join(folder, "Config", "olympus.json")}"`, + icon: path.resolve(__dirname, '..', '..', 'img', 'olympus.ico'), + workingDirectory: path.resolve(__dirname, '..', '..', 'client') + } + }) && createShortcut({ + windows: { + filePath: path.resolve(__dirname, '..', '..', 'client', 'server.vbs'), + name: "DCS Olympus Server", + arguments: `"${path.join(folder, "Config", "olympus.json")}"`, + icon: path.resolve(__dirname, '..', '..', 'img', 'olympus_server.ico'), + workingDirectory: path.resolve(__dirname, '..', '..', 'client') + } + })) { + showPopup("Shortcuts created successfully!") + } else { + showPopup("And error occurred while creating the shortcuts.") + } +} \ No newline at end of file diff --git a/manager/javascripts/managerinstallations.js b/manager/javascripts/managerinstallations.js new file mode 100644 index 00000000..e448f281 --- /dev/null +++ b/manager/javascripts/managerinstallations.js @@ -0,0 +1,32 @@ +const ManagerPage = require("./managerpage"); +const ejs = require('ejs') + +class ManagerInstallations extends ManagerPage { + constructor(options) { + super(options); + + ejs.renderFile("./ejs/managerinstallations.ejs", options, {}, (err, str) => { + if (!err) { + this.render(str); + } else { + console.error(str); + } + }); + } + + render(str) { + const element = this.getElement(); + element.innerHTML = str; + + var options = element.querySelectorAll(".option"); + for (let i = 0; i < options.length; i++) { + options[i].onclick = (e) => {this.onOptionClicked(e);} + } + } + + onOptionClicked(e) { + e.target.classList.toggle("selected") + } +} + +module.exports = ManagerInstallations; \ No newline at end of file diff --git a/manager/javascripts/managermenu.js b/manager/javascripts/managermenu.js new file mode 100644 index 00000000..3ab7f035 --- /dev/null +++ b/manager/javascripts/managermenu.js @@ -0,0 +1,23 @@ +const ManagerPage = require("./managerpage"); +const ejs = require('ejs') + +class ManagerMenu extends ManagerPage { + constructor(options) { + super(options); + + ejs.renderFile("./ejs/managermenu.ejs", options, {}, (err, str) => { + if (!err) { + this.render(str); + } else { + console.error(str); + } + }); + } + + render(str) { + const element = this.getElement(); + element.innerHTML = str; + } +} + +module.exports = ManagerMenu; \ No newline at end of file diff --git a/manager/javascripts/managerpage.js b/manager/javascripts/managerpage.js new file mode 100644 index 00000000..848cd1f4 --- /dev/null +++ b/manager/javascripts/managerpage.js @@ -0,0 +1,15 @@ +class ManagerPage { + element; + + constructor(options) { + this.element = document.createElement('div'); + this.element.classList.add("manager-page"); + } + + getElement() { + return this.element; + } + +} + +module.exports = ManagerPage; \ No newline at end of file diff --git a/manager/javascripts/net.js b/manager/javascripts/net.js new file mode 100644 index 00000000..9b303f54 --- /dev/null +++ b/manager/javascripts/net.js @@ -0,0 +1,12 @@ +const portfinder = require('portfinder') + +export function checkPort(port, callback) { + portfinder.getPort({ port: port, stopPort: port }, (err, res) => { + if (err !== null) { + console.error(`Port ${port} already in use`); + callback(false); + } else { + callback(true); + } + }); +} diff --git a/manager/javascripts/old.js b/manager/javascripts/old.js new file mode 100644 index 00000000..49e09fb9 --- /dev/null +++ b/manager/javascripts/old.js @@ -0,0 +1,273 @@ + +function showPopup(message, otherButton, otherButtonCallback) { + var data = { + message: message, + otherButton: otherButton + }; + + var popups = document.querySelectorAll(".popup"); + + for (let i = 0; i < popups.length; i++) { + document.body.removeChild(popups[i]) + } + + ejs.renderFile("./ejs/popup.ejs", data, {}, (err, str) => { + var div = document.createElement("div"); + div.classList.add("popup"); + div.innerHTML = str; + document.body.appendChild(div); + + div.querySelector(".apply").addEventListener("click", () => { + document.body.removeChild(div); + }) + + div.querySelector(".other").addEventListener("click", () => { + otherButtonCallback(); + }) + }); +} + + +class InstanceDiv { + element = null; + parent = null; + folder = ""; + + constructor(parent, folder) { + this.element = parent; + this.folder = folder; + this.render(); + } + + render() { + this.element = document.createElement("div"); + + var data = { + folder: this.folder, + installed: false, + index: instanceDivs.length * 10 + }; + + var newVersionInfo = vi(path.join("..", "mod", "bin", "olympus.dll")); + data["newVersion"] = newVersionInfo.ProductVersion; + data["version"] = "n/a"; + + if (fs.existsSync(path.join(this.folder, "Config", "olympus.json"))) { + var config = JSON.parse(fs.readFileSync(path.join(this.folder, "Config", "olympus.json"))); + data = { + ...data, + ...config + } + data["installed"] = true; + + try { + data["version"] = vi(path.join(this.folder, "Mods", "Services", "Olympus", "bin", "olympus.dll")).ProductVersion; + } catch (e) { + data["version"] = "n/a"; + } + } + + ejs.renderFile("./ejs/instanceDiv.ejs", data, {}, (err, str) => { + this.element.innerHTML = str; + this.element.querySelector(".add").addEventListener("click", (e) => { + if (!e.srcElement.classList.contains("disabled")) { + showPopup("Please wait while Olympus is being installed"); + window.setTimeout(() => { + if (installOlympus(this.folder)) { + showPopup("Olympus installed successfully. Use the provided form to set Olympus properties. All fields are mandatory. Click on \"Create desktop shortcuts\" to generate Olympus shortcuts on your desktop.", "Create desktop shortcuts", () => { + createDesktopShortcuts(this.folder); + }); + } else { + showPopup("An error has occurred during installation"); + } + }, 100); + } + }); + + this.element.querySelector(".remove").addEventListener("click", (e) => { + if (!e.srcElement.classList.contains("disabled")) { + showPopup("Please wait while Olympus is being uninstalled from DCS instance"); + window.setTimeout(() => { + if (uninstallOlympus(this.folder)) { + showPopup("Olympus uninstalled successfully from DCS instance!"); + } else { + showPopup("An error has occurred during uninstallation"); + } + }, 100); + } + }); + + this.element.querySelector(".apply").addEventListener("click", (e) => { + e.srcElement.classList.remove("blink"); + if (!e.srcElement.classList.contains("disabled")) { + showPopup("Please wait while the configuration is being applied"); + window.setTimeout(() => { + if (applyConfiguration(this.folder, this.getFields())) { + showPopup("Olympus configuration applied successfully!"); + } else { + showPopup("An error has occurred while applying the configuration"); + } + }, 100) + } + }); + + this.element.querySelector(".update").addEventListener("click", (e) => { + if (!e.srcElement.classList.contains("disabled")) { + showPopup("Please wait while Olympus is being updated in the DCS instance"); + window.setTimeout(() => { + if (updateOlympus(this.folder)) { + showPopup("Olympus updated successfully from DCS instance!"); + } else { + showPopup("An error has occurred during the update"); + } + }, 100); + } + }); + + var inputs = this.element.querySelectorAll("input"); + for (let i = 0; i < inputs.length; i++) { + inputs[i].addEventListener("change", () => { + inputs[i].classList.remove("error"); + instanceDivs.forEach((instanceDiv) => instanceDiv.checkFields()) + }) + } + }); + } + + getDiv() { + return this.element; + } + + getFields() { + return { + clientPort: Number(this.element.querySelector("#client-port").value), + backendPort: Number(this.element.querySelector("#backend-port").value), + backendAddress: this.element.querySelector("#backend-address").value, + gameMasterPassword: this.element.querySelector("#game-master-password").value, + blueCommanderPassword: this.element.querySelector("#blue-commander-password").value, + redCommanderPassword: this.element.querySelector("#red-commander-password").value, + } + } + + checkFields() { + var data = this.getFields(); + + /* Clear existing errors */ + var inputs = this.element.querySelectorAll("input"); + for (let i = 0; i < inputs.length; i++) { + inputs[i].classList.remove("error"); + } + var messages = this.element.querySelectorAll(".error"); + for (let i = 0; i < messages.length; i++) { + messages[i].innerText = ""; + } + + /* Enable the button */ + this.element.querySelector(".apply").classList.remove("disabled"); + + if (data["clientPort"] !== 0 && data["backendPort"] !== 0) { + if (data["clientPort"] === data["backendPort"]) { + this.element.querySelector("#client-port").classList.add("error"); + this.element.querySelector("#client-port-error").innerText = "Ports must be different"; + this.element.querySelector("#backend-port").classList.add("error"); + this.element.querySelector("#backend-port-error").innerText = "Ports must be different"; + this.element.querySelector(".apply").classList.add("disabled"); + } + else { + checkPort(data["clientPort"], (res) => { + var otherInstanceUsesPort = instanceDivs.find((instanceDiv) => { + if (instanceDiv != this) { + var fields = instanceDiv.getFields(); + if (fields["clientPort"] === data["clientPort"] || fields["backendPort"] === data["clientPort"]) { + return true; + } + } + }) + + if (!res || otherInstanceUsesPort) { + this.element.querySelector("#client-port").classList.add("error"); + this.element.querySelector("#client-port-error").innerText = "Port already in use"; + this.element.querySelector(".apply").classList.add("disabled"); + } + }); + + checkPort(data["backendPort"], (res) => { + var otherInstanceUsesPort = instanceDivs.find((instanceDiv) => { + if (instanceDiv != this) { + var fields = instanceDiv.getFields(); + if (fields["clientPort"] === data["backendPort"] || fields["backendPort"] === data["backendPort"]) { + return true; + } + } + }) + + if (!res || otherInstanceUsesPort) { + this.element.querySelector("#backend-port").classList.add("error"); + this.element.querySelector("#backend-port-error").innerText = "Port already in use"; + this.element.querySelector(".apply").classList.add("disabled"); + } + }); + } + } + + if (data["gameMasterPassword"] !== "" && data["blueCommanderPassword"] !== "" && data["gameMasterPassword"] === data["blueCommanderPassword"]) { + this.element.querySelector("#game-master-password").classList.add("error"); + this.element.querySelector("#game-master-password-error").innerText = "Passwords must be different"; + this.element.querySelector("#blue-commander-password").classList.add("error"); + this.element.querySelector("#blue-commander-password-error").innerText = "Passwords must be different"; + this.element.querySelector(".apply").classList.add("disabled"); + } + + if (data["gameMasterPassword"] !== "" && data["redCommanderPassword"] !== "" && data["gameMasterPassword"] === data["redCommanderPassword"]) { + this.element.querySelector("#game-master-password").classList.add("error"); + this.element.querySelector("#game-master-password-error").innerText = "Passwords must be different"; + this.element.querySelector("#red-commander-password").classList.add("error"); + this.element.querySelector("#red-commander-password-error").innerText = "Passwords must be different"; + this.element.querySelector(".apply").classList.add("disabled"); + } + + if (data["blueCommanderPassword"] !== "" && data["redCommanderPassword"] !== "" && data["blueCommanderPassword"] === data["redCommanderPassword"]) { + this.element.querySelector("#blue-commander-password").classList.add("error"); + this.element.querySelector("#blue-commander-password-error").innerText = "Passwords must be different"; + this.element.querySelector("#red-commander-password").classList.add("error"); + this.element.querySelector("#red-commander-password-error").innerText = "Passwords must be different"; + this.element.querySelector(".apply").classList.add("disabled"); + } + + if (data["gameMasterPassword"] === "" || data["blueCommanderPassword"] === "" || data["redCommanderPassword"] === "") { + this.element.querySelector(".apply").classList.add("disabled"); + } + } +} + +function loadDivs() { + regedit.list(shellFoldersKey, function (err, result) { + if (err) { + console.log(err); + } + else { + if (result[shellFoldersKey] !== undefined && result[shellFoldersKey]["exists"] && result[shellFoldersKey]['values'][saveGamesKey] !== undefined && result[shellFoldersKey]['values'][saveGamesKey]['value'] !== undefined) { + const searchpath = result[shellFoldersKey]['values'][saveGamesKey]['value']; + const folders = fs.readdirSync(searchpath); + instanceDivs = []; + const mainDiv = document.getElementById("main-div"); + + folders.forEach((folder) => { + if (fs.existsSync(path.join(searchpath, folder, "Config", "appsettings.lua")) || + fs.existsSync(path.join(searchpath, folder, "Config", "serversettings.lua"))) { + instanceDivs.push(new InstanceDiv(mainDiv, path.join(searchpath, folder))); + } + }); + + mainDiv.replaceChildren(...instanceDivs.map((instanceDiv) => { + return instanceDiv.getDiv(); + })); + + instanceDivs.forEach((instanceDiv) => instanceDiv.checkFields()) + + } else { + console.error("An error occured while trying to fetch the location of the DCS folders.") + } + } + }) +} \ No newline at end of file diff --git a/manager/javascripts/preload.js b/manager/javascripts/preload.js index 38d6238a..607359b3 100644 --- a/manager/javascripts/preload.js +++ b/manager/javascripts/preload.js @@ -2,58 +2,59 @@ var regedit = require('regedit') var fs = require('fs') var path = require('path') const ejs = require('ejs') -const portfinder = require('portfinder') -const sha256 = require('sha256') + const contextBridge = require('electron').contextBridge; const ipcRenderer = require('electron').ipcRenderer; -const createShortcut = require('create-desktop-shortcuts'); + const vi = require('win-version-info'); +const ManagerMenu = require("./managermenu"); +const ManagerInstallations = require('./managerinstallations'); const shellFoldersKey = 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders' const saveGamesKey = '{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}' var instanceDivs = []; -// White-listed channels. +/* White-listed channels. */ const ipc = { 'render': { - // From render to main. + /* From render to main. */ 'send': [ - 'window:minimize', // Channel names + 'window:minimize', 'window:maximize', 'window:restore', 'window:close' ], - // From main to render. + /* From main to render. */ 'receive': [ 'event:maximized', 'event:unmaximized' ], - // From render to main and back again. + /* From render to main and back again. */ 'sendReceive': [] } }; -// Exposed protected methods in the render process. +/* Exposed protected methods in the render process. */ contextBridge.exposeInMainWorld( - // Allowed 'ipcRenderer' methods. + /* Allowed 'ipcRenderer' methods. */ 'ipcRender', { - // From render to main. + /* From render to main. */ send: (channel, args) => { let validChannels = ipc.render.send; if (validChannels.includes(channel)) { ipcRenderer.send(channel, args); } }, - // From main to render. + /* From main to render. */ receive: (channel, listener) => { let validChannels = ipc.render.receive; if (validChannels.includes(channel)) { - // Deliberately strip event as it includes `sender`. + /* Deliberately strip event as it includes `sender`. */ ipcRenderer.on(channel, (event, ...args) => listener(...args)); } }, - // From render to main and back again. + /* From render to main and back again. */ invoke: (channel, args) => { let validChannels = ipc.render.sendReceive; if (validChannels.includes(channel)) { @@ -63,402 +64,10 @@ contextBridge.exposeInMainWorld( } ); -function showPopup(message, otherButton, otherButtonCallback) { - var data = { - message: message, - otherButton: otherButton - }; - - var popups = document.querySelectorAll(".popup"); - - for (let i = 0; i < popups.length; i++) { - document.body.removeChild(popups[i]) - } - - ejs.renderFile("./ejs/popup.ejs", data, {}, (err, str) => { - var div = document.createElement("div"); - div.classList.add("popup"); - div.innerHTML = str; - document.body.appendChild(div); - - div.querySelector(".apply").addEventListener("click", () => { - document.body.removeChild(div); - }) - - div.querySelector(".other").addEventListener("click", () => { - otherButtonCallback(); - }) - }); -} - -function checkPort(port, callback) { - portfinder.getPort({ port: port, stopPort: port }, (err, res) => { - if (err !== null) { - console.error(`Port ${port} already in use`); - callback(false); - } else { - callback(true); - } - }); -} - -function installOlympus(folder) { - console.log(`Installing Olympus in ${folder}`); - try { - fs.cpSync(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true }); - fs.cpSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hooks", "OlympusHook.lua")); - fs.cpSync(path.join("..", "olympus.json"), path.join(folder, "Config", "olympus.json")); - if (createShortcut({ - windows: { - filePath: path.resolve(__dirname, '..', '..', 'client', 'client.vbs'), - outputPath: folder, - name: "DCS Olympus Client", - arguments: `"${path.join(folder, "Config", "olympus.json")}"`, - icon: path.resolve(__dirname, '..', '..', 'img', 'olympus.ico'), - workingDirectory: path.resolve(__dirname, '..', '..', 'client') - } - }) && - createShortcut({ - windows: { - filePath: path.resolve(__dirname, '..', '..', 'client', 'server.vbs'), - outputPath: folder, - name: "DCS Olympus Server", - arguments: `"${path.join(folder, "Config", "olympus.json")}"`, - icon: path.resolve(__dirname, '..', '..', 'img', 'olympus_server.ico'), - workingDirectory: path.resolve(__dirname, '..', '..', 'client') - } - })) { - console.log("Shorcuts created succesfully") - } else { - return false; - } - } catch (e) { - console.error(e); - return false; - } - loadDivs(); - return true; -} - -function uninstallOlympus(folder) { - console.log(`Uninstalling Olympus from ${folder}`); - try { - fs.rmSync(path.join(folder, "Mods", "Services", "Olympus"), { recursive: true, force: true }); - fs.rmSync(path.join(folder, "Config", "olympus.json"), {force: true}); - loadDivs(); - } catch (e) { - console.error(e); - return false; - } - return true; -} - -function applyConfiguration(folder, data) { - console.log(`Applying configuration to Olympus from ${folder}`); - - if (fs.existsSync(path.join(folder, "Config", "olympus.json"))) { - var config = JSON.parse(fs.readFileSync(path.join(folder, "Config", "olympus.json"))); - - config["client"]["port"] = data["clientPort"]; - config["server"]["port"] = data["backendPort"]; - config["server"]["address"] = data["backendAddress"]; - config["authentication"]["gameMasterPassword"] = sha256(data["gameMasterPassword"]); - config["authentication"]["blueCommanderPassword"] = sha256(data["blueCommanderPassword"]); - config["authentication"]["redCommanderPassword"] = sha256(data["redCommanderPassword"]); - - try { - fs.writeFileSync(path.join(folder, "Config", "olympus.json"), JSON.stringify(config, null, 4)); - } catch (e) { - console.error(e); - return false; - } - } else { - return false; - } - return true; -} - -function updateOlympus(folder) { - console.log(`Updating Olympus in ${folder}`); - try { - fs.cpSync(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true }); - fs.cpSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hook", "OlympusHook.lua")); - loadDivs(); - } catch (e) { - console.error(e); - return false; - } - return true; -} - -function createDesktopShortcuts(folder) { - if (createShortcut({ - windows: { - filePath: path.resolve(__dirname, '..', '..', 'client', 'client.vbs'), - name: "DCS Olympus Client", - arguments: `"${path.join(folder, "Config", "olympus.json")}"`, - icon: path.resolve(__dirname, '..', '..', 'img', 'olympus.ico'), - workingDirectory: path.resolve(__dirname, '..', '..', 'client') - } - }) && createShortcut({ - windows: { - filePath: path.resolve(__dirname, '..', '..', 'client', 'server.vbs'), - name: "DCS Olympus Server", - arguments: `"${path.join(folder, "Config", "olympus.json")}"`, - icon: path.resolve(__dirname, '..', '..', 'img', 'olympus_server.ico'), - workingDirectory: path.resolve(__dirname, '..', '..', 'client') - } - })) { - showPopup("Shortcuts created successfully!") - } else { - showPopup("And error occurred while creating the shortcuts.") - } -} - -class InstanceDiv { - element = null; - parent = null; - folder = ""; - - constructor(parent, folder) { - this.element = parent; - this.folder = folder; - this.render(); - } - - render() { - this.element = document.createElement("div"); - - var data = { - folder: this.folder, - installed: false, - index: instanceDivs.length * 10 - }; - - var newVersionInfo = vi(path.join("..", "mod", "bin", "olympus.dll")); - data["newVersion"] = newVersionInfo.ProductVersion; - data["version"] = "n/a"; - - if (fs.existsSync(path.join(this.folder, "Config", "olympus.json"))) { - var config = JSON.parse(fs.readFileSync(path.join(this.folder, "Config", "olympus.json"))); - data = { - ...data, - ...config - } - data["installed"] = true; - - try { - data["version"] = vi(path.join(this.folder, "Mods", "Services", "Olympus", "bin", "olympus.dll")).ProductVersion; - } catch (e) { - data["version"] = "n/a"; - } - } - - ejs.renderFile("./ejs/instanceDiv.ejs", data, {}, (err, str) => { - this.element.innerHTML = str; - this.element.querySelector(".add").addEventListener("click", (e) => { - if (!e.srcElement.classList.contains("disabled")) { - showPopup("Please wait while Olympus is being installed"); - window.setTimeout(() => { - if (installOlympus(this.folder)) { - showPopup("Olympus installed successfully. Use the provided form to set Olympus properties. All fields are mandatory. Click on \"Create desktop shortcuts\" to generate Olympus shortcuts on your desktop.", "Create desktop shortcuts", () => { - createDesktopShortcuts(this.folder); - }); - } else { - showPopup("An error has occurred during installation"); - } - }, 100); - } - }); - - this.element.querySelector(".remove").addEventListener("click", (e) => { - if (!e.srcElement.classList.contains("disabled")) { - showPopup("Please wait while Olympus is being uninstalled from DCS instance"); - window.setTimeout(() => { - if (uninstallOlympus(this.folder)) { - showPopup("Olympus uninstalled successfully from DCS instance!"); - } else { - showPopup("An error has occurred during uninstallation"); - } - }, 100); - } - }); - - this.element.querySelector(".apply").addEventListener("click", (e) => { - e.srcElement.classList.remove("blink"); - if (!e.srcElement.classList.contains("disabled")) { - showPopup("Please wait while the configuration is being applied"); - window.setTimeout(() => { - if (applyConfiguration(this.folder, this.getFields())) { - showPopup("Olympus configuration applied successfully!"); - } else { - showPopup("An error has occurred while applying the configuration"); - } - }, 100) - } - }); - - this.element.querySelector(".update").addEventListener("click", (e) => { - if (!e.srcElement.classList.contains("disabled")) { - showPopup("Please wait while Olympus is being updated in the DCS instance"); - window.setTimeout(() => { - if (updateOlympus(this.folder)) { - showPopup("Olympus updated successfully from DCS instance!"); - } else { - showPopup("An error has occurred during the update"); - } - }, 100); - } - }); - - var inputs = this.element.querySelectorAll("input"); - for (let i = 0; i < inputs.length; i++) { - inputs[i].addEventListener("change", () => { - inputs[i].classList.remove("error"); - instanceDivs.forEach((instanceDiv) => instanceDiv.checkFields()) - }) - } - }); - } - - getDiv() { - return this.element; - } - - getFields() { - return { - clientPort: Number(this.element.querySelector("#client-port").value), - backendPort: Number(this.element.querySelector("#backend-port").value), - backendAddress: this.element.querySelector("#backend-address").value, - gameMasterPassword: this.element.querySelector("#game-master-password").value, - blueCommanderPassword: this.element.querySelector("#blue-commander-password").value, - redCommanderPassword: this.element.querySelector("#red-commander-password").value, - } - } - - checkFields() { - var data = this.getFields(); - - /* Clear existing errors */ - var inputs = this.element.querySelectorAll("input"); - for (let i = 0; i < inputs.length; i++) { - inputs[i].classList.remove("error"); - } - var messages = this.element.querySelectorAll(".error"); - for (let i = 0; i < messages.length; i++) { - messages[i].innerText = ""; - } - - /* Enable the button */ - this.element.querySelector(".apply").classList.remove("disabled"); - - if (data["clientPort"] !== 0 && data["backendPort"] !== 0) { - if (data["clientPort"] === data["backendPort"]) { - this.element.querySelector("#client-port").classList.add("error"); - this.element.querySelector("#client-port-error").innerText = "Ports must be different"; - this.element.querySelector("#backend-port").classList.add("error"); - this.element.querySelector("#backend-port-error").innerText = "Ports must be different"; - this.element.querySelector(".apply").classList.add("disabled"); - } - else { - checkPort(data["clientPort"], (res) => { - var otherInstanceUsesPort = instanceDivs.find((instanceDiv) => { - if (instanceDiv != this) { - var fields = instanceDiv.getFields(); - if (fields["clientPort"] === data["clientPort"] || fields["backendPort"] === data["clientPort"]) { - return true; - } - } - }) - - if (!res || otherInstanceUsesPort) { - this.element.querySelector("#client-port").classList.add("error"); - this.element.querySelector("#client-port-error").innerText = "Port already in use"; - this.element.querySelector(".apply").classList.add("disabled"); - } - }); - - checkPort(data["backendPort"], (res) => { - var otherInstanceUsesPort = instanceDivs.find((instanceDiv) => { - if (instanceDiv != this) { - var fields = instanceDiv.getFields(); - if (fields["clientPort"] === data["backendPort"] || fields["backendPort"] === data["backendPort"]) { - return true; - } - } - }) - - if (!res || otherInstanceUsesPort) { - this.element.querySelector("#backend-port").classList.add("error"); - this.element.querySelector("#backend-port-error").innerText = "Port already in use"; - this.element.querySelector(".apply").classList.add("disabled"); - } - }); - } - } - - if (data["gameMasterPassword"] !== "" && data["blueCommanderPassword"] !== "" && data["gameMasterPassword"] === data["blueCommanderPassword"]) { - this.element.querySelector("#game-master-password").classList.add("error"); - this.element.querySelector("#game-master-password-error").innerText = "Passwords must be different"; - this.element.querySelector("#blue-commander-password").classList.add("error"); - this.element.querySelector("#blue-commander-password-error").innerText = "Passwords must be different"; - this.element.querySelector(".apply").classList.add("disabled"); - } - - if (data["gameMasterPassword"] !== "" && data["redCommanderPassword"] !== "" && data["gameMasterPassword"] === data["redCommanderPassword"]) { - this.element.querySelector("#game-master-password").classList.add("error"); - this.element.querySelector("#game-master-password-error").innerText = "Passwords must be different"; - this.element.querySelector("#red-commander-password").classList.add("error"); - this.element.querySelector("#red-commander-password-error").innerText = "Passwords must be different"; - this.element.querySelector(".apply").classList.add("disabled"); - } - - if (data["blueCommanderPassword"] !== "" && data["redCommanderPassword"] !== "" && data["blueCommanderPassword"] === data["redCommanderPassword"]) { - this.element.querySelector("#blue-commander-password").classList.add("error"); - this.element.querySelector("#blue-commander-password-error").innerText = "Passwords must be different"; - this.element.querySelector("#red-commander-password").classList.add("error"); - this.element.querySelector("#red-commander-password-error").innerText = "Passwords must be different"; - this.element.querySelector(".apply").classList.add("disabled"); - } - - if (data["gameMasterPassword"] === "" || data["blueCommanderPassword"] === "" || data["redCommanderPassword"] === "") { - this.element.querySelector(".apply").classList.add("disabled"); - } - } -} - -function loadDivs() { - regedit.list(shellFoldersKey, function (err, result) { - if (err) { - console.log(err); - } - else { - if (result[shellFoldersKey] !== undefined && result[shellFoldersKey]["exists"] && result[shellFoldersKey]['values'][saveGamesKey] !== undefined && result[shellFoldersKey]['values'][saveGamesKey]['value'] !== undefined) { - const searchpath = result[shellFoldersKey]['values'][saveGamesKey]['value']; - const folders = fs.readdirSync(searchpath); - instanceDivs = []; - const mainDiv = document.getElementById("main-div"); - - folders.forEach((folder) => { - if (fs.existsSync(path.join(searchpath, folder, "Config", "appsettings.lua")) || - fs.existsSync(path.join(searchpath, folder, "Config", "serversettings.lua"))) { - instanceDivs.push(new InstanceDiv(mainDiv, path.join(searchpath, folder))); - } - }); - - mainDiv.replaceChildren(...instanceDivs.map((instanceDiv) => { - return instanceDiv.getDiv(); - })); - - instanceDivs.forEach((instanceDiv) => instanceDiv.checkFields()) - - } else { - console.error("An error occured while trying to fetch the location of the DCS folders.") - } - } - }) -} +var managerMenu = new ManagerMenu(); +var managerInstallations = new ManagerInstallations({instances: ["asd/asd1", "asd/asd2"]}); window.addEventListener('DOMContentLoaded', () => { - loadDivs(); + //document.body.appendChild(managerMenu.getElement()); + document.body.appendChild(managerInstallations.getElement()); }) \ No newline at end of file diff --git a/manager/manager.js b/manager/manager.js index be66a47a..626edd84 100644 --- a/manager/manager.js +++ b/manager/manager.js @@ -7,9 +7,10 @@ let window; function createWindow() { const window = new electronBrowserWindow({ - width: 1310, + width: 500, height: 800, frame: false, + resizable: false, webPreferences: { contextIsolation: true, preload: path.join(__dirname, "javascripts", 'preload.js'), diff --git a/manager/stylesheets/style.css b/manager/stylesheets/style.css index 8c8fe480..a2e8cd32 100644 --- a/manager/stylesheets/style.css +++ b/manager/stylesheets/style.css @@ -1,10 +1,20 @@ +:root { + --background: #181e25; + --offwhite: #F2F2F2; + --blue: #247be2; + --red: #FF5858; + --green: #8bff63; + --gray: #cfd9e8; + --darkgray: #3d4651; +} + * { font-family: "Open Sans", sans-serif; box-sizing: border-box; } body { - background-color: #181e25; + background-color: var(--background); padding: 0px; margin: 0px; } @@ -65,37 +75,15 @@ body { color: #F2F2F2; font-weight: bold; font-size: 16px; - padding: 20px; + padding: 20px 20px 0px 20px; column-gap: 10px; } -#header>div:first-of-type{ - width: 300px; -} - -#header>div:last-child { - font-size: 13px; - margin-left: 30px; - font-weight: 400; - text-align: right; - width: 100%; -} - .main-icon { width: 60px; height: 60px; } -#main-div { - display: flex; - flex-direction: row; - height: 100%; - row-gap: 30px; - column-gap: 30px; - flex-wrap: wrap; - padding: 25px; -} - body { overflow: auto; scrollbar-color: white transparent; @@ -121,233 +109,40 @@ body { opacity: 0.8; } -.instance-div { - display: flex; - flex-direction: column; - row-gap: 10px; - background-color: #3d4651; - height: fit-content; - padding: 20px 40px; - border-radius: 5px; - border-left: 5px solid #017DC1; - width: 600px; - box-shadow: 0px 0px 5px #000A; -} - -.instance-content { - display: flex; - flex-direction: column; - row-gap: 15px; -} - -.folder-name { - color: #F2F2F2; - font-weight: bold; - display: flex; - border-bottom: 1px solid #F2F2F2; - padding-bottom: 10px; - width: 100%; -} - -.folder-name span { - width: 500px; - overflow: hidden; - text-wrap: nowrap; - text-overflow: ellipsis; - direction: rtl; - text-align: left; -} - -.folder { - width: 20px; - height: 20px; - background-image: url("../icons/folder-open-solid.svg"); - margin-right: 15px; -} - -.version { - font-size: 13px; - color: #F2F2F2; -} - -.input-table { - padding: 0px; - margin: 0px; - border-width: 0px; - border-collapse: collapse; -} - -.input-table td { - color: #F2F2F2; - padding: 5px 0px; - font-size: 13px; - vertical-align: top; -} - -.input-table td>div { - display: flex; - flex-direction: column; - width: fit-content; -} - -.action-buttons { - display: flex; - flex-direction: row; - column-gap: 12px; - justify-content: start; -} - -.label { - display: flex; - align-items: center; - column-gap: 5px; -} - -.icon { - background-size: 100% 100%; - background-repeat: no-repeat; -} - -.button { - width: fit-content; - height: 40px; - color: #F2F2F2; - border: 1px solid #F2F2F2; - background-size: 40px 60%; - background-position: 0px 50%; - background-repeat: no-repeat; - border-radius: 5px; - padding: 5px 15px 5px 45px; - font-weight: 600; - display: flex; - align-items: center; - font-size: 14px; - background-color: transparent; -} - -.button:not(.disabled) { - cursor: pointer; -} - -.apply { - background-image: url("../icons/check-solid.svg"); - background-color: #017DC1; - border: 1px solid transparent; -} - -.add { - padding: 5px 15px 5px 15px; -} - -.update { - background-image: url("../icons/rotate-right-solid.svg"); -} - -.other { - padding: 5px 15px 5px 15px; -} - -.remove { - background-image: url("../icons/trash-can-regular.svg"); -} - -.disabled { - background-color: #797E83; -} - -.hide { - display: none; -} - -.message { - font-weight: 600; - height: 20px; - font-size: 13px; - color: #F2F2F2; -} - -input { - font-weight: 600; - font-size: 13px; - width: 240px; - border-radius: 4px; -} - -input:focus{ - outline: none; -} - -input.error { - border-color: #FF5858; -} - -.error { - font-weight: bold; - color: #FF5858; -} - .accent-red { - color: #FF5858; + color: var(--red); } .accent-green { - color: #8bff63; + color: var(--green); } -.info { - width: 12px; - height: 12px; - background-image: url("../icons/circle-info-solid.svg"); - background-position: 50% 50%; +.manager-page { + height: 100%; + padding: 35px; } -.blink { - animation: blinker 1s linear infinite; +.page-header { + font-size: 14px; + font-weight: 600; + color: var(--offwhite); + border-bottom: 1px solid var(--offwhite); + padding-bottom: 15px; + margin-bottom: 10px; } -@keyframes blinker { - 50% { - color: transparent; - } -} - -.popup { +.instruction { + color: var(--offwhite); display: flex; flex-direction: column; - row-gap: 15px; - height: 300px; - width: 500px; - position: absolute; - top: 50%; - left: 50%; - background-color: #181e25; - border-radius: 5px; - transform: translate(-250px, -150px); - padding: 30px; - box-shadow: 0px 0px 5px #000A; + row-gap: 4px; } -.popup-header { - height: 20px; - color: #F2F2F2; +.instruction>span:first-child { font-size: 14px; font-weight: 600; } -.popup-content { - height: calc(100% - 50px); - color: #F2F2F2; +.instruction>span:not(:first-child) { font-size: 13px; -} - -.popup-footer { - height: 30px; - display: flex; - justify-content: end; - column-gap: 15px; -} - -.popup-footer .apply { - background-image: none; - padding: 5px 15px 5px 15px; } \ No newline at end of file