mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Merge pull request #255 from Pax1601/38-user-authentication-missing
38 user authentication missing
This commit is contained in:
@@ -3,6 +3,7 @@ var path = require('path');
|
|||||||
var cookieParser = require('cookie-parser');
|
var cookieParser = require('cookie-parser');
|
||||||
var logger = require('morgan');
|
var logger = require('morgan');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
var basicAuth = require('express-basic-auth')
|
||||||
|
|
||||||
var atcRouter = require('./routes/api/atc');
|
var atcRouter = require('./routes/api/atc');
|
||||||
var indexRouter = require('./routes/index');
|
var indexRouter = require('./routes/index');
|
||||||
@@ -26,7 +27,8 @@ app.set('view engine', 'ejs');
|
|||||||
|
|
||||||
let rawdata = fs.readFileSync('../olympus.json');
|
let rawdata = fs.readFileSync('../olympus.json');
|
||||||
let config = JSON.parse(rawdata);
|
let config = JSON.parse(rawdata);
|
||||||
app.get('/config', (req, res) => res.send(config));
|
if (config["server"] != undefined)
|
||||||
|
app.get('/config', (req, res) => res.send(config["server"]));
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
|
||||||
@@ -38,3 +40,8 @@ app.get('/demo/bullseyes', (req, res) => demoDataGenerator.bullseyes(req, res));
|
|||||||
app.get('/demo/airbases', (req, res) => demoDataGenerator.airbases(req, res));
|
app.get('/demo/airbases', (req, res) => demoDataGenerator.airbases(req, res));
|
||||||
app.get('/demo/mission', (req, res) => demoDataGenerator.mission(req, res));
|
app.get('/demo/mission', (req, res) => demoDataGenerator.mission(req, res));
|
||||||
|
|
||||||
|
app.use('/demo', basicAuth({
|
||||||
|
users: { 'admin': 'socks' }
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
19
client/package-lock.json
generated
19
client/package-lock.json
generated
@@ -31,6 +31,7 @@
|
|||||||
"browserify": "^17.0.0",
|
"browserify": "^17.0.0",
|
||||||
"concurrently": "^7.6.0",
|
"concurrently": "^7.6.0",
|
||||||
"esmify": "^2.1.1",
|
"esmify": "^2.1.1",
|
||||||
|
"express-basic-auth": "^1.2.1",
|
||||||
"nodemon": "^2.0.20",
|
"nodemon": "^2.0.20",
|
||||||
"sortablejs": "^1.15.0",
|
"sortablejs": "^1.15.0",
|
||||||
"tsify": "^5.0.4",
|
"tsify": "^5.0.4",
|
||||||
@@ -3283,6 +3284,15 @@
|
|||||||
"node": ">= 0.10.0"
|
"node": ">= 0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/express-basic-auth": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"basic-auth": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/express/node_modules/cookie": {
|
"node_modules/express/node_modules/cookie": {
|
||||||
"version": "0.3.1",
|
"version": "0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
||||||
@@ -8148,6 +8158,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"express-basic-auth": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"basic-auth": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fast-safe-stringify": {
|
"fast-safe-stringify": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
"browserify": "^17.0.0",
|
"browserify": "^17.0.0",
|
||||||
"concurrently": "^7.6.0",
|
"concurrently": "^7.6.0",
|
||||||
"esmify": "^2.1.1",
|
"esmify": "^2.1.1",
|
||||||
|
"express-basic-auth": "^1.2.1",
|
||||||
"nodemon": "^2.0.20",
|
"nodemon": "^2.0.20",
|
||||||
"sortablejs": "^1.15.0",
|
"sortablejs": "^1.15.0",
|
||||||
"tsify": "^5.0.4",
|
"tsify": "^5.0.4",
|
||||||
|
|||||||
@@ -16,12 +16,13 @@
|
|||||||
|
|
||||||
#olympus-toolbar-summary {
|
#olympus-toolbar-summary {
|
||||||
background-image: url("/images/icon-round.png");
|
background-image: url("/images/icon-round.png");
|
||||||
background-position: 25px 20px;
|
background-position: 20px 22px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 36px 36px;
|
background-size: 45px 45px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
text-indent: 44px;
|
text-indent: 60px;
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
dl.ol-data-grid {
|
dl.ol-data-grid {
|
||||||
|
|||||||
@@ -251,13 +251,14 @@ form>div {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 5px;
|
||||||
border-radius: var(--border-radius-sm);
|
border-radius: var(--border-radius-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ol-select>.ol-select-options>div a:hover,
|
.ol-select>.ol-select-options>div a:hover,
|
||||||
.ol-select>.ol-select-options>div button:hover {
|
.ol-select>.ol-select-options>div button:hover {
|
||||||
background-color: #FFF3;
|
background-color: #FFF3;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ol-panel-list {
|
.ol-panel-list {
|
||||||
@@ -725,21 +726,21 @@ body[data-hide-navyunit] #unit-visibility-control-navyunit {
|
|||||||
#splash-screen {
|
#splash-screen {
|
||||||
background-image: url("/images/splash/splash_pic_ship.png");
|
background-image: url("/images/splash/splash_pic_ship.png");
|
||||||
background-position: 100% 50%;
|
background-position: 100% 50%;
|
||||||
background-size: 320px;
|
background-size: 60%;
|
||||||
border-radius: var(--border-radius-lg);
|
border-radius: var(--border-radius-lg);
|
||||||
display: none;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 700px;
|
width: 1200px;
|
||||||
|
z-index: 99999;
|
||||||
}
|
}
|
||||||
|
|
||||||
#splash-content {
|
#splash-content {
|
||||||
background-color: var(--background-steel);
|
background-color: var(--background-steel);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 20px;
|
padding: 30px;
|
||||||
position: relative;
|
position: relative;
|
||||||
row-gap: 10px;
|
row-gap: 10px;
|
||||||
width: 55%;
|
width: 50%;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -747,7 +748,7 @@ body[data-hide-navyunit] #unit-visibility-control-navyunit {
|
|||||||
background-color: var(--background-steel);
|
background-color: var(--background-steel);
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
height: 250px;
|
height: 800px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -775,20 +776,84 @@ body[data-hide-navyunit] #unit-visibility-control-navyunit {
|
|||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#splash-content .app-version {
|
#splash-content .app-version {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#splash-content #legal-stuff h4 {
|
#splash-content #legal-stuff h5 {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
#splash-content #legal-stuff p {
|
#splash-content #legal-stuff p {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
|
color:#FFF7;
|
||||||
|
width: 120%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#splash-content.ol-dialog-content {
|
||||||
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature-splashScreen #splash-screen {
|
.feature-splashScreen #splash-screen {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#gray-out {
|
||||||
|
position: fixed;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
z-index: 9999;
|
||||||
|
background-color: #000A;
|
||||||
|
}
|
||||||
|
|
||||||
|
#authentication-form {
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
column-gap: 10px;
|
||||||
|
margin: 10px 0px;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
#authentication-form>div {
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
row-gap: 4px;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#authentication-form>div>input {
|
||||||
|
height: 35px;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
border: 0px solid transparent;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#splash-content a {
|
||||||
|
color: #FFFB;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connection-status {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connection-status[data-status="connecting"]::before {
|
||||||
|
content: "Connecting...";
|
||||||
|
animation: blinker 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connection-status[data-status="failed"]::before {
|
||||||
|
content: "Incorrect username/password!";
|
||||||
|
color: var(--primary-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blinker {
|
||||||
|
50% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -90,7 +90,7 @@ export class FeatureSwitches {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
new FeatureSwitch({
|
new FeatureSwitch({
|
||||||
"defaultEnabled": false,
|
"defaultEnabled": true,
|
||||||
"label": "Show splash screen",
|
"label": "Show splash screen",
|
||||||
"masterSwitch": true,
|
"masterSwitch": true,
|
||||||
"name": "splashScreen"
|
"name": "splashScreen"
|
||||||
@@ -116,36 +116,24 @@ export class FeatureSwitches {
|
|||||||
|
|
||||||
|
|
||||||
#testSwitches() {
|
#testSwitches() {
|
||||||
|
|
||||||
for ( const featureSwitch of this.#featureSwitches ) {
|
for ( const featureSwitch of this.#featureSwitches ) {
|
||||||
|
|
||||||
if ( featureSwitch.isEnabled() ) {
|
if ( featureSwitch.isEnabled() ) {
|
||||||
|
|
||||||
if ( typeof featureSwitch.onEnabled === "function" ) {
|
if ( typeof featureSwitch.onEnabled === "function" ) {
|
||||||
featureSwitch.onEnabled();
|
featureSwitch.onEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
document.querySelectorAll( "[data-feature-switch='" + featureSwitch.name + "']" ).forEach( el => {
|
document.querySelectorAll( "[data-feature-switch='" + featureSwitch.name + "']" ).forEach( el => {
|
||||||
|
|
||||||
if ( featureSwitch.removeArtifactsIfDisabled === false ) {
|
if ( featureSwitch.removeArtifactsIfDisabled === false ) {
|
||||||
el.remove();
|
el.remove();
|
||||||
} else {
|
} else {
|
||||||
el.classList.add( "hide" );
|
el.classList.add( "hide" );
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.classList.toggle( "feature-" + featureSwitch.name, featureSwitch.isEnabled() );
|
document.body.classList.toggle( "feature-" + featureSwitch.name, featureSwitch.isEnabled() );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
savePreferences() {
|
savePreferences() {
|
||||||
|
|
||||||
let preferences:any = {};
|
let preferences:any = {};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { AIC } from "./aic/aic";
|
|||||||
import { ATC } from "./atc/atc";
|
import { ATC } from "./atc/atc";
|
||||||
import { FeatureSwitches } from "./featureswitches";
|
import { FeatureSwitches } from "./featureswitches";
|
||||||
import { LogPanel } from "./panels/logpanel";
|
import { LogPanel } from "./panels/logpanel";
|
||||||
import { getAirbases, getBullseye as getBullseyes, getConfig, getMission, getUnits, setAddress, toggleDemoEnabled } from "./server/server";
|
import { getAirbases, getBullseye, getConfig, getFreezed, getMission, getUnits, setAddress, setCredentials, setFreezed, startUpdate, toggleDemoEnabled } from "./server/server";
|
||||||
import { UnitDataTable } from "./units/unitdatatable";
|
import { UnitDataTable } from "./units/unitdatatable";
|
||||||
import { keyEventWasInInput } from "./other/utils";
|
import { keyEventWasInInput } from "./other/utils";
|
||||||
import { Popup } from "./popups/popup";
|
import { Popup } from "./popups/popup";
|
||||||
@@ -31,11 +31,8 @@ var logPanel: LogPanel;
|
|||||||
|
|
||||||
var infoPopup: Popup;
|
var infoPopup: Popup;
|
||||||
|
|
||||||
var connected: boolean = false;
|
|
||||||
var paused: boolean = false;
|
|
||||||
var activeCoalition: string = "blue";
|
var activeCoalition: string = "blue";
|
||||||
|
|
||||||
var sessionHash: string | null = null;
|
|
||||||
var unitDataTable: UnitDataTable;
|
var unitDataTable: UnitDataTable;
|
||||||
|
|
||||||
var featureSwitches;
|
var featureSwitches;
|
||||||
@@ -49,15 +46,18 @@ function setup() {
|
|||||||
missionHandler = new MissionHandler();
|
missionHandler = new MissionHandler();
|
||||||
|
|
||||||
/* Panels */
|
/* Panels */
|
||||||
unitInfoPanel = new UnitInfoPanel("unit-info-panel");
|
unitInfoPanel = new UnitInfoPanel("unit-info-panel");
|
||||||
unitControlPanel = new UnitControlPanel("unit-control-panel");
|
unitControlPanel = new UnitControlPanel("unit-control-panel");
|
||||||
connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel");
|
connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel");
|
||||||
mouseInfoPanel = new MouseInfoPanel("mouse-info-panel");
|
mouseInfoPanel = new MouseInfoPanel("mouse-info-panel");
|
||||||
//logPanel = new LogPanel("log-panel");
|
//logPanel = new LogPanel("log-panel");
|
||||||
|
|
||||||
/* Popups */
|
/* Popups */
|
||||||
infoPopup = new Popup("info-popup");
|
infoPopup = new Popup("info-popup");
|
||||||
|
|
||||||
|
/* Controls */
|
||||||
|
new Dropdown("app-icon", () => { });
|
||||||
|
|
||||||
/* Unit data table */
|
/* Unit data table */
|
||||||
unitDataTable = new UnitDataTable("unit-data-table");
|
unitDataTable = new UnitDataTable("unit-data-table");
|
||||||
|
|
||||||
@@ -65,7 +65,6 @@ function setup() {
|
|||||||
let aicFeatureSwitch = featureSwitches.getSwitch("aic");
|
let aicFeatureSwitch = featureSwitches.getSwitch("aic");
|
||||||
if (aicFeatureSwitch?.isEnabled()) {
|
if (aicFeatureSwitch?.isEnabled()) {
|
||||||
aic = new AIC();
|
aic = new AIC();
|
||||||
// TODO: add back buttons
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ATC */
|
/* ATC */
|
||||||
@@ -75,84 +74,25 @@ function setup() {
|
|||||||
atc.startUpdates();
|
atc.startUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
new Dropdown( "app-icon", () => {} );
|
|
||||||
|
|
||||||
/* Setup event handlers */
|
/* Setup event handlers */
|
||||||
setupEvents();
|
setupEvents();
|
||||||
|
|
||||||
getConfig(readConfig)
|
/* Load the config file */
|
||||||
|
getConfig(readConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
function readConfig(config: any)
|
function readConfig(config: any) {
|
||||||
{
|
if (config && config["address"] != undefined && config["port"] != undefined) {
|
||||||
if (config && config["server"] != undefined && config["server"]["address"] != undefined && config["server"]["port"] != undefined)
|
const address = config["address"];
|
||||||
{
|
const port = config["port"];
|
||||||
const address = config["server"]["address"];
|
|
||||||
const port = config["server"]["port"];
|
|
||||||
if (typeof address === 'string' && typeof port == 'number')
|
if (typeof address === 'string' && typeof port == 'number')
|
||||||
setAddress(address == "*"? window.location.hostname: address, <number>port);
|
setAddress(address == "*" ? window.location.hostname : address, <number>port);
|
||||||
|
|
||||||
/* On the first connection, force request of full data */
|
|
||||||
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
|
|
||||||
getBullseyes((data: BullseyesData) => getMissionData()?.update(data));
|
|
||||||
getMission((data: any) => {getMissionData()?.update(data)});
|
|
||||||
getUnits((data: UnitsData) => getUnitsManager()?.update(data), true /* Does a full refresh */);
|
|
||||||
|
|
||||||
/* Start periodically requesting updates */
|
|
||||||
startPeriodicUpdate();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new Error('Could not read configuration file!');
|
throw new Error('Could not read configuration file!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function startPeriodicUpdate() {
|
|
||||||
requestUpdate();
|
|
||||||
requestRefresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
function requestUpdate() {
|
|
||||||
/* Main update rate = 250ms is minimum time, equal to server update time. */
|
|
||||||
getUnits((data: UnitsData) => {
|
|
||||||
if (!getPaused()){
|
|
||||||
getUnitsManager()?.update(data);
|
|
||||||
checkSessionHash(data.sessionHash);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
window.setTimeout(() => requestUpdate(), getConnected() ? 250 : 1000);
|
|
||||||
|
|
||||||
getConnectionStatusPanel()?.update(getConnected());
|
|
||||||
}
|
|
||||||
|
|
||||||
function requestRefresh() {
|
|
||||||
/* Main refresh rate = 5000ms. */
|
|
||||||
getUnits((data: UnitsData) => {
|
|
||||||
if (!getPaused()){
|
|
||||||
getUnitsManager()?.update(data);
|
|
||||||
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
|
|
||||||
getBullseyes((data: BullseyesData) => getMissionData()?.update(data));
|
|
||||||
getMission((data: any) => {
|
|
||||||
getMissionData()?.update(data)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the list of existing units
|
|
||||||
getUnitDataTable()?.update();
|
|
||||||
|
|
||||||
checkSessionHash(data.sessionHash);
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
window.setTimeout(() => requestRefresh(), 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkSessionHash(newSessionHash: string) {
|
|
||||||
if (sessionHash != null) {
|
|
||||||
if (newSessionHash != sessionHash)
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
sessionHash = newSessionHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupEvents() {
|
function setupEvents() {
|
||||||
/* Generic clicks */
|
/* Generic clicks */
|
||||||
document.addEventListener("click", (ev) => {
|
document.addEventListener("click", (ev) => {
|
||||||
@@ -181,7 +121,7 @@ function setupEvents() {
|
|||||||
|
|
||||||
/* Keyup events */
|
/* Keyup events */
|
||||||
document.addEventListener("keyup", ev => {
|
document.addEventListener("keyup", ev => {
|
||||||
if ( keyEventWasInInput( ev ) ) {
|
if (keyEventWasInInput(ev)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (ev.code) {
|
switch (ev.code) {
|
||||||
@@ -195,7 +135,7 @@ function setupEvents() {
|
|||||||
unitDataTable.toggle();
|
unitDataTable.toggle();
|
||||||
break
|
break
|
||||||
case "Space":
|
case "Space":
|
||||||
setPaused(!getPaused());
|
setFreezed(!getFreezed());
|
||||||
break;
|
break;
|
||||||
case "KeyW":
|
case "KeyW":
|
||||||
case "KeyA":
|
case "KeyA":
|
||||||
@@ -212,7 +152,7 @@ function setupEvents() {
|
|||||||
|
|
||||||
/* Keydown events */
|
/* Keydown events */
|
||||||
document.addEventListener("keydown", ev => {
|
document.addEventListener("keydown", ev => {
|
||||||
if ( keyEventWasInInput( ev ) ) {
|
if (keyEventWasInInput(ev)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (ev.code) {
|
switch (ev.code) {
|
||||||
@@ -229,15 +169,31 @@ function setupEvents() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener( "closeDialog", (ev: CustomEventInit) => {
|
document.addEventListener("closeDialog", (ev: CustomEventInit) => {
|
||||||
ev.detail._element.closest( ".ol-dialog" ).classList.add( "hide" );
|
ev.detail._element.closest(".ol-dialog").classList.add("hide");
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener( "toggleElements", (ev: CustomEventInit) => {
|
document.addEventListener("toggleElements", (ev: CustomEventInit) => {
|
||||||
document.querySelectorAll( ev.detail.selector ).forEach( el => {
|
document.querySelectorAll(ev.detail.selector).forEach(el => {
|
||||||
el.classList.toggle( "hide" );
|
el.classList.toggle("hide");
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener("tryConnection", () => {
|
||||||
|
const form = document.querySelector("#splash-content")?.querySelector("#authentication-form");
|
||||||
|
const username = (<HTMLInputElement> (form?.querySelector("#username"))).value;
|
||||||
|
const password = (<HTMLInputElement> (form?.querySelector("#password"))).value;
|
||||||
|
setCredentials(username, btoa("admin" + ":" + password));
|
||||||
|
|
||||||
|
/* Start periodically requesting updates */
|
||||||
|
startUpdate();
|
||||||
|
|
||||||
|
setConnectionStatus("connecting");
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener("reloadPage", () => {
|
||||||
|
location.reload();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMap() {
|
export function getMap() {
|
||||||
@@ -285,23 +241,10 @@ export function getActiveCoalition() {
|
|||||||
return activeCoalition;
|
return activeCoalition;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setConnected(newConnected: boolean) {
|
export function setConnectionStatus(status: string) {
|
||||||
if (connected != newConnected)
|
const el = document.querySelector("#connection-status") as HTMLElement;
|
||||||
newConnected? getInfoPopup().setText("Connected to DCS Olympus server"): getInfoPopup().setText("Disconnected from DCS Olympus server");
|
if (el)
|
||||||
connected = newConnected;
|
el.dataset["status"] = status;
|
||||||
}
|
|
||||||
|
|
||||||
export function getConnected() {
|
|
||||||
return connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setPaused(newPaused: boolean) {
|
|
||||||
paused = newPaused;
|
|
||||||
paused? getInfoPopup().setText("Paused"): getInfoPopup().setText("Unpaused");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPaused() {
|
|
||||||
return paused;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getInfoPopup() {
|
export function getInfoPopup() {
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ export class Map extends L.Map {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setView(bounds.getCenter(), 8);
|
this.setView(bounds.getCenter(), 8);
|
||||||
this.setMaxBounds(bounds);
|
//this.setMaxBounds(bounds);
|
||||||
|
|
||||||
if (this.#miniMap)
|
if (this.#miniMap)
|
||||||
this.#miniMap.remove();
|
this.#miniMap.remove();
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ export class UnitControlPanel extends Panel {
|
|||||||
|
|
||||||
// Default values for "normal" units
|
// Default values for "normal" units
|
||||||
this.#radioCallsignDropdown.setOptions(["Enfield", "Springfield", "Uzi", "Colt", "Dodge", "Ford", "Chevy", "Pontiac"]);
|
this.#radioCallsignDropdown.setOptions(["Enfield", "Springfield", "Uzi", "Colt", "Dodge", "Ford", "Chevy", "Pontiac"]);
|
||||||
this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign);
|
this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign - 1);
|
||||||
|
|
||||||
// Input values
|
// Input values
|
||||||
var tankerCheckbox = this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.querySelector("input")
|
var tankerCheckbox = this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.querySelector("input")
|
||||||
@@ -241,7 +241,7 @@ export class UnitControlPanel extends Panel {
|
|||||||
this.#radioDecimalsDropdown.setValue("." + radioDecimals);
|
this.#radioDecimalsDropdown.setValue("." + radioDecimals);
|
||||||
|
|
||||||
// Make sure its in the valid range
|
// Make sure its in the valid range
|
||||||
if (!this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign))
|
if (!this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign - 1))
|
||||||
this.#radioCallsignDropdown.selectValue(0);
|
this.#radioCallsignDropdown.selectValue(0);
|
||||||
|
|
||||||
// Set options for tankers
|
// Set options for tankers
|
||||||
@@ -249,7 +249,7 @@ export class UnitControlPanel extends Panel {
|
|||||||
if (roles != undefined && Array.prototype.concat.apply([], roles)?.includes("Tanker")){
|
if (roles != undefined && Array.prototype.concat.apply([], roles)?.includes("Tanker")){
|
||||||
this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.classList.remove("hide");
|
this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.classList.remove("hide");
|
||||||
this.#radioCallsignDropdown.setOptions(["Texaco", "Arco", "Shell"]);
|
this.#radioCallsignDropdown.setOptions(["Texaco", "Arco", "Shell"]);
|
||||||
this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign);
|
this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign - 1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.classList.add("hide");
|
this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.classList.add("hide");
|
||||||
@@ -259,7 +259,7 @@ export class UnitControlPanel extends Panel {
|
|||||||
if (roles != undefined && Array.prototype.concat.apply([], roles)?.includes("AWACS")){
|
if (roles != undefined && Array.prototype.concat.apply([], roles)?.includes("AWACS")){
|
||||||
this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.classList.remove("hide");
|
this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.classList.remove("hide");
|
||||||
this.#radioCallsignDropdown.setOptions(["Overlord", "Magic", "Wizard", "Focus", "Darkstar"]);
|
this.#radioCallsignDropdown.setOptions(["Overlord", "Magic", "Wizard", "Focus", "Darkstar"]);
|
||||||
this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign);
|
this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign - 1);
|
||||||
} else {
|
} else {
|
||||||
this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.classList.add("hide");
|
this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.classList.add("hide");
|
||||||
}
|
}
|
||||||
@@ -276,7 +276,7 @@ export class UnitControlPanel extends Panel {
|
|||||||
const TACANCallsign = <string> this.#advancedSettingsDialog.querySelector("#tacan-callsign")?.querySelector("input")?.value
|
const TACANCallsign = <string> this.#advancedSettingsDialog.querySelector("#tacan-callsign")?.querySelector("input")?.value
|
||||||
const radioMHz = Number(this.#advancedSettingsDialog.querySelector("#radio-mhz")?.querySelector("input")?.value);
|
const radioMHz = Number(this.#advancedSettingsDialog.querySelector("#radio-mhz")?.querySelector("input")?.value);
|
||||||
const radioDecimals = this.#radioDecimalsDropdown.getValue();
|
const radioDecimals = this.#radioDecimalsDropdown.getValue();
|
||||||
const radioCallsign = this.#radioCallsignDropdown.getIndex();
|
const radioCallsign = this.#radioCallsignDropdown.getIndex() + 1;
|
||||||
const radioCallsignNumber = Number(this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input")?.value);
|
const radioCallsignNumber = Number(this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input")?.value);
|
||||||
|
|
||||||
var radioFrequency = (radioMHz * 1000 + Number(radioDecimals.substring(1))) * 1000;
|
var radioFrequency = (radioMHz * 1000 + Number(radioDecimals.substring(1))) * 1000;
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import * as L from 'leaflet'
|
import * as L from 'leaflet'
|
||||||
import { setConnected } from '..';
|
import { getConnectionStatusPanel, getInfoPopup, getMissionData, getUnitDataTable, getUnitsManager, setConnectionStatus } from '..';
|
||||||
import { SpawnOptions } from '../controls/mapcontextmenu';
|
import { SpawnOptions } from '../controls/mapcontextmenu';
|
||||||
|
|
||||||
|
var connected: boolean = false;
|
||||||
|
var freezed: boolean = false;
|
||||||
|
|
||||||
var REST_ADDRESS = "http://localhost:30000/olympus";
|
var REST_ADDRESS = "http://localhost:30000/olympus";
|
||||||
var DEMO_ADDRESS = window.location.href + "demo";
|
var DEMO_ADDRESS = window.location.href + "demo";
|
||||||
const UNITS_URI = "units";
|
const UNITS_URI = "units";
|
||||||
@@ -10,29 +13,50 @@ const AIRBASES_URI = "airbases";
|
|||||||
const BULLSEYE_URI = "bullseyes";
|
const BULLSEYE_URI = "bullseyes";
|
||||||
const MISSION_URI = "mission";
|
const MISSION_URI = "mission";
|
||||||
|
|
||||||
|
var username = "";
|
||||||
|
var credentials = "";
|
||||||
|
|
||||||
|
var sessionHash: string | null = null;
|
||||||
var lastUpdateTime = 0;
|
var lastUpdateTime = 0;
|
||||||
var demoEnabled = false;
|
var demoEnabled = false;
|
||||||
|
|
||||||
export function toggleDemoEnabled()
|
export function toggleDemoEnabled() {
|
||||||
{
|
|
||||||
demoEnabled = !demoEnabled;
|
demoEnabled = !demoEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GET(callback: CallableFunction, uri: string, options?: string){
|
export function setCredentials(newUsername: string, newCredentials: string) {
|
||||||
|
username = newUsername;
|
||||||
|
credentials = newCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GET(callback: CallableFunction, uri: string, options?: string) {
|
||||||
var xmlHttp = new XMLHttpRequest();
|
var xmlHttp = new XMLHttpRequest();
|
||||||
xmlHttp.open("GET", `${demoEnabled? DEMO_ADDRESS: REST_ADDRESS}/${uri}${options? options: ''}`, true);
|
xmlHttp.open("GET", `${demoEnabled? DEMO_ADDRESS: REST_ADDRESS}/${uri}${options? options: ''}`, true);
|
||||||
|
if (credentials)
|
||||||
|
xmlHttp.setRequestHeader("Authorization", "Basic " + credentials);
|
||||||
xmlHttp.onload = function (e) {
|
xmlHttp.onload = function (e) {
|
||||||
var data = JSON.parse(xmlHttp.responseText);
|
if (xmlHttp.status == 200) {
|
||||||
if (uri !== UNITS_URI || parseInt(data.time) > lastUpdateTime)
|
var data = JSON.parse(xmlHttp.responseText);
|
||||||
{
|
if (uri !== UNITS_URI || parseInt(data.time) > lastUpdateTime)
|
||||||
callback(data);
|
{
|
||||||
lastUpdateTime = parseInt(data.time);
|
callback(data);
|
||||||
if (isNaN(lastUpdateTime))
|
lastUpdateTime = parseInt(data.time);
|
||||||
lastUpdateTime = 0;
|
if (isNaN(lastUpdateTime))
|
||||||
setConnected(true);
|
lastUpdateTime = 0;
|
||||||
|
setConnected(true);
|
||||||
|
}
|
||||||
|
} else if (xmlHttp.status == 401) {
|
||||||
|
console.error("Incorrect username/password");
|
||||||
|
setConnectionStatus("failed");
|
||||||
|
} else {
|
||||||
|
setConnected(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xmlHttp.onerror = function () {
|
xmlHttp.onreadystatechange = function (res) {
|
||||||
|
console.error("An error occurred during the XMLHttpRequest");
|
||||||
|
setConnected(false);
|
||||||
|
};
|
||||||
|
xmlHttp.onerror = function (res) {
|
||||||
console.error("An error occurred during the XMLHttpRequest");
|
console.error("An error occurred during the XMLHttpRequest");
|
||||||
setConnected(false);
|
setConnected(false);
|
||||||
};
|
};
|
||||||
@@ -40,13 +64,15 @@ export function GET(callback: CallableFunction, uri: string, options?: string){
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function POST(request: object, callback: CallableFunction){
|
export function POST(request: object, callback: CallableFunction){
|
||||||
var xhr = new XMLHttpRequest();
|
var xmlHttp = new XMLHttpRequest();
|
||||||
xhr.open("PUT", demoEnabled? DEMO_ADDRESS: REST_ADDRESS);
|
xmlHttp.open("PUT", demoEnabled? DEMO_ADDRESS: REST_ADDRESS);
|
||||||
xhr.setRequestHeader("Content-Type", "application/json");
|
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||||
xhr.onreadystatechange = () => {
|
if (credentials)
|
||||||
|
xmlHttp.setRequestHeader("Authorization", "Basic " + credentials);
|
||||||
|
xmlHttp.onreadystatechange = () => {
|
||||||
callback();
|
callback();
|
||||||
};
|
};
|
||||||
xhr.send(JSON.stringify(request));
|
xmlHttp.send(JSON.stringify(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getConfig(callback: CallableFunction) {
|
export function getConfig(callback: CallableFunction) {
|
||||||
@@ -209,3 +235,80 @@ export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolea
|
|||||||
var data = { "setAdvancedOptions": command };
|
var data = { "setAdvancedOptions": command };
|
||||||
POST(data, () => { });
|
POST(data, () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function startUpdate() {
|
||||||
|
/* On the first connection, force request of full data */
|
||||||
|
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
|
||||||
|
getBullseye((data: BullseyesData) => getMissionData()?.update(data));
|
||||||
|
getMission((data: any) => { getMissionData()?.update(data) });
|
||||||
|
getUnits((data: UnitsData) => getUnitsManager()?.update(data), true /* Does a full refresh */);
|
||||||
|
|
||||||
|
requestUpdate();
|
||||||
|
requestRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function requestUpdate() {
|
||||||
|
/* Main update rate = 250ms is minimum time, equal to server update time. */
|
||||||
|
getUnits((data: UnitsData) => {
|
||||||
|
if (!getFreezed()) {
|
||||||
|
getUnitsManager()?.update(data);
|
||||||
|
checkSessionHash(data.sessionHash);
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
window.setTimeout(() => requestUpdate(), getConnected() ? 250 : 1000);
|
||||||
|
|
||||||
|
getConnectionStatusPanel()?.update(getConnected());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function requestRefresh() {
|
||||||
|
/* Main refresh rate = 5000ms. */
|
||||||
|
getUnits((data: UnitsData) => {
|
||||||
|
if (!getFreezed()) {
|
||||||
|
getUnitsManager()?.update(data);
|
||||||
|
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
|
||||||
|
getBullseye((data: BullseyesData) => getMissionData()?.update(data));
|
||||||
|
getMission((data: any) => {
|
||||||
|
getMissionData()?.update(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the list of existing units
|
||||||
|
getUnitDataTable()?.update();
|
||||||
|
|
||||||
|
checkSessionHash(data.sessionHash);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
window.setTimeout(() => requestRefresh(), 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkSessionHash(newSessionHash: string) {
|
||||||
|
if (sessionHash != null) {
|
||||||
|
if (newSessionHash != sessionHash)
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sessionHash = newSessionHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setConnected(newConnected: boolean) {
|
||||||
|
if (connected != newConnected)
|
||||||
|
newConnected ? getInfoPopup().setText("Connected to DCS Olympus server") : getInfoPopup().setText("Disconnected from DCS Olympus server");
|
||||||
|
connected = newConnected;
|
||||||
|
|
||||||
|
if (connected) {
|
||||||
|
document.querySelector("#splash-screen")?.classList.add("hide");
|
||||||
|
document.querySelector("#gray-out")?.classList.add("hide");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getConnected() {
|
||||||
|
return connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setFreezed(newFreezed: boolean) {
|
||||||
|
freezed = newFreezed;
|
||||||
|
freezed ? getInfoPopup().setText("Freezed") : getInfoPopup().setText("Unfreezed");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFreezed() {
|
||||||
|
return freezed;
|
||||||
|
}
|
||||||
@@ -1,20 +1,25 @@
|
|||||||
<div id="splash-screen" class="ol-dialog" data-on-click="closeDialog" oncontextmenu="return false;">
|
<div id="splash-screen" class="ol-dialog" oncontextmenu="return false;">
|
||||||
|
|
||||||
<div id="splash-content" class="ol-dialog-content">
|
<div id="splash-content" class="ol-dialog-content">
|
||||||
|
|
||||||
<div id="app-summary">
|
<div id="app-summary">
|
||||||
<h2>DCS Olympus</h2>
|
<h2>DCS Olympus</h2>
|
||||||
<h4>Dynamic Unit Command</h4>
|
<h4>Dynamic Unit Command</h4>
|
||||||
<div class="app-version">Version <span class="app-version-number">v0.2.0</span></div>
|
<div class="app-version">Version <span class="app-version-number">v0.2.0</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="legal-stuff">
|
<div id="authentication-form">
|
||||||
<h4>Disclaimer</h4>
|
<div><h5>Username</h5> <input type="text" id="username" name="username" required autocomplete="username" placeholder="Enter username..."></div>
|
||||||
<p>We ain't no friends with no Eagle Dynamics.</p>
|
<div><h5>Password</h5> <input type="password" id="password" name="password" minlength="8" required autocomplete="current-password" placeholder="Enter password..."></div>
|
||||||
|
<button id="connection-button" class="ol-button-apply" data-on-click="tryConnection">Connect</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
<h5 id="connection-status"><br></h5>
|
||||||
|
|
||||||
|
<div id="legal-stuff">
|
||||||
|
<h5>DISCLAIMER</h5>
|
||||||
|
<p> DCS Olympus (the "MATERIAL" or "Software") is provided completely free to users subject to the terms of the <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0 Licence</a> except where such terms conflict with this disclaimer, in which case, the terms of this disclaimer shall prevail.
|
||||||
|
The authors and/or copyright holders of the Software have not received any financial benefit in connection with the Software. In any event, the Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and non-infringement. In no event shall the authors and/or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software. Any party making use of the Software in any manner agrees to be bound by the terms set out in this disclaimer. THIS MATERIAL IS NOT MADE OR SUPPORTED BY EAGLE DYNAMICS SA.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="advanced-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
<div id="advanced-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||||
|
|||||||
@@ -35,6 +35,8 @@
|
|||||||
<%- include('unitdatatable.ejs') %>
|
<%- include('unitdatatable.ejs') %>
|
||||||
<%- include('popups.ejs') %>
|
<%- include('popups.ejs') %>
|
||||||
|
|
||||||
|
<div id="gray-out"></div>
|
||||||
|
|
||||||
<% /* %>
|
<% /* %>
|
||||||
<%- include('log.ejs') %>
|
<%- include('log.ejs') %>
|
||||||
<% */ %>
|
<% */ %>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ol-select-options">
|
<div class="ol-select-options">
|
||||||
<div id="olympus-toolbar-summary">
|
<div id="olympus-toolbar-summary">
|
||||||
<h3>Olympus</h3>
|
<h3>DCS Olympus</h3>
|
||||||
<div class="accent-green app-version-number">v0.2.0</div>
|
<div class="accent-green app-version-number">version v0.2.0</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="https://www.discord.com" target="_blank">Discord</a>
|
<a href="https://www.discord.com" target="_blank">Discord</a>
|
||||||
@@ -15,6 +15,9 @@
|
|||||||
<div>
|
<div>
|
||||||
<a href="https://github.com/Pax1601/DCSOlympus" target="_blank">Github</a>
|
<a href="https://github.com/Pax1601/DCSOlympus" target="_blank">Github</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div data-on-click="reloadPage">
|
||||||
|
<a href="" target="_blank" data-on-click="reloadPage">Restart Olyumpus</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,8 @@
|
|||||||
"server": {
|
"server": {
|
||||||
"address": "localhost",
|
"address": "localhost",
|
||||||
"port": 30000
|
"port": 30000
|
||||||
|
},
|
||||||
|
"authentication": {
|
||||||
|
"password": "password"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,7 @@
|
|||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</PrecompiledHeaderFile>
|
</PrecompiledHeaderFile>
|
||||||
<AdditionalIncludeDirectories>include;..\..\third-party\lua\include;..\utils\include;..\shared\include;..\dcstools\include;..\logger\include;..\luatools\include</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>include;..\..\third-party\base64\include;..\..\third-party\lua\include;..\utils\include;..\shared\include;..\dcstools\include;..\logger\include;..\luatools\include</AdditionalIncludeDirectories>
|
||||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
|||||||
@@ -27,5 +27,7 @@ private:
|
|||||||
void task();
|
void task();
|
||||||
|
|
||||||
atomic<bool> runListener;
|
atomic<bool> runListener;
|
||||||
|
|
||||||
|
wstring password = L"";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -96,14 +96,13 @@ public:
|
|||||||
void pushActivePathBack(Coords newActivePathBack);
|
void pushActivePathBack(Coords newActivePathBack);
|
||||||
void popActivePathFront();
|
void popActivePathFront();
|
||||||
void setTargetID(int newTargetID) { targetID = newTargetID; addMeasure(L"targetID", json::value(newTargetID));}
|
void setTargetID(int newTargetID) { targetID = newTargetID; addMeasure(L"targetID", json::value(newTargetID));}
|
||||||
void setIsTanker(bool newIsTanker) { isTanker = newIsTanker; addMeasure(L"isTanker", json::value(newIsTanker));}
|
void setIsTanker(bool newIsTanker);
|
||||||
void setIsAWACS(bool newIsAWACS) { isAWACS = newIsAWACS; addMeasure(L"isAWACS", json::value(newIsAWACS));}
|
void setIsAWACS(bool newIsAWACS);
|
||||||
void setTACANOn(bool newTACANOn);
|
|
||||||
void setTACANChannel(int newTACANChannel);
|
void setTACANChannel(int newTACANChannel);
|
||||||
void setTACANXY(wstring newTACANXY);
|
void setTACANXY(wstring newTACANXY);
|
||||||
void setTACANCallsign(wstring newTACANCallsign);
|
void setTACANCallsign(wstring newTACANCallsign);
|
||||||
void setTACAN();
|
void setTACAN();
|
||||||
void setRadioOn(bool newRadioOn);
|
void setEPLRS(bool state);
|
||||||
void setRadioFrequency(int newRadioFrequency);
|
void setRadioFrequency(int newRadioFrequency);
|
||||||
void setRadioCallsign(int newRadioCallsign);
|
void setRadioCallsign(int newRadioCallsign);
|
||||||
void setRadioCallsignNumber(int newRadioCallsignNumber);
|
void setRadioCallsignNumber(int newRadioCallsignNumber);
|
||||||
@@ -116,11 +115,9 @@ public:
|
|||||||
int getTargetID() { return targetID; }
|
int getTargetID() { return targetID; }
|
||||||
bool getIsTanker() { return isTanker; }
|
bool getIsTanker() { return isTanker; }
|
||||||
bool getIsAWACS() { return isAWACS; }
|
bool getIsAWACS() { return isAWACS; }
|
||||||
bool getTACANOn() { return TACANOn; }
|
|
||||||
int getTACANChannel() { return TACANChannel; }
|
int getTACANChannel() { return TACANChannel; }
|
||||||
wstring getTACANXY() { return TACANXY; }
|
wstring getTACANXY() { return TACANXY; }
|
||||||
wstring getTACANCallsign() { return TACANCallsign; }
|
wstring getTACANCallsign() { return TACANCallsign; }
|
||||||
bool getRadioOn() { return radioOn; }
|
|
||||||
int getRadioFrequency() { return radioFrequency; }
|
int getRadioFrequency() { return radioFrequency; }
|
||||||
int getRadioCallsign() { return radioCallsign; }
|
int getRadioCallsign() { return radioCallsign; }
|
||||||
int getRadioCallsignNumber() { return radioCallsignNumber; }
|
int getRadioCallsignNumber() { return radioCallsignNumber; }
|
||||||
@@ -182,11 +179,9 @@ protected:
|
|||||||
int targetID = NULL;
|
int targetID = NULL;
|
||||||
bool isTanker = false;
|
bool isTanker = false;
|
||||||
bool isAWACS = false;
|
bool isAWACS = false;
|
||||||
bool TACANOn = false;
|
|
||||||
int TACANChannel = 40;
|
int TACANChannel = 40;
|
||||||
wstring TACANXY = L"X";
|
wstring TACANXY = L"X";
|
||||||
wstring TACANCallsign = L"TKR";
|
wstring TACANCallsign = L"TKR";
|
||||||
bool radioOn = false;
|
|
||||||
int radioFrequency = 260000000; // MHz
|
int radioFrequency = 260000000; // MHz
|
||||||
int radioCallsign = 1;
|
int radioCallsign = 1;
|
||||||
int radioCallsignNumber = 1;
|
int radioCallsignNumber = 1;
|
||||||
|
|||||||
@@ -251,13 +251,11 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
|||||||
unit->setIsTanker(value[L"isTanker"].as_bool());
|
unit->setIsTanker(value[L"isTanker"].as_bool());
|
||||||
unit->setIsAWACS(value[L"isAWACS"].as_bool());
|
unit->setIsAWACS(value[L"isAWACS"].as_bool());
|
||||||
|
|
||||||
unit->setTACANOn(true); // TODO Remove
|
|
||||||
unit->setTACANChannel(value[L"TACANChannel"].as_number().to_int32());
|
unit->setTACANChannel(value[L"TACANChannel"].as_number().to_int32());
|
||||||
unit->setTACANXY(value[L"TACANXY"].as_string());
|
unit->setTACANXY(value[L"TACANXY"].as_string());
|
||||||
unit->setTACANCallsign(value[L"TACANCallsign"].as_string());
|
unit->setTACANCallsign(value[L"TACANCallsign"].as_string());
|
||||||
unit->setTACAN();
|
unit->setTACAN();
|
||||||
|
|
||||||
unit->setRadioOn(true); // TODO Remove
|
|
||||||
unit->setRadioFrequency(value[L"radioFrequency"].as_number().to_int32());
|
unit->setRadioFrequency(value[L"radioFrequency"].as_number().to_int32());
|
||||||
unit->setRadioCallsign(value[L"radioCallsign"].as_number().to_int32());
|
unit->setRadioCallsign(value[L"radioCallsign"].as_number().to_int32());
|
||||||
unit->setRadioCallsignNumber(value[L"radioCallsignNumber"].as_number().to_int32());
|
unit->setRadioCallsignNumber(value[L"radioCallsignNumber"].as_number().to_int32());
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
#include "luatools.h"
|
#include "luatools.h"
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include "base64.hpp"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
using namespace base64;
|
||||||
|
|
||||||
extern UnitsManager* unitsManager;
|
extern UnitsManager* unitsManager;
|
||||||
extern Scheduler* scheduler;
|
extern Scheduler* scheduler;
|
||||||
@@ -54,10 +56,10 @@ void Server::stop(lua_State* L)
|
|||||||
void Server::handle_options(http_request request)
|
void Server::handle_options(http_request request)
|
||||||
{
|
{
|
||||||
http_response response(status_codes::OK);
|
http_response response(status_codes::OK);
|
||||||
response.headers().add(U("Allow"), U("GET, POST, PUT, OPTIONS"));
|
response.headers().add(U("Allow"), U("GET, PUT, OPTIONS"));
|
||||||
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
|
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
|
||||||
response.headers().add(U("Access-Control-Allow-Methods"), U("GET, POST, PUT, OPTIONS"));
|
response.headers().add(U("Access-Control-Allow-Methods"), U("GET, PUT, OPTIONS"));
|
||||||
response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type"));
|
response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type, Authorization"));
|
||||||
|
|
||||||
request.reply(response);
|
request.reply(response);
|
||||||
}
|
}
|
||||||
@@ -68,87 +70,102 @@ void Server::handle_get(http_request request)
|
|||||||
lock_guard<mutex> guard(mutexLock);
|
lock_guard<mutex> guard(mutexLock);
|
||||||
|
|
||||||
http_response response(status_codes::OK);
|
http_response response(status_codes::OK);
|
||||||
response.headers().add(U("Allow"), U("GET, POST, PUT, OPTIONS"));
|
string authorization = to_base64("admin:" + to_string(password));
|
||||||
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
|
if (password == L"" || (request.headers().has(L"Authorization") && request.headers().find(L"Authorization")->second == L"Basic " + to_wstring(authorization)))
|
||||||
response.headers().add(U("Access-Control-Allow-Methods"), U("GET, POST, PUT, OPTIONS"));
|
{
|
||||||
response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type"));
|
std::exception_ptr eptr;
|
||||||
|
try {
|
||||||
|
auto answer = json::value::object();
|
||||||
|
auto path = uri::split_path(uri::decode(request.relative_uri().path()));
|
||||||
|
|
||||||
std::exception_ptr eptr;
|
if (path.size() > 0)
|
||||||
try {
|
|
||||||
auto answer = json::value::object();
|
|
||||||
auto path = uri::split_path(uri::decode(request.relative_uri().path()));
|
|
||||||
|
|
||||||
if (path.size() > 0)
|
|
||||||
{
|
|
||||||
if (path[0] == UNITS_URI)
|
|
||||||
{
|
{
|
||||||
map<utility::string_t, utility::string_t> query = request.relative_uri().split_query(request.relative_uri().query());
|
if (path[0] == UNITS_URI)
|
||||||
long long time = 0;
|
|
||||||
if (query.find(L"time") != query.end())
|
|
||||||
{
|
{
|
||||||
try {
|
map<utility::string_t, utility::string_t> query = request.relative_uri().split_query(request.relative_uri().query());
|
||||||
time = stoll((*(query.find(L"time"))).second);
|
long long time = 0;
|
||||||
}
|
if (query.find(L"time") != query.end())
|
||||||
catch (const std::exception& e) {
|
{
|
||||||
time = 0;
|
try {
|
||||||
|
time = stoll((*(query.find(L"time"))).second);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
time = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
unitsManager->getData(answer, time);
|
||||||
}
|
}
|
||||||
unitsManager->getData(answer, time);
|
else if (path[0] == LOGS_URI)
|
||||||
}
|
{
|
||||||
else if (path[0] == LOGS_URI)
|
auto logs = json::value::object();
|
||||||
{
|
getLogsJSON(logs, 100); // By reference, for thread safety. Get the last 100 log entries
|
||||||
auto logs = json::value::object();
|
answer[L"logs"] = logs;
|
||||||
getLogsJSON(logs, 100); // By reference, for thread safety. Get the last 100 log entries
|
}
|
||||||
answer[L"logs"] = logs;
|
else if (path[0] == AIRBASES_URI)
|
||||||
}
|
answer[L"airbases"] = airbases;
|
||||||
else if (path[0] == AIRBASES_URI)
|
else if (path[0] == BULLSEYE_URI)
|
||||||
answer[L"airbases"] = airbases;
|
answer[L"bullseyes"] = bullseyes;
|
||||||
else if (path[0] == BULLSEYE_URI)
|
else if (path[0] == MISSION_URI)
|
||||||
answer[L"bullseyes"] = bullseyes;
|
answer[L"mission"] = mission;
|
||||||
else if (path[0] == MISSION_URI)
|
|
||||||
answer[L"mission"] = mission;
|
|
||||||
|
|
||||||
milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
|
milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
|
||||||
answer[L"time"] = json::value::string(to_wstring(ms.count()));
|
answer[L"time"] = json::value::string(to_wstring(ms.count()));
|
||||||
answer[L"sessionHash"] = json::value::string(to_wstring(sessionHash));
|
answer[L"sessionHash"] = json::value::string(to_wstring(sessionHash));
|
||||||
|
}
|
||||||
|
|
||||||
|
response.set_body(answer);
|
||||||
}
|
}
|
||||||
|
catch (...) {
|
||||||
|
eptr = std::current_exception(); // capture
|
||||||
|
}
|
||||||
|
handle_eptr(eptr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response = status_codes::Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
response.set_body(answer);
|
response.headers().add(U("Allow"), U("GET, PUT, OPTIONS"));
|
||||||
}
|
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
|
||||||
catch (...) {
|
response.headers().add(U("Access-Control-Allow-Methods"), U("GET, PUT, OPTIONS"));
|
||||||
eptr = std::current_exception(); // capture
|
response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type, Authorization"));
|
||||||
}
|
|
||||||
handle_eptr(eptr);
|
|
||||||
|
|
||||||
request.reply(response);
|
request.reply(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::handle_request(http_request request, function<void(json::value const&, json::value&)> action)
|
void Server::handle_request(http_request request, function<void(json::value const&, json::value&)> action)
|
||||||
{
|
{
|
||||||
auto answer = json::value::object();
|
|
||||||
request.extract_json().then([&answer, &action](pplx::task<json::value> task)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto const& jvalue = task.get();
|
|
||||||
|
|
||||||
if (!jvalue.is_null())
|
|
||||||
{
|
|
||||||
action(jvalue, answer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (http_exception const& e)
|
|
||||||
{
|
|
||||||
log(e.what());
|
|
||||||
}
|
|
||||||
}).wait();
|
|
||||||
|
|
||||||
http_response response(status_codes::OK);
|
http_response response(status_codes::OK);
|
||||||
response.headers().add(U("Allow"), U("GET, POST, PUT, OPTIONS"));
|
string authorization = to_base64("admin:" + to_string(password));
|
||||||
|
if (password == L"" || (request.headers().has(L"Authorization") && request.headers().find(L"Authorization")->second == L"Basic " + to_wstring(authorization)))
|
||||||
|
{
|
||||||
|
auto answer = json::value::object();
|
||||||
|
request.extract_json().then([&answer, &action](pplx::task<json::value> task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto const& jvalue = task.get();
|
||||||
|
|
||||||
|
if (!jvalue.is_null())
|
||||||
|
{
|
||||||
|
action(jvalue, answer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (http_exception const& e)
|
||||||
|
{
|
||||||
|
log(e.what());
|
||||||
|
}
|
||||||
|
}).wait();
|
||||||
|
response.set_body(answer);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response = status_codes::Unauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.headers().add(U("Allow"), U("GET, PUT, OPTIONS"));
|
||||||
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
|
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
|
||||||
response.headers().add(U("Access-Control-Allow-Methods"), U("GET, POST, PUT, OPTIONS"));
|
response.headers().add(U("Access-Control-Allow-Methods"), U("GET, PUT, OPTIONS"));
|
||||||
response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type"));
|
response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type, Authorization"));
|
||||||
response.set_body(answer);
|
|
||||||
request.reply(response);
|
request.reply(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,9 +214,15 @@ void Server::task()
|
|||||||
log(L"Starting server on " + address);
|
log(L"Starting server on " + address);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
log(L"Error reading configuration file. Starting server on " + address);
|
log(L"Error reading configuration file. Starting server on " + address);
|
||||||
|
|
||||||
|
if (config.is_object() && config.has_object_field(L"authentication") &&
|
||||||
|
config[L"authentication"].has_string_field(L"password"))
|
||||||
|
{
|
||||||
|
password = config[L"authentication"][L"password"].as_string();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
log(L"Error reading configuration file. No password set.");
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -337,9 +337,17 @@ void Unit::landAt(Coords loc) {
|
|||||||
setState(State::LAND);
|
setState(State::LAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::setTACANOn(bool newTACANOn) {
|
void Unit::setIsTanker(bool newIsTanker) {
|
||||||
TACANOn = newTACANOn;
|
isTanker = newIsTanker;
|
||||||
addMeasure(L"TACANOn", json::value(newTACANOn));
|
resetTask();
|
||||||
|
addMeasure(L"isTanker", json::value(newIsTanker));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unit::setIsAWACS(bool newIsAWACS) {
|
||||||
|
isAWACS = newIsAWACS;
|
||||||
|
resetTask();
|
||||||
|
addMeasure(L"isAWACS", json::value(newIsAWACS));
|
||||||
|
setEPLRS(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::setTACANChannel(int newTACANChannel) {
|
void Unit::setTACANChannel(int newTACANChannel) {
|
||||||
@@ -356,11 +364,6 @@ void Unit::setTACANCallsign(wstring newTACANCallsign) {
|
|||||||
addMeasure(L"TACANCallsign", json::value(newTACANCallsign));
|
addMeasure(L"TACANCallsign", json::value(newTACANCallsign));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::setRadioOn(bool newRadioOn) {
|
|
||||||
radioOn = newRadioOn;
|
|
||||||
addMeasure(L"radioOn", json::value(newRadioOn));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unit::setRadioFrequency(int newRadioFrequency) {
|
void Unit::setRadioFrequency(int newRadioFrequency) {
|
||||||
radioFrequency = newRadioFrequency;
|
radioFrequency = newRadioFrequency;
|
||||||
addMeasure(L"radioFrequency", json::value(newRadioFrequency));
|
addMeasure(L"radioFrequency", json::value(newRadioFrequency));
|
||||||
@@ -376,6 +379,19 @@ void Unit::setRadioCallsignNumber(int newRadioCallsignNumber) {
|
|||||||
addMeasure(L"radioCallsignNumber", json::value(newRadioCallsignNumber));
|
addMeasure(L"radioCallsignNumber", json::value(newRadioCallsignNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Unit::setEPLRS(bool state)
|
||||||
|
{
|
||||||
|
std::wostringstream commandSS;
|
||||||
|
commandSS << "{"
|
||||||
|
<< "id = 'EPLRS',"
|
||||||
|
<< "params = {"
|
||||||
|
<< "value = " << (state? "true": "false") << ", "
|
||||||
|
<< "}"
|
||||||
|
<< "}";
|
||||||
|
Command* command = dynamic_cast<Command*>(new SetCommand(ID, commandSS.str()));
|
||||||
|
scheduler->appendCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
void Unit::setTACAN()
|
void Unit::setTACAN()
|
||||||
{
|
{
|
||||||
std::wostringstream commandSS;
|
std::wostringstream commandSS;
|
||||||
@@ -383,9 +399,9 @@ void Unit::setTACAN()
|
|||||||
<< "id = 'ActivateBeacon',"
|
<< "id = 'ActivateBeacon',"
|
||||||
<< "params = {"
|
<< "params = {"
|
||||||
<< "type = " << ((TACANXY.compare(L"X") == 0)? 4: 5) << ","
|
<< "type = " << ((TACANXY.compare(L"X") == 0)? 4: 5) << ","
|
||||||
<< "system = 4,"
|
<< "system = 3,"
|
||||||
<< "name = Olympus_TACAN,"
|
<< "name = \"Olympus_TACAN\","
|
||||||
<< "callsign = " << TACANCallsign << ", "
|
<< "callsign = \"" << TACANCallsign << "\", "
|
||||||
<< "frequency = " << TACANChannelToFrequency(TACANChannel, TACANXY) << ","
|
<< "frequency = " << TACANChannelToFrequency(TACANChannel, TACANXY) << ","
|
||||||
<< "}"
|
<< "}"
|
||||||
<< "}";
|
<< "}";
|
||||||
|
|||||||
81
third-party/base64/include/base64.hpp
vendored
Normal file
81
third-party/base64/include/base64.hpp
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#ifndef BASE_64_HPP
|
||||||
|
#define BASE_64_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace base64 {
|
||||||
|
|
||||||
|
inline std::string get_base64_chars() {
|
||||||
|
static std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
|
"0123456789+/";
|
||||||
|
return base64_chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string to_base64(std::string const &data) {
|
||||||
|
int counter = 0;
|
||||||
|
uint32_t bit_stream = 0;
|
||||||
|
const std::string base64_chars = get_base64_chars();
|
||||||
|
std::string encoded;
|
||||||
|
int offset = 0;
|
||||||
|
for (unsigned char c : data) {
|
||||||
|
auto num_val = static_cast<unsigned int>(c);
|
||||||
|
offset = 16 - counter % 3 * 8;
|
||||||
|
bit_stream += num_val << offset;
|
||||||
|
if (offset == 16) {
|
||||||
|
encoded += base64_chars.at(bit_stream >> 18 & 0x3f);
|
||||||
|
}
|
||||||
|
if (offset == 8) {
|
||||||
|
encoded += base64_chars.at(bit_stream >> 12 & 0x3f);
|
||||||
|
}
|
||||||
|
if (offset == 0 && counter != 3) {
|
||||||
|
encoded += base64_chars.at(bit_stream >> 6 & 0x3f);
|
||||||
|
encoded += base64_chars.at(bit_stream & 0x3f);
|
||||||
|
bit_stream = 0;
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
if (offset == 16) {
|
||||||
|
encoded += base64_chars.at(bit_stream >> 12 & 0x3f);
|
||||||
|
encoded += "==";
|
||||||
|
}
|
||||||
|
if (offset == 8) {
|
||||||
|
encoded += base64_chars.at(bit_stream >> 6 & 0x3f);
|
||||||
|
encoded += '=';
|
||||||
|
}
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string from_base64(std::string const &data) {
|
||||||
|
int counter = 0;
|
||||||
|
uint32_t bit_stream = 0;
|
||||||
|
std::string decoded;
|
||||||
|
int offset = 0;
|
||||||
|
const std::string base64_chars = get_base64_chars();
|
||||||
|
for (unsigned char c : data) {
|
||||||
|
auto num_val = base64_chars.find(c);
|
||||||
|
if (num_val != std::string::npos) {
|
||||||
|
offset = 18 - counter % 4 * 6;
|
||||||
|
bit_stream += num_val << offset;
|
||||||
|
if (offset == 12) {
|
||||||
|
decoded += static_cast<char>(bit_stream >> 16 & 0xff);
|
||||||
|
}
|
||||||
|
if (offset == 6) {
|
||||||
|
decoded += static_cast<char>(bit_stream >> 8 & 0xff);
|
||||||
|
}
|
||||||
|
if (offset == 0 && counter != 4) {
|
||||||
|
decoded += static_cast<char>(bit_stream & 0xff);
|
||||||
|
bit_stream = 0;
|
||||||
|
}
|
||||||
|
} else if (c != '=') {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BASE_64_HPP
|
||||||
Reference in New Issue
Block a user