mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Added olympus manager
This commit is contained in:
parent
e80a5e8ca1
commit
9c055e0d71
6
.gitignore
vendored
6
.gitignore
vendored
@ -23,5 +23,7 @@ client/public/javascripts/L.Path.Drag.js
|
||||
/installer/archive/Scripts
|
||||
/installer/archive/Mods
|
||||
/installer/installer/DCSOlympus*.exe
|
||||
client/public/javascripts/L.Path.Drag.js
|
||||
client/public/stylesheets/leaflet/leaflet-gesture-handling.css
|
||||
|
||||
L.Path.Drag.js
|
||||
leaflet-gesture-handling.css
|
||||
package-lock.json
|
||||
|
||||
@ -3,7 +3,11 @@ const path = require('path')
|
||||
const yargs = require('yargs');
|
||||
const prompt = require('prompt-sync')({sigint: true});
|
||||
const sha256 = require('sha256');
|
||||
const jsonPath = path.join('..', 'olympus.json');
|
||||
var jsonPath = path.join('..', 'olympus.json');
|
||||
var regedit = require('regedit')
|
||||
|
||||
const shellFoldersKey = 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'
|
||||
const saveGamesKey = '{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}'
|
||||
|
||||
/* Set the acceptable values */
|
||||
yargs.alias('a', 'address').describe('a', 'Backend address').string('a');
|
||||
@ -12,6 +16,7 @@ yargs.alias('c', 'clientPort').describe('c', 'Client port').number('c');
|
||||
yargs.alias('p', 'gameMasterPassword').describe('p', 'Game Master password').string('p');
|
||||
yargs.alias('bp', 'blueCommanderPassword').describe('bp', 'Blue Commander password').string('bp');
|
||||
yargs.alias('rp', 'redCommanderPassword').describe('rp', 'Red Commander password').string('rp');
|
||||
yargs.alias('d', 'directory').describe('d', 'Directory where the DCS Olympus configurator is located').string('rp');
|
||||
args = yargs.argv;
|
||||
|
||||
async function run() {
|
||||
@ -30,21 +35,6 @@ async function run() {
|
||||
if (args.address === undefined && args.clientPort === undefined && args.backendPort === undefined &&
|
||||
args.gameMasterPassword === undefined && args.blueCommanderPassword === undefined && args.redCommanderPassword === undefined) {
|
||||
|
||||
console.log('\x1b[36m%s\x1b[0m', "*********************************************************************");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* _____ _____ _____ ____ _ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | __ \\ / ____|/ ____| / __ \\| | *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | | | | | | (___ | | | | |_ _ _ __ ___ _ __ _ _ ___ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | | | | | \\___ \\ | | | | | | | | '_ ` _ \\| '_ \\| | | / __| *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | |__| | |____ ____) | | |__| | | |_| | | | | | | |_) | |_| \\__ \\ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* |_____/ \\_____|_____/ \\____/|_|\\__, |_| |_| |_| .__/ \\__,_|___/ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* __/ | | | *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* |___/ |_| *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "*********************************************************************");
|
||||
console.log('\x1b[36m%s\x1b[0m', "");
|
||||
|
||||
console.log("DCS Olympus configurator {{OLYMPUS_VERSION_NUMBER}}.{{OLYMPUS_COMMIT_HASH}}");
|
||||
console.log("");
|
||||
|
||||
var newValue;
|
||||
var result;
|
||||
|
||||
@ -113,5 +103,65 @@ async function run() {
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
}
|
||||
|
||||
console.log('\x1b[36m%s\x1b[0m', "*********************************************************************");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* _____ _____ _____ ____ _ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | __ \\ / ____|/ ____| / __ \\| | *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | | | | | | (___ | | | | |_ _ _ __ ___ _ __ _ _ ___ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | | | | | \\___ \\ | | | | | | | | '_ ` _ \\| '_ \\| | | / __| *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | |__| | |____ ____) | | |__| | | |_| | | | | | | |_) | |_| \\__ \\ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* |_____/ \\_____|_____/ \\____/|_|\\__, |_| |_| |_| .__/ \\__,_|___/ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* __/ | | | *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* |___/ |_| *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "*********************************************************************");
|
||||
console.log('\x1b[36m%s\x1b[0m', "");
|
||||
|
||||
console.log("DCS Olympus configurator {{OLYMPUS_VERSION_NUMBER}}.{{OLYMPUS_COMMIT_HASH}}");
|
||||
console.log("");
|
||||
|
||||
/* Run the configurator */
|
||||
run();
|
||||
if (args.directory) {
|
||||
jsonPath = path.join(args.directory, "olympus.json");
|
||||
}
|
||||
else {
|
||||
/* Automatically detect possible DCS installation folders */
|
||||
regedit.list(shellFoldersKey, function(err, result) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
else {
|
||||
if (result[shellFoldersKey] !== undefined && result[shellFoldersKey]["exists"] && result[shellFoldersKey]['values'][saveGamesKey] !== undefined && result[shellFoldersKey]['values'][saveGamesKey]['value'] !== undefined)
|
||||
{
|
||||
const searchpath = result[shellFoldersKey]['values'][saveGamesKey]['value'];
|
||||
const folders = fs.readdirSync(searchpath);
|
||||
var options = [];
|
||||
folders.forEach((folder) => {
|
||||
if (fs.existsSync(path.join(searchpath, folder, "Logs", "dcs.log"))) {
|
||||
options.push(folder);
|
||||
}
|
||||
})
|
||||
console.log("The following DCS Saved Games folders have been automatically detected.")
|
||||
options.forEach((folder, index) => {
|
||||
console.log(`(${index + 1}) ${folder}`)
|
||||
});
|
||||
while (true) {
|
||||
var newValue = prompt(`Please choose a folder onto which the configurator shall operate by typing the associated number: `)
|
||||
result = Number(newValue);
|
||||
|
||||
if (!isNaN(result) && Number.isInteger(result) && result > 0 && result <= options.length) {
|
||||
jsonPath = path.join(searchpath, options[result - 1], "Config", "olympus.json");
|
||||
break;
|
||||
}
|
||||
else {
|
||||
console.log(`Please type a number between 1 and ${options.length}`);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
console.error("An error occured while trying to fetch the location of the DCS folder. Please type the folder location manually.")
|
||||
jsonPath = path.join(prompt(`DCS Saved Games folder location: `), "olympus.json");
|
||||
}
|
||||
console.log(`Configurator will run on ${jsonPath}, if this is incorrect please restart the configurator`)
|
||||
run();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -14,15 +14,18 @@
|
||||
"watch": "watchify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ]"
|
||||
},
|
||||
"dependencies": {
|
||||
"appjs": "^0.0.20",
|
||||
"body-parser": "^1.20.2",
|
||||
"debug": "~2.6.9",
|
||||
"ejs": "^3.1.8",
|
||||
"electron": "^28.0.0",
|
||||
"express": "~4.16.1",
|
||||
"express-basic-auth": "^1.2.1",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"js-sha256": "^0.10.1",
|
||||
"morgan": "~1.9.1",
|
||||
"prompt-sync": "^4.2.0",
|
||||
"regedit": "^5.1.2",
|
||||
"save": "^2.9.0",
|
||||
"sha256": "^0.2.0",
|
||||
"srtm-elevation": "^2.1.2",
|
||||
@ -31,10 +34,9 @@
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"leaflet-gesture-handling": "^1.2.2",
|
||||
"@turf/turf": "^6.5.0",
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@tanem/svg-injector": "^10.1.68",
|
||||
"@turf/turf": "^6.5.0",
|
||||
"@types/formatcoords": "^1.1.0",
|
||||
"@types/geojson": "^7946.0.10",
|
||||
"@types/leaflet": "^1.9.0",
|
||||
@ -50,6 +52,7 @@
|
||||
"geodesy": "^1.1.2",
|
||||
"leaflet": "^1.9.3",
|
||||
"leaflet-control-mini-map": "^0.4.0",
|
||||
"leaflet-gesture-handling": "^1.2.2",
|
||||
"leaflet-path-drag": "*",
|
||||
"leaflet.nauticscale": "^1.1.0",
|
||||
"nodemon": "^2.0.20",
|
||||
|
||||
13
manager/.vscode/launch.json
vendored
Normal file
13
manager/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": [ "start" ],
|
||||
"port": 9229
|
||||
}
|
||||
]
|
||||
}
|
||||
33
manager/app.js
Normal file
33
manager/app.js
Normal file
@ -0,0 +1,33 @@
|
||||
const { app, BrowserWindow } = require('electron/main')
|
||||
const path = require('node:path')
|
||||
|
||||
function createWindow() {
|
||||
const win = new BrowserWindow({
|
||||
width: 1310,
|
||||
height: 800,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
nodeIntegration: true, // like here
|
||||
},
|
||||
icon: "./../img/olympus.ico"
|
||||
})
|
||||
|
||||
win.loadFile('index.html');
|
||||
win.setMenuBarVisibility(false);
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
1
manager/icons/check-solid.svg
Normal file
1
manager/icons/check-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path fill="#F2F2F2" d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 449 B |
1
manager/icons/circle-info-solid.svg
Normal file
1
manager/icons/circle-info-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path fill="#F2F2F2" d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
|
||||
|
After Width: | Height: | Size: 521 B |
1
manager/icons/folder-open-solid.svg
Normal file
1
manager/icons/folder-open-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="18" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path fill="#F2F2F2" d="M88.7 223.8L0 375.8V96C0 60.7 28.7 32 64 32H181.5c17 0 33.3 6.7 45.3 18.7l26.5 26.5c12 12 28.3 18.7 45.3 18.7H416c35.3 0 64 28.7 64 64v32H144c-22.8 0-43.8 12.1-55.3 31.8zm27.6 16.1C122.1 230 132.6 224 144 224H544c11.5 0 22 6.1 27.7 16.1s5.7 22.2-.1 32.1l-112 192C453.9 474 443.4 480 432 480H32c-11.5 0-22-6.1-27.7-16.1s-5.7-22.2 .1-32.1l112-192z"/></svg>
|
||||
|
After Width: | Height: | Size: 614 B |
1
manager/icons/plus-solid.svg
Normal file
1
manager/icons/plus-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path fill="#F2F2F2" d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32V224H48c-17.7 0-32 14.3-32 32s14.3 32 32 32H192V432c0 17.7 14.3 32 32 32s32-14.3 32-32V288H400c17.7 0 32-14.3 32-32s-14.3-32-32-32H256V80z"/></svg>
|
||||
|
After Width: | Height: | Size: 450 B |
1
manager/icons/trash-can-regular.svg
Normal file
1
manager/icons/trash-can-regular.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path fill="#F2F2F2" d="M170.5 51.6L151.5 80h145l-19-28.4c-1.5-2.2-4-3.6-6.7-3.6H177.1c-2.7 0-5.2 1.3-6.7 3.6zm147-26.6L354.2 80H368h48 8c13.3 0 24 10.7 24 24s-10.7 24-24 24h-8V432c0 44.2-35.8 80-80 80H112c-44.2 0-80-35.8-80-80V128H24c-13.3 0-24-10.7-24-24S10.7 80 24 80h8H80 93.8l36.7-55.1C140.9 9.4 158.4 0 177.1 0h93.7c18.7 0 36.2 9.4 46.6 24.9zM80 128V432c0 17.7 14.3 32 32 32H336c17.7 0 32-14.3 32-32V128H80zm80 64V400c0 8.8-7.2 16-16 16s-16-7.2-16-16V192c0-8.8 7.2-16 16-16s16 7.2 16 16zm80 0V400c0 8.8-7.2 16-16 16s-16-7.2-16-16V192c0-8.8 7.2-16 16-16s16 7.2 16 16zm80 0V400c0 8.8-7.2 16-16 16s-16-7.2-16-16V192c0-8.8 7.2-16 16-16s16 7.2 16 16z"/></svg>
|
||||
|
After Width: | Height: | Size: 896 B |
27
manager/index.html
Normal file
27
manager/index.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;600;700;800&display=swap" />
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<title>DCS Olympus Manager v{{OLYMPUS_VERSION_NUMBER}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<img class="main-icon" src="./../img/OlympusLogoFinal_4k.png"\>
|
||||
<div>
|
||||
<div> DCS Olympus Manager</div>
|
||||
<div class="accent-green">v{{OLYMPUS_VERSION_NUMBER}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main-div">
|
||||
|
||||
</div>
|
||||
<div id="footer">
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
51
manager/instanceDiv.ejs
Normal file
51
manager/instanceDiv.ejs
Normal file
@ -0,0 +1,51 @@
|
||||
<div class="instance-div">
|
||||
<div class="folder-name">
|
||||
<div class="icon folder"></div>
|
||||
<span><%= folder %></span>
|
||||
</div>
|
||||
<div class="instance-content">
|
||||
<table class="input-table">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="label">Client port <div class="icon info" title="This is the port that will allow users to connect to Olympus and must be forwarded in your firewall."></div></div>
|
||||
<input id="client-port" type='number' value="<%= typeof client !== 'undefined' ? client["port"]: "" %>" min="1023" max="65535" <%= !installed? "disabled": "" %> tabindex="<%= index %>">
|
||||
<span id="client-port-error" class="error"></span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="label">Game Master password <div class="icon info" title="This is the password you need to input to connect with the Game Master role."></div></div>
|
||||
<input id="game-master-password" type="password" <%= !installed? "disabled": "" %> tabindex="<%= index + 3 %>">
|
||||
<span id="game-master-password-error" class="error"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="label">Backend port <div class="icon info" title="This is the backend port used by Olympus to connect to DCS. You don't need to forward it in your firewall anymore."></div></div>
|
||||
<input id="backend-port" type='number' value="<%= typeof server !== 'undefined' ? server["port"]: "" %>" min="1023" max="65535" <%= !installed? "disabled": "" %> tabindex="<%= index + 1 %>">
|
||||
<span id="backend-port-error" class="error"></span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="label">Blue Commander password <div class="icon info" title="This is the password you need to input to connect with the Blue Commander role."></div></div>
|
||||
<input id="blue-commander-password" type="password" <%= !installed? "disabled": "" %> tabindex="<%= index + 4 %>">
|
||||
<span id="blue-commander-password-error" class="error"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="label">Backend address <div class="icon info" title="This is the address that DCS will listen on. Unless you want to send direct API commands, leave this to localhost even for dedicated server installations."></div></div>
|
||||
<input id="backend-address" type='text' value="<%= typeof server !== 'undefined' ? server["address"]: "" %>" min="1023" max="65535" <%= !installed? "disabled": "" %> tabindex="<%= index + 2 %>">
|
||||
<span id="backend-address-error" class="error"></span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="label">Red Commander password <div class="icon info" title="This is the password you need to input to connect with the Red Commander role."></div></div>
|
||||
<input id="red-commander-password" type="password" <%= !installed? "disabled": "" %> tabindex="<%= index + 5 %>">
|
||||
<span id="red-commander-password-error" class="error"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="action-buttons">
|
||||
<button class="button add <%= installed? "hide": "" %>" tabindex="<%= index + 6 %>" title="Clicking on this will install all the necessary files in your DCS instance. Remember to close DCS before doing this!">Install Olympus to instance</button>
|
||||
<button class="button apply <%= !installed? "hide": "" %>" tabindex="<%= index + 7 %>" title="Clicking on this will apply your changes to the configuration. Remember to restart any running mission!">Apply changes</button>
|
||||
<button class="button remove <%= !installed? "hide": "" %>" tabindex="<%= index + 8 %>" title="Clicking on this will remove Olympus from your DCS folder.">Remove Olympus</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
18
manager/package.json
Normal file
18
manager/package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "dcsolympus_manager",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "electron ."
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"ejs": "^3.1.9",
|
||||
"electron": "^28.0.0",
|
||||
"portfinder": "^1.0.32",
|
||||
"regedit": "^5.1.2",
|
||||
"sha256": "^0.2.0"
|
||||
}
|
||||
}
|
||||
253
manager/preload.js
Normal file
253
manager/preload.js
Normal file
@ -0,0 +1,253 @@
|
||||
var regedit = require('regedit')
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
const ejs = require('ejs')
|
||||
const portfinder = require('portfinder')
|
||||
const sha256 = require('sha256')
|
||||
|
||||
const shellFoldersKey = 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'
|
||||
const saveGamesKey = '{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}'
|
||||
|
||||
var instanceDivs = [];
|
||||
|
||||
function checkPort(port, callback) {
|
||||
portfinder.getPort({port: port, stopPort: port}, (err, res) => {
|
||||
if (err !== null) {
|
||||
console.error(`Port ${port} already in use`);
|
||||
callback(false);
|
||||
} else {
|
||||
callback(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function installOlympus(folder) {
|
||||
console.log(`Installing Olympus in ${folder}`);
|
||||
fs.cpSync(path.join("..", "Mod"), path.join(folder, "Mods", "Services", "Olympus"), {recursive: true});
|
||||
fs.cpSync(path.join("..", "Scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "OlympusHook.lua"), {recursive: true});
|
||||
fs.cpSync(path.join("..", "olympus.json"), path.join(folder, "Config", "olympus.json"), {recursive: true});
|
||||
loadDivs();
|
||||
}
|
||||
|
||||
function uninstallOlympus(folder) {
|
||||
console.log(`Uninstalling Olympus from ${folder}`);
|
||||
fs.rmSync(path.join(folder, "Mods", "Services", "Olympus"), {recursive: true});
|
||||
fs.rmSync(path.join(folder, "Config", "olympus.json"), {recursive: true});
|
||||
loadDivs();
|
||||
}
|
||||
|
||||
function applyConfiguration(folder, data) {
|
||||
console.log(`Applying configuration to Olympus from ${folder}`);
|
||||
|
||||
if (fs.existsSync(path.join(folder, "Config", "olympus.json"))) {
|
||||
var config = JSON.parse(fs.readFileSync(path.join(folder, "Config", "olympus.json")));
|
||||
|
||||
config["client"]["port"] = data["clientPort"];
|
||||
config["server"]["port"] = data["backendPort"];
|
||||
config["server"]["address"] = data["backendAddress"];
|
||||
config["authentication"]["gameMasterPassword"] = sha256(data["gameMasterPassword"]);
|
||||
config["authentication"]["blueCommanderPassword"] = sha256(data["blueCommanderPassword"]);
|
||||
config["authentication"]["redCommanderPassword"] = sha256(data["redCommanderPassword"]);
|
||||
|
||||
fs.writeFileSync(path.join(folder, "Config", "olympus.json"), JSON.stringify(config, null, 4));
|
||||
}
|
||||
}
|
||||
|
||||
class InstanceDiv {
|
||||
element = null;
|
||||
parent = null;
|
||||
folder = "";
|
||||
|
||||
constructor(parent, folder) {
|
||||
this.element = parent;
|
||||
this.folder = folder;
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
this.element = document.createElement("div");
|
||||
|
||||
var data = {
|
||||
folder: this.folder,
|
||||
installed: false,
|
||||
index: instanceDivs.length * 9
|
||||
};
|
||||
|
||||
|
||||
if (fs.existsSync(path.join(this.folder, "Config", "olympus.json"))) {
|
||||
var config = JSON.parse(fs.readFileSync(path.join(this.folder, "Config", "olympus.json")));
|
||||
data = {
|
||||
...data,
|
||||
...config
|
||||
}
|
||||
data["installed"] = true;
|
||||
}
|
||||
|
||||
ejs.renderFile("./instanceDiv.ejs", data, {}, (err, str) => {
|
||||
this.element.innerHTML = str;
|
||||
this.element.querySelector(".add").addEventListener("click", (e) => {
|
||||
if (!e.srcElement.classList.contains("disabled"))
|
||||
installOlympus(this.folder);
|
||||
});
|
||||
|
||||
this.element.querySelector(".remove").addEventListener("click", (e) => {
|
||||
if (!e.srcElement.classList.contains("disabled"))
|
||||
uninstallOlympus(this.folder);
|
||||
});
|
||||
|
||||
this.element.querySelector(".apply").addEventListener("click", (e) => {
|
||||
if (!e.srcElement.classList.contains("disabled"))
|
||||
applyConfiguration(this.folder, this.getFields());
|
||||
});
|
||||
|
||||
var inputs = this.element.querySelectorAll("input");
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
inputs[i].addEventListener("change", () => {
|
||||
inputs[i].classList.remove("error");
|
||||
instanceDivs.forEach((instanceDiv) => instanceDiv.checkFields())
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getDiv() {
|
||||
return this.element;
|
||||
}
|
||||
|
||||
getFields() {
|
||||
return {
|
||||
clientPort: Number(this.element.querySelector("#client-port").value),
|
||||
backendPort: Number(this.element.querySelector("#backend-port").value),
|
||||
backendAddress: this.element.querySelector("#backend-address").value,
|
||||
gameMasterPassword: this.element.querySelector("#game-master-password").value,
|
||||
blueCommanderPassword: this.element.querySelector("#blue-commander-password").value,
|
||||
redCommanderPassword: this.element.querySelector("#red-commander-password").value,
|
||||
}
|
||||
}
|
||||
|
||||
checkFields() {
|
||||
var data = this.getFields();
|
||||
|
||||
/* Clear existing errors */
|
||||
var inputs = this.element.querySelectorAll("input");
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
inputs[i].classList.remove("error");
|
||||
}
|
||||
var messages = this.element.querySelectorAll(".error");
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
messages[i].innerText = "";
|
||||
}
|
||||
|
||||
/* Enable the button */
|
||||
this.element.querySelector(".apply").classList.remove("disabled");
|
||||
|
||||
if (data["clientPort"] !== 0 && data["backendPort"] !== 0) {
|
||||
if (data["clientPort"] === data["backendPort"]) {
|
||||
this.element.querySelector("#client-port").classList.add("error");
|
||||
this.element.querySelector("#client-port-error").innerText = "Ports must be different";
|
||||
this.element.querySelector("#backend-port").classList.add("error");
|
||||
this.element.querySelector("#backend-port-error").innerText = "Ports must be different";
|
||||
this.element.querySelector(".apply").classList.add("disabled");
|
||||
}
|
||||
else {
|
||||
checkPort(data["clientPort"], (res) => {
|
||||
var otherInstanceUsesPort = instanceDivs.find((instanceDiv) => {
|
||||
if (instanceDiv != this) {
|
||||
var fields = instanceDiv.getFields();
|
||||
if (fields["clientPort"] === data["clientPort"] || fields["backendPort"] === data["clientPort"]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!res || otherInstanceUsesPort) {
|
||||
this.element.querySelector("#client-port").classList.add("error");
|
||||
this.element.querySelector("#client-port-error").innerText = "Port already in use";
|
||||
this.element.querySelector(".apply").classList.add("disabled");
|
||||
}
|
||||
});
|
||||
|
||||
checkPort(data["backendPort"], (res) => {
|
||||
var otherInstanceUsesPort = instanceDivs.find((instanceDiv) => {
|
||||
if (instanceDiv != this) {
|
||||
var fields = instanceDiv.getFields();
|
||||
if (fields["clientPort"] === data["backendPort"] || fields["backendPort"] === data["backendPort"]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!res || otherInstanceUsesPort) {
|
||||
this.element.querySelector("#backend-port").classList.add("error");
|
||||
this.element.querySelector("#backend-port-error").innerText = "Port already in use";
|
||||
this.element.querySelector(".apply").classList.add("disabled");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (data["gameMasterPassword"] !== "" && data["blueCommanderPassword"] !== "" && data["gameMasterPassword"] === data["blueCommanderPassword"]) {
|
||||
this.element.querySelector("#game-master-password").classList.add("error");
|
||||
this.element.querySelector("#game-master-password-error").innerText = "Passwords must be different";
|
||||
this.element.querySelector("#blue-commander-password").classList.add("error");
|
||||
this.element.querySelector("#blue-commander-password-error").innerText = "Passwords must be different";
|
||||
this.element.querySelector(".apply").classList.add("disabled");
|
||||
}
|
||||
|
||||
if (data["gameMasterPassword"] !== "" && data["redCommanderPassword"] !== "" && data["gameMasterPassword"] === data["redCommanderPassword"]) {
|
||||
this.element.querySelector("#game-master-password").classList.add("error");
|
||||
this.element.querySelector("#game-master-password-error").innerText = "Passwords must be different";
|
||||
this.element.querySelector("#red-commander-password").classList.add("error");
|
||||
this.element.querySelector("#red-commander-password-error").innerText = "Passwords must be different";
|
||||
this.element.querySelector(".apply").classList.add("disabled");
|
||||
}
|
||||
|
||||
if (data["blueCommanderPassword"] !== "" && data["redCommanderPassword"] !== "" && data["blueCommanderPassword"] === data["redCommanderPassword"]) {
|
||||
this.element.querySelector("#blue-commander-password").classList.add("error");
|
||||
this.element.querySelector("#blue-commander-password-error").innerText = "Passwords must be different";
|
||||
this.element.querySelector("#red-commander-password").classList.add("error");
|
||||
this.element.querySelector("#red-commander-password-error").innerText = "Passwords must be different";
|
||||
this.element.querySelector(".apply").classList.add("disabled");
|
||||
}
|
||||
|
||||
if (data["gameMasterPassword"] === "" || data["blueCommanderPassword"] === "" || data["redCommanderPassword"] === "") {
|
||||
this.element.querySelector(".apply").classList.add("disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadDivs() {
|
||||
regedit.list(shellFoldersKey, function(err, result) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
else {
|
||||
if (result[shellFoldersKey] !== undefined && result[shellFoldersKey]["exists"] && result[shellFoldersKey]['values'][saveGamesKey] !== undefined && result[shellFoldersKey]['values'][saveGamesKey]['value'] !== undefined)
|
||||
{
|
||||
const searchpath = result[shellFoldersKey]['values'][saveGamesKey]['value'];
|
||||
const folders = fs.readdirSync(searchpath);
|
||||
instanceDivs = [];
|
||||
const mainDiv = document.getElementById("main-div");
|
||||
|
||||
folders.forEach((folder) => {
|
||||
if (fs.existsSync(path.join(searchpath, folder, "Logs", "dcs.log"))) {
|
||||
instanceDivs.push(new InstanceDiv(mainDiv, path.join(searchpath, folder)));
|
||||
}
|
||||
});
|
||||
|
||||
mainDiv.replaceChildren(...instanceDivs.map((instanceDiv) => {
|
||||
return instanceDiv.getDiv();
|
||||
}));
|
||||
|
||||
instanceDivs.forEach((instanceDiv) => instanceDiv.checkFields())
|
||||
|
||||
} else {
|
||||
console.error("An error occured while trying to fetch the location of the DCS folders.")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
loadDivs();
|
||||
})
|
||||
226
manager/style.css
Normal file
226
manager/style.css
Normal file
@ -0,0 +1,226 @@
|
||||
* {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #181e25;
|
||||
}
|
||||
|
||||
#header {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
color: #F2F2F2;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#header>div:first-child>div:last-child {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.main-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#main-div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
row-gap: 30px;
|
||||
column-gap: 30px;
|
||||
flex-wrap: wrap;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-y: auto;
|
||||
scrollbar-color: white transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
border-bottom-right-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: white;
|
||||
border-radius: 100px;
|
||||
margin-top: 10px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.instance-div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
background-color: #3d4651;
|
||||
height: fit-content;
|
||||
padding: 20px 40px;
|
||||
border-radius: 5px;
|
||||
border-left: 5px solid #017DC1;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.instance-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 15px;
|
||||
}
|
||||
|
||||
.folder-name {
|
||||
color: #F2F2F2;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
border-bottom: 1px solid #F2F2F2;
|
||||
padding-bottom: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.folder-name span {
|
||||
width: 500px;
|
||||
overflow: hidden;
|
||||
text-wrap: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
direction: rtl;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.folder {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-image: url("./icons/folder-open-solid.svg");
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.input-table {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
border-width: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.input-table td {
|
||||
color: #F2F2F2;
|
||||
padding: 5px 0px;
|
||||
font-size: 13px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.input-table td>* {
|
||||
margin: 2px 0px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
column-gap: 12px;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 5px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: fit-content;
|
||||
height: 40px;
|
||||
color: #F2F2F2;
|
||||
border: 1px solid #F2F2F2;
|
||||
background-size: 40px 60%;
|
||||
background-position: 0px 50%;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 5px;
|
||||
padding: 5px 15px 5px 45px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.button:not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.apply {
|
||||
background-image: url("./icons/check-solid.svg");
|
||||
background-color: #017DC1;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.add {
|
||||
padding: 5px 15px 5px 15px;
|
||||
}
|
||||
|
||||
.update {
|
||||
padding: 5px 15px 5px 15px;
|
||||
}
|
||||
|
||||
.remove {
|
||||
background-image: url("./icons/trash-can-regular.svg");
|
||||
}
|
||||
|
||||
.disabled {
|
||||
background-color: #797E83;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message {
|
||||
font-weight: 600;
|
||||
height: 20px;
|
||||
font-size: 13px;
|
||||
color: #F2F2F2;
|
||||
}
|
||||
|
||||
input {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
width: 240px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
input:focus{
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input.error {
|
||||
border-color: #FF5858;
|
||||
}
|
||||
|
||||
.error {
|
||||
font-weight: bold;
|
||||
color: #FF5858;
|
||||
}
|
||||
|
||||
.accent-green {
|
||||
color: #8bff63;
|
||||
}
|
||||
|
||||
.info {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-image: url("./icons/circle-info-solid.svg");
|
||||
background-position: 50% 50%;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user