Modified manager to new design

This commit is contained in:
Pax1601
2023-12-29 11:28:28 +01:00
parent 7450c9e506
commit 7391006a2f
38 changed files with 1779 additions and 1395 deletions

View File

@@ -1,7 +1,7 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerConnections extends ManagerPage {
class ConnectionsPage extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
@@ -24,11 +24,10 @@ class ManagerConnections extends ManagerPage {
this.element.querySelector(".backend-address").querySelector("input").addEventListener("change", async (e) => { this.instance.setBackendAddress(e.target.value); })
}
show(instance) {
this.instance = instance;
this.options["instance"] = instance;
show() {
this.instance = this.options.instance;
ejs.renderFile("./ejs/managerconnections.ejs", this.options, {}, (err, str) => {
ejs.renderFile("./ejs/connections.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
this.setClientPort(this.instance.clientPort);
@@ -66,4 +65,4 @@ class ManagerConnections extends ManagerPage {
}
}
module.exports = ManagerConnections;
module.exports = ConnectionsPage;

View File

@@ -4,9 +4,12 @@ const saveGamesKey = '{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}'
var fs = require('fs')
var path = require('path')
const vi = require('win-version-info');
const checkPort = require('./net')
const { checkPort, fetchWithTimeout } = require('./net')
const dircompare = require('dir-compare');
const { installJSON } = require('./filesystem')
const { spawn } = require('child_process');
const find = require('find-process');
const { deleteMod, uninstallInstance } = require('./filesystem')
const { showErrorPopup, showConfirmPopup } = require('./popup')
class DCSInstance {
static instances = null;
@@ -29,14 +32,14 @@ class DCSInstance {
const searchpath = result[shellFoldersKey]['values'][saveGamesKey]['value'];
const folders = fs.readdirSync(searchpath);
var instances = [];
folders.forEach((folder) => {
if (fs.existsSync(path.join(searchpath, folder, "Config", "appsettings.lua")) ||
fs.existsSync(path.join(searchpath, folder, "Config", "serversettings.lua"))) {
instances.push(new DCSInstance(path.join(searchpath, folder)));
}
})
res(instances);
} else {
console.error("An error occured while trying to fetch the location of the DCS instances.")
@@ -45,10 +48,10 @@ class DCSInstance {
}
})
});
return promise;
}
folder = "";
name = "";
clientPort = 3000;
@@ -57,19 +60,26 @@ class DCSInstance {
gameMasterPassword = "";
blueCommanderPassword = "";
redCommanderPassword = "";
gameMasterPasswordHash = "";
installed = false;
error = false;
webserverOnline = false;
backendOnline = false;
missionTime = "";
load = 0;
fps = 0;
constructor(folder) {
this.folder = folder;
this.name = path.basename(folder);
if (fs.existsSync(path.join(folder, "Config", "olympus.json"))){
if (fs.existsSync(path.join(folder, "Config", "olympus.json"))) {
try {
var config = JSON.parse(fs.readFileSync(path.join(folder, "Config", "olympus.json")));
this.clientPort = config["client"]["port"];
this.backendPort = config["server"]["port"];
this.backendAddress = config["server"]["address"];
this.gameMasterPasswordHash = config["authentication"]["gameMasterPassword"];
} catch (err) {
console.error(err)
}
@@ -85,18 +95,44 @@ class DCSInstance {
res2 = dircompare.compareSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hooks", "OlympusHook.lua"), options);
err1 = res1.differences !== 0;
err2 = res2.differences !== 0;
} catch(e) {
} catch (e) {
console.log(e);
}
if (err1 || err2) {
console.log(res1)
console.log(res2)
this.error = true;
}
}
window.setInterval(async () => {
await this.getData();
var page = document.getElementById("manager-instances");
if (page) {
var instanceDivs = page.querySelectorAll(`.option`);
for (let i = 0; i < instanceDivs.length; i++) {
if (instanceDivs[i].dataset.folder == this.folder) {
var instanceDiv = instanceDivs[i];
if (instanceDiv.querySelector(".webserver.online") !== null) {
instanceDiv.querySelector(".webserver.online").classList.toggle("hide", !this.webserverOnline)
instanceDiv.querySelector(".webserver.offline").classList.toggle("hide", this.webserverOnline)
instanceDiv.querySelector(".backend.online").classList.toggle("hide", !this.backendOnline)
instanceDiv.querySelector(".backend.offline").classList.toggle("hide", this.backendOnline)
if (this.backendOnline) {
instanceDiv.querySelector(".fps .data").innerText = this.fps;
instanceDiv.querySelector(".load .data").innerTexr = this.load;
}
instanceDiv.querySelector(".start-stop-server").innerText = this.webserverOnline ? "Stop server" : "Start server";
}
}
}
}
}, 1000);
}
async setClientPort(newPort) {
if (await this.checkClientPort(newPort)) {
console.log(`Instance ${this.folder} client port set to ${newPort}`)
@@ -136,7 +172,7 @@ class DCSInstance {
checkPort(port, async (portFree) => {
if (portFree) {
portFree = !(await DCSInstance.getInstances()).some((instance) => {
if (instance !== this) {
if (instance !== this && instance.installed) {
if (instance.clientPort === port || instance.backendPort === port) {
console.log(`Port ${port} already selected by other instance`);
return true;
@@ -148,7 +184,7 @@ class DCSInstance {
}
}
return false;
})
})
}
else {
console.log(`Port ${port} currently in use`);
@@ -164,7 +200,7 @@ class DCSInstance {
checkPort(port, async (portFree) => {
if (portFree) {
portFree = !(await DCSInstance.getInstances()).some((instance) => {
if (instance !== this) {
if (instance !== this && instance.installed) {
if (instance.clientPort === port || instance.backendPort === port) {
console.log(`Port ${port} already selected by other instance`);
return true;
@@ -185,6 +221,90 @@ class DCSInstance {
})
return promise;
}
async getData() {
if (this.installed && !this.error) {
fetchWithTimeout(`http://localhost:${this.clientPort}`, { timeout: 250 })
.then(async (response) => {
this.webserverOnline = (await response.text()).includes("Olympus");
}, () => {
this.webserverOnline = false;
});
let headers = new Headers();
headers.set('Authorization', 'Basic ' + Buffer.from("manager" + ":" + this.gameMasterPasswordHash).toString('base64'));
fetchWithTimeout(`http://${this.backendAddress}:${this.backendPort}/olympus/mission`, {
method: 'GET',
headers: headers,
timeout: 250
}).then(async (response) => {
if (response.ok) {
this.backendOnline = true;
return response.text();
} else {
return Promise.reject(`Reponse error, status code: ${response.status}`);
}
}, () => {
this.backendOnline = false;
}).then((text) => {
var data = JSON.parse(text);
this.fps = data.frameRate;
this.load = data.load;
}, (err) => {
console.warn(err);
}).catch((err) => {
console.warn(err);
});
}
}
startServer() {
console.log(`Starting server for instance at ${this.folder}`)
const out = fs.openSync(`./${this.name}.log`, 'a');
const err = fs.openSync(`./${this.name}.log`, 'a');
const sub = spawn('npm.cmd', ['run', 'start', '--', '--config', path.join(this.folder, "Config", "olympus.json")], {
detached: true,
cwd: "../client",
stdio: ['ignore', out, err]
});
sub.unref();
}
stopServer() {
find('port', this.clientPort)
.then((list) => {
if (list.length !== 1) {
list.length === 0 ? console.error("No processes found on the specified port") : console.error("Too many processes found on the specified port");
} else {
if (list[0].name.includes("node.exe")) {
console.log(`Killing process ${list[0].name}`)
process.kill(list[0].pid);
}
else {
console.log(list[0])
console.error(`The process listening on the specified port has an incorrect name: ${list[0].name}`)
}
}
}, () => {
console.error("Error retrieving list of processes")
})
}
uninstall() {
showConfirmPopup("Are you sure you want to completely remove this Olympus installation?", () =>
uninstallInstance(this.folder).then(
() => {
location.reload();
},
() => {
showErrorPopup("An error has occurred while uninstalling the Olympus instance. Make sure Olympus and DCS are not running.", () => {
location.reload();
});
}
));
}
}
module.exports = DCSInstance;

View File

@@ -2,38 +2,35 @@ const sha256 = require('sha256')
const createShortcut = require('create-desktop-shortcuts');
const fs = require('fs');
const path = require('path');
const { showErrorPopup, showWaitPopup } = require('./popup');
async function fixInstances(instances) {
var promise = new Promise((res, rej) => {
var instancePromises = instances.map((instance) => {
var instancePromise = new Promise((instanceRes, instanceErr) => {
installMod(instance.folder)
.then(() => installHooks(instance.folder))
.then(() => installShortCuts(instance.folder, instance.name))
console.log(`Fixing Olympus in ${instance.folder}`)
deleteMod(instance.folder)
.then(() => deleteHooks(instance.folder), (err) => { return Promise.reject(err); })
.then(() => installMod(instance.folder), (err) => { return Promise.reject(err); })
.then(() => installHooks(instance.folder), (err) => { return Promise.reject(err); })
.then(() => installShortCuts(instance.folder, instance.name), (err) => { return Promise.reject(err); })
.then(() => instanceRes(true), (err) => { instanceErr(err) })
})
return instancePromise;
});
console.log(instancePromises);
Promise.all(instancePromises).then(() => res(true), (err) => { rej(err) });
})
console.log(promise);
return promise;
}
async function installMod(folder) {
console.log(`Installing mod in ${folder}`)
async function uninstallInstance(folder) {
console.log(`Uninstalling Olympus from ${folder}`)
showWaitPopup("Please wait while the Olympus installation is being uninstalled.")
var promise = new Promise((res, rej) => {
fs.cp(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true }, (err) => {
if (err) {
console.log(`Error installing mod in ${folder}: ${err}`)
rej(err);
}
else {
console.log(`Mod succesfully installed in ${folder}`)
res(true);
}
});
deleteMod(folder)
.then(() => deleteHooks(folder), (err) => { return Promise.reject(err); })
.then(() => deleteJSON(folder), (err) => { return Promise.reject(err); })
.then(() => res(true), (err) => { rej(err) });
})
return promise;
}
@@ -55,6 +52,23 @@ async function installHooks(folder) {
return promise;
}
async function installMod(folder) {
console.log(`Installing mod in ${folder}`)
var promise = new Promise((res, rej) => {
fs.cp(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true }, (err) => {
if (err) {
console.log(`Error installing mod in ${folder}: ${err}`)
rej(err);
}
else {
console.log(`Mod succesfully installed in ${folder}`)
res(true);
}
});
})
return promise;
}
async function installJSON(folder) {
console.log(`Installing config in ${folder}`)
var promise = new Promise((res, rej) => {
@@ -138,11 +152,83 @@ async function installShortCuts(folder, name) {
return promise;
}
async function deleteHooks(folder) {
console.log(`Deleting hooks from ${folder}`);
var promise = new Promise((res, rej) => {
if (fs.existsSync(path.join(folder, "Scripts", "Hooks", "OlympusHook.lua"))) {
fs.rm(path.join(folder, "Scripts", "Hooks", "OlympusHook.lua"), (err) => {
if (err) {
console.log(`Error removing hooks from ${folder}: ${err}`)
rej(err);
}
else {
console.log(`Hooks succesfully removed from ${folder}`)
res(true);
}
});
} else {
res(true);
}
})
return promise;
}
async function deleteMod(folder) {
console.log(`Deleting mod from ${folder}`);
var promise = new Promise((res, rej) => {
if (fs.existsSync(path.join(folder, "Mods", "Services", "Olympus"))) {
fs.rmdir(path.join(folder, "Mods", "Services", "Olympus"), { recursive: true, force: true }, (err) => {
if (err) {
console.log(`Error removing mod from ${folder}: ${err}`)
rej(err);
}
else {
console.log(`Mod succesfully removed from ${folder}`)
res(true);
}
})
} else {
res(true);
};
})
return promise;
}
async function deleteJSON(folder) {
console.log(`Deleting JSON from ${folder}`);
var promise = new Promise((res, rej) => {
if (fs.existsSync(path.join(folder, "Config", "olympus.json"))) {
fs.rm(path.join(folder, "Config", "olympus.json"), (err) => {
if (err) {
console.log(`Error removing JSON from ${folder}: ${err}`)
rej(err);
}
else {
console.log(`JSON succesfully removed from ${folder}`)
res(true);
}
});
}
else {
res(true);
}
})
return promise;
}
async function deleteShortCuts() {
}
module.exports = {
applyConfiguration: applyConfiguration,
installJSON: installJSON,
installHooks: installHooks,
installMod: installMod,
installShortCuts, installShortCuts,
fixInstances: fixInstances
fixInstances: fixInstances,
deleteHooks: deleteHooks,
deleteJSON: deleteJSON,
deleteMod: deleteMod,
uninstallInstance: uninstallInstance
}

View File

@@ -0,0 +1,41 @@
const DCSInstance = require("./dcsinstance");
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class InstallationsPage extends ManagerPage {
onCancelClicked;
setSelectedInstance;
constructor(options) {
super(options);
}
render(str) {
this.element.innerHTML = str;
var options = this.element.querySelectorAll(".option");
for (let i = 0; i < options.length; i++) {
options[i].onclick = (e) => {this.onOptionClicked(e);}
}
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
}
async onOptionClicked(e) {
this.setSelectedInstance((await DCSInstance.getInstances()).find((instance) => {return instance.folder === e.target.dataset.folder}));
}
show() {
ejs.renderFile("./ejs/installations.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
console.error(err);
}
});
super.show();
}
}
module.exports = InstallationsPage;

View File

@@ -0,0 +1,64 @@
const DCSInstance = require("./dcsinstance");
const ManagerPage = require("./managerpage");
const ejs = require('ejs');
const { showErrorPopup } = require("./popup");
class InstancesPage extends ManagerPage {
onCancelClicked;
setSelectedInstance;
startInstance;
constructor(options) {
super(options);
}
render(str) {
this.element.innerHTML = str;
var editButtons = this.element.querySelectorAll(".edit");
for (let i = 0; i < editButtons.length; i++) {
editButtons[i].onclick = (e) => {this.onEditClicked(e);}
}
var uninstallButtons = this.element.querySelectorAll(".uninstall");
for (let i = 0; i < uninstallButtons.length; i++) {
uninstallButtons[i].onclick = (e) => {this.onUninstallClicked(e);}
}
var startButtons = this.element.querySelectorAll(".start-stop-server");
for (let i = 0; i < startButtons.length; i++) {
startButtons[i].onclick = (e) => {this.onStartStopClicked(e);}
}
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
}
async onEditClicked(e) {
console.log(e.target.dataset.folder)
this.setSelectedInstance((await DCSInstance.getInstances()).find((instance) => {return instance.folder === e.target.closest('.option').dataset.folder}));
}
async onStartStopClicked(e) {
var instance = (await DCSInstance.getInstances()).find((instance) => {return instance.folder === e.target.closest('.option').dataset.folder});
instance.webserverOnline? instance.stopServer(): instance.startServer();
}
async onUninstallClicked(e) {
var instance = (await DCSInstance.getInstances()).find((instance) => {return instance.folder === e.target.closest('.option').dataset.folder});
instance.webserverOnline || instance.backendOnline? showErrorPopup("Error, the selected Olympus instance is currently active, please stop Olympus before uninstalling it!") : instance.uninstall();
}
show() {
ejs.renderFile("./ejs/instances.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
console.error(err);
}
});
super.show();
}
}
module.exports = InstancesPage;

View File

@@ -0,0 +1,205 @@
const MenuPage = require("./menu");
const InstallationsPage = require('./installations');
const ConnectionsPage = require('./connections');
const PasswordsPage = require('./passwords');
const ResultPage = require('./result');
const InstancesPage = require('./instances');
const DCSInstance = require('./dcsinstance');
const { showErrorPopup } = require('./popup');
const { fixInstances } = require('./filesystem');
class Manager {
constructor() {
}
async start() {
var instances = await DCSInstance.getInstances();
document.getElementById("loader").classList.add("hide");
if (instances.some((instance) => {
return instance.installed && instance.error;
})) {
showErrorPopup("One or more Olympus instances are corrupted or need updating. Press Close to fix this.", async () => {
fixInstances(instances.filter((instance) => {
return instance.installed && instance.error;
})).then(
() => { location.reload() },
() => { showErrorPopup("An error occurred while trying to fix you installations. Please reinstall Olympus manually"); }
)
})
}
/* Menu */
var menuPage = new MenuPage();
menuPage.onInstallClicked = (e) => {
menuPage.hide();
installationsPage.show();
}
menuPage.onUpdateClicked = (e) => {
menuPage.hide();
instancesPage.options = {
...instancesPage.options,
manage: false
}
instancesPage.show();
}
menuPage.onManageClicked = (e) => {
menuPage.hide();
instancesPage.options = {
...instancesPage.options,
manage: true
}
instancesPage.show();
}
/* Installations */
var installationsPage = new InstallationsPage();
installationsPage.options = {
...installationsPage.options,
instances: instances
}
installationsPage.setSelectedInstance = (activeInstance) => {
connectionsPage.options = {
...connectionsPage.options,
instance: activeInstance,
install: true
}
passwordsPage.options = {
...passwordsPage.options,
instance: activeInstance,
install: true
}
resultPage.options = {
...resultPage.options,
instance: activeInstance,
install: true
}
installationsPage.hide();
connectionsPage.show();
connectionsPage.onBackClicked = (e) => {
connectionsPage.hide();
installationsPage.show();
}
}
installationsPage.onCancelClicked = (e) => {
installationsPage.hide();
menuPage.show();
}
/* Instances */
var instancesPage = new InstancesPage();
instancesPage.options = {
...instancesPage.options,
instances: instances.filter((instance) => { return instance.installed; })
}
instancesPage.setSelectedInstance = (activeInstance) => {
connectionsPage.options = {
...connectionsPage.options,
instance: activeInstance,
install: false
}
passwordsPage.options = {
...passwordsPage.options,
instance: activeInstance,
install: false
}
resultPage.options = {
...resultPage.options,
instance: activeInstance,
install: false
}
instancesPage.hide();
connectionsPage.show();
connectionsPage.onBackClicked = (e) => {
connectionsPage.hide();
instancesPage.show();
}
}
instancesPage.onCancelClicked = (e) => {
instancesPage.hide();
menuPage.show();
}
/* Connections */
var connectionsPage = new ConnectionsPage();
connectionsPage.onNextClicked = async (e) => {
let activeInstance = connectionsPage.options.instance;
if (activeInstance) {
if (await activeInstance.checkClientPort(activeInstance.clientPort) && await activeInstance.checkBackendPort(activeInstance.backendPort)) {
connectionsPage.hide();
passwordsPage.show();
} else {
showErrorPopup("Please make sure the selected ports are not already in use.")
}
} else {
showErrorPopup("An error has occurred, please restart the Olympus Manager.")
}
}
connectionsPage.onCancelClicked = (e) => {
connectionsPage.hide();
menuPage.show();
}
/* Passwords */
var passwordsPage = new PasswordsPage();
passwordsPage.onBackClicked = (e) => {
let activeInstance = connectionsPage.options.instance;
if (activeInstance) {
passwordsPage.hide();
connectionsPage.show();
} else {
showErrorPopup("An error has occurred, please restart the Olympus Manager.")
}
}
passwordsPage.onNextClicked = (e) => {
let activeInstance = connectionsPage.options.instance;
if (activeInstance) {
if (activeInstance.gameMasterPassword === "" || activeInstance.blueCommanderPassword === "" || activeInstance.redCommanderPassword === "") {
showErrorPopup("Please fill all the password inputs.")
}
else if (activeInstance.gameMasterPassword === activeInstance.blueCommanderPassword || activeInstance.blueCommanderPassword === activeInstance.redCommanderPassword || activeInstance.gameMasterPassword === activeInstance.redCommanderPassword) {
showErrorPopup("All the passwords must be different from each other.")
} else {
passwordsPage.hide();
resultPage.show();
resultPage.startInstallation();
}
} else {
showErrorPopup("An error has occurred, please restart the Olympus Manager.")
}
}
passwordsPage.onCancelClicked = (e) => {
passwordsPage.hide();
menuPage.show();
}
/* Result */
var resultPage = new ResultPage();
resultPage.onBackClicked = (e) => {
resultPage.hide();
location.reload();
}
resultPage.onCancelClicked = (e) => {
resultPage.hide();
location.reload();
}
document.body.appendChild(menuPage.getElement());
document.body.appendChild(installationsPage.getElement());
document.body.appendChild(instancesPage.getElement());
document.body.appendChild(connectionsPage.getElement());
document.body.appendChild(passwordsPage.getElement());
document.body.appendChild(resultPage.getElement());
menuPage.show();
}
}
module.exports = Manager;

View File

@@ -1,55 +0,0 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerInstallations extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
constructor(options) {
super(options);
}
render(str) {
this.element.innerHTML = str;
var options = this.element.querySelectorAll(".option");
for (let i = 0; i < options.length; i++) {
options[i].onclick = (e) => {this.onOptionClicked(e);}
}
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
}
onOptionClicked(e) {
var options = this.element.querySelectorAll(".option");
for (let i = 0; i < options.length; i++) {
options[i].classList.remove("selected");
}
e.target.classList.add("selected");
}
getSelectedInstance() {
return this.options.instances.find((instance) => {
const selected = this.element.querySelector(".selected");
return selected? selected.dataset.folder === instance.folder: false;
});
}
show() {
ejs.renderFile("./ejs/managerinstallations.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
console.error(err);
}
});
super.show();
}
}
module.exports = ManagerInstallations;

View File

@@ -1,55 +0,0 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerInstances extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
constructor(options) {
super(options);
}
render(str) {
this.element.innerHTML = str;
var options = this.element.querySelectorAll(".option");
for (let i = 0; i < options.length; i++) {
options[i].onclick = (e) => {this.onOptionClicked(e);}
}
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
}
onOptionClicked(e) {
var options = this.element.querySelectorAll(".option");
for (let i = 0; i < options.length; i++) {
options[i].classList.remove("selected");
}
e.target.classList.add("selected");
}
getSelectedInstance() {
return this.options.instances.find((instance) => {
const selected = this.element.querySelector(".selected");
return selected? selected.dataset.folder === instance.folder: false;
});
}
show() {
ejs.renderFile("./ejs/managerinstances.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
console.error(err);
}
});
super.show();
}
}
module.exports = ManagerInstances;

View File

@@ -1,7 +1,7 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerMenu extends ManagerPage {
class MenuPage extends ManagerPage {
onInstallClicked;
onUpdateClicked;
onManageClicked;
@@ -9,7 +9,7 @@ class ManagerMenu extends ManagerPage {
constructor(options) {
super(options);
ejs.renderFile("./ejs/managermenu.ejs", options, {}, (err, str) => {
ejs.renderFile("./ejs/menu.ejs", options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
@@ -28,4 +28,4 @@ class ManagerMenu extends ManagerPage {
}
}
module.exports = ManagerMenu;
module.exports = MenuPage;

View File

@@ -11,4 +11,22 @@ function checkPort(port, callback) {
});
}
module.exports = checkPort;
async function fetchWithTimeout(resource, options = {}) {
const { timeout = 8000 } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(resource, {
...options,
signal: controller.signal
});
clearTimeout(id);
return response;
}
module.exports = {
checkPort: checkPort,
fetchWithTimeout: fetchWithTimeout
}

View File

@@ -1,273 +0,0 @@
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.")
}
}
})
}

View File

@@ -1,7 +1,7 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerPasswords extends ManagerPage {
class PasswordsPage extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
@@ -23,11 +23,10 @@ class ManagerPasswords extends ManagerPage {
this.element.querySelector(".red-commander").querySelector("input").addEventListener("change", async (e) => { this.instance.setRedCommanderPassword(e.target.value); })
}
show(instance) {
this.instance = instance;
this.options["instance"] = instance;
show() {
this.instance = this.options.instance;
ejs.renderFile("./ejs/managerpasswords.ejs", this.options, {}, (err, str) => {
ejs.renderFile("./ejs/passwords.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
@@ -39,4 +38,4 @@ class ManagerPasswords extends ManagerPage {
}
}
module.exports = ManagerPasswords;
module.exports = PasswordsPage;

View File

@@ -1,10 +1,55 @@
function showPopup(message, onCloseCallback) {
function showErrorPopup(message, onCloseCallback) {
document.getElementById("grayout").classList.remove("hide");
document.getElementById("popup").classList.remove("hide");
document.getElementById("popup").querySelector(".close-popup").addEventListener("click", (e) => {
document.getElementById("popup").querySelector(".error").classList.remove("hide");
document.getElementById("popup").querySelector(".wait").classList.add("hide");
document.getElementById("popup").querySelector(".confirm").classList.add("hide");
document.getElementById("popup").querySelector(".close-popup").classList.remove("hide");
document.getElementById("popup").querySelector(".accept-popup").classList.add("hide");
/* Not using event listeners to make sure we only have one callback */
document.getElementById("popup").querySelector(".close-popup").onclick = (e) => {
hidePopup();
onCloseCallback();
})
if (onCloseCallback)
onCloseCallback();
}
document.getElementById("popup").querySelector(".content").innerText = message;
}
function showWaitPopup(message) {
document.getElementById("grayout").classList.remove("hide");
document.getElementById("popup").classList.remove("hide");
document.getElementById("popup").querySelector(".error").classList.add("hide");
document.getElementById("popup").querySelector(".wait").classList.remove("hide");
document.getElementById("popup").querySelector(".confirm").classList.add("hide");
document.getElementById("popup").querySelector(".close-popup").classList.add("hide");
document.getElementById("popup").querySelector(".accept-popup").classList.add("hide");
document.getElementById("popup").querySelector(".content").innerText = message;
}
function showConfirmPopup(message, onAcceptCallback, onCloseCallback) {
document.getElementById("grayout").classList.remove("hide");
document.getElementById("popup").classList.remove("hide");
document.getElementById("popup").querySelector(".error").classList.add("hide");
document.getElementById("popup").querySelector(".wait").classList.add("hide");
document.getElementById("popup").querySelector(".confirm").classList.remove("hide");
document.getElementById("popup").querySelector(".close-popup").classList.remove("hide");
document.getElementById("popup").querySelector(".accept-popup").classList.remove("hide");
/* Not using event listeners to make sure we only have one callback */
document.getElementById("popup").querySelector(".close-popup").onclick = (e) => {
hidePopup();
if (onCloseCallback)
onCloseCallback();
}
/* Not using event listeners to make sure we only have one callback */
document.getElementById("popup").querySelector(".accept-popup").onclick = (e) => {
hidePopup();
if (onAcceptCallback)
onAcceptCallback();
}
document.getElementById("popup").querySelector(".content").innerText = message;
}
@@ -14,6 +59,8 @@ function hidePopup() {
}
module.exports = {
showPopup: showPopup,
showErrorPopup: showErrorPopup,
showConfirmPopup: showConfirmPopup,
showWaitPopup: showWaitPopup,
hidePopup: hidePopup
}

View File

@@ -1,15 +1,8 @@
const Manager = require('./manager');
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
const ManagerMenu = require("./managermenu");
const ManagerInstallations = require('./managerinstallations');
const DCSInstance = require('./dcsinstance');
const ManagerConnections = require('./managerconnections');
const ManagerPasswords = require('./managerpasswords');
const { showPopup } = require('./popup');
const ManagerResult = require('./managerresult');
const { fixInstances } = require('./filesystem');
const ManagerInstances = require('./managerinstances');
const { exec } = require("child_process");
/* White-listed channels. */
const ipc = {
@@ -57,156 +50,35 @@ contextBridge.exposeInMainWorld(
return ipcRenderer.invoke(channel, args);
}
}
}
);
});
var activeInstance;
async function setup() {
var instances = await DCSInstance.getInstances();
if (instances.some((instance) => {
return instance.installed && instance.error;
})) {
showPopup("One or more Olympus instances are corrupted or need updating. Press Close to fix this.", async () => {
fixInstances(instances.filter((instance) => {
return instance.installed && instance.error;
})).then(
() => { location.reload() },
() => { showPopup("An error occurred while trying to fix you installations. Please reinstall Olympus manually") }
)
})
}
/* Menu */
var managerMenu = new ManagerMenu();
managerMenu.onInstallClicked = (e) => {
managerMenu.hide();
managerInstallations.show();
}
managerMenu.onUpdateClicked = (e) => {
managerMenu.hide();
managerInstances.show();
}
/* Installations */
var managerInstallations = new ManagerInstallations({ instances: instances });
managerInstallations.onBackClicked = (e) => {
managerInstallations.hide();
managerMenu.show();
}
managerInstallations.onNextClicked = (e) => {
activeInstance = managerInstallations.getSelectedInstance();
if (activeInstance) {
managerInstallations.hide();
managerConnections.show(activeInstance);
} else {
showPopup("Please select the instance you want to install Olympus into.")
}
}
managerInstallations.onCancelClicked = (e) => {
managerInstallations.hide();
managerMenu.show();
}
/* Instances */
var managerInstances = new ManagerInstances({ instances: instances.filter((instance) => {return instance.installed; }) });
managerInstances.onBackClicked = (e) => {
managerInstances.hide();
managerMenu.show();
}
managerInstances.onNextClicked = (e) => {
activeInstance = managerInstances.getSelectedInstance();
if (activeInstance) {
managerInstances.hide();
managerConnections.show(activeInstance);
} else {
showPopup("Please select the instance you want to manage.")
}
}
managerInstances.onCancelClicked = (e) => {
managerInstances.hide();
managerMenu.show();
}
/* Connections */
var managerConnections = new ManagerConnections();
managerConnections.onBackClicked = (e) => {
managerConnections.hide();
managerInstallations.show();
}
managerConnections.onNextClicked = async (e) => {
if (activeInstance) {
if (await activeInstance.checkClientPort(activeInstance.clientPort) && await activeInstance.checkBackendPort(activeInstance.backendPort)) {
managerConnections.hide();
managerPasswords.show(activeInstance);
} else {
showPopup("Please make sure the selected ports are not already in use.")
}
} else {
showPopup("An error has occurred, please restart the Olympus Manager.")
}
}
managerConnections.onCancelClicked = (e) => {
managerConnections.hide();
managerMenu.show();
}
/* Passwords */
var managerPasswords = new ManagerPasswords();
managerPasswords.onBackClicked = (e) => {
if (activeInstance) {
managerPasswords.hide();
managerConnections.show(activeInstance);
} else {
showPopup("An error has occurred, please restart the Olympus Manager.")
}
}
managerPasswords.onNextClicked = (e) => {
if (activeInstance) {
if (activeInstance.gameMasterPassword === "" || activeInstance.blueCommanderPassword === "" || activeInstance.redCommanderPassword === "") {
showPopup("Please fill all the password inputs.")
}
else if (activeInstance.gameMasterPassword === activeInstance.blueCommanderPassword || activeInstance.blueCommanderPassword === activeInstance.redCommanderPassword || activeInstance.gameMasterPassword === activeInstance.redCommanderPassword) {
showPopup("All the passwords must be different from each other.")
} else {
managerPasswords.hide();
managerResult.show(activeInstance);
managerResult.startInstallation();
}
} else {
showPopup("An error has occurred, please restart the Olympus Manager.")
}
}
managerPasswords.onCancelClicked = (e) => {
managerPasswords.hide();
managerMenu.show();
}
/* Result */
var managerResult = new ManagerResult();
managerResult.onBackClicked = (e) => {
managerResult.hide();
location.reload();
}
managerResult.onCancelClicked = (e) => {
managerResult.hide();
location.reload();
}
document.body.appendChild(managerMenu.getElement());
document.body.appendChild(managerInstallations.getElement());
document.body.appendChild(managerInstances.getElement());
document.body.appendChild(managerConnections.getElement());
document.body.appendChild(managerPasswords.getElement());
document.body.appendChild(managerResult.getElement());
managerMenu.show();
}
const manager = new Manager();
/* On content loaded */
window.addEventListener('DOMContentLoaded', () => {
setup();
})
window.addEventListener('DOMContentLoaded', async () => {
computePagesHeight();
document.getElementById("loader").classList.remove("hide");
await manager.start();
computePagesHeight();
var links = document.querySelectorAll(".link");
for (let i = 0; i < links.length; i++) {
links[i].addEventListener("click", (e) => {
exec("start " + e.target.dataset.link);
})
}
})
window.addEventListener('resize', () => {
computePagesHeight();
})
function computePagesHeight() {
var pages = document.querySelectorAll(".manager-page");
var titleBar = document.querySelector("#title-bar");
var header = document.querySelector("#header");
for (let i = 0; i < pages.length; i++) {
pages[i].style.height = (window.innerHeight - (titleBar.clientHeight + header.clientHeight)) + "px";
}
}

View File

@@ -2,7 +2,7 @@ const { installMod, installHooks, installJSON, applyConfiguration, installShortC
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerResult extends ManagerPage {
class ResultPage extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
@@ -16,14 +16,12 @@ class ManagerResult extends ManagerPage {
element.innerHTML = str;
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
//this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
}
show(instance) {
this.instance = instance;
this.options["instance"] = instance;
show() {
this.instance = this.options.instance;
ejs.renderFile("./ejs/managerresult.ejs", this.options, {}, (err, str) => {
ejs.renderFile("./ejs/result.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
@@ -110,4 +108,4 @@ class ManagerResult extends ManagerPage {
}
}
module.exports = ManagerResult;
module.exports = ResultPage;