From cbb8920de736922ece2a84921ddafa4a59c7375e Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Wed, 19 Jul 2023 17:41:05 +0200 Subject: [PATCH 1/3] Styled the server load panel --- client/demo.js | 4 +- client/public/stylesheets/layout/layout.css | 11 ++++- client/public/stylesheets/olympus.css | 1 + .../stylesheets/panels/connectionstatus.css | 2 +- .../stylesheets/panels/serverstatus.css | 43 +++++++++++++++++++ client/src/index.ts | 9 +++- client/src/panels/connectionstatuspanel.ts | 8 ---- client/src/panels/serverstatuspanel.ts | 26 +++++++++++ client/src/server/server.ts | 6 +-- client/views/index.ejs | 1 + client/views/panels/serverstatus.ejs | 10 +++++ 11 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 client/public/stylesheets/panels/serverstatus.css create mode 100644 client/src/panels/serverstatuspanel.ts create mode 100644 client/views/panels/serverstatus.ejs diff --git a/client/demo.js b/client/demo.js index 6c7c354b..a92efcd6 100644 --- a/client/demo.js +++ b/client/demo.js @@ -290,8 +290,10 @@ class DemoDataGenerator { } logs(req, res){ - var ret = {logs: {}}; + var ret = {logs: {"1": "I'm a log!", "2": "I'm a different log!"}}; ret.time = Date.now(); + ret.frameRate = 60; + ret.load = 0; res.send(JSON.stringify(ret)); }; diff --git a/client/public/stylesheets/layout/layout.css b/client/public/stylesheets/layout/layout.css index 63a525e6..b1053f4b 100644 --- a/client/public/stylesheets/layout/layout.css +++ b/client/public/stylesheets/layout/layout.css @@ -38,7 +38,16 @@ font-size: 12px; position: absolute; right: 10px; - width: 250px; + width: 180px; + z-index: 9999; +} + +#server-status-panel { + bottom: 20px; + font-size: 12px; + position: absolute; + right: 200px; + width: 300px; z-index: 9999; } diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css index b6f2db41..ae397f53 100644 --- a/client/public/stylesheets/olympus.css +++ b/client/public/stylesheets/olympus.css @@ -3,6 +3,7 @@ @import url("atc/unitdatatable.css"); @import url("aic/aic.css"); @import url("panels/connectionstatus.css"); +@import url("panels/serverstatus.css"); @import url("panels/mouseinfo.css"); @import url("panels/unitcontrol.css"); @import url("panels/unitinfo.css"); diff --git a/client/public/stylesheets/panels/connectionstatus.css b/client/public/stylesheets/panels/connectionstatus.css index 715850a9..2b7dcec4 100644 --- a/client/public/stylesheets/panels/connectionstatus.css +++ b/client/public/stylesheets/panels/connectionstatus.css @@ -11,7 +11,7 @@ } #connection-status-panel[data-is-connected] dt::before { - content: "Connected FPS: " attr(data-framerate) " Load: " attr(data-load); + content: "Connected"; } #connection-status-panel[data-is-connected] dd::after { diff --git a/client/public/stylesheets/panels/serverstatus.css b/client/public/stylesheets/panels/serverstatus.css new file mode 100644 index 00000000..c001de99 --- /dev/null +++ b/client/public/stylesheets/panels/serverstatus.css @@ -0,0 +1,43 @@ +#server-status-panel { + display: flex; + flex-direction: row; + justify-content: space-between; + column-gap: 10px; +} + +#server-status-panel .ol-data-grid { + width: 100%; +} + +#server-status-panel .ol-data-grid:first-of-type { + border-right: 1px solid gray; + padding-right: 10px; +} + +#server-status-panel dd { + font-weight: bold; +} + +.fps-low { + color: red; +} + +.fps-medium { + color: orange; +} + +.fps-high { + color: lightgreen; +} + +.load-low { + color: lightgreen; +} + +.load-medium { + color: orange; +} + +.load-high { + color: red; +} \ No newline at end of file diff --git a/client/src/index.ts b/client/src/index.ts index 07787e68..6c20538b 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -17,6 +17,7 @@ import { Dropdown } from "./controls/dropdown"; import { HotgroupPanel } from "./panels/hotgrouppanel"; import { SVGInjector } from "@tanem/svg-injector"; import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "./constants/constants"; +import { ServerStatusPanel } from "./panels/serverstatuspanel"; var map: Map; @@ -28,6 +29,7 @@ var atc: ATC; var unitInfoPanel: UnitInfoPanel; var connectionStatusPanel: ConnectionStatusPanel; +var serverStatusPanel: ServerStatusPanel; var unitControlPanel: UnitControlPanel; var mouseInfoPanel: MouseInfoPanel; var logPanel: LogPanel; @@ -53,9 +55,10 @@ function setup() { unitInfoPanel = new UnitInfoPanel("unit-info-panel"); unitControlPanel = new UnitControlPanel("unit-control-panel"); connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel"); + serverStatusPanel = new ServerStatusPanel("server-status-panel"); mouseInfoPanel = new MouseInfoPanel("mouse-info-panel"); hotgroupPanel = new HotgroupPanel("hotgroup-panel"); - //logPanel = new LogPanel("log-panel"); + /* Popups */ infoPopup = new Popup("info-popup"); @@ -247,6 +250,10 @@ export function getConnectionStatusPanel() { return connectionStatusPanel; } +export function getServerStatusPanel() { + return serverStatusPanel; +} + export function getHotgroupPanel() { return hotgroupPanel; } diff --git a/client/src/panels/connectionstatuspanel.ts b/client/src/panels/connectionstatuspanel.ts index 686595c2..54a3f7f6 100644 --- a/client/src/panels/connectionstatuspanel.ts +++ b/client/src/panels/connectionstatuspanel.ts @@ -8,12 +8,4 @@ export class ConnectionStatusPanel extends Panel { update(connected: boolean) { this.getElement().toggleAttribute( "data-is-connected", connected ); } - - setMetrics(frameRate: number, load: number) { - const dt = this.getElement().querySelector("dt"); - if (dt) { - dt.dataset["framerate"] = String(frameRate); - dt.dataset["load"] = String(load); - } - } } \ No newline at end of file diff --git a/client/src/panels/serverstatuspanel.ts b/client/src/panels/serverstatuspanel.ts new file mode 100644 index 00000000..33cb1b7a --- /dev/null +++ b/client/src/panels/serverstatuspanel.ts @@ -0,0 +1,26 @@ +import { Panel } from "./panel"; + +export class ServerStatusPanel extends Panel { + constructor(ID: string) { + super(ID); + } + + update(frameRate: number, load: number) { + const frameRateEl = this.getElement().querySelector("#server-frame-rate"); + if (frameRateEl) { + frameRateEl.textContent = `${frameRate}`; + frameRateEl.classList.toggle("fps-high", frameRate >= 60) + frameRateEl.classList.toggle("fps-medium", frameRate >= 30 && frameRate < 60) + frameRateEl.classList.toggle("fps-low", frameRate <= 30) + } + + const loadEl = this.getElement().querySelector("#server-load"); + if (loadEl) { + loadEl.textContent = `${load}`; + loadEl.classList.toggle("load-high", load >= 1000) + loadEl.classList.toggle("load-medium", load >= 100 && load < 1000) + loadEl.classList.toggle("load-low", load <= 100) + } + + } +} \ No newline at end of file diff --git a/client/src/server/server.ts b/client/src/server/server.ts index afa39e74..fcfcd4bb 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -1,5 +1,5 @@ import { LatLng } from 'leaflet'; -import { getConnectionStatusPanel, getInfoPopup, getMissionData, getUnitDataTable, getUnitsManager, setLoginStatus } from '..'; +import { getConnectionStatusPanel, getInfoPopup, getMissionData, getServerStatusPanel, getUnitDataTable, getUnitsManager, setLoginStatus } from '..'; import { GeneralSettings, Radio, TACAN } from '../@types/unit'; import { ROEs, emissionsCountermeasures, reactionsToThreat } from '../constants/constants'; @@ -59,8 +59,8 @@ export function GET(callback: CallableFunction, uri: string, options?: { time?: const result = JSON.parse(xmlHttp.responseText); lastUpdateTimes[uri] = callback(result); - if ("frameRate" in result && "load" in result) - getConnectionStatusPanel().setMetrics(result.frameRate, result.load); + if (result.frameRate && result.load) + getServerStatusPanel().update(result.frameRate, result.load); } } else if (xmlHttp.status == 401) { /* Bad credentials */ diff --git a/client/views/index.ejs b/client/views/index.ejs index 558d6b67..86ecd092 100644 --- a/client/views/index.ejs +++ b/client/views/index.ejs @@ -24,6 +24,7 @@ <%- include('panels/unitinfo.ejs') %> <%- include('panels/mouseinfo.ejs') %> <%- include('panels/connectionstatus.ejs') %> + <%- include('panels/serverstatus.ejs') %> <%- include('panels/hotgroup.ejs') %> <%- include('panels/navbar.ejs') %> diff --git a/client/views/panels/serverstatus.ejs b/client/views/panels/serverstatus.ejs new file mode 100644 index 00000000..6cf7d63b --- /dev/null +++ b/client/views/panels/serverstatus.ejs @@ -0,0 +1,10 @@ +
+
+
Server frame rate:
+
+
+
+
Olympus load:
+
+
+
\ No newline at end of file From 1d5f24dc1a98bc7c6ec29de0d14bfd171d650560 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Wed, 19 Jul 2023 20:56:19 +0200 Subject: [PATCH 2/3] Added check on repeated commands on scheduler --- client/src/server/server.ts | 2 +- scripts/OlympusCommand.lua | 4 ++-- src/core/include/commands.h | 30 +++++++++++++++--------------- src/core/src/commands.cpp | 28 ++++++++++++++-------------- src/core/src/core.cpp | 19 +++++++++---------- src/core/src/scheduler.cpp | 11 ++++++++--- src/logger/src/logger.cpp | 2 -- src/shared/include/defines.h | 2 +- 8 files changed, 50 insertions(+), 48 deletions(-) diff --git a/client/src/server/server.ts b/client/src/server/server.ts index fcfcd4bb..80a85da5 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -59,7 +59,7 @@ export function GET(callback: CallableFunction, uri: string, options?: { time?: const result = JSON.parse(xmlHttp.responseText); lastUpdateTimes[uri] = callback(result); - if (result.frameRate && result.load) + if (result.frameRate !== undefined && result.load !== undefined) getServerStatusPanel().update(result.frameRate, result.load); } } else if (xmlHttp.status == 401) { diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index f4fa8274..f1dd11c8 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -506,7 +506,7 @@ function Olympus.generateGroundUnitsTable(units) ["y"] = spawnLocation.z + value.dy, ["heading"] = 0, ["skill"] = "High", - ["name"] = "GroundUnit-" .. Olympus.unitCounter .. "-" .. #unitTable + 1 + ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1 } end else @@ -517,7 +517,7 @@ function Olympus.generateGroundUnitsTable(units) ["y"] = spawnLocation.z, ["heading"] = 0, ["skill"] = "High", - ["name"] = "GroundUnit-" .. Olympus.unitCounter .. "-" .. #unitTable + 1 + ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1 } end end diff --git a/src/core/include/commands.h b/src/core/include/commands.h index dd128127..263de2f0 100644 --- a/src/core/include/commands.h +++ b/src/core/include/commands.h @@ -95,7 +95,7 @@ class Command { public: unsigned int getPriority() { return priority; } - virtual string getString(lua_State* L) = 0; + virtual string getString() = 0; virtual unsigned int getLoad() = 0; protected: @@ -118,7 +118,7 @@ public: { priority = CommandPriority::HIGH; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return 2; } private: @@ -142,7 +142,7 @@ public: { priority = CommandPriority::LOW; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return 2; } private: @@ -162,7 +162,7 @@ public: { priority = immediate? CommandPriority::IMMEDIATE: CommandPriority::LOW; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return immediate? 1: 30; } private: @@ -184,7 +184,7 @@ public: { priority = immediate ? CommandPriority::IMMEDIATE : CommandPriority::LOW; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return immediate ? 1 : 30; } private: @@ -208,7 +208,7 @@ public: { priority = immediate ? CommandPriority::IMMEDIATE : CommandPriority::LOW; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return immediate ? 1 : 30; } private: @@ -235,7 +235,7 @@ public: { priority = immediate ? CommandPriority::IMMEDIATE : CommandPriority::LOW; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return immediate ? 1 : 30; } private: @@ -257,7 +257,7 @@ public: { priority = CommandPriority::LOW; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return 30; } private: @@ -277,7 +277,7 @@ public: priority = CommandPriority::HIGH; immediate = immediate; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return immediate? 1: 5; } private: @@ -296,7 +296,7 @@ public: { priority = CommandPriority::MEDIUM; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return 1; } private: @@ -313,7 +313,7 @@ public: { priority = CommandPriority::HIGH; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return 1; } private: @@ -330,7 +330,7 @@ public: { priority = CommandPriority::HIGH; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return 1; } private: @@ -361,7 +361,7 @@ public: { priority = CommandPriority::HIGH; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return 1; } private: @@ -382,7 +382,7 @@ public: { priority = CommandPriority::HIGH; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return 1; } private: @@ -400,7 +400,7 @@ public: { priority = CommandPriority::MEDIUM; }; - virtual string getString(lua_State* L); + virtual string getString(); virtual unsigned int getLoad() { return 4; } private: diff --git a/src/core/src/commands.cpp b/src/core/src/commands.cpp index 25d234c4..511abcd4 100644 --- a/src/core/src/commands.cpp +++ b/src/core/src/commands.cpp @@ -7,7 +7,7 @@ extern UnitsManager* unitsManager; /* Move command */ -string Move::getString(lua_State* L) +string Move::getString() { std::ostringstream commandSS; @@ -26,7 +26,7 @@ string Move::getString(lua_State* L) } /* Smoke command */ -string Smoke::getString(lua_State* L) +string Smoke::getString() { std::ostringstream commandSS; commandSS.precision(10); @@ -38,7 +38,7 @@ string Smoke::getString(lua_State* L) } /* Spawn ground units command */ -string SpawnGroundUnits::getString(lua_State* L) +string SpawnGroundUnits::getString() { if (unitTypes.size() != locations.size()) return ""; @@ -62,7 +62,7 @@ string SpawnGroundUnits::getString(lua_State* L) /* Spawn ground units command */ -string SpawnNavyUnits::getString(lua_State* L) +string SpawnNavyUnits::getString() { if (unitTypes.size() != locations.size()) return ""; @@ -85,7 +85,7 @@ string SpawnNavyUnits::getString(lua_State* L) } /* Spawn aircrafts command */ -string SpawnAircrafts::getString(lua_State* L) +string SpawnAircrafts::getString() { if (unitTypes.size() != locations.size() || unitTypes.size() != loadouts.size()) return ""; @@ -112,7 +112,7 @@ string SpawnAircrafts::getString(lua_State* L) /* Spawn helicopters command */ -string SpawnHelicopters::getString(lua_State* L) +string SpawnHelicopters::getString() { if (unitTypes.size() != locations.size() || unitTypes.size() != loadouts.size()) return ""; @@ -139,7 +139,7 @@ string SpawnHelicopters::getString(lua_State* L) /* Clone unit command */ -string Clone::getString(lua_State* L) +string Clone::getString() { Unit* unit = unitsManager->getUnit(ID); if (unit != nullptr) @@ -160,7 +160,7 @@ string Clone::getString(lua_State* L) } /* Delete unit command */ -string Delete::getString(lua_State* L) +string Delete::getString() { std::ostringstream commandSS; commandSS.precision(10); @@ -171,7 +171,7 @@ string Delete::getString(lua_State* L) } /* Set task command */ -string SetTask::getString(lua_State* L) +string SetTask::getString() { std::ostringstream commandSS; commandSS.precision(10); @@ -183,7 +183,7 @@ string SetTask::getString(lua_State* L) } /* Reset task command */ -string ResetTask::getString(lua_State* L) +string ResetTask::getString() { std::ostringstream commandSS; commandSS.precision(10); @@ -194,7 +194,7 @@ string ResetTask::getString(lua_State* L) } /* Set command command */ -string SetCommand::getString(lua_State* L) +string SetCommand::getString() { std::ostringstream commandSS; commandSS.precision(10); @@ -206,7 +206,7 @@ string SetCommand::getString(lua_State* L) } /* Set option command */ -string SetOption::getString(lua_State* L) +string SetOption::getString() { std::ostringstream commandSS; commandSS.precision(10); @@ -226,7 +226,7 @@ string SetOption::getString(lua_State* L) } /* Set onOff command */ -string SetOnOff::getString(lua_State* L) +string SetOnOff::getString() { std::ostringstream commandSS; commandSS.precision(10); @@ -239,7 +239,7 @@ string SetOnOff::getString(lua_State* L) } /* Explosion command */ -string Explosion::getString(lua_State* L) +string Explosion::getString() { std::ostringstream commandSS; commandSS.precision(10); diff --git a/src/core/src/core.cpp b/src/core/src/core.cpp index fa8690e8..502ad4ea 100644 --- a/src/core/src/core.cpp +++ b/src/core/src/core.cpp @@ -70,19 +70,18 @@ extern "C" DllExport int coreFrame(lua_State* L) frameCounter++; const std::chrono::duration executionDuration = std::chrono::system_clock::now() - lastExecution; - if (executionDuration.count() > EXECUTION_TIME_INTERVAL) { - if (scheduler != nullptr) { - scheduler->execute(L); - - if (executionDuration.count() > 0) { - scheduler->setFrameRate(frameCounter / executionDuration.count()); - frameCounter = 0; - } - - lastExecution = std::chrono::system_clock::now(); + if (executionDuration.count() > FRAMERATE_TIME_INTERVAL) { + if (executionDuration.count() > 0) { + scheduler->setFrameRate(frameCounter / executionDuration.count()); + frameCounter = 0; } + + lastExecution = std::chrono::system_clock::now(); } + if (scheduler != nullptr) + scheduler->execute(L); + return(0); } diff --git a/src/core/src/scheduler.cpp b/src/core/src/scheduler.cpp index a73cabfb..dac8bebf 100644 --- a/src/core/src/scheduler.cpp +++ b/src/core/src/scheduler.cpp @@ -18,9 +18,13 @@ Scheduler::~Scheduler() } -void Scheduler::appendCommand(Command* command) +void Scheduler::appendCommand(Command* newCommand) { - commands.push_back(command); + for (auto command : commands) { + if (command->getString().compare(newCommand->getString()) == 0 && command->getPriority() == newCommand->getPriority()) + return; + } + commands.push_back(newCommand); } int Scheduler::getLoad() @@ -47,13 +51,14 @@ void Scheduler::execute(lua_State* L) { if (command->getPriority() == priority) { - string commandString = "Olympus.protectedCall(" + command->getString(L) + ")"; + string commandString = "Olympus.protectedCall(" + command->getString() + ")"; if (dostring_in(L, "server", (commandString))) log("Error executing command " + commandString); else log("Command '" + commandString + "' executed correctly, current load " + to_string(getLoad())); load = command->getLoad(); commands.remove(command); + delete command; return; } } diff --git a/src/logger/src/logger.cpp b/src/logger/src/logger.cpp index 47b3ed37..a3605964 100644 --- a/src/logger/src/logger.cpp +++ b/src/logger/src/logger.cpp @@ -37,8 +37,6 @@ void Logger::Close() void Logger::toJSON(json::value& json, unsigned long long time) { lock_guard guard(mutexLock); - json[L"requestTime"] = time; - /* Loop on the logs in reverse since we are usually only interested in the very last added logs */ auto itr = m_logs.end(); while (itr != m_logs.begin()) diff --git a/src/shared/include/defines.h b/src/shared/include/defines.h index 3047901f..f061cd1c 100644 --- a/src/shared/include/defines.h +++ b/src/shared/include/defines.h @@ -10,4 +10,4 @@ #define BULLSEYE_URI "bullseyes" #define MISSION_URI "mission" -#define EXECUTION_TIME_INTERVAL 0.05 \ No newline at end of file +#define FRAMERATE_TIME_INTERVAL 0.05 \ No newline at end of file From 018e37cd2a505336e81985bc268c09756b2c38b3 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Thu, 20 Jul 2023 10:59:48 +0200 Subject: [PATCH 3/3] Minor graphic tweaks --- client/demo.js | 35 ++++++++++++++++++++++++++++++- client/src/constants/constants.ts | 1 - client/src/map/map.ts | 18 +++++++++++++--- client/src/units/dataextractor.ts | 8 +++++-- client/src/units/unit.ts | 15 +++++++------ client/src/units/unitsmanager.ts | 6 +++--- 6 files changed, 65 insertions(+), 18 deletions(-) diff --git a/client/demo.js b/client/demo.js index a92efcd6..04522530 100644 --- a/client/demo.js +++ b/client/demo.js @@ -14,7 +14,7 @@ const DEMO_UNIT_DATA = { TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 }, radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 }, generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false }, - ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ], + ammo: [{ quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } ], contacts: [{ID: 2, detectionMethod: 1}, {ID: 3, detectionMethod: 4}], activePath: [{lat: 38, lng: -115, alt: 0}, {lat: 38, lng: -114, alt: 0}] }, @@ -63,6 +63,38 @@ const DEMO_UNIT_DATA = { ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ], contacts: [{ID: 1, detectionMethod: 16}], activePath: [ ] + }, ["5"]:{ category: "GroundUnit", alive: true, human: false, controlled: true, coalition: 1, country: 0, name: "Gepard", unitName: "Cool guy 2-1", groupName: "Cool group 4", state: 1, task: "Being cool", + hasTask: false, position: { lat: 37.2, lng: -116.1, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50, + desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0, + formationOffset: { x: 0, y: 0, z: 0 }, + targetID: 0, + targetPosition: { lat: 0, lng: 0, alt: 0 }, + ROE: 2, + reactionToThreat: 1, + emissionsCountermeasures: 1, + TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 }, + radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 }, + generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false }, + ammo: [{ quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } ], + contacts: [{ID: 1, detectionMethod: 16}], + activePath: [ ], + isLeader: true + }, ["6"]:{ category: "GroundUnit", alive: true, human: false, controlled: true, coalition: 1, country: 0, name: "Gepard", unitName: "Cool guy 2-2", groupName: "Cool group 4", state: 1, task: "Being cool", + hasTask: false, position: { lat: 37.21, lng: -116.1, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50, + desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0, + formationOffset: { x: 0, y: 0, z: 0 }, + targetID: 0, + targetPosition: { lat: 0, lng: 0, alt: 0 }, + ROE: 2, + reactionToThreat: 1, + emissionsCountermeasures: 1, + TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 }, + radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 }, + generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false }, + ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ], + contacts: [{ID: 1, detectionMethod: 16}], + activePath: [ ], + isLeader: false } } @@ -128,6 +160,7 @@ class DemoDataGenerator { array = this.appendAmmo(array, unit.ammo, 35); array = this.appendContacts(array, unit.contacts, 36); array = this.appendActivePath(array, unit.activePath, 37); + array = this.appendUint8(array, unit.isLeader, 38); array = this.concat(array, this.uint8ToByteArray(255)); } res.end(Buffer.from(array, 'binary')); diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index 7e47dff6..54e2f760 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -1,6 +1,5 @@ import { LatLng, LatLngBounds } from "leaflet"; -export const HIDE_ALL = "Hide all"; export const GAME_MASTER = "Game master"; export const BLUE_COMMANDER = "Blue commander"; export const RED_COMMANDER = "Red commander"; diff --git a/client/src/map/map.ts b/client/src/map/map.ts index 88758a00..8bad025d 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -46,6 +46,7 @@ export class Map extends L.Map { #miniMapLayerGroup: L.LayerGroup; #temporaryMarkers: TemporaryUnitMarker[] = []; #selecting: boolean = false; + #isZooming: boolean = false; #destinationGroupRotation: number = 0; #computeDestinationRotation: boolean = false; @@ -67,7 +68,7 @@ export class Map extends L.Map { constructor(ID: string) { /* Init the leaflet map */ //@ts-ignore Needed because the boxSelect option is non-standard - super(ID, { preferCanvas: true, doubleClickZoom: false, zoomControl: false, boxZoom: false, boxSelect: true, zoomAnimation: true, maxBoundsViscosity: 1.0, minZoom: 7, keyboard: true, keyboardPanDelta: 0 }); + super(ID, { zoomSnap: 0, zoomDelta: 0.25, preferCanvas: true, doubleClickZoom: false, zoomControl: false, boxZoom: false, boxSelect: true, zoomAnimation: true, maxBoundsViscosity: 1.0, minZoom: 7, keyboard: true, keyboardPanDelta: 0 }); this.setView([37.23, -115.8], 10); this.#ID = ID; @@ -93,7 +94,8 @@ export class Map extends L.Map { /* Register event handles */ this.on("click", (e: any) => this.#onClick(e)); this.on("dblclick", (e: any) => this.#onDoubleClick(e)); - this.on("zoomstart", (e: any) => this.#onZoom(e)); + this.on("zoomstart", (e: any) => this.#onZoomStart(e)); + this.on("zoomend", (e: any) => this.#onZoomEnd(e)); this.on("drag", (e: any) => this.centerOnUnit(null)); this.on("contextmenu", (e: any) => this.#onContextMenu(e)); this.on('selectionstart', (e: any) => this.#onSelectionStart(e)); @@ -270,6 +272,10 @@ export class Map extends L.Map { this.#coalitionAreaContextMenu.hide(); } + isZooming() { + return this.#isZooming; + } + /* Mouse coordinates */ getMousePosition() { return this.#lastMousePosition; @@ -544,11 +550,17 @@ export class Map extends L.Map { this.#updateDestinationCursors(); } - #onZoom(e: any) { + #onZoomStart(e: any) { if (this.#centerUnit != null) this.#panToUnit(this.#centerUnit); + this.#isZooming = true; } + #onZoomEnd(e: any) { + this.#isZooming = false; + } + + #panToUnit(unit: Unit) { var unitPosition = new L.LatLng(unit.getPosition().lat, unit.getPosition().lng); this.setView(unitPosition, this.getZoom(), { animate: false }); diff --git a/client/src/units/dataextractor.ts b/client/src/units/dataextractor.ts index 9390be62..278d4a72 100644 --- a/client/src/units/dataextractor.ts +++ b/client/src/units/dataextractor.ts @@ -64,9 +64,13 @@ export class DataExtractor { extractString(length?: number) { if (length === undefined) length = this.extractUInt16() - const value = this.#decoder.decode(this.#buffer.slice(this.#seekPosition, this.#seekPosition + length)); + var stringBuffer = this.#buffer.slice(this.#seekPosition, this.#seekPosition + length); + var view = new Int8Array(stringBuffer); + var stringLength = length; + view.forEach((value: number, idx: number) => { if (value === 0) stringLength = idx; }); + const value = this.#decoder.decode(stringBuffer); this.#seekPosition += length; - return value; + return value.substring(0, stringLength).trim(); } extractChar() { diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index efaa85ab..7f7ded20 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -6,7 +6,7 @@ import { CustomMarker } from '../map/custommarker'; import { SVGInjector } from '@tanem/svg-injector'; import { UnitDatabase } from './unitdatabase'; import { TargetMarker } from '../map/targetmarker'; -import { BLUE_COMMANDER, BOMBING, CARPET_BOMBING, DLINK, DataIndexes, FIRE_AT_AREA, GAME_MASTER, HIDE_ALL, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, RED_COMMANDER, ROEs, RWR, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; +import { BLUE_COMMANDER, BOMBING, CARPET_BOMBING, DLINK, DataIndexes, FIRE_AT_AREA, GAME_MASTER, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, RED_COMMANDER, ROEs, RWR, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN, UnitIconOptions } from '../@types/unit'; import { DataExtractor } from './dataextractor'; import { groundUnitDatabase } from './groundunitdatabase'; @@ -157,6 +157,7 @@ export class Unit extends CustomMarker { this.on('contextmenu', (e) => this.#onContextMenu(e)); this.on('mouseover', () => { this.setHighlighted(true); }) this.on('mouseout', () => { this.setHighlighted(false); }) + getMap().on("zoomend", () => {this.#onZoom();}) /* Deselect units if they are hidden */ document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => { @@ -165,9 +166,7 @@ export class Unit extends CustomMarker { document.addEventListener("toggleUnitVisibility", (ev: CustomEventInit) => { window.setTimeout(() => { this.setSelected(this.getSelected() && !this.getHidden()) }, 300); - }); - - getMap().on("zoomend", () => {this.#onZoom();}) + }); } getCategory() { @@ -379,8 +378,6 @@ export class Unit extends CustomMarker { } belongsToCommandedCoalition() { - if (getUnitsManager().getCommandMode() === HIDE_ALL) - return false; if (getUnitsManager().getCommandMode() === BLUE_COMMANDER && this.#coalition !== "blue") return false; if (getUnitsManager().getCommandMode() === RED_COMMANDER && this.#coalition !== "red") @@ -499,7 +496,6 @@ export class Unit extends CustomMarker { (this.#controlled == false && hiddenUnits.includes("dcs")) || (hiddenUnits.includes(this.getMarkerCategory())) || (hiddenUnits.includes(this.#coalition)) || - (getUnitsManager().getCommandMode() === HIDE_ALL) || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0) || (!this.#isLeader && this.getCategory() == "GroundUnit" && getMap().getZoom() < 13)) && !(this.getSelected()); @@ -512,7 +508,10 @@ export class Unit extends CustomMarker { /* Add the marker if not present */ if (!getMap().hasLayer(this) && !this.getHidden()) { - this.addTo(getMap()); + if (getMap().isZooming()) + this.once("zoomend", () => {this.addTo(getMap())}) + else + this.addTo(getMap()); } /* Hide the marker if necessary*/ diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 6288918e..ce50335b 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -5,7 +5,7 @@ import { cloneUnit, deleteUnit, spawnAircrafts, spawnGroundUnits } from "../serv import { bearingAndDistanceToLatLng, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils"; import { CoalitionArea } from "../map/coalitionarea"; import { groundUnitDatabase } from "./groundunitdatabase"; -import { DataIndexes, HIDE_ALL, IADSDensities, IDLE, MOVE_UNIT } from "../constants/constants"; +import { DataIndexes, GAME_MASTER, IADSDensities, IDLE, MOVE_UNIT } from "../constants/constants"; import { DataExtractor } from "./dataextractor"; import { Contact } from "../@types/unit"; import { citiesDatabase } from "./citiesdatabase"; @@ -16,7 +16,7 @@ export class UnitsManager { #selectionEventDisabled: boolean = false; #pasteDisabled: boolean = false; #hiddenTypes: string[] = []; - #commandMode: string = HIDE_ALL; + #commandMode: string = GAME_MASTER; #requestDetectionUpdate: boolean = false; constructor() { @@ -93,7 +93,7 @@ export class UnitsManager { this.#units[ID]?.setData(dataExtractor); } - if (this.#requestDetectionUpdate) { + if (this.#requestDetectionUpdate && this.getCommandMode() != GAME_MASTER) { for (let ID in this.#units) { var unit = this.#units[ID]; if (!unit.belongsToCommandedCoalition())