Merge branch 'performance-optimization' into 326-add-advanced-rts-options

This commit is contained in:
Pax1601
2023-07-20 11:02:26 +02:00
23 changed files with 220 additions and 80 deletions

View File

@@ -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'));
@@ -290,8 +323,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));
};

View File

@@ -49,7 +49,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;
}

View File

@@ -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");

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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";

View File

@@ -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");
@@ -243,6 +246,10 @@ export function getConnectionStatusPanel() {
return connectionStatusPanel;
}
export function getServerStatusPanel() {
return serverStatusPanel;
}
export function getHotgroupPanel() {
return hotgroupPanel;
}

View File

@@ -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 });

View File

@@ -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);
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -1,5 +1,5 @@
import { LatLng } from 'leaflet';
import { getConnectionStatusPanel, getInfoPopup, getMissionHandler, 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 !== undefined && result.load !== undefined)
getServerStatusPanel().update(result.frameRate, result.load);
}
} else if (xmlHttp.status == 401) {
/* Bad credentials */

View File

@@ -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() {

View File

@@ -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().getCommandedCoalition() !== this.#coalition)
return false;
return true;
@@ -497,7 +494,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());
@@ -510,7 +506,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*/

View File

@@ -5,7 +5,7 @@ import { cloneUnit, deleteUnit, spawnAircrafts, spawnGroundUnits, spawnHelicopte
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 { BLUE_COMMANDER, DataIndexes, GAME_MASTER, HIDE_ALL, IADSDensities, IDLE, MOVE_UNIT, RED_COMMANDER } from "../constants/constants";
import { BLUE_COMMANDER, DataIndexes, GAME_MASTER, IADSDensities, IDLE, MOVE_UNIT, RED_COMMANDER } from "../constants/constants";
import { DataExtractor } from "./dataextractor";
import { Contact } from "../@types/unit";
import { citiesDatabase } from "./citiesdatabase";
@@ -19,7 +19,7 @@ export class UnitsManager {
#selectionEventDisabled: boolean = false;
#pasteDisabled: boolean = false;
#hiddenTypes: string[] = [];
#commandMode: string = HIDE_ALL;
#commandMode: string = GAME_MASTER;
#requestDetectionUpdate: boolean = false;
constructor() {
@@ -96,7 +96,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())

View File

@@ -24,6 +24,7 @@
<%- include('panels/unitinfo.ejs') %>
<%- include('panels/mouseinfo.ejs') %>
<%- include('panels/connectionstatus.ejs') %>
<%- include('panels/serverstatus.ejs') %>
<%- include('panels/hotgroup.ejs') %>
<div id="toolbar-container">

View File

@@ -0,0 +1,10 @@
<div id="server-status-panel" class="ol-panel" oncontextmenu="return false;">
<dl class="ol-data-grid">
<dt>Server frame rate:</dt>
<dd id="server-frame-rate"></dd>
</dl>
<dl class="ol-data-grid">
<dt>Olympus load:</dt>
<dd id="server-load"></dd>
</dl>
</div>