Merge branch 'manager-wizard' of https://github.com/Pax1601/DCSOlympus into manager-wizard

This commit is contained in:
Pax1601 2024-01-17 08:08:33 +01:00
commit 4a5c4ed7d7
29 changed files with 1343 additions and 668 deletions

View File

@ -105,6 +105,10 @@ declare module "constants/constants" {
export const ROEs: string[];
export const reactionsToThreat: string[];
export const emissionsCountermeasures: string[];
export const ERAS: {
name: string;
chronologicalOrder: number;
}[];
export const ROEDescriptions: string[];
export const reactionsToThreatDescriptions: string[];
export const emissionsCountermeasuresDescriptions: string[];
@ -836,6 +840,7 @@ declare module "other/utils" {
}): UnitBlueprint | null;
export function getMarkerCategoryByName(name: string): "aircraft" | "helicopter" | "groundunit-sam" | "navyunit" | "groundunit-other";
export function getUnitDatabaseByCategory(category: string): import("unit/databases/aircraftdatabase").AircraftDatabase | import("unit/databases/helicopterdatabase").HelicopterDatabase | import("unit/databases/groundunitdatabase").GroundUnitDatabase | import("unit/databases/navyunitdatabase").NavyUnitDatabase | null;
export function getCategoryBlueprintIconSVG(category: string, unitName: string): string | false;
export function base64ToBytes(base64: string): ArrayBufferLike;
export function enumToState(state: number): string;
export function enumToROE(ROE: number): string;
@ -1600,6 +1605,7 @@ declare module "map/map" {
import { CoalitionAreaContextMenu } from "contextmenus/coalitionareacontextmenu";
import { AirbaseSpawnContextMenu } from "contextmenus/airbasespawnmenu";
export type MapMarkerVisibilityControl = {
"category"?: string;
"image": string;
"isProtected"?: boolean;
"name": string;
@ -1997,6 +2003,25 @@ declare module "unit/importexport/unitdatafileexport" {
showForm(units: Unit[]): void;
}
}
declare module "schemas/schema" {
import Ajv from "ajv";
import { AnySchemaObject } from "ajv/dist/core";
abstract class JSONSchemaValidator {
#private;
constructor(schema: AnySchemaObject);
getAjv(): Ajv;
getCompiledValidator(): any;
getErrors(): any;
getSchema(): AnySchemaObject;
validate(data: any): any;
}
export class AirbasesJSONSchemaValidator extends JSONSchemaValidator {
constructor();
}
export class ImportFileJSONSchemaValidator extends JSONSchemaValidator {
constructor();
}
}
declare module "unit/importexport/unitdatafileimport" {
import { Dialog } from "dialog/dialog";
import { UnitDataFile } from "unit/importexport/unitdatafile";

View File

@ -60,6 +60,11 @@
padding: 0;
}
.leaflet-container img.leaflet-tile {
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
mix-blend-mode: plus-lighter;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
@ -646,7 +651,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
}
/* Printing */
@media print {
/* Prevent printers from removing background-images of controls. */
.leaflet-control {

97
manager/1.js Normal file
View File

@ -0,0 +1,97 @@
/* 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.basic = 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(this, {
installEnabled: installEnabled,
manageEnabled: manageEnabled
});
/* Installations */
this.installationPage = new installationPage(this, {
instances: instances
});
/* Instances */
this.instancesPage = new InstancesPage(this, {
instances: instances.filter((instance) => {
return instance.installed;
})
});
/* Connections */
this.connectionsPage = new ConnectionsPage(this);
/* Passwords */
this.passwordsPage = new PasswordsPage(this);
/* Result */
this.resultPage = new ResultPage(this, {
logLocation: path.join(__dirname, "..", "manager.log")
});
/* Create all the HTML pages */
document.body.appendChild(this.menuPage.getElement());
document.body.appendChild(this.installationPage.getElement());
document.body.appendChild(this.instancesPage.getElement());
document.body.appendChild(this.connectionsPage.getElement());
document.body.appendChild(this.passwordsPage.getElement());
document.body.appendChild(this.resultPage.getElement());
/* In basic mode we directly show the connections page */
if (this.basic) {
const options = {
instance: instances[0],
basic: this.basic,
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();
}
}

View File

@ -1,4 +1,33 @@
<style>
#manager-connections .option {
background-color: var(--background);
border: 1px solid var(--offwhite);
width: 220px;
height: 60px;
color: var(--offwhite);
display: flex;
font-size: 18px;
font-weight: 600;
padding-left: 15px;
align-items: center;
border-radius: 5px;
cursor: pointer;
background-color: transparent;
color: var(--offwhite);
justify-content: center;
}
#manager-connections .option:hover {
color: var(--background);
background-color: var(--offwhite);
}
#manager-connections .buttons {
display: flex;
flex-direction: column;
row-gap: 15px;
}
#manager-connections .success,
#manager-connections .error {
position: absolute;
@ -29,65 +58,85 @@
</style>
<div id="manager-connections">
<div class="step-summary">
<div class="blue <%= !install || simplified? 'hide': '' %>">User path</div>
<div class="blue <%= singleInstance? 'hide': '' %>"><%= install? 'User path': 'Instance selection' %></div>
<div class="blue">Type of install</div>
<div class="white">Ports and address</div>
<div class="empty">Passwords</div>
<div class="empty"> <%= install? 'Install': 'Update' %></div>
</div>
<div class="content">
<div class="instructions">
<span>
Accept or modify port settings (optional)
</span>
<span>
If you are installing Olympus locally for Single player use, it's recommended you leave these as default and continue.
If you are installing a dedicated server, then follow the instructions available on the DCS Olympus Wiki.
</span>
</div>
<div class="input-group client-port">
<span>Client port
<img src="./icons/circle-info-solid.svg" title="This port is used to allow access to Olympus. Be sure to allow this port through your firewall if you want people to connect remotely">
</span>
<div>
<input type="number" min="1024" max="65535" value="<%= instance["clientPort"] %>">
<img class="success hide">
<div class="error hide">
<img> <span>Port already in use</span>
<% if (selectAutoOrManual) { %>
<div class="instructions">
<span>
Do you want to set port and address settings manually?
</span>
<span>
We can auto setup ports and addresses for you, or you can set the manually. <br>
If you don't have an understanding of how Olympus works, we recommend the auto option.
</span>
</div>
<div class="buttons">
<div class="option button auto">
Auto apply settings
</div>
<div class="option button manual">
Manually set options
</div>
</div>
</div>
<div class="input-group backend-port">
<span>Backend port
<img src="./icons/circle-info-solid.svg" title="This port is used to allow access to Olympus. Be sure to allow this port through your firewall if you want people to connect remotely.">
</span>
<div>
<input type="number" min="1024" max="65535" value="<%= instance["backendPort"] %>">
<img class="success hide">
<div class="error hide">
<img> <span>Port already in use</span>
<% } else { %>
<div class="instructions">
<span>
Enter the ports and address to use.
</span>
<span>
Select client and backend ports, making sure they are free to use with the provided check. <br>
Unless you want to support direct API calls to the backend, you can keep the address to localhost even for dedicated servers.
</span>
</div>
<div class="input-group client-port">
<span>Client port
<img src="./icons/circle-info-solid.svg" title="This port is used to allow access to Olympus. Be sure to allow this port through your firewall if you want people to connect remotely">
</span>
<div>
<input type="number" min="1024" max="65535" value="<%= activeInstance["clientPort"] %>">
<img class="success hide">
<div class="error hide">
<img> <span>Port already in use</span>
</div>
</div>
</div>
</div>
<div class="input-group backend-address">
<span>Backend address
<img src="./icons/circle-info-solid.svg" title="This is the backend address Olympus will listen on. Unless you know what you are doing, leave it as localhost, even for dedicated server installations.">
</span>
<input type="text" value="<%= instance["backendAddress"] %>">
</div>
<div class="input-group backend-port">
<span>Backend port
<img src="./icons/circle-info-solid.svg" title="This port is used to allow access to Olympus. Be sure to allow this port through your firewall if you want people to connect remotely.">
</span>
<div>
<input type="number" min="1024" max="65535" value="<%= activeInstance["backendPort"] %>">
<img class="success hide">
<div class="error hide">
<img> <span>Port already in use</span>
</div>
</div>
</div>
<div class="input-group backend-address">
<span>Backend address
<img src="./icons/circle-info-solid.svg" title="This is the backend address Olympus will listen on. Unless you know what you are doing, leave it as localhost, even for dedicated server installations.">
</span>
<input type="text" value="<%= activeInstance["backendAddress"] %>">
</div>
<% } %>
<div class="buttons-footer">
<% if (!simplified) { %>
<div class="button back">
Back
</div>
<% } %>
<div class="button next">
Next
</div>
</div>
<% if (!simplified) { %>
<div class="button cancel">
<%= install? "Cancel installation": "Cancel editing" %>
</div>
<% } %>
</div>
</div>

