Added distinction between basic and advanced mode

This commit is contained in:
Pax1601
2024-01-13 10:01:30 +01:00
parent 0f0ba4c725
commit d56a95cfa3
28 changed files with 1251 additions and 446 deletions

View File

@@ -6,58 +6,73 @@ const { logger } = require("./filesystem")
*
*/
class ConnectionsPage extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
instance;
constructor(options) {
super(options);
constructor(manager, options) {
super(manager, options);
}
render(str) {
const element = this.getElement();
element.innerHTML = str;
if (this.element.querySelector(".back"))
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
if (this.element.querySelector(".button.auto"))
this.element.querySelector(".button.auto").addEventListener("click", (e) => this.onOptionSelected(true));
if (this.element.querySelector(".next"))
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
if (this.element.querySelector(".button.manual"))
this.element.querySelector(".button.manual").addEventListener("click", (e) => this.onOptionSelected(false));
if (this.element.querySelector(".cancel"))
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
if (this.element.querySelector(".client-port"))
this.element.querySelector(".client-port").querySelector("input").addEventListener("change", async (e) => { this.setClientPort(Number(e.target.value)); })
this.element.querySelector(".client-port").querySelector("input").addEventListener("change", async (e) => { this.setClientPort(Number(e.target.value)); })
this.element.querySelector(".backend-port").querySelector("input").addEventListener("change", async (e) => { this.setBackendPort(Number(e.target.value)); })
this.element.querySelector(".backend-address").querySelector("input").addEventListener("change", async (e) => { this.instance.setBackendAddress(e.target.value); })
if (this.element.querySelector(".backend-port"))
this.element.querySelector(".backend-port").querySelector("input").addEventListener("change", async (e) => { this.setBackendPort(Number(e.target.value)); })
if (this.element.querySelector(".backend-address"))
this.element.querySelector(".backend-address").querySelector("input").addEventListener("change", async (e) => { this.manager.getActiveInstance().setBackendAddress(e.target.value); })
super.render();
}
show() {
this.instance = this.options.instance;
ejs.renderFile("./ejs/connections.ejs", this.options, {}, (err, str) => {
show(previousPage) {
ejs.renderFile("./ejs/connections.ejs", {...this.options, ...this.manager.options}, {}, (err, str) => {
if (!err) {
this.render(str);
/* Call the port setters to check if the ports are free */
this.setClientPort(this.instance.clientPort);
this.setBackendPort(this.instance.backendPort);
this.setClientPort(this.manager.getActiveInstance().clientPort);
this.setBackendPort(this.manager.getActiveInstance().backendPort);
} else {
logger.error(err);
}
});
super.show();
super.show(previousPage);
}
onNextClicked() {
this.hide();
this.manager.passwordsPage.show(this);
}
onCancelClicked() {
this.hide();
this.manager.menuPage.show()
}
onOptionSelected(auto) {
this.options.selectAutoOrManual = false;
this.options.auto = auto;
if (auto) {
} else {
this.show();
}
}
/** Asynchronously check if the client port is free and if it is, set the new value
*
*/
async setClientPort(newPort) {
const success = await this.instance.setClientPort(newPort);
const success = await this.manager.getActiveInstance().setClientPort(newPort);
var successEls = this.element.querySelector(".client-port").querySelectorAll(".success");
for (let i = 0; i < successEls.length; i++) {
successEls[i].classList.toggle("hide", !success);
@@ -72,7 +87,7 @@ class ConnectionsPage extends ManagerPage {
*
*/
async setBackendPort(newPort) {
const success = await this.instance.setBackendPort(newPort);
const success = await this.manager.getActiveInstance().setBackendPort(newPort);
var successEls = this.element.querySelector(".backend-port").querySelectorAll(".success");
for (let i = 0; i < successEls.length; i++) {
successEls[i].classList.toggle("hide", !success);

View File

@@ -1,3 +1,4 @@
const { getManager } = require('./managerfactory')
var regedit = require('regedit')
const shellFoldersKey = 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'
const saveGamesKey = '{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}'
@@ -124,36 +125,7 @@ class DCSInstance {
/* Periodically "ping" Olympus to check if either the client or the backend are active */
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").innerText = this.load;
}
instanceDiv.querySelector(".button.start").classList.toggle("hide", this.webserverOnline)
instanceDiv.querySelector(".button.uninstall").classList.toggle("hide", this.webserverOnline)
instanceDiv.querySelector(".button.edit").classList.toggle("hide", this.webserverOnline)
instanceDiv.querySelector(".button.open-browser").classList.toggle("hide", !this.webserverOnline)
instanceDiv.querySelector(".button.stop").classList.toggle("hide", !this.webserverOnline)
if (this.webserverOnline)
instanceDiv.querySelector(".button.start").classList.remove("loading")
}
}
}
}
getManager().instancesPage.update();
}, 1000);
}
@@ -370,7 +342,7 @@ class DCSInstance {
/* Uninstall this instance */
uninstall() {
showConfirmPopup("Are you sure you want to completely remove this Olympus installation?", () =>
showConfirmPopup("<div style='font-size: 18px; max-width: 100%'> Are you sure you want to remove Olympus? </div> If you click Accept, the Olympus mod will be removed from your DCS installation.", () =>
uninstallInstance(this.folder, this.name).then(
() => {
location.reload();

View File

@@ -1,14 +1,12 @@
const DCSInstance = require("./dcsinstance");
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
const { logger } = require("./filesystem")
const { logger } = require("./filesystem");
const { showConfirmPopup } = require("./popup");
class InstallationsPage extends ManagerPage {
onCancelClicked;
setSelectedInstance;
constructor(options) {
super(options);
constructor(manager, options) {
super(manager, options);
}
render(str) {
@@ -19,17 +17,18 @@ class InstallationsPage extends ManagerPage {
options[i].onclick = (e) => {this.onOptionClicked(e);}
}
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
if (this.element.querySelector(".cancel"))
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
super.render();
}
async onOptionClicked(e) {
this.setSelectedInstance((await DCSInstance.getInstances()).find((instance) => {return instance.folder === e.target.dataset.folder}));
this.onInstanceSelection((await DCSInstance.getInstances()).find((instance) => {return instance.folder === e.target.dataset.folder}));
}
show() {
ejs.renderFile("./ejs/installations.ejs", this.options, {}, (err, str) => {
show(previousPage) {
ejs.renderFile("./ejs/installations.ejs", {...this.options, ...this.manager.options}, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
@@ -37,7 +36,31 @@ class InstallationsPage extends ManagerPage {
}
});
super.show();
super.show(previousPage);
}
onInstanceSelection(activeInstance) {
this.manager.options.activeInstance = activeInstance;
this.manager.options.install = !activeInstance.installed;
/* Show the connections page */
if (!activeInstance.installed || !this.managers.options.install) {
this.hide();
this.manager.typePage.show(this);
} else {
showConfirmPopup("<div style='font-size: 18px; max-width: 100%'> Olympus is already installed in this instance! </div> If you click Accept, it will be installed again and all changes, e.g. custom databases or mods support, will be lost. Are you sure you want to continue?",
() => {
this.hide();
this.manager.typePage.show(this);
}
)
}
}
onCancelClicked(e) {
/* Go back to the main menu */
this.hide();
this.manager.menuPage.show();
}
}

View File

@@ -6,12 +6,10 @@ const { exec } = require("child_process");
const { logger } = require("./filesystem")
class InstancesPage extends ManagerPage {
onCancelClicked;
setSelectedInstance;
startInstance;
constructor(options) {
super(options);
constructor(manager, options) {
super(manager, options);
}
render(str) {
@@ -22,6 +20,11 @@ class InstancesPage extends ManagerPage {
editButtons[i].onclick = (e) => {this.onEditClicked(e);}
}
var installButtons = this.element.querySelectorAll(".button.install");
for (let i = 0; i < installButtons.length; i++) {
installButtons[i].onclick = (e) => {this.onInstallClicked(e);}
}
var uninstallButtons = this.element.querySelectorAll(".button.uninstall");
for (let i = 0; i < uninstallButtons.length; i++) {
uninstallButtons[i].onclick = (e) => {this.onUninstallClicked(e);}
@@ -47,19 +50,9 @@ class InstancesPage extends ManagerPage {
stopButtons[i].onclick = (e) => {this.onStopClicked(e);}
}
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
super.render();
}
async onEditClicked(e) {
this.getClickedInstance(e).then((instance) => {
instance.webserverOnline || instance.backendOnline? showErrorPopup("Error, the selected Olympus instance is currently active, please stop Olympus before editing it!") :
this.setSelectedInstance(instance);
}
);
}
async onStartServerClicked(e) {
e.target.closest(".collapse").classList.add("loading");
this.getClickedInstance(e).then((instance) => instance.startServer());
@@ -78,6 +71,30 @@ class InstancesPage extends ManagerPage {
this.getClickedInstance(e).then((instance) => instance.stop());
}
async onEditClicked(e) {
this.getClickedInstance(e).then((instance) => {
if (instance.webserverOnline || instance.backendOnline) {
showErrorPopup("Error, the selected Olympus instance is currently active, please stop Olympus before editing it!")
} else {
this.manager.options.activeInstance = instance;
this.manager.options.install = false;
this.manager.options.singleInstance = false;
this.hide();
this.manager.typePage.show(this);
}
});
}
async onInstallClicked(e) {
this.getClickedInstance(e).then((instance) => {
this.manager.options.activeInstance = instance;
this.manager.options.install = true;
this.manager.options.singleInstance = false;
this.hide();
this.manager.typePage.show(this);
});
}
async onUninstallClicked(e) {
this.getClickedInstance(e).then((instance) => {
instance.webserverOnline || instance.backendOnline? showErrorPopup("Error, the selected Olympus instance is currently active, please stop Olympus before uninstalling it!") : instance.uninstall();
@@ -92,8 +109,8 @@ class InstancesPage extends ManagerPage {
});
}
show() {
ejs.renderFile("./ejs/instances.ejs", this.options, {}, (err, str) => {
show(previousPage) {
ejs.renderFile("./ejs/instances.ejs", {...this.options, ...this.manager.options}, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
@@ -101,7 +118,51 @@ class InstancesPage extends ManagerPage {
}
});
super.show();
var instanceDivs = this.element.querySelectorAll(`.option`);
for (let i = 0; i < instanceDivs.length; i++) {
var instanceDiv = instanceDivs[i];
var instance = this.manager.options.instances.find((instance) => { return instance.folder === instanceDivs[i].dataset.folder;})
if (instance) {
instanceDiv.querySelector(".button.install").classList.toggle("hide", instance.installed);
instanceDiv.querySelector(".button.start").classList.toggle("hide", !instance.installed)
instanceDiv.querySelector(".button.uninstall").classList.toggle("hide", !instance.installed)
instanceDiv.querySelector(".button.edit").classList.toggle("hide", !instance.installed)
}
}
super.show(previousPage);
}
update() {
var instanceDivs = this.element.querySelectorAll(`.option`);
for (let i = 0; i < instanceDivs.length; i++) {
var instance = this.manager.options.instances.find((instance) => { return instance.folder === instanceDivs[i].dataset.folder;})
if (instance && instance.installed) {
var instanceDiv = instanceDivs[i];
if (instanceDiv.querySelector(".webserver.online") !== null) {
instanceDiv.querySelector(".webserver.online").classList.toggle("hide", !instance.webserverOnline)
instanceDiv.querySelector(".webserver.offline").classList.toggle("hide", instance.webserverOnline)
instanceDiv.querySelector(".backend.online").classList.toggle("hide", !instance.backendOnline)
instanceDiv.querySelector(".backend.offline").classList.toggle("hide", instance.backendOnline)
if (this.backendOnline) {
instanceDiv.querySelector(".fps .data").innerText = instance.fps;
instanceDiv.querySelector(".load .data").innerText = instance.load;
}
instanceDiv.querySelector(".button.start").classList.toggle("hide", instance.webserverOnline)
instanceDiv.querySelector(".button.uninstall").classList.toggle("hide", instance.webserverOnline)
instanceDiv.querySelector(".button.edit").classList.toggle("hide", instance.webserverOnline)
instanceDiv.querySelector(".button.open-browser").classList.toggle("hide", !instance.webserverOnline)
instanceDiv.querySelector(".button.stop").classList.toggle("hide", !instance.webserverOnline)
if (this.webserverOnline)
instanceDiv.querySelector(".button.start").classList.remove("loading")
}
}
}
}
}

View File

@@ -6,258 +6,131 @@ const ResultPage = require('./result');
const InstancesPage = require('./instances');
const DCSInstance = require('./dcsinstance');
const { showErrorPopup, showWaitPopup } = require('./popup');
const { showErrorPopup, showWaitPopup, showConfirmPopup } = require('./popup');
const { fixInstances } = require('./filesystem');
const { logger } = require("./filesystem")
const path = require("path")
const fs = require("fs");
const WelcomePage = require("./welcome");
const TypePage = require("./type");
class Manager {
simplified = true;
options = {
logLocation: path.join(__dirname, "..", "manager.log"),
configLoaded: false
};
welcomePage = null;
installationsPage = null;
typePage = null;
connectionsPage = null;
passwordsPage = null;
resultPage = null;
instancesPage = null;
constructor() {
}
async start() {
/* Get the list of DCS instances */
var instances = await DCSInstance.getInstances();
/* If there is only 1 DCS Instance and Olympus is not installed in it, go straight to the installation page (since there is nothing else to do) */
this.simplified = instances.length === 1 && !instances[0].installed;
document.getElementById("loader").classList.add("hide");
/* Check if there are corrupted or outdate instances */
if (instances.some((instance) => {
return instance.installed && instance.error;
})) {
/* Ask the user for confirmation */
showErrorPopup("One or more Olympus instances are corrupted or need updating. Press Close to fix this.", async () => {
showWaitPopup("Please wait while your instances are being fixed.")
fixInstances(instances.filter((instance) => {
return instance.installed && instance.error;
})).then(
() => { location.reload() },
(err) => {
logger.error(err);
showErrorPopup(`An error occurred while trying to fix your installations. Please reinstall Olympus manually. <br><br> You can find more info in ${path.join(__dirname, "..", "manager.log")}`);
}
)
})
}
/* Check which buttons should be enabled */
const installEnabled = true;
const manageEnabled = instances.some((instance) => { return instance.installed; });
/* Menu */
var menuPage = new MenuPage();
menuPage.options = {
...menuPage.options,
installEnabled: installEnabled,
manageEnabled: manageEnabled
}
/* When the install button is clicked go the installation page */
menuPage.onInstallClicked = (e) => {
menuPage.hide();
installationsPage.show();
}
/* When the manage button is clicked go to the instances page in "manage mode" (i.e. manage = true) */
menuPage.onManageClicked = (e) => {
menuPage.hide();
instancesPage.show();
}
/* Installations */
var installationsPage = new InstallationsPage();
installationsPage.options = {
...installationsPage.options,
instances: instances
}
installationsPage.setSelectedInstance = (activeInstance) => {
/* Set the active options for the pages */
const options = {
instance: activeInstance,
simplified: this.simplified,
install: true
if (fs.existsSync("options.json")) {
/* Load the options from the json file */
try {
this.options = {...this.options, ...JSON.parse(fs.readFileSync("options.json"))};
this.options.configLoaded = true;
} catch (e) {
logger.error(`An error occurred while reading the options.json file: ${e}`);
}
connectionsPage.options = {
...connectionsPage.options,
...options
}
if (!this.options.configLoaded) {
/* Hide the loading page */
document.getElementById("loader").classList.add("hide");
/* Show page to select basic vs advanced mode */
this.welcomePage = new WelcomePage(this);
document.body.appendChild(this.welcomePage.getElement());
this.welcomePage.show();
}
else {
document.getElementById("header").classList.remove("hide");
if (this.options.mode === "basic") {
document.getElementById("switch-mode").innerText = "Advanced mode";
document.getElementById("switch-mode").onclick = () => { this.switchMode("advanced"); }
}
passwordsPage.options = {
...passwordsPage.options,
...options
}
resultPage.options = {
...resultPage.options,
...options
else {
document.getElementById("switch-mode").innerText = "Basic mode";
document.getElementById("switch-mode").onclick = () => { this.switchMode("basic"); }
}
/* Show the connections page */
installationsPage.hide();
connectionsPage.show();
/* Get the list of DCS instances */
this.options.instances = await DCSInstance.getInstances();
connectionsPage.onBackClicked = (e) => {
/* Show the installation page */
connectionsPage.hide();
installationsPage.show();
}
}
installationsPage.onCancelClicked = (e) => {
/* Go back to the main menu */
installationsPage.hide();
menuPage.show();
}
/* Instances */
var instancesPage = new InstancesPage();
instancesPage.options = {
...instancesPage.options,
instances: instances.filter((instance) => { return instance.installed; })
}
instancesPage.setSelectedInstance = (activeInstance) => {
/* Set the active options for the pages */
const options = {
instance: activeInstance,
simplified: this.simplified,
install: false
}
connectionsPage.options = {
...connectionsPage.options,
...options
}
passwordsPage.options = {
...passwordsPage.options,
...options
}
resultPage.options = {
...resultPage.options,
...options
/* Check if there are corrupted or outdate instances */
if (this.options.instances.some((instance) => {
return instance.installed && instance.error;
})) {
/* Ask the user for confirmation */
showConfirmPopup("<div style='font-size: 18px; max-width: 100%;'>One or more of your Olympus instances are not up to date! </div> <br> <br> If you have just updated Olympus this is normal. <br> <br> Press Accept and the Manager will fix your instances for you. <br> Press Close to update your instances manually using the Installation Wizard", async () => {
showWaitPopup("Please wait while your instances are being fixed.")
fixInstances(this.options.instances.filter((instance) => {
return instance.installed && instance.error;
})).then(
() => { location.reload() },
(err) => {
logger.error(err);
showErrorPopup(`An error occurred while trying to fix your installations. Please reinstall Olympus manually. <br><br> You can find more info in ${path.join(__dirname, "..", "manager.log")}`);
}
)
})
}
/* Show the connections page */
instancesPage.hide();
connectionsPage.show();
this.options.installEnabled = true;
this.options.editEnabled = this.options.instances.find(instance => instance.installed);
this.options.uninstallEnabled = this.options.instances.find(instance => instance.installed);
connectionsPage.onBackClicked = (e) => {
/* Show the instances page */
connectionsPage.hide();
instancesPage.show();
}
}
instancesPage.onCancelClicked = (e) => {
/* Go back to the main menu */
instancesPage.hide();
menuPage.show();
}
/* Hide the loading page */
document.getElementById("loader").classList.add("hide");
/* Connections */
var connectionsPage = new ConnectionsPage();
connectionsPage.onNextClicked = async (e) => {
let activeInstance = connectionsPage.options.instance;
if (activeInstance) {
/* Check that the selected ports are free before proceeding */
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.")
}
this.options.singleInstance = this.options.instances.length === 1;
/* Create all the HTML pages */
this.menuPage = new MenuPage(this);
this.installationsPage = new InstallationsPage(this);
this.typePage = new TypePage(this);
this.connectionsPage = new ConnectionsPage(this);
this.passwordsPage = new PasswordsPage(this);
this.resultPage = new ResultPage(this);
this.instancesPage = new InstancesPage(this);
document.body.appendChild(this.menuPage.getElement());
document.body.appendChild(this.installationsPage.getElement());
document.body.appendChild(this.typePage.getElement());
document.body.appendChild(this.connectionsPage.getElement());
document.body.appendChild(this.passwordsPage.getElement());
document.body.appendChild(this.resultPage.getElement());
document.body.appendChild(this.instancesPage.getElement());
if (this.options.mode === "basic") {
/* In basic mode no dashboard is shown */
this.menuPage.show();
} else {
showErrorPopup(`An error has occurred, please restart the Olympus Manager. <br><br> You can find more info in ${path.join(__dirname, "..", "manager.log")}`)
/* In advanced mode we go directly to the dashboard */
this.instancesPage.show();
}
}
connectionsPage.onCancelClicked = (e) => {
/* Go back to the main menu */
connectionsPage.hide();
menuPage.show();
}
}
/* Passwords */
var passwordsPage = new PasswordsPage();
passwordsPage.onBackClicked = (e) => {
/* Go back to the connections page */
let activeInstance = connectionsPage.options.instance;
if (activeInstance) {
passwordsPage.hide();
connectionsPage.show();
} else {
showErrorPopup(`An error has occurred, please restart the Olympus Manager. <br><br> You can find more info in ${path.join(__dirname, "..", "manager.log")}`)
}
}
passwordsPage.onNextClicked = (e) => {
let activeInstance = connectionsPage.options.instance;
if (activeInstance) {
/* Check that all the passwords have been set */
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. <br><br> You can find more info in ${path.join(__dirname, "..", "manager.log")}`)
}
getActiveInstance() {
return this.options.activeInstance;
}
}
passwordsPage.onCancelClicked = (e) => {
/* Go back to the main menu */
passwordsPage.hide();
menuPage.show();
}
/* Result */
var resultPage = new ResultPage({logLocation: path.join(__dirname, "..", "manager.log")});
resultPage.onBackClicked = (e) => {
/* Reload the page to apply changes */
resultPage.hide();
location.reload();
}
resultPage.onCancelClicked = (e) => {
/* Reload the page to apply changes */
resultPage.hide();
location.reload();
}
/* Create all the HTML pages */
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());
/* In simplified mode we directly show the connections page */
if (this.simplified) {
const options = {
instance: instances[0],
simplified: this.simplified,
install: true
}
connectionsPage.options = {
...connectionsPage.options,
...options
}
passwordsPage.options = {
...passwordsPage.options,
...options
}
resultPage.options = {
...resultPage.options,
...options
}
/* Show the connections page directly */
instancesPage.hide();
connectionsPage.show();
} else {
/* Show the main menu */
menuPage.show();
}
switchMode(newMode) {
var options = JSON.parse(fs.readFileSync("options.json"));
options.mode = newMode;
fs.writeFileSync("options.json", JSON.stringify(options));
location.reload();
}
}

View File

@@ -0,0 +1,15 @@
var manager = null;
function getManager() {
if (manager) {
return manager;
} else {
const Manager = require("./manager");
manager = new Manager();
return manager;
}
}
module.exports = {
getManager: getManager
};

View File

@@ -1,8 +1,11 @@
class ManagerPage {
manager;
element;
options;
previousPage;
constructor(options) {
constructor(manager, options) {
this.manager = manager;
this.options = options ?? {};
this.element = document.createElement('div');
this.element.classList.add("manager-page", "hide");
@@ -12,8 +15,11 @@ class ManagerPage {
return this.element;
}
show() {
show(previousPage) {
this.element.classList.remove("hide");
if (previousPage !== undefined)
this.previousPage = previousPage;
}
hide() {
@@ -28,6 +34,21 @@ class ManagerPage {
buttons[i].classList.toggle("open");
})
}
/* Connect the back, next and cancel buttons */
if (this.element.querySelector(".back"))
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
if (this.element.querySelector(".next"))
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
if (this.element.querySelector(".cancel"))
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
}
onBackClicked() {
this.hide();
this.previousPage.show()
}
}

View File

@@ -1,14 +1,11 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
const { logger } = require("./filesystem")
const { logger } = require("./filesystem");
const { showConfirmPopup } = require("./popup");
class MenuPage extends ManagerPage {
onInstallClicked;
onUpdateClicked;
onManageClicked;
constructor(options) {
super(options);
constructor(manager, options) {
super(manager, options);
}
render(str) {
@@ -16,15 +13,14 @@ class MenuPage extends ManagerPage {
element.innerHTML = str;
element.querySelector(".install").addEventListener("click", (e) => this.onInstallClicked(e));
element.querySelector(".manage").addEventListener("click", (e) => this.onManageClicked(e));
element.querySelector(".edit").addEventListener("click", (e) => this.onEditClicked(e));
element.querySelector(".uninstall").addEventListener("click", (e) => this.onUninstallClicked(e));
super.render();
}
show() {
this.instance = this.options.instance;
ejs.renderFile("./ejs/menu.ejs", this.options, {}, (err, str) => {
show(previousPage) {
ejs.renderFile("./ejs/menu.ejs", {...this.options, ...this.manager.options}, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
@@ -32,8 +28,60 @@ class MenuPage extends ManagerPage {
}
});
super.show();
super.show(previousPage);
}
/* When the install button is clicked go the installation page */
onInstallClicked(e) {
this.manager.options.install = true;
if (this.manager.options.singleInstance) {
this.manager.options.activeInstance = this.manager.options.instances[0];
/* Show the connections page */
if (!this.manager.options.activeInstance.installed) {
this.hide();
this.manager.typePage.show(this);
} else {
showConfirmPopup("<div style='font-size: 18px; max-width: 100%'> Olympus is already installed in this instance! </div> If you click Accept, it will be installed again and all changes, e.g. custom databases or mods support, will be lost. Are you sure you want to continue?",
() => {
this.hide();
this.manager.typePage.show(this);
}
)
}
} else {
this.hide();
this.manager.installationsPage.show(this);
}
}
/* When the edit button is clicked go to the instances page */
onEditClicked(e) {
this.hide();
this.manager.options.install = false;
if (this.manager.options.singleInstance) {
this.manager.options.activeInstance = this.manager.options.instances[0];
this.manager.typePage.show(this);
} else {
this.manager.installationsPage.show(this);
}
}
/* When the remove button is clicked go to the instances page */
onUninstallClicked(e) {
this.manager.options.install = false;
if (this.manager.options.singleInstance) {
this.manager.options.activeInstance = this.manager.options.instances[0];
this.manager.options.activeInstance.uninstall();
} else {
// TODO select instance to remove
}
}
}
module.exports = MenuPage;

View File

@@ -3,38 +3,23 @@ const ejs = require('ejs')
const { logger } = require("./filesystem")
class PasswordsPage extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
constructor(options) {
super(options);
constructor(manager, options) {
super(manager, options);
}
render(str) {
const element = this.getElement();
element.innerHTML = str;
if (this.element.querySelector(".back"))
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
if (this.element.querySelector(".next"))
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
if (this.element.querySelector(".cancel"))
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
this.element.querySelector(".game-master").querySelector("input").addEventListener("change", async (e) => { this.instance.setGameMasterPassword(e.target.value); })
this.element.querySelector(".blue-commander").querySelector("input").addEventListener("change", async (e) => { this.instance.setBlueCommanderPassword(e.target.value); })
this.element.querySelector(".red-commander").querySelector("input").addEventListener("change", async (e) => { this.instance.setRedCommanderPassword(e.target.value); })
this.element.querySelector(".game-master").querySelector("input").addEventListener("change", async (e) => { this.manager.getActiveInstance().setGameMasterPassword(e.target.value); })
this.element.querySelector(".blue-commander").querySelector("input").addEventListener("change", async (e) => { this.manager.getActiveInstance().setBlueCommanderPassword(e.target.value); })
this.element.querySelector(".red-commander").querySelector("input").addEventListener("change", async (e) => { this.manager.getActiveInstance().setRedCommanderPassword(e.target.value); })
super.render();
}
show() {
this.instance = this.options.instance;
ejs.renderFile("./ejs/passwords.ejs", this.options, {}, (err, str) => {
show(previousPage) {
ejs.renderFile("./ejs/passwords.ejs", {...this.options, ...this.manager.options}, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
@@ -42,7 +27,18 @@ class PasswordsPage extends ManagerPage {
}
});
super.show();
super.show(previousPage);
}
onNextClicked() {
this.hide();
this.manager.resultPage.show();
this.manager.resultPage.startInstallation();
}
onCancelClicked() {
this.hide();
this.manager.menuPage.show()
}
}

View File

@@ -1,5 +1,24 @@
// TODO: we can probably refactor this to be a bit cleaner
function showInfoPopup(message, 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.add("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();
}
document.getElementById("popup").querySelector(".content").innerHTML = message;
}
function showErrorPopup(message, onCloseCallback) {
document.getElementById("grayout").classList.remove("hide");
document.getElementById("popup").classList.remove("hide");
@@ -61,6 +80,7 @@ function hidePopup() {
}
module.exports = {
showInfoPopup: showInfoPopup,
showErrorPopup: showErrorPopup,
showConfirmPopup: showConfirmPopup,
showWaitPopup: showWaitPopup,

View File

@@ -1,5 +1,3 @@
const Manager = require('./manager');
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
const { exec, spawn } = require("child_process");
@@ -10,7 +8,8 @@ const https = require('follow-redirects').https;
const fs = require('fs');
const AdmZip = require("adm-zip");
const { Octokit } = require('octokit');
const { logger } = require("./filesystem")
const { logger } = require("./filesystem");
const { getManager } = require('./managerFactory');
const VERSION = "{{OLYMPUS_VERSION_NUMBER}}";
logger.log(`Running in ${__dirname}`);
@@ -257,15 +256,13 @@ contextBridge.exposeInMainWorld(
}
});
/* New instance of the manager app */
const manager = new Manager();
/* On content loaded */
window.addEventListener('DOMContentLoaded', async () => {
/* Compute the height of the content page */
computePagesHeight();
document.getElementById("loader").classList.remove("hide");
await manager.start();
await getManager().start();
/* Compute the height of the content page to account for the pages created by the manager*/
computePagesHeight();
@@ -273,7 +270,8 @@ window.addEventListener('DOMContentLoaded', async () => {
var links = document.querySelectorAll(".link");
for (let i = 0; i < links.length; i++) {
links[i].addEventListener("click", (e) => {
exec("start " + e.target.dataset.link);
if (e.target.dataset.link)
exec("start " + e.target.dataset.link);
})
}
})
@@ -298,4 +296,5 @@ function computePagesHeight() {
ipcRenderer.on("check-version", () => {
/* Check if a new version is available */
checkVersion();
})
})

View File

@@ -4,27 +4,19 @@ const ejs = require('ejs')
const { logger } = require("./filesystem")
class ResultPage extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
constructor(options) {
super(options);
constructor(manager, options) {
super(manager, options);
}
render(str) {
const element = this.getElement();
element.innerHTML = str;
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
super.render();
}
show() {
this.instance = this.options.instance;
ejs.renderFile("./ejs/result.ejs", this.options, {}, (err, str) => {
show(previousPage) {
ejs.renderFile("./ejs/result.ejs", {...this.options, ...this.manager.options}, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
@@ -32,14 +24,18 @@ class ResultPage extends ManagerPage {
}
});
super.show();
super.show(previousPage);
}
onBackClicked() {
location.reload();
}
/** Installation is performed by using an then chain of async functions. Installation is aborted on any error along the chain
*
*/
startInstallation() {
installHooks(this.instance.folder).then(
installHooks(this.manager.getActiveInstance().folder).then(
() => {
this.applyStepSuccess(".hook");
},
@@ -47,7 +43,7 @@ class ResultPage extends ManagerPage {
this.applyStepFailure(".hook");
return Promise.reject(err);
}
).then(() => installMod(this.instance.folder, this.instance.name)).then(
).then(() => installMod(this.manager.getActiveInstance().folder, this.manager.getActiveInstance().name)).then(
() => {
this.applyStepSuccess(".mod");
},
@@ -55,7 +51,7 @@ class ResultPage extends ManagerPage {
this.applyStepFailure(".mod");
return Promise.reject(err);
}
).then(() => installJSON(this.instance.folder)).then(
).then(() => installJSON(this.manager.getActiveInstance().folder)).then(
() => {
this.applyStepSuccess(".json");
},
@@ -63,7 +59,7 @@ class ResultPage extends ManagerPage {
this.applyStepFailure(".json");
return Promise.reject(err);
}
).then(() => applyConfiguration(this.instance.folder, this.instance)).then(
).then(() => applyConfiguration(this.manager.getActiveInstance().folder, this.manager.getActiveInstance())).then(
() => {
this.applyStepSuccess(".config");
},
@@ -71,7 +67,7 @@ class ResultPage extends ManagerPage {
this.applyStepFailure(".config");
return Promise.reject(err);
}
).then(() => installShortCuts(this.instance.folder, this.instance.name)).then(
).then(() => installShortCuts(this.manager.getActiveInstance().folder, this.manager.getActiveInstance().name)).then(
() => {
this.applyStepSuccess(".shortcuts");
},

View File

@@ -0,0 +1,49 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
const { logger } = require("./filesystem")
/** Type of install page, allows the user to select single player or multi player installation
*
*/
class TypePage extends ManagerPage {
constructor(manager, options) {
super(manager, options);
}
render(str) {
const element = this.getElement();
element.innerHTML = str;
this.element.querySelector(".singleplayer").addEventListener("click", (e) => this.onOptionSelected(false));
this.element.querySelector(".multiplayer").addEventListener("click", (e) => this.onOptionSelected(true));
super.render();
}
show(previousPage) {
ejs.renderFile("./ejs/type.ejs", {...this.options, ...this.manager.options}, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
logger.error(err);
}
});
super.show(previousPage);
}
onCancelClicked() {
this.hide();
this.manager.menuPage.show()
}
onOptionSelected(multiplayer) {
this.manager.options.multiplayer = multiplayer;
this.hide();
this.manager.connectionsPage.options.selectAutoOrManual = true;
this.manager.connectionsPage.show(this);
}
}
module.exports = TypePage;

View File

@@ -0,0 +1,52 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
const { logger } = require("./filesystem")
const fs = require("fs");
const { showErrorPopup } = require("./popup");
class WelcomePage extends ManagerPage {
constructor(manager, options) {
super(manager, options);
}
render(str) {
const element = this.getElement();
element.innerHTML = str;
element.querySelector(".basic").addEventListener("click", (e) => this.onbasicClicked(e));
element.querySelector(".advanced").addEventListener("click", (e) => this.onAdvancedClicked(e));
super.render();
}
show(previousPage) {
ejs.renderFile("./ejs/welcome.ejs", {...this.options, ...this.manager.options}, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
logger.error(err);
}
});
super.show(previousPage);
}
onbasicClicked(e) {
this.createOptionsFile("basic");
}
onAdvancedClicked(e) {
this.createOptionsFile("advanced");
}
createOptionsFile(mode) {
try {
fs.writeFileSync("options.json", JSON.stringify({mode: mode}));
location.reload();
} catch (e) {
showErrorPopup(`A critical error occurred, check ${this.manager.options.logLocation} for more info.`)
}
}
}
module.exports = WelcomePage;