diff --git a/client/public/stylesheets/selectionscroll.css b/client/public/stylesheets/contextmenu.css similarity index 100% rename from client/public/stylesheets/selectionscroll.css rename to client/public/stylesheets/contextmenu.css diff --git a/client/public/stylesheets/selectionwheel.css b/client/public/stylesheets/selectionwheel.css deleted file mode 100644 index a3ff61f7..00000000 --- a/client/public/stylesheets/selectionwheel.css +++ /dev/null @@ -1,102 +0,0 @@ -.ol-selection-wheel { - margin: 0; - position: fixed; - z-index: 1000; - width: 220px; - height: 220px; - overflow: visible; - display: flex; - align-items: center; - justify-content: center; -} - -.ol-wheel { - width: 100%; - border-radius: 50%; - background-color: var(--background-color-dark); - -webkit-mask: radial-gradient(transparent 30%, #000 31%); - mask: radial-gradient(transparent 30%, #000 31%); - transition: background-color 0.2s; -} - -.ol-wheel:before { - content: ""; - display: block; - padding-top: 100%; -} - -.selection-wheel-button { - position: fixed; - z-index: 1000; - width: 50px; - height: 50px; - opacity: 0; - /*transition: opacity var(--animation_duration), left var(--animation_duration), top var(--animation_duration);*/ - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; -} - -.selection-wheel-image { - width: 45px; - height: 45px; - /*filter: invert(100%);*/ - transition: width var(--animation_duration), height var(--animation_duration); - filter: invert(100%); -} - -.selection-wheel-button:hover { - -} - -.selection-wheel-button:hover .selection-wheel-image { - width: 50px; - height: 50px; - /*filter: invert(21%) sepia(23%) saturate(775%) hue-rotate(170deg) brightness(92%) contrast(90%);*/ -} - -.ol-selection-wheel label { - width: 0; - height: 0; -} - -.ol-selection-wheel input { - width: 0; - height: 0; -} - -.ol-selection-wheel-switch { - position: absolute; - top: 50%; - left: 50%; - display: inline-block; - width: 60px; - height: 34px; - background-color: var(--active-coalition-color); - border-radius: 17px; - margin-left: -30px; - margin-top: -17px; - cursor: pointer; -} - -.ol-selection-wheel-switch:before { - position: absolute; - content: ""; - height: 26px; - width: 26px; - left: 4px; - bottom: 4px; - background-color: white; - -webkit-transition: 0.2s; - transition: 0.2s; - border-radius: 50%; - -} - -input:checked+.ol-selection-wheel-switch:before { - -webkit-transform: translateX(26px); - -ms-transform: translateX(26px); - transform: translateX(26px); -} \ No newline at end of file diff --git a/client/public/stylesheets/style.css b/client/public/stylesheets/style.css index a1fd4c99..b853db9c 100644 --- a/client/public/stylesheets/style.css +++ b/client/public/stylesheets/style.css @@ -3,7 +3,7 @@ @import url("dropdown.css"); @import url("selectionwheel.css"); -@import url("selectionscroll.css"); +@import url("contextmenu.css"); @import url("unitmarker.css"); @import url("airbasemarker.css"); diff --git a/client/src/@types/dom.d.ts b/client/src/@types/dom.d.ts new file mode 100644 index 00000000..5f363bfb --- /dev/null +++ b/client/src/@types/dom.d.ts @@ -0,0 +1,29 @@ +interface CustomEventMap { + "unitSelection": CustomEvent, + "unitsSelection": CustomEvent, + "clearSelection": CustomEvent<>, + "unitCreation": CustomEvent, + "unitDeletion": CustomEvent, + "unitUpdated": CustomEvent, + "unitMoveCommand": CustomEvent, + "unitAttackCommand": CustomEvent, + "unitLandCommand": CustomEvent, + "unitSetAltitudeCommand": CustomEvent, + "unitSetSpeedCommand": CustomEvent, + "unitSetROECommand": CustomEvent, + "unitSetReactionToThreatCommand": CustomEvent, + "groupCreation": CustomEvent, + "groupDeletion": CustomEvent, + "mapStateChanged": CustomEvent, + "mapContextMenu": CustomEvent<> +} + +declare global { + interface Document { + addEventListener(type: K, + listener: (this: Document, ev: CustomEventMap[K]) => void): void; + dispatchEvent(ev: CustomEventMap[K]): void; + } +} + +export { }; \ No newline at end of file diff --git a/client/src/controls/selectionscroll.ts b/client/src/controls/contextmenu.ts similarity index 99% rename from client/src/controls/selectionscroll.ts rename to client/src/controls/contextmenu.ts index 40f638d2..a2ed287a 100644 --- a/client/src/controls/selectionscroll.ts +++ b/client/src/controls/contextmenu.ts @@ -1,7 +1,7 @@ import { LatLng } from "leaflet"; import { getActiveCoalition, setActiveCoalition } from ".."; -export class SelectionScroll { +export class ContextMenu { #container: HTMLElement | null; #display: string; diff --git a/client/src/controls/selectionwheel.ts b/client/src/controls/selectionwheel.ts deleted file mode 100644 index 76c30539..00000000 --- a/client/src/controls/selectionwheel.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { getActiveCoalition, setActiveCoalition } from ".."; -import { deg2rad } from "../other/utils"; - -export class SelectionWheel { - #container: HTMLElement | null; - #display: string; - - constructor(id: string) { - this.#container = document.getElementById(id); - this.#display = ''; - if (this.#container != null) { - this.#container.querySelector("#coalition-switch")?.addEventListener('change', (e) => this.#onSwitch(e)) - this.#display = this.#container.style.display; - this.hide(); - } - } - - show(x: number, y: number, options: any, showCoalition: boolean) { - /* Hide to remove buttons, if present */ - this.hide(); - - if (this.#container != null) { - this.#container.style.display = this.#display; - this.#container.style.left = x - 110 + "px"; - this.#container.style.top = y - 110 + "px"; - - var angularSize = 360 / options.length; - var r = 80; - - /* Create the buttons */ - for (let id in options) { - var button = document.createElement("div"); - button.classList.add("selection-wheel-button"); - button.style.left = x - 25 + "px"; - button.style.top = y - 25 + "px"; - button.addEventListener('click', (e) => options[id].callback(e)); - this.#container.appendChild(button); - var angle = parseInt(id) * angularSize; - button.style.opacity = "1"; - button.style.left = x + r * Math.sin(deg2rad(angle)) - 25 + "px"; - button.style.top = y - r * Math.cos(deg2rad(angle)) - 25 + "px"; - - var image = document.createElement("img"); - image.classList.add("selection-wheel-image"); - image.src = `images/buttons/${options[id].src}` - image.title = options[id].tooltip; - if ('tint' in options[id]) { - button.style.setProperty('background-color', options[id].tint); - image.style.opacity = "0"; - } - button.appendChild(image); - } - - /* Hide the coalition switch if required */ - var switchContainer = this.#container.querySelector("#coalition-switch-container"); - if (showCoalition == false) { - switchContainer.style.display = "none"; - document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--neutral-coalition-color")); - } - else { - switchContainer.style.display = "block"; - if (getActiveCoalition() == "blue") - document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--blue-coalition-color")); - else - document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--red-coalition-color")); - } - } - } - - hide() { - if (this.#container != null) { - this.#container.style.display = "none"; - var buttons = this.#container.querySelectorAll(".selection-wheel-button"); - for (let child of buttons) { - this.#container.removeChild(child); - } - } - } - - #onSwitch(e: any) { - if (this.#container != null) { - if (e.currentTarget.checked) { - document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--red-coalition-color")); - setActiveCoalition("red"); - } - else { - document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--blue-coalition-color")); - setActiveCoalition("blue"); - } - } - } -} \ No newline at end of file diff --git a/client/src/index.ts b/client/src/index.ts index fc80d99d..0c753d02 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -1,9 +1,8 @@ import { Map } from "./map/map" import { getDataFromDCS } from "./dcs/dcs" -import { SelectionWheel } from "./controls/selectionwheel"; import { UnitsManager } from "./units/unitsmanager"; import { UnitInfoPanel } from "./panels/unitinfopanel"; -import { SelectionScroll } from "./controls/selectionscroll"; +import { ContextMenu } from "./controls/contextmenu"; import { Dropdown } from "./controls/dropdown"; import { ConnectionStatusPanel } from "./panels/connectionstatuspanel"; import { MissionData } from "./missiondata/missiondata"; @@ -14,8 +13,7 @@ import { LogPanel } from "./panels/logpanel"; /* TODO: should this be a class? */ var map: Map; -var selectionWheel: SelectionWheel; -var selectionScroll: SelectionScroll; +var contextMenu: ContextMenu; var unitsManager: UnitsManager; var missionData: MissionData; @@ -38,8 +36,7 @@ function setup() { unitsManager = new UnitsManager(); missionData = new MissionData(); - selectionWheel = new SelectionWheel("selection-wheel"); - selectionScroll = new SelectionScroll("selection-scroll"); + contextMenu = new ContextMenu("selection-scroll"); unitInfoPanel = new UnitInfoPanel("unit-info-panel"); unitControlPanel = new UnitControlPanel("unit-control-panel"); @@ -78,12 +75,8 @@ export function getMissionData() { return missionData; } -export function getSelectionWheel() { - return selectionWheel; -} - -export function getSelectionScroll() { - return selectionScroll; +export function getContextMenu() { + return contextMenu; } export function getUnitsManager() { diff --git a/client/src/map/map.ts b/client/src/map/map.ts index 91a0aa24..a144a3ad 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -1,8 +1,8 @@ import * as L from "leaflet" -import { getSelectionWheel, getSelectionScroll, getUnitsManager, getActiveCoalition, getMouseInfoPanel } from ".."; +import { getContextMenu, getUnitsManager, getActiveCoalition, getMouseInfoPanel } from ".."; import { spawnAircraft, spawnGroundUnit, spawnSmoke } from "../dcs/dcs"; import { bearing, distance, zeroAppend } from "../other/utils"; -import { aircraftDatabase, getAircraftLabelsByRole, getLoadoutsByName, getLoadoutNamesByRole, getAircraftNameByLabel } from "../units/aircraftDatabase"; +import { getAircraftLabelsByRole, getLoadoutsByName, getLoadoutNamesByRole, getAircraftNameByLabel } from "../units/aircraftDatabase"; import { unitTypes } from "../units/unitTypes"; import { BoxSelect } from "./boxselect"; @@ -14,14 +14,14 @@ export interface ClickEvent { latlng: L.LatLng; } -export interface SpawnEvent extends ClickEvent{ +export interface SpawnEvent extends ClickEvent { airbaseName: string | null; coalitionID: number | null; } export class Map extends L.Map { #state: string; - #layer?: L.TileLayer; + #layer: L.TileLayer | null = null; #preventLeftClick: boolean = false; #leftClickTimer: number = 0; #measurePoint: L.LatLng | null; @@ -113,39 +113,30 @@ export class Map extends L.Map { /* State machine */ setState(state: string) { this.#state = state; - if (this.#state === "IDLE") { L.DomUtil.removeClass(this.getContainer(),'crosshair-cursor-enabled'); } else if (this.#state === "MOVE_UNIT") { L.DomUtil.addClass(this.getContainer(),'crosshair-cursor-enabled'); } + document.dispatchEvent(new CustomEvent("mapStateChanged")); } getState() { return this.#state; } - /* Selection wheel */ - showSelectionWheel(e: ClickEvent | SpawnEvent, options: any, showCoalition: boolean) { - var x = e.x; - var y = e.y; - getSelectionWheel().show(x, y, options, showCoalition); - } - - hideSelectionWheel() { - getSelectionWheel().hide(); - } - /* Selection scroll */ - showSelectionScroll(e: ClickEvent | SpawnEvent, title: string, options: any, callback: CallableFunction, showCoalition: boolean = false) { + showContextMenu(e: ClickEvent | SpawnEvent, title: string, options: any, callback: CallableFunction, showCoalition: boolean = false) { var x = e.x; var y = e.y; - getSelectionScroll().show(x, y, title, options, callback, showCoalition); + getContextMenu().show(x, y, title, options, callback, showCoalition); + document.dispatchEvent(new CustomEvent("mapContextMenu")); } - hideSelectionScroll() { - getSelectionScroll().hide(); + hideContextMenu() { + getContextMenu().hide(); + document.dispatchEvent(new CustomEvent("mapContextMenu")); } getMousePosition() { @@ -156,11 +147,16 @@ export class Map extends L.Map { return this.containerPointToLatLng(this.#lastMousePosition); } + /* Spawn from air base */ + spawnFromAirbase(e: SpawnEvent) + { + this.#aircraftSpawnMenu(e); + } + /* Event handlers */ #onClick(e: any) { if (!this.#preventLeftClick) { - this.hideSelectionWheel(); - this.hideSelectionScroll(); + this.hideContextMenu(); if (this.#state === "IDLE") { if (e.originalEvent.ctrlKey) if (!this.#measurePoint) @@ -179,8 +175,7 @@ export class Map extends L.Map { else if (this.#state === "MOVE_UNIT") { this.setState("IDLE"); getUnitsManager().deselectAllUnits(); - this.hideSelectionWheel(); - this.hideSelectionScroll(); + this.hideContextMenu(); } } } @@ -190,8 +185,7 @@ export class Map extends L.Map { } #onContextMenu(e: any) { - this.hideSelectionWheel(); - this.hideSelectionScroll(); + this.hideContextMenu(); if (this.#state === "IDLE") { var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: null, coalitionID: null}; if (this.#state == "IDLE") { @@ -201,7 +195,7 @@ export class Map extends L.Map { { "tooltip": "Smoke", "src": "spawnSmoke.png", "callback": () => this.#smokeSpawnMenu(spawnEvent) }, //{ "tooltip": "Explosion", "src": "spawnExplosion.png", "callback": () => this.#explosionSpawnMenu(e) } ] - this.showSelectionScroll(spawnEvent, "Action", options, () => {}, false); + this.showContextMenu(spawnEvent, "Action", options, () => {}, false); } } else if (this.#state === "MOVE_UNIT") { @@ -265,12 +259,6 @@ export class Map extends L.Map { this.#hideMeasureLine(); } - /* Spawn from air base */ - spawnFromAirbase(e: SpawnEvent) - { - this.#aircraftSpawnMenu(e); - } - /* Spawning menus */ #aircraftSpawnMenu(e: SpawnEvent) { var options = [ @@ -284,9 +272,9 @@ export class Map extends L.Map { { 'coalition': true, 'tooltip': 'Transport', 'src': 'spawnTransport.png', 'callback': () => this.#selectAircraft(e, "transport") }, ] if (e.airbaseName != null) - this.showSelectionScroll(e, "Spawn at " + e.airbaseName, options, () => {}, true); + this.showContextMenu(e, "Spawn at " + e.airbaseName, options, () => {}, true); else - this.showSelectionScroll(e, "Spawn air unit", options, () => {}, true); + this.showContextMenu(e, "Spawn air unit", options, () => {}, true); } #groundUnitSpawnMenu(e: SpawnEvent) { @@ -299,20 +287,19 @@ export class Map extends L.Map { {'coalition': true, 'tooltip': 'Radar', 'src': 'spawnRadar.png', 'callback': () => this.#selectGroundUnit(e, "Radar")}, {'coalition': true, 'tooltip': 'Unarmed', 'src': 'spawnUnarmed.png', 'callback': () => this.#selectGroundUnit(e, "Unarmed")} ] - this.showSelectionScroll(e, "Spawn ground unit", options, () => {}, true); + this.showContextMenu(e, "Spawn ground unit", options, () => {}, true); } #smokeSpawnMenu(e: SpawnEvent) { - this.hideSelectionWheel(); - this.hideSelectionScroll(); + this.hideContextMenu(); var options = [ - {'tooltip': 'Red smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideSelectionWheel(); this.hideSelectionScroll(); spawnSmoke('red', e.latlng)}, 'tint': 'red'}, - {'tooltip': 'White smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideSelectionWheel(); this.hideSelectionScroll(); spawnSmoke('white', e.latlng)}, 'tint': 'white'}, - {'tooltip': 'Blue smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideSelectionWheel(); this.hideSelectionScroll(); spawnSmoke('blue', e.latlng)}, 'tint': 'blue'}, - {'tooltip': 'Green smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideSelectionWheel(); this.hideSelectionScroll(); spawnSmoke('green', e.latlng)}, 'tint': 'green'}, - {'tooltip': 'Orange smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideSelectionWheel(); this.hideSelectionScroll(); spawnSmoke('orange', e.latlng)}, 'tint': 'orange'}, + {'tooltip': 'Red smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('red', e.latlng)}, 'tint': 'red'}, + {'tooltip': 'White smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('white', e.latlng)}, 'tint': 'white'}, + {'tooltip': 'Blue smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('blue', e.latlng)}, 'tint': 'blue'}, + {'tooltip': 'Green smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('green', e.latlng)}, 'tint': 'green'}, + {'tooltip': 'Orange smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('orange', e.latlng)}, 'tint': 'orange'}, ] - this.showSelectionScroll(e, "Spawn smoke", options, () => {}, false); + this.showContextMenu(e, "Spawn smoke", options, () => {}, false); } #explosionSpawnMenu(e: SpawnEvent) { @@ -321,12 +308,10 @@ export class Map extends L.Map { /* Show unit selection for air units */ #selectAircraft(e: SpawnEvent, role: string) { - this.hideSelectionWheel(); - this.hideSelectionScroll(); + this.hideContextMenu(); var options = getAircraftLabelsByRole(role); - this.showSelectionScroll(e, "Select aircraft", options, (label: string) => { - this.hideSelectionWheel(); - this.hideSelectionScroll(); + this.showContextMenu(e, "Select aircraft", options, (label: string) => { + this.hideContextMenu(); var name = getAircraftNameByLabel(label); if (name != null) this.#unitSelectPayload(e, name, role); @@ -335,15 +320,13 @@ export class Map extends L.Map { /* Show weapon selection for air units */ #unitSelectPayload(e: SpawnEvent, unitType: string, role: string) { - this.hideSelectionWheel(); - this.hideSelectionScroll(); + this.hideContextMenu(); var options = getLoadoutNamesByRole(unitType, role); //options = payloadNames[unitType] if (options != undefined && options.length > 0) { options.sort(); - this.showSelectionScroll({x: e.x, y: e.y, latlng: e.latlng}, "Select loadout", options, (loadoutName: string) => { - this.hideSelectionWheel(); - this.hideSelectionScroll(); + this.showContextMenu({x: e.x, y: e.y, latlng: e.latlng}, "Select loadout", options, (loadoutName: string) => { + this.hideContextMenu(); var loadout = getLoadoutsByName(unitType, loadoutName); spawnAircraft(unitType, e.latlng, getActiveCoalition(), loadout.code, e.airbaseName); }, true); @@ -356,13 +339,11 @@ export class Map extends L.Map { /* Show unit selection for ground units */ #selectGroundUnit(e: any, group: string) { - this.hideSelectionWheel(); - this.hideSelectionScroll(); + this.hideContextMenu(); var options = unitTypes.vehicles[group]; options.sort(); - this.showSelectionScroll(e, "Select ground unit", options, (unitType: string) => { - this.hideSelectionWheel(); - this.hideSelectionScroll(); + this.showContextMenu(e, "Select ground unit", options, (unitType: string) => { + this.hideContextMenu(); spawnGroundUnit(unitType, e.latlng, getActiveCoalition()); }, true); } diff --git a/client/src/missiondata/missiondata.ts b/client/src/missiondata/missiondata.ts index f5ccedad..aedb2a76 100644 --- a/client/src/missiondata/missiondata.ts +++ b/client/src/missiondata/missiondata.ts @@ -79,7 +79,7 @@ export class MissionData options = ["Spawn unit", "Land here"]; else options = ["Spawn unit"]; - getMap().showSelectionScroll(e.originalEvent, e.sourceTarget.getName(), options, (option: string) => this.#onAirbaseOptionSelection(e, option), false); + getMap().showContextMenu(e.originalEvent, e.sourceTarget.getName(), options, (option: string) => this.#onAirbaseOptionSelection(e, option), false); } @@ -90,8 +90,7 @@ export class MissionData } else if (option === "Land here") { - getMap().hideSelectionWheel(); - getMap().hideSelectionScroll(); + getMap().hideContextMenu(); getUnitsManager().selectedUnitsLandAt(e.latlng); } } diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 38490982..8ec633ac 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -118,8 +118,7 @@ export class Unit { if ((this.alive || !selected) && this.#selectable && this.#selected != selected) { this.#selected = selected; this.#marker.setSelected(selected); - getUnitsManager().onUnitSelection(); - + document.dispatchEvent(new CustomEvent("unitSelection", {detail: this})); } } @@ -207,11 +206,11 @@ export class Unit { 'Follow' ] - getMap().showSelectionScroll(e.originalEvent, "Action: " + this.unitName, options, (action: string) => this.#executeAction(action)); + getMap().showContextMenu(e.originalEvent, "Action: " + this.unitName, options, (action: string) => this.#executeAction(action)); } #executeAction(action: string) { - getMap().hideSelectionScroll(); + getMap().hideContextMenu(); if (action === "Attack") getUnitsManager().selectedUnitsAttackUnit(this.ID); } diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index ea58ec74..af9ec2e7 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -13,6 +13,7 @@ export class UnitsManager { document.addEventListener('copy', () => this.copyUnits()); document.addEventListener('paste', () => this.pasteUnits()); + document.addEventListener('unitSelection', () => this.onUnitSelection()); } #updateUnitControlPanel() { @@ -98,13 +99,12 @@ export class UnitsManager { onUnitSelection() { if (this.getSelectedUnits().length > 0) { getMap().setState("MOVE_UNIT"); - //unitControlPanel.setEnabled(true); + document.dispatchEvent(new CustomEvent("unitsSelection", {detail: this.getSelectedUnits()})); } else { getMap().setState("IDLE"); - //unitControlPanel.setEnabled(false); + document.dispatchEvent(new CustomEvent("clearSelection")); } - this.#updateUnitControlPanel(); } diff --git a/client/views/selectionscroll.ejs b/client/views/contextmenu.ejs similarity index 100% rename from client/views/selectionscroll.ejs rename to client/views/contextmenu.ejs diff --git a/client/views/index.ejs b/client/views/index.ejs index 1ee2d5b5..bca01508 100644 --- a/client/views/index.ejs +++ b/client/views/index.ejs @@ -11,8 +11,7 @@
- <%- include('selectionwheel.ejs') %> - <%- include('selectionscroll.ejs') %> + <%- include('contextmenu.ejs') %>
<%- include('unitinfopanel.ejs') %> diff --git a/client/views/selectionwheel.ejs b/client/views/selectionwheel.ejs deleted file mode 100644 index bf714b7c..00000000 --- a/client/views/selectionwheel.ejs +++ /dev/null @@ -1,8 +0,0 @@ -
-
-
- -
\ No newline at end of file diff --git a/src/core/include/server.h b/src/core/include/server.h index 1d87ccf2..d55e5174 100644 --- a/src/core/include/server.h +++ b/src/core/include/server.h @@ -12,10 +12,13 @@ class Server { public: Server(lua_State* L); - ~Server(); + + void start(lua_State* L); + void stop(lua_State* L); private: std::thread* serverThread; + json::value answer; void handle_options(http_request request); void handle_get(http_request request); diff --git a/src/core/src/core.cpp b/src/core/src/core.cpp index 5bc61a81..804b9028 100644 --- a/src/core/src/core.cpp +++ b/src/core/src/core.cpp @@ -24,6 +24,8 @@ extern "C" DllExport int coreDeinit(lua_State* L) log("Olympus coreDeinit called successfully"); + server->stop(L); + delete unitsManager; delete server; delete scheduler; @@ -42,6 +44,8 @@ extern "C" DllExport int coreInit(lua_State* L) registerLuaFunctions(L); + server->start(L); + initialized = true; return(0); } diff --git a/src/core/src/server.cpp b/src/core/src/server.cpp index 670daea5..28ef3ad4 100644 --- a/src/core/src/server.cpp +++ b/src/core/src/server.cpp @@ -26,15 +26,25 @@ void handle_eptr(std::exception_ptr eptr) } Server::Server(lua_State* L): + serverThread(nullptr), runListener(true) { - LogInfo(L, "Starting RESTServer"); + +} + + +void Server::start(lua_State* L) +{ + log("Starting RESTServer"); serverThread = new thread(&Server::task, this); } -Server::~Server() +void Server::stop(lua_State* L) { + log("Stopping RESTServer"); runListener = false; + if (serverThread != nullptr) + serverThread->join(); } void Server::handle_options(http_request request) @@ -59,18 +69,18 @@ void Server::handle_get(http_request request) response.headers().add(U("Access-Control-Allow-Methods"), U("GET, POST, PUT, OPTIONS")); response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type")); - auto answer = json::value::object(); std::exception_ptr eptr; try { unitsManager->updateAnswer(answer); + + /* Get the logs from the logger */ + auto logs = json::value::object(); + logsToJSON(logs); // By reference, for thread safety + answer[L"airbases"] = airbasesData; answer[L"bullseye"] = bullseyeData; - answer[L"logs"] = json::value::object(); - - int i = 0; - for (auto log : getLogs()) - answer[L"logs"][to_wstring(i++)] = json::value::string(to_wstring(log)); - + answer[L"logs"] = logs; + response.set_body(answer); } catch (...) { @@ -156,7 +166,10 @@ void Server::task() while (runListener); - listener.close(); + listener.close() + .then([&listener]() {log("RESTServer stopping connections"); }) + .wait(); + log("RESTServer stopped listening"); } catch (exception const& e) diff --git a/src/logger/include/interface.h b/src/logger/include/interface.h index f0ef79bc..0d374e11 100644 --- a/src/logger/include/interface.h +++ b/src/logger/include/interface.h @@ -3,4 +3,4 @@ void DllExport log(const std::string& sMessage); void DllExport log(const std::wstring& sMessage); -std::list DllExport getLogs(); +void DllExport logsToJSON(json::value& json); diff --git a/src/logger/include/logger.h b/src/logger/include/logger.h index 96c126fb..c546879a 100644 --- a/src/logger/include/logger.h +++ b/src/logger/include/logger.h @@ -5,11 +5,12 @@ class Logger { public: - void Log(const string& sMessage); - void Log(const wstring& sMessage); - std::list getLogs() { return m_logs; }; + void log(const string& sMessage); + void log(const wstring& sMessage); + void toJSON(json::value& json); static Logger* GetLogger(); + private: Logger(); Logger(const Logger&) {}; // copy constructor is private @@ -20,6 +21,8 @@ private: static ofstream m_Logfile; static std::list m_logs; + mutex mutexLock; + void Open(); void Close(); }; diff --git a/src/logger/src/interface.cpp b/src/logger/src/interface.cpp index 5f1e714c..b2061177 100644 --- a/src/logger/src/interface.cpp +++ b/src/logger/src/interface.cpp @@ -6,15 +6,15 @@ void log(const string& message) { - LOGGER->Log(message); + LOGGER->log(message); } void log(const wstring& message) { - LOGGER->Log(message); + LOGGER->log(message); } -std::list getLogs() +void logsToJSON(json::value& json) { - return LOGGER->getLogs(); + LOGGER->toJSON(json); } \ No newline at end of file diff --git a/src/logger/src/logger.cpp b/src/logger/src/logger.cpp index 5e286104..8fdab6d4 100644 --- a/src/logger/src/logger.cpp +++ b/src/logger/src/logger.cpp @@ -16,8 +16,7 @@ Logger* Logger::GetLogger() if (m_pThis == NULL) { m_pThis = new Logger(); std::filesystem::path dirPath = std::filesystem::temp_directory_path(); - m_Logfile.open((dirPath.string() + m_sFileName).c_str(), ios::out | ios::app); - m_pThis->Log("**************************************************"); + m_Logfile.open((dirPath.string() + m_sFileName).c_str(), ios::out); } return m_pThis; } @@ -33,8 +32,17 @@ void Logger::Close() m_Logfile.close(); } -void Logger::Log(const string& message) +void Logger::toJSON(json::value& json) { + lock_guard guard(mutexLock); + int i = 0; + for (auto log : m_logs) + json[to_wstring(i++)] = json::value::string(to_wstring(log)); +} + +void Logger::log(const string& message) +{ + lock_guard guard(mutexLock); Open(); m_Logfile << CurrentDateTime() << ":\t"; m_Logfile << message << "\n"; @@ -42,8 +50,9 @@ void Logger::Log(const string& message) Close(); } -void Logger::Log(const wstring& message) +void Logger::log(const wstring& message) { + lock_guard guard(mutexLock); Open(); m_Logfile << CurrentDateTime() << ":\t"; m_Logfile << to_string(message) << "\n";