View File

@ -88,27 +88,41 @@
</style>
<div id="manager-installations">
<div class="step-summary">
<div class="white">User path</div>
<div class="white <%= singleInstance? 'hide': '' %>"><%= install? 'User path': 'Instance selection' %></div>
<div class="blue">Type of install</div>
<div class="empty">Ports and address</div>
<div class="empty">Passwords</div>
<div class="empty">Install</div>
<div class="empty"> <%= install? 'Install': 'Update' %></div>
</div>
<div class="content">
<div class="instructions">
<span>
Select a DCS path to install Olympus to.
<% if (install) { %>
Select a DCS path to install Olympus to.
<% } else { %>
Select an Olympus instance to edit.
<% } %>
</span>
<span>
We have automatically detected the following DCS installations under your Saved Games / DCS folder.
<% if (install) { %>
We have automatically detected the following DCS installations under your Saved Games / DCS folder.
<% } else { %>
These are the DCS instances in which Olympus has already been installed.
<% } %>
</span>
<span>
Please select which DCS installations you want to add Olympus to.
<% if (install) { %>
Please select which DCS installations you want to add Olympus to.
<% } else { %>
Please select which Olympus installations you want to edit.
<% } %>
</span>
</div>
<div class="scroll-container">
<div class="scrollable">
<% for (let i = 0; i < instances.length; i++) {%>
<div class="option <%= instances[i].installed? 'installed': '' %>" data-folder="<%= instances[i].folder %>">
<div class="option" data-folder="<%= instances[i].folder %>">
<span><%= instances[i].name %></span>
<span><img src="./icons/folder-open-solid.svg"> <%= instances[i].folder %></span>
<span class="<%= instances[i].installed? (instances[i].error? 'error': 'installed'): '' %>">
@ -118,9 +132,12 @@
<% } %>
</div>
</div>
<div class="button cancel">
Cancel installation
<% if (install) { %>
Cancel installation
<% } else { %>
Cancel editing
<% } %>
</div>
</div>
</div>

View File

@ -36,6 +36,18 @@
row-gap: 25px;
}
#manager-instances .option:not(.installed) {
background-color: var(--background-disabled);
}
#manager-instances .option:not(.installed) .info {
opacity: 50%;
}
#manager-instances .option:not(.installed) .server-data {
opacity: 50%;
}
#manager-instances>.instructions {
margin-bottom: 10px;
}
@ -192,6 +204,7 @@
}
#manager-instances .edit,
#manager-instances .install,
#manager-instances .uninstall,
#manager-instances .stop {
color: var(--offwhite);
@ -200,34 +213,36 @@
}
#manager-instances .edit:hover,
#manager-instances .install:hover,
#manager-instances .uninstall:hover,
#manager-instances .stop:hover {
color: var(--background);
background-color: var(--offwhite);
}
#manager-instances .install {
margin-left: auto;
}
</style>
<div id="manager-instances">
<div class="content">
<div class="button cancel">
<img src="./icons/chevron-left-solid.svg"/> Return to menu
</div>
<div class="instructions">
<span>
View and manage installs
</span>
<span>
The following Oympus installs have been identified. <br>You can start an Olympus server, modify settings and uninstall below.
The following DCS installations have been identified. <br>You can start an Olympus server, modify settings and uninstall below.
</span>
</div>
<div class="scroll-container">
<div class="scrollable">
<% for (let i = 0; i < instances.length; i++) {%>
<div class="option" data-folder="<%= instances[i].folder %>">
<div class="option <%= instances[i].installed? 'installed': '' %>" data-folder="<%= instances[i].folder %>">
<div class="instance-info">
<span><%= instances[i].name %></span>
<span class="<%= instances[i].installed? (instances[i].error? 'error': ''): '' %>">
<%= instances[i].installed? (instances[i].error? 'Corrupted/outdated Olympus installation': ''): '' %>
<span class="<%= instances[i].installed? (instances[i].error? 'error': 'installed'): '' %>">
<%= instances[i].installed? (instances[i].error? 'Corrupted/outdated Olympus installation': 'Olympus installed'): 'Olympus not installed' %>
</span>
<span><img src="./icons/folder-open-solid.svg"> <%= instances[i].folder %></span>
<div class="server-data">
@ -242,15 +257,15 @@
<div class="divider"></div>
<div class="info">
<div>Client port</div>
<div> <%= instances[i].clientPort %> </div>
<div> <%= instances[i].installed? instances[i].clientPort: "N/A" %> </div>
</div>
<div class="info">
<div>Backend port</div>
<div> <%= instances[i].backendPort %> </div>
<div> <%= instances[i].installed? instances[i].backendPort: "N/A" %> </div>
</div>
<div class="info">
<div>Backend address</div>
<div> <%= instances[i].backendAddress %> </div>
<div> <%= instances[i].installed? instances[i].backendAddress: "N/A" %> </div>
</div>
</div>
<div class="instance-buttons">
@ -263,6 +278,7 @@
</div>
<div class="button edit">Edit settings</div>
<div class="button install">Install Olympus</div>
<div class="button uninstall">Uninstall Olympus</div>
<div class="button open-browser hide">Open in browser</div>
<div class="button stop hide">Stop Olympus</div>

View File

@ -1,62 +1,55 @@
<style>
#summary {
width: 70%;
#manager-menu {
display: flex;
flex-direction: column;
flex-direction: row;
height: 100%;
justify-content: center;
color: var(--offwhite);
padding: 80px;
min-height: 100%;
align-items: center;
}
#summary .icon {
height: 100px;
width: 100px;
margin-bottom: -30px;
}
#summary div:nth-child(1) {
font-size: 50px;
font-weight: bold;
}
#summary div:nth-child(2) {
font-size: 20px;
font-weight: bold;
}
#summary div:nth-child(3) {
color: var(--lightgray);
font-size: 13px;
font-weight: normal;
margin-top: 20px;
width: 300px;
}
#manager-menu #menu {
#manager-menu>div {
display: flex;
flex-direction: column;
row-gap: 20px;
width: 60%;
justify-content: center;
align-items: center;
}
#manager-menu .option {
background-color: var(--background);
border: 1px solid var(--offwhite);
width: 460px;
height: 70px;
height: 110px;
color: var(--offwhite);
display: flex;
font-size: 18px;
font-weight: 600;
padding-left: 15px;
align-items: center;
align-items: start;
border-radius: 5px;
cursor: pointer;
background-color: transparent;
color: var(--offwhite);
display: flex;
flex-direction: column;
justify-content: center;
row-gap: 15px;
position: relative;
}
#manager-menu .option>div {
font-size: 14px;
font-weight: normal;
}
#manager-menu .option::after {
position: absolute;
display: block;
content: " ";
width: 20px;
height: 20px;
background-image: url("./icons/chevron-right-solid.svg");
background-repeat: no-repeat;
background-position: 50% 50%;
right: 15px;
}
#manager-menu .option:hover {
@ -73,20 +66,31 @@
#manager-menu .option * {
pointer-events: none;
}
</style>
<div id="manager-menu">
<div id="summary">
<div>DCS OLYMPUS <img class="icon" src="../img/OlympusLogoFinal_4k.png" \></div>
<div>INSTALL WIZARD AND MANAGER</div>
<div>Using this manager, you can install Olympus, update settings, and view and manage instances</div>
</div>
<div id="menu">
<div class="option install <%= installEnabled? '': 'disabled' %>">
Install Olympus
<div id="summary" style="width: 70%; height: 100%; color: var(--offwhite); padding: 80px;">
<div style="font-size: 50px; font-weight: bold;">
DCS OLYMPUS <img src="../img/OlympusLogoFinal_4k.png" style="height: 100px; width: 100px; margin-bottom: -30px;"\>
</div>
<div class="option manage <%= manageEnabled? '': 'disabled' %>">
View / Manage installs
<div style="font-size: 20px; font-weight: bold;">
INSTALL WIZARD AND MANAGER
</div>
<div style="color: var(--lightgray); font-size: 13px; font-weight: normal; margin-top: 20px; width: 300px;">
Using this manager, you can install Olympus, update settings, and view and manage instances
</div>
</div>
<div id="menu" style="row-gap: 20px; width: 60%;">
<div class="option <%= installEnabled? '': 'disabled' %>" onclick="signal('onInstallClicked')">
Add Olympus
<div>
Add or update Olympus to a new DCS instance
</div>
</div>
<div class="option <%= editEnabled? '': 'disabled' %>" onclick="signal('onEditClicked')">
Change settings
<div>
Adjust port, address and password settings
</div>
</div>
</div>
</div>

View File

@ -3,7 +3,8 @@
</style>
<div id="manager-passwords">
<div class="step-summary">
<div class="blue <%= !install || simplified? 'hide': '' %>">User path</div>
<div class="blue <%= singleInstance? 'hide': '' %>"><%= install? 'User path': 'Instance selection' %></div>
<div class="blue">Type of install</div>
<div class="blue">Ports and address</div>
<div class="white">Passwords</div>
<div class="empty"> <%= install? 'Install': 'Update' %></div>
@ -41,7 +42,7 @@
Next
</div>
</div>
<% if (!simplified) { %>
<% if (!singleInstance) { %>
<div class="button cancel">
<%= install? "Cancel installation": "Cancel editing" %>
</div>

View File

@ -129,10 +129,10 @@
<img class="wait"><img class="success hide"><img class="error hide">
<div>
<span>
<%= instance.name %>
<%= activeInstance.name %>
</span>
<span><img src="./icons/folder-open-solid.svg">
<%= instance.folder %>
<%= activeInstance.folder %>
</span>
</div>
</div>

23
manager/ejs/type.ejs Normal file
View File

@ -0,0 +1,23 @@
<style>
</style>
<div class="instructions">
<div class="step">
Step 1 of 4
</div>
<div class="title">
Do you want to add Olympus for singleplayer or multiplayer?
</div>
<div class="description">
Select singleplayer if you only want to play locally on your own computer. <br>
Select multiplayer if you want Olympus to be useable over the internet from a different computer, or this instance is a dedicated server.
</div>
</div>
<div class="buttons">
<div class="button">
Singleplayer
</div>
<div class="button">
Multiplayer
</div>
</div>

67
manager/ejs/welcome.ejs Normal file
View File

@ -0,0 +1,67 @@
<style>
#manager-welcome {
display: flex;
flex-direction: column;
row-gap: 20px;
width: 100%;
justify-content: center;
align-items: center;
}
#manager-welcome .instructions {
width: 40%;
text-align: center;
color: var(--offwhite);
}
#manager-welcome .option {
background-color: var(--background);
border: 1px solid var(--offwhite);
width: 460px;
height: 70px;
color: var(--offwhite);
display: flex;
font-size: 18px;
font-weight: 600;
padding-left: 15px;
align-items: center;
border-radius: 5px;
cursor: pointer;
background-color: transparent;
color: var(--offwhite);
}
#manager-welcome .option:hover {
color: var(--background);
background-color: var(--offwhite);
}
#manager-welcome .option.disabled {
pointer-events: none;
color: var(--darkgray);
border-color: var(--darkgray);
}
#manager-welcome .option * {
pointer-events: none;
}
</style>
<div id="manager-welcome">
<div class="instructions" style="font-size: 28px; font-weight: bold;">
Do you want to use the Olympus Manager in basic or Expert mode?
</div>
<div class="instructions" style="color: var(--gray);">
Basic mode is recommended for most users. <br>
Expert mode is for those who know how Olympus works or for server owners.
</div>
<div class="instructions" style="color: var(--gray); font-weight: bold;">
You can change this setting at any time.
</div>
<div class="option basic" onclick="signal('onBasicClicked')">
Basic mode
</div>
<div class="option expert" onclick="signal('onExpertClicked')">
Expert mode
</div>
</div>

53
manager/ejs/wizard.ejs Normal file
View File

@ -0,0 +1,53 @@
<style>
.wizard-page {
display: flex;
flex-direction: column;
row-gap: 30px;
padding: 60px 120px;
}
.buttons-footer {
display: flex;
column-gap: 10px;
justify-content: start;
}
.instructions {
color: var(--offwhite);
}
.instructions .step {
}
.instructions .title {
font-size: 24px;
font-weight: bold;
}
.content {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
row-gap: 20px;
align-items: start;
justify-content: center;
}
</style>
<div class="wizard-page">
<div class="cancel" style="font-size: 14px; font-weight: 600; color: var(--offwhite); display: flex; align-items: center; column-gap: 10px;">
<img src="./icons/chevron-left-solid.svg" style="height: 14px;">Cancel install
</div>
<div class="content">
</div>
<div class="buttons-footer">
<div class="button back" style="color: var(--offwhite); background-color: var(--background); border: 1px solid var(--offwhite);">
Back
</div>
<div class="button next" style="color: var(--background); background-color: var(--offwhite);">
Next
</div>
</div>
</div>

View File

@ -20,18 +20,20 @@
<button class="title-bar-button maximize"></button>
<button class="title-bar-button close"></button>
</div>
<div id="header">
<div id="header" class="hide">
<img class="main-icon" src="../img/OlympusLogoFinal_4k.png" \>
<div class="version">
<div> DCS Olympus Manager</div>
<div class="accent-green">{{OLYMPUS_VERSION_NUMBER}}</div>
</div>
<div class="link first" data-link="https://github.com/Pax1601/DCSOlympus/wiki/2.-User-Guide">User Guide</div>
<div class="link" data-link="https://github.com/Pax1601/DCSOlympus/wiki/Setup-Troubleshooting">Troubleshooting Guide</div>
<div class="link" data-link="https://github.com/Pax1601/DCSOlympus/wiki/Setup-Troubleshooting">Troubleshooting
Guide</div>
<div id="switch-mode" class="link"> </div>
<div style="width: 15px;"></div>
<img class="link" data-link="https://github.com/Pax1601/DCSOlympus" src="./icons/github.svg"/>
<img class="link" data-link="https://discord.gg/pCfCykAdrw" src="./icons/discord.svg"/>
<img class="link" data-link="https://www.youtube.com/@DCSOlympus" src="./icons/youtube.svg"/>
<img class="link" data-link="https://github.com/Pax1601/DCSOlympus" src="./icons/github.svg" />
<img class="link" data-link="https://discord.gg/pCfCykAdrw" src="./icons/discord.svg" />
<img class="link" data-link="https://www.youtube.com/@DCSOlympus" src="./icons/youtube.svg" />
</div>
<div id="loader" class="manager-page hide">
Loading, please wait...
@ -42,7 +44,7 @@
<img src="./icons/circle-question-regular.svg" class="confirm">
<img src="./icons/spinner-solid.svg" class="wait">
<div class="content">
</div>
<div class="footer">
<div class="button accept-popup"> Accept </div>
@ -77,6 +79,11 @@
document.querySelector('.restore').classList.add("hide");
document.querySelector('.maximize').classList.remove("hide");
})
function signal(callback, params) {
const event = new CustomEvent("signal", { detail: { callback: callback, params: params } });
document.dispatchEvent(event);
}
</script>
</html>

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,33 +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)
}
}
}
}
getManager().instancesPage.update();
}, 1000);
}
@ -367,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);
class installationPage extends ManagerPage {
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,8 +36,32 @@ 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();
}
}
module.exports = InstallationsPage;
module.exports = installationPage;

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,24 +50,16 @@ 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());
}
async onStartClientClicked(e) {
e.target.closest(".collapse").classList.add("loading");
this.getClickedInstance(e).then(instance => instance.startClient());
}
@ -76,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();
@ -90,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 {
@ -99,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

@ -1,264 +1,197 @@
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 path = require("path")
const fs = require("fs");
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 ManagerPage = require("./managerpage");
const WizardPage = require("./wizardpage");
class Manager {
simplified = true;
options = {
logLocation: path.join(__dirname, "..", "manager.log"),
configLoaded: false
};
welcomePage = null;
folderPage = null;
typePage = null;
connectionsPage = null;
passwordsPage = null;
resultPage = null;
instancesPage = null;
constructor() {
}
document.addEventListener("signal", (ev) => {
const callback = ev.detail.callback;
const params = ev.detail.params;
try {
eval(`this.${callback}(${params})`)
} catch (e) {
console.error(e);
}
});
}
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
/* Check if the options file exists */
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 expert mode */
this.welcomePage = new ManagerPage(this, "./ejs/welcome.ejs");
this.welcomePage.show();
}
else {
document.getElementById("header").classList.remove("hide");
/* Initialize mode switching */
if (this.options.mode === "basic") {
document.getElementById("switch-mode").innerText = "Expert mode";
document.getElementById("switch-mode").onclick = () => { this.switchMode("expert"); }
}
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 ManagerPage(this, "./ejs/menu.ejs");
this.folderPage = new WizardPage(this, "./ejs/installation.ejs");
this.typePage = new WizardPage(this, "./ejs/type.ejs");
//this.connectionsPage = new ConnectionsPage(this);
//this.passwordsPage = new PasswordsPage(this);
//this.resultPage = new ResultPage(this);
//this.instancesPage = new InstancesPage(this);
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 Expert 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")}`)
}
}
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();
}
}
getActiveInstance() {
return this.options.activeInstance;
}
createOptionsFile(mode) {
try {
fs.writeFileSync("options.json", JSON.stringify({mode: mode}));
location.reload();
} catch (e) {
showErrorPopup(`A critical error occurred, check ${this.options.logLocation} for more info.`)
}
}
switchMode(newMode) {
/* Change the mode in the options.json and reload the page */
var options = JSON.parse(fs.readFileSync("options.json"));
options.mode = newMode;
fs.writeFileSync("options.json", JSON.stringify(options));
location.reload();
}
/************************************************/
/* CALLBACKS */
/************************************************/
/* Switch to basic mode */
onBasicClicked() {
this.createOptionsFile("basic");
}
/* Switch to expert mode */
onExpertClicked() {
this.createOptionsFile("expert");
}
/* When the install button is clicked go the installation page */
onInstallClicked() {
this.options.install = true;
if (this.options.singleInstance) {
this.options.activeInstance = this.options.instances[0];
/* Show the type selection page */
if (!this.options.activeInstance.installed) {
this.menuPage.hide();
this.typePage.show(this.menuPage);
} 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.menuPage.hide();
this.typePage.show(this.menuPage);
}
)
}
} else {
/* Show the folder selection page */
this.menuPage.hide();
this.folderPage.show(this.menuPage);
}
}
/* When the edit button is clicked go to the instances page */
onEditClicked() {
this.hide();
this.options.install = false;
if (this.options.singleInstance) {
this.options.activeInstance = this.options.instances[0];
this.typePage.show(this);
} else {
this.folderPage.show(this);
}
}
}
module.exports = Manager;

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,33 +1,54 @@
class ManagerPage {
element;
options;
const { logger } = require("./filesystem");
const ejs = require('ejs')
constructor(options) {
this.options = options ?? {};
class ManagerPage {
manager;
ejsFile;
element;
options = {};
previousPage;
constructor(manager, ejsFile) {
this.manager = manager;
this.element = document.createElement('div');
this.element.classList.add("manager-page", "hide");
this.ejsFile = ejsFile;
document.body.appendChild(this.element);
}
getElement() {
return this.element;
}
show() {
show(previousPage) {
ejs.renderFile(this.ejsFile, {...this.options, ...this.manager.options}, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
logger.error(err);
}
});
this.element.classList.remove("hide");
if (previousPage !== undefined)
this.previousPage = previousPage;
}
hide() {
this.element.classList.add("hide");
}
render() {
render(str) {
this.element.innerHTML = str;
/* Connect all the collapsable buttons */
let buttons = document.querySelectorAll(".button.collapse");
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", () => {
buttons[i].classList.toggle("open");
})
}
}
}
}

View File

@ -1,39 +0,0 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
const { logger } = require("./filesystem")
class MenuPage extends ManagerPage {
onInstallClicked;
onUpdateClicked;
onManageClicked;
constructor(options) {
super(options);
}
render(str) {
const element = this.getElement();
element.innerHTML = str;
element.querySelector(".install").addEventListener("click", (e) => this.onInstallClicked(e));
element.querySelector(".manage").addEventListener("click", (e) => this.onManageClicked(e));
super.render();
}
show() {
this.instance = this.options.instance;
ejs.renderFile("./ejs/menu.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
logger.error(err);
}
});
super.show();
}
}
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,12 @@ 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 +269,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 +295,4 @@ 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,53 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class WizardPage extends ManagerPage {
contentEjsFile;
constructor(manager, contentEjsFile) {
super(manager, './ejs/wizard.ejs');
this.contentEjsFile = contentEjsFile;
}
render(str) {
super.render(str);
/* 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));
ejs.renderFile(this.contentEjsFile, {...this.options, ...this.manager.options}, {}, (err, str) => {
if (!err) {
this.element.querySelector(".content").innerHTML = str;
} else {
logger.error(err);
}
});
}
onBackClicked() {
console.log(this.previousPage)
this.hide();
this.previousPage.show()
}
onCancelClicked() {
this.hide();
if (this.manager.options.mode === "basic") {
/* In basic mode no dashboard is shown */
this.manager.menuPage.show();
} else {
/* In Expert mode we go directly to the dashboard */
this.manager.instancesPage.show();
}
}
}
module.exports = WizardPage;

317
manager/manager.log Normal file
View File

@ -0,0 +1,317 @@
======================= New log starting at Thu Jan 11 2024 16:07:31 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
Development build detected, skipping version checks...
======================= New log starting at Thu Jan 11 2024 16:11:52 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:11:54 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:11:56 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:13:03 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:13:31 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:13:41 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:13:48 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:23:11 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
ReferenceError: ./ejs/connections.ejs:32
30| <div id="manager-connections">
31| <div class="step-summary">
>> 32| <div class="blue <%= !install || basic? 'hide': '' %>">User path</div>
33| <div class="white">Ports and address</div>
34| <div class="empty">Passwords</div>
35| <div class="empty"> <%= install? 'Install': 'Update' %></div>
basic is not defined
at eval ("./ejs/connections.ejs":12:38)
at connections (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at ConnectionsPage.show (D:\Documents\DCSOlympus\manager\javascripts\connections.js:41:13)
at Manager.start (D:\Documents\DCSOlympus\manager\javascripts\manager.js:70:42)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async D:\Documents\DCSOlympus\manager\javascripts\preload.js:268:5 {
path: './ejs/connections.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:24:03 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
ReferenceError: ./ejs/connections.ejs:32
30| <div id="manager-connections">
31| <div class="step-summary">
>> 32| <div class="blue <%= !install || basic? 'hide': '' %>">User path</div>
33| <div class="white">Ports and address</div>
34| <div class="empty">Passwords</div>
35| <div class="empty"> <%= install? 'Install': 'Update' %></div>
basic is not defined
at eval ("./ejs/connections.ejs":12:38)
at connections (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at ConnectionsPage.show (D:\Documents\DCSOlympus\manager\javascripts\connections.js:41:13)
at Manager.start (D:\Documents\DCSOlympus\manager\javascripts\manager.js:70:42)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async D:\Documents\DCSOlympus\manager\javascripts\preload.js:268:5 {
path: './ejs/connections.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:24:25 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
ReferenceError: ./ejs/connections.ejs:32
30| <div id="manager-connections">
31| <div class="step-summary">
>> 32| <div class="blue <%= !install || basic? 'hide': '' %>">User path</div>
33| <div class="white">Ports and address</div>
34| <div class="empty">Passwords</div>
35| <div class="empty"> <%= install? 'Install': 'Update' %></div>
basic is not defined
at eval ("./ejs/connections.ejs":12:38)
at connections (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at ConnectionsPage.show (D:\Documents\DCSOlympus\manager\javascripts\connections.js:41:13)
at Manager.start (D:\Documents\DCSOlympus\manager\javascripts\manager.js:71:42)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async D:\Documents\DCSOlympus\manager\javascripts\preload.js:268:5 {
path: './ejs/connections.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:24:45 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
ReferenceError: ./ejs/connections.ejs:32
30| <div id="manager-connections">
31| <div class="step-summary">
>> 32| <div class="blue <%= !install || basic? 'hide': '' %>">User path</div>
33| <div class="white">Ports and address</div>
34| <div class="empty">Passwords</div>
35| <div class="empty"> <%= install? 'Install': 'Update' %></div>
basic is not defined
at eval ("./ejs/connections.ejs":12:38)
at connections (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at ConnectionsPage.show (D:\Documents\DCSOlympus\manager\javascripts\connections.js:41:13)
at Manager.start (D:\Documents\DCSOlympus\manager\javascripts\manager.js:71:42)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async D:\Documents\DCSOlympus\manager\javascripts\preload.js:268:5 {
path: './ejs/connections.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:25:20 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
ReferenceError: ./ejs/connections.ejs:32
30| <div id="manager-connections">
31| <div class="step-summary">
>> 32| <div class="blue <%= !install || basic? 'hide': '' %>">User path</div>
33| <div class="white">Ports and address</div>
34| <div class="empty">Passwords</div>
35| <div class="empty"> <%= install? 'Install': 'Update' %></div>
basic is not defined
at eval ("./ejs/connections.ejs":12:38)
at connections (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at ConnectionsPage.show (D:\Documents\DCSOlympus\manager\javascripts\connections.js:41:13)
at Manager.start (D:\Documents\DCSOlympus\manager\javascripts\manager.js:78:42)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async D:\Documents\DCSOlympus\manager\javascripts\preload.js:268:5 {
path: './ejs/connections.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:28:55 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
ReferenceError: ./ejs/connections.ejs:52
50| </span>
51| <div>
>> 52| <input type="number" min="1024" max="65535" value="<%= instance["clientPort"] %>">
53| <img class="success hide">
54| <div class="error hide">
55| <img> <span>Port already in use</span>
instance is not defined
at eval ("./ejs/connections.ejs":18:26)
at connections (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at ConnectionsPage.show (D:\Documents\DCSOlympus\manager\javascripts\connections.js:41:13)
at Manager.start (D:\Documents\DCSOlympus\manager\javascripts\manager.js:78:42)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async D:\Documents\DCSOlympus\manager\javascripts\preload.js:268:5 {
path: './ejs/connections.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:29:02 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
ReferenceError: ./ejs/connections.ejs:52
50| </span>
51| <div>
>> 52| <input type="number" min="1024" max="65535" value="<%= instance["clientPort"] %>">
53| <img class="success hide">
54| <div class="error hide">
55| <img> <span>Port already in use</span>
instance is not defined
at eval ("./ejs/connections.ejs":18:26)
at connections (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at ConnectionsPage.show (D:\Documents\DCSOlympus\manager\javascripts\connections.js:41:13)
at Manager.start (D:\Documents\DCSOlympus\manager\javascripts\manager.js:78:42)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async D:\Documents\DCSOlympus\manager\javascripts\preload.js:268:5 {
path: './ejs/connections.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:31:06 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
ReferenceError: ./ejs/connections.ejs:52
50| </span>
51| <div>
>> 52| <input type="number" min="1024" max="65535" value="<%= instance["clientPort"] %>">
53| <img class="success hide">
54| <div class="error hide">
55| <img> <span>Port already in use</span>
instance is not defined
at eval ("./ejs/connections.ejs":18:26)
at connections (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at ConnectionsPage.show (D:\Documents\DCSOlympus\manager\javascripts\connections.js:41:13)
at Manager.start (D:\Documents\DCSOlympus\manager\javascripts\manager.js:77:42)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async D:\Documents\DCSOlympus\manager\javascripts\preload.js:268:5 {
path: './ejs/connections.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:37:31 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
ReferenceError: ./ejs/connections.ejs:52
50| </span>
51| <div>
>> 52| <input type="number" min="1024" max="65535" value="<%= instance["clientPort"] %>">
53| <img class="success hide">
54| <div class="error hide">
55| <img> <span>Port already in use</span>
instance is not defined
at eval ("./ejs/connections.ejs":18:26)
at connections (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at ConnectionsPage.show (D:\Documents\DCSOlympus\manager\javascripts\connections.js:38:13)
at Manager.start (D:\Documents\DCSOlympus\manager\javascripts\manager.js:77:42)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async D:\Documents\DCSOlympus\manager\javascripts\preload.js:268:5 {
path: './ejs/connections.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:38:57 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:39:52 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:41:14 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:41:25 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:41:48 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:42:27 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:42:52 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:43:06 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:43:24 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3000
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3001
======================= New log starting at Thu Jan 11 2024 16:54:16 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
======================= New log starting at Thu Jan 11 2024 16:54:21 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3000
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3001
ReferenceError: ./ejs/passwords.ejs:6
4| <div id="manager-passwords">
5| <div class="step-summary">
>> 6| <div class="blue <%= !install || singleInstance? 'hide': '' %>">User path</div>
7| <div class="blue">Ports and address</div>
8| <div class="white">Passwords</div>
9| <div class="empty"> <%= install? 'Install': 'Update' %></div>
install is not defined
at eval ("./ejs/passwords.ejs":12:27)
at passwords (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at PasswordsPage.show (D:\Documents\DCSOlympus\manager\javascripts\passwords.js:35:13)
at ConnectionsPage.onNextClicked (D:\Documents\DCSOlympus\manager\javascripts\connections.js:58:36)
at HTMLDivElement.<anonymous> (D:\Documents\DCSOlympus\manager\javascripts\connections.js:21:87) {
path: './ejs/passwords.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:54:49 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3000
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3001
ReferenceError: ./ejs/passwords.ejs:44
42| </div>
43| </div>
>> 44| <% if (!basic) { %>
45| <div class="button cancel">
46| <%= install? "Cancel installation": "Cancel editing" %>
47| </div>
basic is not defined
at eval ("./ejs/passwords.ejs":18:8)
at passwords (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at PasswordsPage.show (D:\Documents\DCSOlympus\manager\javascripts\passwords.js:35:13)
at ConnectionsPage.onNextClicked (D:\Documents\DCSOlympus\manager\javascripts\connections.js:58:36)
at HTMLDivElement.<anonymous> (D:\Documents\DCSOlympus\manager\javascripts\connections.js:21:87) {
path: './ejs/passwords.ejs'
}
======================= New log starting at Thu Jan 11 2024 16:55:57 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3000
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3001
======================= New log starting at Fri Jan 12 2024 08:51:22 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
Development build detected, skipping version checks...
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3000
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3001
======================= New log starting at Fri Jan 12 2024 08:53:17 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3000
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3001
======================= New log starting at Fri Jan 12 2024 08:53:42 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3000
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3001
ReferenceError: ./ejs/result.ejs:132
130| <div>
131| <span>
>> 132| <%= instance.name %>
133| </span>
134| <span><img src="./icons/folder-open-solid.svg">
135| <%= instance.folder %>
instance is not defined
at eval ("./ejs/result.ejs":27:26)
at result (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:274:36)
at exports.renderFile (D:\Documents\DCSOlympus\manager\node_modules\ejs\lib\ejs.js:491:10)
at ResultPage.show (D:\Documents\DCSOlympus\manager\javascripts\result.js:25:13)
at PasswordsPage.onNextClicked (D:\Documents\DCSOlympus\manager\javascripts\passwords.js:40:33)
at HTMLDivElement.<anonymous> (D:\Documents\DCSOlympus\manager\javascripts\managerpage.js:39:87) {
path: './ejs/result.ejs'
}
======================= New log starting at Fri Jan 12 2024 08:54:41 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3000
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3001
======================= New log starting at Fri Jan 12 2024 08:56:11 GMT+0100 (Central European Standard Time) =======================
Running in D:\Documents\DCSOlympus\manager\javascripts
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3000
Instance C:\Users\dpassoni\Saved Games\dcs.openbeta client port set to 3001
Installing hooks in C:\Users\dpassoni\Saved Games\dcs.openbeta
Error installing hooks in C:\Users\dpassoni\Saved Games\dcs.openbeta: Error: ENOENT: no such file or directory, lstat 'D:\Documents\DCSOlympus\scripts\OlympusHook.lua'

View File

@ -186,34 +186,6 @@ body {
margin-bottom: 10px;
}
.instructions {
color: var(--offwhite);
display: flex;
flex-direction: column;
row-gap: 10px;
width: 50%;
}
.instructions>span {
text-align: center;
}
.instructions>span:first-child {
font-size: 22px;
font-weight: 600;
}
.instructions>span:not(:first-child) {
font-size: 15px;
color: var(--gray);
}
.buttons-footer {
display: flex;
column-gap: 10px;
justify-content: center;
}
.button {
padding: 10px 15px;
border-radius: 5px;
@ -225,23 +197,6 @@ body {
column-gap: 10px;
}
.next {
color: var(--background);
background-color: var(--offwhite);
}
.back {
color: var(--offwhite);
background-color: var(--background);
border: 1px solid var(--offwhite);
}
.cancel {
padding: 10px 5px;
color: var(--offwhite);
text-decoration: underline;
}
.close-popup {
color: var(--offwhite);
background-color: var(--blue);
@ -273,13 +228,13 @@ input {
left: 0px;
width: 100%;
height: 100%;
background-color: black;
opacity: 30%;
background-color: white;
opacity: 15%;
z-index: 999;
}
#popup {
width: 400px;
width: 450px;
height: fit-content;
min-height: 200px;
position: absolute;
@ -290,7 +245,7 @@ input {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 20px;
padding: 20px 40px;
align-items: start;
z-index: 999;
}
@ -311,6 +266,8 @@ input {
width: 100%;
text-align: left;
padding: 15px 0px !important;
word-wrap: break-word;
overflow-wrap: anywhere;
}
#popup .footer {
@ -326,87 +283,6 @@ input {
overflow-y: auto;
}
.manager-page>div {
display: flex;
flex-direction: row;
height: 100%;
min-height: 100%;
align-items: center;
}
.step-summary {
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
width: 30%;
font-size: 18px;
font-weight: 600;
color: var(--offwhite);
border-left: 1px dashed var(--offwhite);
height: 200px;
row-gap: 100px;
margin-left: 80px;
}
.step-summary div {
display: flex;
width: 280px;
height: 80px;
align-items: center;
column-gap: 15px;
margin-left: -15px;
margin-top: -40px;
margin-bottom: -40px;
font-size: 14px;
color: var(--gray);
}
.step-summary div:before {
display: inline-block;
content: "";
width: 30px;
height: 30px;
border: 1px solid var(--offwhite);
border-radius: 999px;
}
.step-summary div.white {
color: var(--offwhite);
}
.step-summary div.blue {
text-decoration: underline;
}
.step-summary div.white:before {
background-color: var(--offwhite);
}
.step-summary div.empty:before {
background-color: var(--background);
}
.step-summary div.blue:before {
border: 1px solid var(--blue);
background-color: var(--blue);
}
.content {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
row-gap: 20px;
align-items: center;
justify-content: center;
padding: 20px;
}
.content>div {
max-width: 60%;
}
.input-group {
color: var(--offwhite);
display: flex;
@ -445,10 +321,6 @@ input {
margin-bottom: 10px;
}
.buttons-footer {
margin-top: 10px;
}
.divider {
border-top: 0px solid transparent !important;
border-bottom: 1px solid var(--offwhite) !important;
@ -482,6 +354,11 @@ input {
background-position: 50% 50%;
}
.button.collapse.loading::after {
background-image: url("../icons/spinner-solid.svg");
animation: rotate 2s linear infinite;
}
.button.collapse>div {
display: none;
position: absolute;