From dd2e858db4f13d7893a47767de56aef7501e6799 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Fri, 23 Jun 2023 17:32:38 +0200 Subject: [PATCH] More work on client conversion to binary data --- client/src/@types/unit.d.ts | 141 +++++---- client/src/atc/atcboard.ts | 6 +- client/src/atc/board/tower.ts | 4 +- client/src/atc/unitdatatable.ts | 6 +- client/src/constants/constants.ts | 31 +- client/src/map/map.ts | 2 +- client/src/panels/mouseinfopanel.ts | 2 +- client/src/panels/unitcontrolpanel.ts | 59 ++-- client/src/panels/unitinfopanel.ts | 16 +- client/src/server/server.ts | 1 + client/src/units/dataextractor.ts | 219 ++++++++++++++ client/src/units/unit.ts | 398 ++++++++++++-------------- client/src/units/unitsmanager.ts | 120 +++----- src/core/include/unit.h | 4 +- src/core/src/unit.cpp | 20 +- 15 files changed, 597 insertions(+), 432 deletions(-) create mode 100644 client/src/units/dataextractor.ts diff --git a/client/src/@types/unit.d.ts b/client/src/@types/unit.d.ts index 0a7847b9..0c7d400a 100644 --- a/client/src/@types/unit.d.ts +++ b/client/src/@types/unit.d.ts @@ -1,60 +1,23 @@ -interface UpdateData { - [key: string]: any +import { LatLng } from "leaflet" + +interface UnitIconOptions { + showState: boolean, + showVvi: boolean, + showHotgroup: boolean, + showUnitIcon: boolean, + showShortLabel: boolean, + showFuel: boolean, + showAmmo: boolean, + showSummary: boolean, + rotateToHeading: boolean } -interface BaseData { - controlled: boolean; - name: string; - unitName: string; - groupName: string; - alive: boolean; - category: string; -} - -interface FlightData { - latitude: number; - longitude: number; - altitude: number; - heading: number; - speed: number; -} - -interface MissionData { - fuel: number; - flags: any; - ammo: any; - contacts: any; - hasTask: boolean; - coalition: string; -} - -interface FormationData { - leaderID: number; -} - -interface TaskData { - currentState: string; - currentTask: string; - activePath: any; - desiredSpeed: number; - desiredSpeedType: string; - desiredAltitude: number; - desiredAltitudeType: string; - targetLocation: any; - isTanker: boolean; - isAWACS: boolean; - onOff: boolean; - followRoads: boolean; - targetID: number; -} - -interface OptionsData { - ROE: string; - reactionToThreat: string; - emissionsCountermeasures: string; - TACAN: TACAN; - radio: Radio; - generalSettings: GeneralSettings; +interface GeneralSettings { + prohibitJettison: boolean; + prohibitAA: boolean; + prohibitAG: boolean; + prohibitAfterburner: boolean; + prohibitAirWpn: boolean; } interface TACAN { @@ -70,31 +33,55 @@ interface Radio { callsignNumber: number; } -interface GeneralSettings { - prohibitJettison: boolean; - prohibitAA: boolean; - prohibitAG: boolean; - prohibitAfterburner: boolean; - prohibitAirWpn: boolean; +interface Ammo { + quantity: number, + name: string, + guidance: number, + category: number, + missileCategory: number } -interface UnitIconOptions { - showState: boolean, - showVvi: boolean, - showHotgroup: boolean, - showUnitIcon: boolean, - showShortLabel: boolean, - showFuel: boolean, - showAmmo: boolean, - showSummary: boolean, - rotateToHeading: boolean +interface Contact { + ID: number, + detectionMethod: number } interface UnitData { - baseData: BaseData; - flightData: FlightData; - missionData: MissionData; - formationData: FormationData; - taskData: TaskData; - optionsData: OptionsData; + ID: number, + alive: boolean, + human: boolean, + controlled: boolean, + hasTask: boolean, + desiredAltitudeType: boolean, + desiredSpeedType: boolean, + isTanker: boolean, + isAWACS: boolean, + onOff: boolean, + followRoads: boolean, + EPLRS: boolean, + generalSettings: GeneralSettings + position: LatLng, + speed: number, + heading: number, + fuel: number, + desiredSpeed: number, + desiredAltitude: number, + targetID: number, + leaderID: number, + targetPosition: LatLng, + state: string, + ROE: string, + reactionToThreat: string, + emissionsCountermeasures: string, + TACAN: TACAN, + radio: Radio, + activePath: LatLng[], + ammo: Ammo[], + contacts: Contact[], + name: string, + unitName: string, + groupName: string, + category: string, + coalition: string, + task: string } \ No newline at end of file diff --git a/client/src/atc/atcboard.ts b/client/src/atc/atcboard.ts index 2edcbc9e..a65f55bd 100644 --- a/client/src/atc/atcboard.ts +++ b/client/src/atc/atcboard.ts @@ -115,7 +115,7 @@ export abstract class ATCBoard { addFlight( unit:Unit ) { - const baseData = unit.getBaseData(); + const baseData = unit.getData(); const unitCanBeAdded = () => { @@ -345,7 +345,7 @@ export abstract class ATCBoard { const results = Object.keys( units ).reduce( ( acc:Unit[], unitId:any ) => { const unit = units[ unitId ]; - const baseData = unit.getBaseData(); + const baseData = unit.getData(); if ( !unitIdsBeingMonitored.includes( parseInt( unitId ) ) && baseData.unitName.toLowerCase().indexOf( searchString ) > -1 ) { acc.push( unit ); @@ -359,7 +359,7 @@ export abstract class ATCBoard { results.forEach( unit => { - const baseData = unit.getBaseData(); + const baseData = unit.getData(); const a = document.createElement( "a" ); a.innerText = baseData.unitName; diff --git a/client/src/atc/board/tower.ts b/client/src/atc/board/tower.ts index c1698966..8800dfc2 100644 --- a/client/src/atc/board/tower.ts +++ b/client/src/atc/board/tower.ts @@ -34,13 +34,13 @@ export class ATCBoardTower extends ATCBoard { return; } - const flightData:FlightData = { + const flightData = { latitude: -1, longitude: -1, altitude: -1, heading: -1, speed: -1, - ...( selectableUnits.hasOwnProperty( flight.unitId ) ? selectableUnits[flight.unitId].getFlightData() : {} ) + ...( selectableUnits.hasOwnProperty( flight.unitId ) ? selectableUnits[flight.unitId].getData() : {} ) }; if ( !strip ) { diff --git a/client/src/atc/unitdatatable.ts b/client/src/atc/unitdatatable.ts index 1ef21f2c..f54a4553 100644 --- a/client/src/atc/unitdatatable.ts +++ b/client/src/atc/unitdatatable.ts @@ -12,8 +12,8 @@ export class UnitDataTable extends Panel { var units = getUnitsManager().getUnits(); const unitsArray = Object.values(units).sort((a: Unit, b: Unit) => { - const aVal = a.getBaseData().unitName?.toLowerCase(); - const bVal = b.getBaseData().unitName?.toLowerCase(); + const aVal = a.getData().unitName?.toLowerCase(); + const bVal = b.getData().unitName?.toLowerCase(); if (aVal > bVal) { return 1; @@ -48,7 +48,7 @@ export class UnitDataTable extends Panel { for (const unit of unitsArray) { - const dataset = [unit.getBaseData().unitName, unit.getBaseData().name, unit.getBaseData().category, (unit.getBaseData().controlled) ? "AI" : "Human"]; + const dataset = [unit.getData().unitName, unit.getData().name, unit.getData().category, (unit.getData().controlled) ? "AI" : "Human"]; addRow(el, dataset); } diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index 4fabffcf..1cff2f7d 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -1,12 +1,31 @@ import { LatLng, LatLngBounds, TileLayer, tileLayer } from "leaflet"; -export const ROEs: string[] = ["Hold", "Return", "Designated", "Free"]; -export const reactionsToThreat: string[] = ["None", "Manoeuvre", "Passive", "Evade"]; -export const emissionsCountermeasures: string[] = ["Silent", "Attack", "Defend", "Free"]; +export const states: string[] = ["none", "idle", "reach-destination", "attack", "follow", "land", "refuel", "AWACS", "tanker", "bomb-point", "carpet-bomb", "bomb-building", "fire-at-area"]; +export const ROEs: string[] = ["free", "designated", "return", "hold"]; +export const reactionsToThreat: string[] = ["none", "manoeuvre", "passive", "evade"]; +export const emissionsCountermeasures: string[] = ["silent", "attack", "defend", "free"]; -export const ROEDescriptions: string[] = ["Hold (Never fire)", "Return (Only fire if fired upon)", "Designated (Attack the designated target only)", "Free (Attack anyone)"]; -export const reactionsToThreatDescriptions: string[] = ["None (No reaction)", "Manoeuvre (no countermeasures)", "Passive (Countermeasures only, no manoeuvre)", "Evade (Countermeasures and manoeuvers)"]; -export const emissionsCountermeasuresDescriptions: string[] = ["Silent (Radar OFF, no ECM)", "Attack (Radar only for targeting, ECM only if locked)", "Defend (Radar for searching, ECM if locked)", "Always on (Radar and ECM always on)"]; +export const ROEDescriptions: string[] = [ + "Free (Attack anyone)", + "Designated (Attack the designated target only)", + "", + "Return (Only fire if fired upon)", + "Hold (Never fire)" +]; + +export const reactionsToThreatDescriptions: string[] = [ + "None (No reaction)", + "Manoeuvre (no countermeasures)", + "Passive (Countermeasures only, no manoeuvre)", + "Evade (Countermeasures and manoeuvers)" +]; + +export const emissionsCountermeasuresDescriptions: string[] = [ + "Silent (Radar OFF, no ECM)", + "Attack (Radar only for targeting, ECM only if locked)", + "Defend (Radar for searching, ECM if locked)", + "Always on (Radar and ECM always on)" +]; export const minSpeedValues: { [key: string]: number } = { Aircraft: 100, Helicopter: 0, NavyUnit: 0, GroundUnit: 0 }; export const maxSpeedValues: { [key: string]: number } = { Aircraft: 800, Helicopter: 300, NavyUnit: 60, GroundUnit: 60 }; diff --git a/client/src/map/map.ts b/client/src/map/map.ts index fc4e0cc7..30c0cc0c 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -547,7 +547,7 @@ export class Map extends L.Map { } #panToUnit(unit: Unit) { - var unitPosition = new L.LatLng(unit.getFlightData().latitude, unit.getFlightData().longitude); + var unitPosition = new L.LatLng(unit.getData().position.lat, unit.getData().position.lng); this.setView(unitPosition, this.getZoom(), { animate: false }); } diff --git a/client/src/panels/mouseinfopanel.ts b/client/src/panels/mouseinfopanel.ts index b5e7e8eb..dfb48d92 100644 --- a/client/src/panels/mouseinfopanel.ts +++ b/client/src/panels/mouseinfopanel.ts @@ -36,7 +36,7 @@ export class MouseInfoPanel extends Panel { var selectedUnitPosition = null; var selectedUnits = getUnitsManager().getSelectedUnits(); if (selectedUnits && selectedUnits.length == 1) - selectedUnitPosition = new LatLng(selectedUnits[0].getFlightData().latitude, selectedUnits[0].getFlightData().longitude); + selectedUnitPosition = new LatLng(selectedUnits[0].getData().position.lat, selectedUnits[0].getData().position.lng); /* Draw measures from selected unit, from pin location, and from bullseyes */ this.#drawMeasure("ref-measure-position", "measure-position", this.#measurePoint, mousePosition); diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index c5d6f489..df2b1b0a 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -8,6 +8,7 @@ import { Panel } from "./panel"; import { Switch } from "../controls/switch"; import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, emissionsCountermeasuresDescriptions, maxAltitudeValues, maxSpeedValues, minAltitudeValues, minSpeedValues, reactionsToThreat, reactionsToThreatDescriptions, speedIncrements } from "../constants/constants"; import { ftToM, knotsToMs, mToFt, msToKnots } from "../other/utils"; +import { GeneralSettings, Radio, TACAN } from "../@types/unit"; export class UnitControlPanel extends Panel { #altitudeSlider: Slider; @@ -98,13 +99,13 @@ export class UnitControlPanel extends Panel { if (units.length < 20) { this.getElement().querySelector("#selected-units-container")?.replaceChildren(...units.map((unit: Unit, index: number) => { var button = document.createElement("button"); - var callsign = unit.getBaseData().unitName || ""; - var label = unit.getDatabase()?.getByName(unit.getBaseData().name)?.label || unit.getBaseData().name; + var callsign = unit.getData().unitName || ""; + var label = unit.getDatabase()?.getByName(unit.getData().name)?.label || unit.getData().name; button.setAttribute("data-label", label); button.setAttribute("data-callsign", callsign); - button.setAttribute("data-coalition", unit.getMissionData().coalition); + button.setAttribute("data-coalition", unit.getData().coalition); button.classList.add("pill", "highlight-coalition") button.addEventListener("click", () => { @@ -139,12 +140,12 @@ export class UnitControlPanel extends Panel { element.toggleAttribute("data-show-advanced-settings-button", units.length == 1); /* Flight controls */ - var desiredAltitude: number | undefined = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredAltitude}); - var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredAltitudeType}); - var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredSpeed}); - var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredSpeedType}); - var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().onOff}); - var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().followRoads}); + var desiredAltitude: number | undefined = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().desiredAltitude}); + var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().desiredAltitudeType}); + var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().desiredSpeed}); + var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().desiredSpeedType}); + var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().onOff}); + var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().followRoads}); if (selectedUnitsTypes.length == 1) { this.#altitudeTypeSwitch.setValue(desiredAltitudeType != undefined? desiredAltitudeType == "AGL": undefined, false); @@ -170,15 +171,15 @@ export class UnitControlPanel extends Panel { /* Option buttons */ this.#optionButtons["ROE"].forEach((button: HTMLButtonElement) => { - button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().ROE === button.value)) + button.classList.toggle("selected", units.every((unit: Unit) => unit.getData().ROE === button.value)) }); this.#optionButtons["reactionToThreat"].forEach((button: HTMLButtonElement) => { - button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().reactionToThreat === button.value)) + button.classList.toggle("selected", units.every((unit: Unit) => unit.getData().reactionToThreat === button.value)) }); this.#optionButtons["emissionsCountermeasures"].forEach((button: HTMLButtonElement) => { - button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().emissionsCountermeasures === button.value)) + button.classList.toggle("selected", units.every((unit: Unit) => unit.getData().emissionsCountermeasures === button.value)) }); this.#onOffSwitch.setValue(onOff, false); @@ -207,11 +208,11 @@ export class UnitControlPanel extends Panel { const radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input") as HTMLInputElement; const unit = units[0]; - const roles = aircraftDatabase.getByName(unit.getBaseData().name)?.loadouts.map((loadout) => {return loadout.roles}) + const roles = aircraftDatabase.getByName(unit.getData().name)?.loadouts.map((loadout) => {return loadout.roles}) const tanker = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("Tanker"); const AWACS = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("AWACS"); - const radioMHz = Math.floor(unit.getOptionsData().radio.frequency / 1000000); - const radioDecimals = (unit.getOptionsData().radio.frequency / 1000000 - radioMHz) * 1000; + const radioMHz = Math.floor(unit.getData().radio.frequency / 1000000); + const radioDecimals = (unit.getData().radio.frequency / 1000000 - radioMHz) * 1000; /* Activate the correct options depending on unit type */ this.#advancedSettingsDialog.toggleAttribute("data-show-settings", !tanker && !AWACS); @@ -223,28 +224,28 @@ export class UnitControlPanel extends Panel { /* Set common properties */ // Name - unitNameEl.innerText = unit.getBaseData().unitName; + unitNameEl.innerText = unit.getData().unitName; // General settings - prohibitJettisonCheckbox.checked = unit.getOptionsData().generalSettings.prohibitJettison; - prohibitAfterburnerCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAfterburner; - prohibitAACheckbox.checked = unit.getOptionsData().generalSettings.prohibitAA; - prohibitAGCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAG; - prohibitAirWpnCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAirWpn; + prohibitJettisonCheckbox.checked = unit.getData().generalSettings.prohibitJettison; + prohibitAfterburnerCheckbox.checked = unit.getData().generalSettings.prohibitAfterburner; + prohibitAACheckbox.checked = unit.getData().generalSettings.prohibitAA; + prohibitAGCheckbox.checked = unit.getData().generalSettings.prohibitAG; + prohibitAirWpnCheckbox.checked = unit.getData().generalSettings.prohibitAirWpn; // Tasking - tankerCheckbox.checked = unit.getTaskData().isTanker; - AWACSCheckbox.checked = unit.getTaskData().isAWACS; + tankerCheckbox.checked = unit.getData().isTanker; + AWACSCheckbox.checked = unit.getData().isAWACS; // TACAN - TACANCheckbox.checked = unit.getOptionsData().TACAN.isOn; - TACANChannelInput.value = String(unit.getOptionsData().TACAN.channel); - TACANCallsignInput.value = String(unit.getOptionsData().TACAN.callsign); - this.#TACANXYDropdown.setValue(unit.getOptionsData().TACAN.XY); + TACANCheckbox.checked = unit.getData().TACAN.isOn; + TACANChannelInput.value = String(unit.getData().TACAN.channel); + TACANCallsignInput.value = String(unit.getData().TACAN.callsign); + this.#TACANXYDropdown.setValue(unit.getData().TACAN.XY); // Radio radioMhzInput.value = String(radioMHz); - radioCallsignNumberInput.value = String(unit.getOptionsData().radio.callsignNumber); + radioCallsignNumberInput.value = String(unit.getData().radio.callsignNumber); this.#radioDecimalsDropdown.setValue("." + radioDecimals); if (tanker) /* Set tanker specific options */ @@ -255,7 +256,7 @@ export class UnitControlPanel extends Panel { this.#radioCallsignDropdown.setOptions(["Enfield", "Springfield", "Uzi", "Colt", "Dodge", "Ford", "Chevy", "Pontiac"]); // This must be done after setting the options - if (!this.#radioCallsignDropdown.selectValue(unit.getOptionsData().radio.callsign - 1)) // Ensure the selected value is in the acceptable range + if (!this.#radioCallsignDropdown.selectValue(unit.getData().radio.callsign - 1)) // Ensure the selected value is in the acceptable range this.#radioCallsignDropdown.selectValue(0); } } diff --git a/client/src/panels/unitinfopanel.ts b/client/src/panels/unitinfopanel.ts index 91e18920..8ca140b0 100644 --- a/client/src/panels/unitinfopanel.ts +++ b/client/src/panels/unitinfopanel.ts @@ -51,21 +51,21 @@ export class UnitInfoPanel extends Panel { #onUnitUpdate(unit: Unit) { if (this.getElement() != null && this.getVisible() && unit.getSelected()) { - const baseData = unit.getBaseData(); + const baseData = unit.getData(); /* Set the unit info */ this.#unitLabel.innerText = aircraftDatabase.getByName(baseData.name)?.label || baseData.name; this.#unitName.innerText = baseData.unitName; - if (unit.getMissionData().flags.Human) + if (unit.getData().human) this.#unitControl.innerText = "Human"; else if (baseData.controlled) this.#unitControl.innerText = "Olympus controlled"; else this.#unitControl.innerText = "DCS Controlled"; - this.#fuelBar.style.width = String(unit.getMissionData().fuel + "%"); - this.#fuelPercentage.dataset.percentage = "" + unit.getMissionData().fuel; - this.#currentTask.dataset.currentTask = unit.getTaskData().currentTask !== "" ? unit.getTaskData().currentTask : "No task"; - this.#currentTask.dataset.coalition = unit.getMissionData().coalition; + this.#fuelBar.style.width = String(unit.getData().fuel + "%"); + this.#fuelPercentage.dataset.percentage = "" + unit.getData().fuel; + this.#currentTask.dataset.currentTask = unit.getData().task !== "" ? unit.getData().task : "No task"; + this.#currentTask.dataset.coalition = unit.getData().coalition; this.#silhouette.src = `/images/units/${unit.getDatabase()?.getByName(baseData.name)?.filename}`; this.#silhouette.classList.toggle("hide", unit.getDatabase()?.getByName(baseData.name)?.filename == undefined || unit.getDatabase()?.getByName(baseData.name)?.filename == ''); @@ -74,9 +74,9 @@ export class UnitInfoPanel extends Panel { const items = this.#loadoutContainer.querySelector("#loadout-items"); if (items) { - const ammo = Object.values(unit.getMissionData().ammo); + const ammo = Object.values(unit.getData().ammo); if (ammo.length > 0) { - items.replaceChildren(...Object.values(unit.getMissionData().ammo).map( + items.replaceChildren(...Object.values(unit.getData().ammo).map( (ammo: any) => { var el = document.createElement("div"); el.dataset.qty = ammo.count; diff --git a/client/src/server/server.ts b/client/src/server/server.ts index f6249184..ca392e5f 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -1,6 +1,7 @@ import { LatLng } from 'leaflet'; import { getConnectionStatusPanel, getInfoPopup, getMissionData, getUnitDataTable, getUnitsManager, setConnectionStatus } from '..'; import { SpawnOptions } from '../controls/mapcontextmenu'; +import { GeneralSettings, Radio, TACAN } from '../@types/unit'; var connected: boolean = false; var paused: boolean = false; diff --git a/client/src/units/dataextractor.ts b/client/src/units/dataextractor.ts new file mode 100644 index 00000000..8b45a504 --- /dev/null +++ b/client/src/units/dataextractor.ts @@ -0,0 +1,219 @@ +import { LatLng } from "leaflet"; +import { UnitData } from "../@types/unit"; +import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants"; + +export class DataExtractor { + #offset = 0; + #dataview: DataView; + #decoder: TextDecoder; + #buffer: ArrayBuffer; + + constructor(buffer: ArrayBuffer) { + this.#buffer = buffer; + this.#dataview = new DataView(this.#buffer); + this.#decoder = new TextDecoder("utf-8"); + } + + extractData(offset: number) { + this.#offset = offset; + + const ID = this.#extractUInt32(); + const bitmask = this.#extractUInt32(); + + const unitData: UnitData = { + ID: ID, + alive: this.#extractFromBitmask(bitmask, 0), + human: this.#extractFromBitmask(bitmask, 1), + controlled: this.#extractFromBitmask(bitmask, 2), + hasTask: this.#extractFromBitmask(bitmask, 3), + desiredAltitudeType: this.#extractFromBitmask(bitmask, 16), + desiredSpeedType: this.#extractFromBitmask(bitmask, 17), + isTanker: this.#extractFromBitmask(bitmask, 18), + isAWACS: this.#extractFromBitmask(bitmask, 19), + onOff: this.#extractFromBitmask(bitmask, 20), + followRoads: this.#extractFromBitmask(bitmask, 21), + EPLRS: this.#extractFromBitmask(bitmask, 22), + generalSettings: { + prohibitAA: this.#extractFromBitmask(bitmask, 23), + prohibitAfterburner: this.#extractFromBitmask(bitmask, 24), + prohibitAG: this.#extractFromBitmask(bitmask, 25), + prohibitAirWpn: this.#extractFromBitmask(bitmask, 26), + prohibitJettison: this.#extractFromBitmask(bitmask, 27), + }, + position: new LatLng( + this.#extractFloat64(), + this.#extractFloat64(), + this.#extractFloat64() + ), + speed: this.#extractFloat64(), + heading: this.#extractFloat64(), + fuel: this.#extractUInt16(), + desiredSpeed: this.#extractFloat64(), + desiredAltitude: this.#extractFloat64(), + targetID: this.#extractUInt32(), + leaderID: 0, + targetPosition: new LatLng( + this.#extractFloat64(), + this.#extractFloat64(), + this.#extractFloat64() + ), + state: this.#getState(this.#extractUint8()), + ROE: this.#getROE(this.#extractUint8()), + reactionToThreat: this.#getReactionToThreat(this.#extractUint8()), + emissionsCountermeasures: this.#getEmissionCountermeasure(this.#extractUint8()), + TACAN: { + isOn: this.#extractBool(), + channel: this.#extractUint8(), + XY: this.#extractChar(), + callsign: this.#extractString(4) + }, + radio: { + frequency: this.#extractUInt32(), + callsign: this.#extractUint8(), + callsignNumber: this.#extractUint8() + }, + activePath: [], + ammo: [], + contacts: [], + name: "", + unitName: "", + groupName: "", + category: "", + coalition: "", + task: "" + } + + const pathLength = this.#extractUInt16(); + const ammoLength = this.#extractUInt16(); + const contactsLength = this.#extractUInt16(); + const nameLength = this.#extractUInt16(); + const unitNameLength = this.#extractUInt16(); + const groupNameLength = this.#extractUInt16(); + const categoryLength = this.#extractUInt16(); + const coalitionLength = this.#extractUInt16(); + + if (pathLength > 0) { + unitData.activePath = []; + for (let idx = 0; idx < pathLength; idx++) { + unitData.activePath.push(new LatLng(this.#extractFloat64(), this.#extractFloat64(), this.#extractFloat64())); + } + } + + if (ammoLength > 0) { + unitData.ammo = []; + for (let idx = 0; idx < pathLength; idx++) { + unitData.ammo.push({ + quantity: this.#extractUInt16(), + name: this.#extractString(32), + guidance: this.#extractUint8(), + category: this.#extractUint8(), + missileCategory: this.#extractUint8() + }); + } + } + + if (contactsLength > 0) { + unitData.contacts = []; + for (let idx = 0; idx < pathLength; idx++) { + unitData.contacts.push({ + ID: this.#extractUInt32(), + detectionMethod: this.#extractUint8() + }); + } + } + + if (nameLength > 0) { + unitData.name = this.#extractString(nameLength); + } + + if (unitNameLength > 0) { + unitData.unitName = this.#extractString(unitNameLength); + } + + if (groupNameLength > 0) { + unitData.groupName = this.#extractString(groupNameLength); + } + + if (categoryLength > 0) { + unitData.category = this.#extractString(categoryLength); + } + + if (coalitionLength > 0) { + unitData.coalition = this.#extractString(coalitionLength); + } + + return {data: unitData, offset: this.#offset}; + } + + #extractBool() { + const value = this.#dataview.getUint8(this.#offset); + this.#offset += 1; + return value > 0; + } + + #extractUint8() { + const value = this.#dataview.getUint8(this.#offset); + this.#offset += 1; + return value; + } + + #extractUInt16() { + const value = this.#dataview.getUint16(this.#offset); + this.#offset += 2; + return value; + } + + #extractUInt32() { + const value = this.#dataview.getUint32(this.#offset); + this.#offset += 4; + return value; + } + + #extractFloat64() { + const value = this.#dataview.getFloat64(this.#offset); + this.#offset += 8; + return value; + } + + #extractFromBitmask(bitmask: number, position: number) { + return (bitmask >> position & 1) > 0; + } + + #extractString(length: number) { + const value = this.#decoder.decode(this.#buffer.slice(this.#offset, length)); + this.#offset += length; + return value; + } + + #extractChar() { + return this.#extractString(1); + } + + #getState(state: number) { + if (state < states.length) + return states[state]; + else + return states[0]; + } + + #getROE(ROE: number) { + if (ROE < ROEs.length) + return ROEs[ROE]; + else + return ROEs[0]; + } + + #getReactionToThreat(reactionToThreat: number) { + if (reactionToThreat < reactionsToThreat.length) + return reactionsToThreat[reactionToThreat]; + else + return reactionsToThreat[0]; + } + + #getEmissionCountermeasure(emissionCountermeasure: number) { + if (emissionCountermeasure < emissionsCountermeasures.length) + return emissionsCountermeasures[emissionCountermeasure]; + else + return emissionsCountermeasures[0]; + } +} \ No newline at end of file diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 200c19a6..4d2416ce 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -6,7 +6,8 @@ import { CustomMarker } from '../map/custommarker'; import { SVGInjector } from '@tanem/svg-injector'; import { UnitDatabase } from './unitdatabase'; import { TargetMarker } from '../map/targetmarker'; -import { BOMBING, CARPET_BOMBING, FIRE_AT_AREA, IDLE, MOVE_UNIT } from '../constants/constants'; +import { BOMBING, CARPET_BOMBING, FIRE_AT_AREA, IDLE, MOVE_UNIT, ROEs, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; +import { GeneralSettings, Radio, TACAN, UnitData, UnitIconOptions } from '../@types/unit'; var pathIcon = new Icon({ iconUrl: '/resources/theme/images/markers/marker-icon.png', @@ -18,55 +19,58 @@ export class Unit extends CustomMarker { ID: number; #data: UnitData = { - baseData: { - controlled: false, - name: "", - unitName: "", - groupName: "", - alive: true, - category: "", + ID: 0, + alive: false, + human: false, + controlled: false, + hasTask: false, + desiredAltitudeType: false, + desiredSpeedType: false, + isTanker: false, + isAWACS: false, + onOff: false, + followRoads: false, + EPLRS: false, + generalSettings: { + prohibitAA: false, + prohibitAfterburner: false, + prohibitAG: false, + prohibitAirWpn: false, + prohibitJettison: false }, - flightData: { - latitude: 0, - longitude: 0, - altitude: 0, - heading: 0, - speed: 0, + position: new LatLng(0, 0), + speed: 0, + heading: 0, + fuel: 0, + desiredSpeed: 0, + desiredAltitude: 0, + targetID: 0, + leaderID: 0, + targetPosition: new LatLng(0, 0), + state: states[0], + ROE: ROEs[0], + reactionToThreat: reactionsToThreat[0], + emissionsCountermeasures: emissionsCountermeasures[0], + TACAN: { + isOn: false, + XY: 'X', + callsign: '', + channel: 0 }, - missionData: { - fuel: 0, - flags: {}, - ammo: {}, - contacts: {}, - hasTask: false, - coalition: "", + radio: { + frequency: 0, + callsign: 0, + callsignNumber: 0 }, - formationData: { - leaderID: 0 - }, - taskData: { - currentState: "NONE", - currentTask: "", - activePath: {}, - desiredSpeed: 0, - desiredSpeedType: "GS", - desiredAltitude: 0, - desiredAltitudeType: "AGL", - targetLocation: {}, - isTanker: false, - isAWACS: false, - onOff: true, - followRoads: false, - targetID: 0 - }, - optionsData: { - ROE: "", - reactionToThreat: "", - emissionsCountermeasures: "", - TACAN: { isOn: false, channel: 0, XY: "X", callsign: "" }, - radio: { frequency: 0, callsign: 1, callsignNumber: 1}, - generalSettings: { prohibitJettison: false, prohibitAA: false, prohibitAG: false, prohibitAfterburner: false, prohibitAirWpn: false} - } + activePath: [], + ammo: [], + contacts: [], + name: "", + unitName: "", + groupName: "", + category: "", + coalition: "", + task: "" }; #selectable: boolean; @@ -80,8 +84,8 @@ export class Unit extends CustomMarker { #pathPolyline: Polyline; #contactsPolylines: Polyline[]; #miniMapMarker: CircleMarker | null = null; - #targetLocationMarker: TargetMarker; - #targetLocationPolyline: Polyline; + #targetPositionMarker: TargetMarker; + #targetPositionPolyline: Polyline; #timer: number = 0; @@ -96,26 +100,24 @@ export class Unit extends CustomMarker { if (type === "NavyUnit") return NavyUnit; } - constructor(ID: number, data: UpdateData) { + constructor(ID: number, data: UnitData) { super(new LatLng(0, 0), { riseOnHover: true, keyboard: false }); this.ID = ID; - this.#selectable = true; + this.#pathPolyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1 }); + this.#pathPolyline.addTo(getMap()); + this.#contactsPolylines = []; + this.#targetPositionMarker = new TargetMarker(new LatLng(0, 0)); + this.#targetPositionPolyline = new Polyline([], { color: '#FF0000', weight: 3, opacity: 0.5, smoothFactor: 1 }); + this.on('click', (e) => this.#onClick(e)); this.on('dblclick', (e) => this.#onDoubleClick(e)); this.on('contextmenu', (e) => this.#onContextMenu(e)); this.on('mouseover', () => { this.setHighlighted(true); }) this.on('mouseout', () => { this.setHighlighted(false); }) - this.#pathPolyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1 }); - this.#pathPolyline.addTo(getMap()); - this.#contactsPolylines = []; - - this.#targetLocationMarker = new TargetMarker(new LatLng(0, 0)); - this.#targetLocationPolyline = new Polyline([], { color: '#FF0000', weight: 3, opacity: 0.5, smoothFactor: 1 }); - /* Deselect units if they are hidden */ document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => { window.setTimeout(() => { this.setSelected(this.getSelected() && !this.getHidden()) }, 300); @@ -156,7 +158,7 @@ export class Unit extends CustomMarker { setSelected(selected: boolean) { /* Only alive units can be selected. Some units are not selectable (weapons) */ - if ((this.getBaseData().alive || !selected) && this.getSelectable() && this.getSelected() != selected) { + if ((this.getData().alive || !selected) && this.getSelectable() && this.getSelected() != selected) { this.#selected = selected; this.getElement()?.querySelector(`[data-object|="unit"]`)?.toggleAttribute("data-is-selected", selected); if (selected) { @@ -206,42 +208,34 @@ export class Unit extends CustomMarker { } getGroupMembers() { - return Object.values(getUnitsManager().getUnits()).filter((unit: Unit) => {return unit != this && unit.getBaseData().groupName === this.getBaseData().groupName;}); + return Object.values(getUnitsManager().getUnits()).filter((unit: Unit) => {return unit != this && unit.getData().groupName === this.getData().groupName;}); } /********************** Unit data *************************/ - setData(data: UpdateData) { + setData(data: UnitData) { /* Check if data has changed comparing new values to old values */ - const positionChanged = (data.flightData != undefined && data.flightData.latitude != undefined && data.flightData.longitude != undefined && (this.getFlightData().latitude != data.flightData.latitude || this.getFlightData().longitude != data.flightData.longitude)); - const headingChanged = (data.flightData != undefined && data.flightData.heading != undefined && this.getFlightData().heading != data.flightData.heading); - const aliveChanged = (data.baseData != undefined && data.baseData.alive != undefined && this.getBaseData().alive != data.baseData.alive); - const stateChanged = (data.taskData != undefined && data.taskData.currentState != undefined && this.getTaskData().currentState != data.taskData.currentState); - const controlledChanged = (data.baseData != undefined && data.baseData.controlled != undefined && this.getBaseData().controlled != data.baseData.controlled); - var updateMarker = (positionChanged || headingChanged || aliveChanged || stateChanged || controlledChanged || !getMap().hasLayer(this)); + const positionChanged = this.getData().position.lat != data.position.lat || this.getData().position.lng != data.position.lng; + const headingChanged = this.getData().heading != data.heading; + const aliveChanged = this.getData().alive != data.alive; + const stateChanged = this.getData().state != data.state; + const controlledChanged = this.getData().controlled != data.controlled; + var updateMarker = positionChanged || headingChanged || aliveChanged || stateChanged || controlledChanged || !getMap().hasLayer(this); - /* Load the data from the received json */ - Object.keys(this.#data).forEach((key1: string) => { - Object.keys(this.#data[key1 as keyof(UnitData)]).forEach((key2: string) => { - if (key1 in data && key2 in data[key1]) { - var value1 = this.#data[key1 as keyof(UnitData)]; - var value2 = value1[key2 as keyof typeof value1]; - if (typeof data[key1][key2] === typeof value2 || typeof value2 === "undefined") - //@ts-ignore - this.#data[key1 as keyof(UnitData)][key2 as keyof typeof struct] = data[key1][key2]; - } - }); - }); + /* Assign the data */ + /* TODO Allow for partial updates */ + this.#data = data; /* Fire an event when a unit dies */ - if (aliveChanged && this.getBaseData().alive == false) + if (aliveChanged && this.getData().alive == false) document.dispatchEvent(new CustomEvent("unitDeath", { detail: this })); /* Dead units can't be selected */ - this.setSelected(this.getSelected() && this.getBaseData().alive && !this.getHidden()) + this.setSelected(this.getSelected() && this.getData().alive && !this.getHidden()) if (updateMarker) this.#updateMarker(); + // TODO dont delete the polylines of the detected units this.#clearDetectedUnits(); if (this.getSelected()) { this.#drawPath(); @@ -253,7 +247,6 @@ export class Unit extends CustomMarker { this.#clearTarget(); } - document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this })); } @@ -261,30 +254,6 @@ export class Unit extends CustomMarker { return this.#data; } - getBaseData() { - return this.getData().baseData; - } - - getFlightData() { - return this.getData().flightData; - } - - getTaskData() { - return this.getData().taskData; - } - - getMissionData() { - return this.getData().missionData; - } - - getFormationData() { - return this.getData().formationData; - } - - getOptionsData() { - return this.getData().optionsData; - } - /********************** Icon *************************/ createIcon(): void { /* Set the icon */ @@ -298,7 +267,7 @@ export class Unit extends CustomMarker { var el = document.createElement("div"); el.classList.add("unit"); el.setAttribute("data-object", `unit-${this.getMarkerCategory()}`); - el.setAttribute("data-coalition", this.getMissionData().coalition); + el.setAttribute("data-coalition", this.getData().coalition); // Generate and append elements depending on active options // Velocity vector @@ -342,7 +311,7 @@ export class Unit extends CustomMarker { if (this.getIconOptions().showShortLabel) { var shortLabel = document.createElement("div"); shortLabel.classList.add("unit-short-label"); - shortLabel.innerText = getUnitDatabaseByCategory(this.getMarkerCategory())?.getByName(this.getBaseData().name)?.shortLabel || ""; + shortLabel.innerText = getUnitDatabaseByCategory(this.getMarkerCategory())?.getByName(this.getData().name)?.shortLabel || ""; el.append(shortLabel); } @@ -371,7 +340,7 @@ export class Unit extends CustomMarker { summary.classList.add("unit-summary"); var callsign = document.createElement("div"); callsign.classList.add("unit-callsign"); - callsign.innerText = this.getBaseData().unitName; + callsign.innerText = this.getData().unitName; var altitude = document.createElement("div"); altitude.classList.add("unit-altitude"); var speed = document.createElement("div"); @@ -389,15 +358,15 @@ export class Unit extends CustomMarker { updateVisibility() { var hidden = false; const hiddenUnits = getUnitsManager().getHiddenTypes(); - if (this.getMissionData().flags.Human && hiddenUnits.includes("human")) + if (this.getData().human && hiddenUnits.includes("human")) hidden = true; - else if (this.getBaseData().controlled == false && hiddenUnits.includes("dcs")) + else if (this.getData().controlled == false && hiddenUnits.includes("dcs")) hidden = true; else if (hiddenUnits.includes(this.getMarkerCategory())) hidden = true; - else if (hiddenUnits.includes(this.getMissionData().coalition)) + else if (hiddenUnits.includes(this.getData().coalition)) hidden = true; - this.setHidden(hidden || !this.getBaseData().alive); + this.setHidden(hidden || !this.getData().alive); } setHidden(hidden: boolean) { @@ -419,24 +388,24 @@ export class Unit extends CustomMarker { } getLeader() { - return getUnitsManager().getUnitByID(this.getFormationData().leaderID); + return getUnitsManager().getUnitByID(this.getData().leaderID); } canFulfillRole(roles: string | string[]) { if (typeof(roles) === "string") roles = [roles]; - return this.getDatabase()?.getByName(this.getBaseData().name)?.loadouts.some((loadout: LoadoutBlueprint) => { + return this.getDatabase()?.getByName(this.getData().name)?.loadouts.some((loadout: LoadoutBlueprint) => { return (roles as string[]).some((role: string) => {return loadout.roles.includes(role)}); }); } /********************** Unit commands *************************/ addDestination(latlng: L.LatLng) { - if (!this.getMissionData().flags.Human) { + if (!this.getData().human) { var path: any = {}; - if (this.getTaskData().activePath != undefined) { - path = this.getTaskData().activePath; + if (this.getData().activePath.length > 0) { + path = this.getData().activePath; path[(Object.keys(path).length + 1).toString()] = latlng; } else { @@ -447,86 +416,86 @@ export class Unit extends CustomMarker { } clearDestinations() { - if (!this.getMissionData().flags.Human) - this.getTaskData().activePath = undefined; + if (!this.getData().human) + this.getData().activePath = []; } attackUnit(targetID: number) { /* Units can't attack themselves */ - if (!this.getMissionData().flags.Human) + if (!this.getData().human) if (this.ID != targetID) attackUnit(this.ID, targetID); } followUnit(targetID: number, offset: { "x": number, "y": number, "z": number }) { /* Units can't follow themselves */ - if (!this.getMissionData().flags.Human) + if (!this.getData().human) if (this.ID != targetID) followUnit(this.ID, targetID, offset); } landAt(latlng: LatLng) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) landAt(this.ID, latlng); } changeSpeed(speedChange: string) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) changeSpeed(this.ID, speedChange); } changeAltitude(altitudeChange: string) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) changeAltitude(this.ID, altitudeChange); } setSpeed(speed: number) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setSpeed(this.ID, speed); } setSpeedType(speedType: string) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setSpeedType(this.ID, speedType); } setAltitude(altitude: number) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setAltitude(this.ID, altitude); } setAltitudeType(altitudeType: string) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setAltitudeType(this.ID, altitudeType); } setROE(ROE: string) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setROE(this.ID, ROE); } setReactionToThreat(reactionToThreat: string) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setReactionToThreat(this.ID, reactionToThreat); } setEmissionsCountermeasures(emissionCountermeasure: string) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setEmissionsCountermeasures(this.ID, emissionCountermeasure); } setLeader(isLeader: boolean, wingmenIDs: number[] = []) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setLeader(this.ID, isLeader, wingmenIDs); } setOnOff(onOff: boolean) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setOnOff(this.ID, onOff); } setFollowRoads(followRoads: boolean) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setFollowRoads(this.ID, followRoads); } @@ -535,12 +504,12 @@ export class Unit extends CustomMarker { } refuel() { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) refuel(this.ID); } setAdvancedOptions(isTanker: boolean, isAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings) { - if (!this.getMissionData().flags.Human) + if (!this.getData().human) setAdvacedOptions(this.ID, isTanker, isAWACS, TACAN, radio, generalSettings); } @@ -565,7 +534,7 @@ export class Unit extends CustomMarker { super.onAdd(map); /* If this is the first time adding this unit to the map, remove the temporary marker */ if (getUnitsManager().getUnitByID(this.ID) == null) - getMap().removeTemporaryMarker(new LatLng(this.getFlightData().latitude, this.getFlightData().longitude)); + getMap().removeTemporaryMarker(new LatLng(this.getData().position.lat, this.getData().position.lng)); return this; } @@ -602,12 +571,12 @@ export class Unit extends CustomMarker { options["follow"] = {text: "Follow", tooltip: "Follow the unit at a user defined distance and position"};; } else if ((selectedUnits.length > 0 && (selectedUnits.includes(this))) || selectedUnits.length == 0) { - if (this.getBaseData().category == "Aircraft") { + if (this.getData().category == "Aircraft") { options["refuel"] = {text: "Air to air refuel", tooltip: "Refuel unit at the nearest AAR Tanker. If no tanker is available the unit will RTB."}; // TODO Add some way of knowing which aircraft can AAR } } - if ((selectedUnits.length === 0 && this.getBaseData().category == "Aircraft") || (selectedUnitTypes.length === 1 && ["Aircraft"].includes(selectedUnitTypes[0]))) + if ((selectedUnits.length === 0 && this.getData().category == "Aircraft") || (selectedUnitTypes.length === 1 && ["Aircraft"].includes(selectedUnitTypes[0]))) { if (selectedUnits.concat([this]).every((unit: Unit) => {return unit.canFulfillRole(["CAS", "Strike"])})) { options["bomb"] = {text: "Precision bombing", tooltip: "Precision bombing of a specific point"}; @@ -615,7 +584,7 @@ export class Unit extends CustomMarker { } } - if ((selectedUnits.length === 0 && this.getBaseData().category == "GroundUnit") || selectedUnitTypes.length === 1 && ["GroundUnit"].includes(selectedUnitTypes[0])) { + if ((selectedUnits.length === 0 && this.getData().category == "GroundUnit") || selectedUnitTypes.length === 1 && ["GroundUnit"].includes(selectedUnitTypes[0])) { if (selectedUnits.concat([this]).every((unit: Unit) => {return unit.canFulfillRole(["Gun Artillery", "Rocket Artillery", "Infantry", "IFV", "Tank"])})) options["fire-at-area"] = {text: "Fire at area", tooltip: "Fire at a large area"}; } @@ -683,12 +652,12 @@ export class Unit extends CustomMarker { this.updateVisibility(); /* Draw the minimap marker */ - if (this.getBaseData().alive) { + if (this.getData().alive) { if (this.#miniMapMarker == null) { - this.#miniMapMarker = new CircleMarker(new LatLng(this.getFlightData().latitude, this.getFlightData().longitude), { radius: 0.5 }); - if (this.getMissionData().coalition == "neutral") + this.#miniMapMarker = new CircleMarker(new LatLng(this.getData().position.lat, this.getData().position.lng), { radius: 0.5 }); + if (this.getData().coalition == "neutral") this.#miniMapMarker.setStyle({ color: "#CFD9E8" }); - else if (this.getMissionData().coalition == "red") + else if (this.getData().coalition == "red") this.#miniMapMarker.setStyle({ color: "#ff5858" }); else this.#miniMapMarker.setStyle({ color: "#247be2" }); @@ -696,7 +665,7 @@ export class Unit extends CustomMarker { this.#miniMapMarker.bringToBack(); } else { - this.#miniMapMarker.setLatLng(new LatLng(this.getFlightData().latitude, this.getFlightData().longitude)); + this.#miniMapMarker.setLatLng(new LatLng(this.getData().position.lat, this.getData().position.lng)); this.#miniMapMarker.bringToBack(); } } @@ -709,39 +678,39 @@ export class Unit extends CustomMarker { /* Draw the marker */ if (!this.getHidden()) { - this.setLatLng(new LatLng(this.getFlightData().latitude, this.getFlightData().longitude)); + this.setLatLng(new LatLng(this.getData().position.lat, this.getData().position.lng)); var element = this.getElement(); if (element != null) { /* Draw the velocity vector */ - element.querySelector(".unit-vvi")?.setAttribute("style", `height: ${15 + this.getFlightData().speed / 5}px;`); + element.querySelector(".unit-vvi")?.setAttribute("style", `height: ${15 + this.getData().speed / 5}px;`); /* Set fuel data */ - element.querySelector(".unit-fuel-level")?.setAttribute("style", `width: ${this.getMissionData().fuel}%`); - element.querySelector(".unit")?.toggleAttribute("data-has-low-fuel", this.getMissionData().fuel < 20); + element.querySelector(".unit-fuel-level")?.setAttribute("style", `width: ${this.getData().fuel}%`); + element.querySelector(".unit")?.toggleAttribute("data-has-low-fuel", this.getData().fuel < 20); /* Set dead/alive flag */ - element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.getBaseData().alive); + element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.getData().alive); /* Set current unit state */ - if (this.getMissionData().flags.Human) // Unit is human + if (this.getData().human) // Unit is human element.querySelector(".unit")?.setAttribute("data-state", "human"); - else if (!this.getBaseData().controlled) // Unit is under DCS control (not Olympus) + else if (!this.getData().controlled) // Unit is under DCS control (not Olympus) element.querySelector(".unit")?.setAttribute("data-state", "dcs"); - else if ((this.getBaseData().category == "Aircraft" || this.getBaseData().category == "Helicopter") && !this.getMissionData().hasTask) + else if ((this.getData().category == "Aircraft" || this.getData().category == "Helicopter") && !this.getData().hasTask) element.querySelector(".unit")?.setAttribute("data-state", "no-task"); - else // Unit is under Olympus control - element.querySelector(".unit")?.setAttribute("data-state", this.getTaskData().currentState.toLowerCase()); + else // Unit is under Olympus control + element.querySelector(".unit")?.setAttribute("data-state", this.getData().state.toLowerCase()); /* Set altitude and speed */ if (element.querySelector(".unit-altitude")) - (element.querySelector(".unit-altitude")).innerText = "FL" + String(Math.floor(mToFt(this.getFlightData().altitude) / 100)); + (element.querySelector(".unit-altitude")).innerText = "FL" + String(Math.floor(mToFt(this.getData().position.alt as number) / 100)); if (element.querySelector(".unit-speed")) - (element.querySelector(".unit-speed")).innerText = String(Math.floor(msToKnots(this.getFlightData().speed))) + "GS"; + (element.querySelector(".unit-speed")).innerText = String(Math.floor(msToKnots(this.getData().speed))) + "GS"; /* Rotate elements according to heading */ element.querySelectorAll("[data-rotate-to-heading]").forEach(el => { - const headingDeg = rad2deg(this.getFlightData().heading); + const headingDeg = rad2deg(this.getData().heading); let currentStyle = el.getAttribute("style") || ""; el.setAttribute("style", currentStyle + `transform:rotate(${headingDeg}deg);`); }); @@ -756,7 +725,7 @@ export class Unit extends CustomMarker { var newHasFox2 = false; var newHasFox3 = false; var newHasOtherAmmo = false; - Object.values(this.getMissionData().ammo).forEach((ammo: any) => { + Object.values(this.getData().ammo).forEach((ammo: any) => { if (ammo.desc.category == 1 && ammo.desc.missileCategory == 1) { if (ammo.desc.guidance == 4 || ammo.desc.guidance == 5) newHasFox1 = true; @@ -786,30 +755,30 @@ export class Unit extends CustomMarker { /* Set vertical offset for altitude stacking */ var pos = getMap().latLngToLayerPoint(this.getLatLng()).round(); - this.setZIndexOffset(1000 + Math.floor(this.getFlightData().altitude) - pos.y + (this.#highlighted || this.#selected ? 5000 : 0)); + this.setZIndexOffset(1000 + Math.floor(this.getData().position.alt as number) - pos.y + (this.#highlighted || this.#selected ? 5000 : 0)); } } #drawPath() { - if (this.getTaskData().activePath != undefined) { + if (this.getData().activePath != undefined) { var points = []; - points.push(new LatLng(this.getFlightData().latitude, this.getFlightData().longitude)); + points.push(new LatLng(this.getData().position.lat, this.getData().position.lng)); /* Add markers if missing */ - while (this.#pathMarkers.length < Object.keys(this.getTaskData().activePath).length) { + while (this.#pathMarkers.length < Object.keys(this.getData().activePath).length) { var marker = new Marker([0, 0], { icon: pathIcon }).addTo(getMap()); this.#pathMarkers.push(marker); } /* Remove markers if too many */ - while (this.#pathMarkers.length > Object.keys(this.getTaskData().activePath).length) { + while (this.#pathMarkers.length > Object.keys(this.getData().activePath).length) { getMap().removeLayer(this.#pathMarkers[this.#pathMarkers.length - 1]); this.#pathMarkers.splice(this.#pathMarkers.length - 1, 1) } /* Update the position of the existing markers (to avoid creating markers uselessly) */ - for (let WP in this.getTaskData().activePath) { - var destination = this.getTaskData().activePath[WP]; + for (let WP in this.getData().activePath) { + var destination = this.getData().activePath[WP]; this.#pathMarkers[parseInt(WP) - 1].setLatLng([destination.lat, destination.lng]); points.push(new LatLng(destination.lat, destination.lng)); this.#pathPolyline.setLatLngs(points); @@ -829,27 +798,25 @@ export class Unit extends CustomMarker { } #drawDetectedUnits() { - for (let index in this.getMissionData().contacts) { - var targetData = this.getMissionData().contacts[index]; - if (targetData.object != undefined){ - var target = getUnitsManager().getUnitByID(targetData.object["id_"]) - if (target != null) { - var startLatLng = new LatLng(this.getFlightData().latitude, this.getFlightData().longitude) - var endLatLng = new LatLng(target.getFlightData().latitude, target.getFlightData().longitude) + for (let index in this.getData().contacts) { + var targetData = this.getData().contacts[index]; + var target = getUnitsManager().getUnitByID(targetData.ID) + if (target != null) { + var startLatLng = new LatLng(this.getData().position.lat, this.getData().position.lng) + var endLatLng = new LatLng(target.getData().position.lat, target.getData().position.lng) - var color; - if (targetData.detectionMethod === "RADAR") - color = "#FFFF00"; - else if (targetData.detectionMethod === "VISUAL") - color = "#FF00FF"; - else if (targetData.detectionMethod === "RWR") - color = "#00FF00"; - else - color = "#FFFFFF"; - var targetPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, opacity: 0.4, smoothFactor: 1, dashArray: "4, 8" }); - targetPolyline.addTo(getMap()); - this.#contactsPolylines.push(targetPolyline) - } + var color; + if (targetData.detectionMethod === 1) + color = "#FF00FF"; + else if (targetData.detectionMethod === 4) + color = "#FFFF00"; + else if (targetData.detectionMethod === 16) + color = "#00FF00"; + else + color = "#FFFFFF"; + var targetPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, opacity: 0.4, smoothFactor: 1, dashArray: "4, 8" }); + targetPolyline.addTo(getMap()); + this.#contactsPolylines.push(targetPolyline) } } } @@ -861,40 +828,33 @@ export class Unit extends CustomMarker { } #drawTarget() { - const targetLocation = this.getTaskData().targetLocation; - - if (targetLocation.latitude && targetLocation.longitude && targetLocation.latitude != 0 && targetLocation.longitude != 0) { - const lat = targetLocation.latitude; - const lng = targetLocation.longitude; - if (lat && lng) - this.#drawTargetLocation(new LatLng(lat, lng)); + if (this.getData().targetPosition.lat != 0 && this.getData().targetPosition.lng != 0) { + this.#drawtargetPosition(this.getData().targetPosition); } - else if (this.getTaskData().targetID != 0 && getUnitsManager().getUnitByID(this.getTaskData().targetID)) { - const flightData = getUnitsManager().getUnitByID(this.getTaskData().targetID)?.getFlightData(); - const lat = flightData?.latitude; - const lng = flightData?.longitude; - if (lat && lng) - this.#drawTargetLocation(new LatLng(lat, lng)); + else if (this.getData().targetID != 0 && getUnitsManager().getUnitByID(this.getData().targetID)) { + const position = getUnitsManager().getUnitByID(this.getData().targetID)?.getData().position; + if (position) + this.#drawtargetPosition(position); } else this.#clearTarget(); } - #drawTargetLocation(targetLocation: LatLng) { - if (!getMap().hasLayer(this.#targetLocationMarker)) - this.#targetLocationMarker.addTo(getMap()); - if (!getMap().hasLayer(this.#targetLocationPolyline)) - this.#targetLocationPolyline.addTo(getMap()); - this.#targetLocationMarker.setLatLng(new LatLng(targetLocation.lat, targetLocation.lng)); - this.#targetLocationPolyline.setLatLngs([new LatLng(this.getFlightData().latitude, this.getFlightData().longitude), new LatLng(targetLocation.lat, targetLocation.lng)]) + #drawtargetPosition(targetPosition: LatLng) { + if (!getMap().hasLayer(this.#targetPositionMarker)) + this.#targetPositionMarker.addTo(getMap()); + if (!getMap().hasLayer(this.#targetPositionPolyline)) + this.#targetPositionPolyline.addTo(getMap()); + this.#targetPositionMarker.setLatLng(new LatLng(targetPosition.lat, targetPosition.lng)); + this.#targetPositionPolyline.setLatLngs([new LatLng(this.getData().position.lat, this.getData().position.lng), new LatLng(targetPosition.lat, targetPosition.lng)]) } #clearTarget() { - if (getMap().hasLayer(this.#targetLocationMarker)) - this.#targetLocationMarker.removeFrom(getMap()); + if (getMap().hasLayer(this.#targetPositionMarker)) + this.#targetPositionMarker.removeFrom(getMap()); - if (getMap().hasLayer(this.#targetLocationPolyline)) - this.#targetLocationPolyline.removeFrom(getMap()); + if (getMap().hasLayer(this.#targetPositionPolyline)) + this.#targetPositionPolyline.removeFrom(getMap()); } } @@ -915,7 +875,7 @@ export class AirUnit extends Unit { } export class Aircraft extends AirUnit { - constructor(ID: number, data: UpdateData) { + constructor(ID: number, data: UnitData) { super(ID, data); } @@ -925,7 +885,7 @@ export class Aircraft extends AirUnit { } export class Helicopter extends AirUnit { - constructor(ID: number, data: UpdateData) { + constructor(ID: number, data: UnitData) { super(ID, data); } @@ -935,7 +895,7 @@ export class Helicopter extends AirUnit { } export class GroundUnit extends Unit { - constructor(ID: number, data: UpdateData) { + constructor(ID: number, data: UnitData) { super(ID, data); } @@ -954,12 +914,12 @@ export class GroundUnit extends Unit { } getMarkerCategory() { - return getMarkerCategoryByName(this.getBaseData().name); + return getMarkerCategoryByName(this.getData().name); } } export class NavyUnit extends Unit { - constructor(ID: number, data: UpdateData) { + constructor(ID: number, data: UnitData) { super(ID, data); } @@ -983,7 +943,7 @@ export class NavyUnit extends Unit { } export class Weapon extends Unit { - constructor(ID: number, data: UpdateData) { + constructor(ID: number, data: UnitData) { super(ID, data); this.setSelectable(false); } @@ -1004,7 +964,7 @@ export class Weapon extends Unit { } export class Missile extends Weapon { - constructor(ID: number, data: UpdateData) { + constructor(ID: number, data: UnitData) { super(ID, data); } @@ -1014,7 +974,7 @@ export class Missile extends Weapon { } export class Bomb extends Weapon { - constructor(ID: number, data: UpdateData) { + constructor(ID: number, data: UnitData) { super(ID, data); } diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 2bb85f3c..2f56b189 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -7,6 +7,8 @@ import { CoalitionArea } from "../map/coalitionarea"; import { Airbase } from "../missionhandler/airbase"; import { groundUnitsDatabase } from "./groundunitsdatabase"; import { IADSRoles, IDLE, MOVE_UNIT } from "../constants/constants"; +import { DataExtractor } from "./dataextractor"; +import { UnitData } from "../@types/unit"; export class UnitsManager { #units: { [ID: number]: Unit }; @@ -31,7 +33,7 @@ export class UnitsManager { getSelectableAircraft() { const units = this.getUnits(); return Object.keys(units).reduce((acc: { [key: number]: Unit }, unitId: any) => { - const baseData = units[unitId].getBaseData(); + const baseData = units[unitId].getData(); if (baseData.category === "Aircraft" && baseData.alive === true) { acc[unitId] = units[unitId]; } @@ -51,13 +53,13 @@ export class UnitsManager { } getUnitsByHotgroup(hotgroup: number) { - return Object.values(this.#units).filter((unit: Unit) => { return unit.getBaseData().alive && unit.getHotgroup() == hotgroup }); + return Object.values(this.#units).filter((unit: Unit) => { return unit.getData().alive && unit.getHotgroup() == hotgroup }); } addUnit(ID: number, data: UnitData) { - if (data.baseData && data.baseData.category){ - /* The name of the unit category is exactly the same as the constructor name */ - var constructor = Unit.getConstructor(data.baseData.category); + if (data.category){ + /* The name of the unit category is exactly the same as the constructor name */ + var constructor = Unit.getConstructor(data.category); if (constructor != undefined) { this.#units[ID] = new constructor(ID, data); } @@ -68,82 +70,42 @@ export class UnitsManager { } - update(data: string) { + update(encodedData: string) { var updatedUnits: Unit[] = []; - var buffer = base64ToBytes(data); - - /*Coords position; - double speed; - double heading; - unsigned short fuel; - double desiredSpeed; - double desiredAltitude; - unsigned int targetID; - Coords targetPosition; - unsigned char state; - unsigned char ROE; - unsigned char reactionToThreat; - unsigned char emissionsCountermeasures; - Options::TACAN TACAN; - Options::Radio Radio; - unsigned short pathLength; - unsigned char nameLength; - unsigned char unitNameLength; - unsigned char groupNameLength; - unsigned char categoryLength; - unsigned char coalitionLength;*/ + var buffer = base64ToBytes(encodedData); + var dataExtractor = new DataExtractor(buffer); + var data: {[key: string]: UnitData} = {}; var offset = 0; - var dataview = new DataView(buffer); - const ID = dataview.getUint32(offset, true); offset += 4; - const bitmask = dataview.getUint32(offset , true); offset += 4; - const alive = bitmask & (1 << 0); - const human = bitmask >> 1 & 1; - const controlled = bitmask >> 2 & 1; - const hasTask = bitmask >> 3 & 1; - const desiredAltitudeType = bitmask >> 16 & 1; - const desiredSpeedType = bitmask >> 17 & 1; - const isTanker = bitmask >> 18 & 1; - const isAWACS = bitmask >> 19 & 1; - const onOff = bitmask >> 20 & 1; - const followRoads = bitmask >> 21 & 1; - const EPLRS = bitmask >> 22 & 1; - const prohibitAA = bitmask >> 23 & 1; - const prohibitAfterburner = bitmask >> 24 & 1; - const prohibitAG = bitmask >> 25 & 1; - const prohibitAirWpn = bitmask >> 26 & 1; - const prohibitJettison = bitmask >> 27 & 1; + while (offset < buffer.byteLength) { + const result = dataExtractor.extractData(offset); + data[result.data.ID] = result.data; + offset = result.offset; + } - const latitude = dataview.getFloat64(offset , true); offset += 8; - const longitude = dataview.getFloat64(offset , true); offset += 8; - const altitude = dataview.getFloat64(offset , true); offset += 8; - const speed = dataview.getFloat64(offset , true); offset += 8; - const heading = dataview.getFloat64(offset , true); offset += 8; - - - var foo = 12; - /*Object.keys(data.units) + Object.keys(data) .filter((ID: string) => !(ID in this.#units)) .reduce((timeout: number, ID: string) => { window.setTimeout(() => { if (!(ID in this.#units)) - this.addUnit(parseInt(ID), data.units[ID]); - this.#units[parseInt(ID)]?.setData(data.units[ID]); + this.addUnit(parseInt(ID), data[ID]); + this.#units[parseInt(ID)]?.setData(data[ID]); }, timeout); return timeout + 10; }, 10); - Object.keys(data.units) + Object.keys(data) .filter((ID: string) => ID in this.#units) .forEach((ID: string) => { updatedUnits.push(this.#units[parseInt(ID)]); - this.#units[parseInt(ID)]?.setData(data.units[ID]) + this.#units[parseInt(ID)]?.setData(data[ID]); }); - this.getSelectedUnits().forEach((unit: Unit) => { - if (!updatedUnits.includes(unit)) - unit.setData({}) - });*/ + // TODO why did we do this? + //this.getSelectedUnits().forEach((unit: Unit) => { + // if (!updatedUnits.includes(unit)) + // unit.setData(null); + //}); } setHiddenType(key: string, value: boolean) { @@ -170,7 +132,7 @@ export class UnitsManager { this.deselectAllUnits(); for (let ID in this.#units) { if (this.#units[ID].getHidden() == false) { - var latlng = new LatLng(this.#units[ID].getFlightData().latitude, this.#units[ID].getFlightData().longitude); + var latlng = new LatLng(this.#units[ID].getData().position.lat, this.#units[ID].getData().position.lng); if (bounds.contains(latlng)) { this.#units[ID].setSelected(true); } @@ -187,11 +149,11 @@ export class UnitsManager { } if (options) { if (options.excludeHumans) - selectedUnits = selectedUnits.filter((unit: Unit) => { return !unit.getMissionData().flags.Human }); + selectedUnits = selectedUnits.filter((unit: Unit) => { return !unit.getData().human }); if (options.onlyOnePerGroup) { var temp: Unit[] = []; for (let unit of selectedUnits) { - if (!temp.some((otherUnit: Unit) => unit.getBaseData().groupName == otherUnit.getBaseData().groupName)) + if (!temp.some((otherUnit: Unit) => unit.getData().groupName == otherUnit.getData().groupName)) temp.push(unit); } selectedUnits = temp; @@ -236,7 +198,7 @@ export class UnitsManager { if (this.getSelectedUnits().length == 0) return undefined; return this.getSelectedUnits().map((unit: Unit) => { - return unit.getMissionData().coalition + return unit.getData().coalition })?.reduce((a: any, b: any) => { return a == b ? a : undefined }); @@ -256,8 +218,8 @@ export class UnitsManager { for (let idx in selectedUnits) { const unit = selectedUnits[idx]; /* If a unit is following another unit, and that unit is also selected, send the command to the followed unit */ - if (unit.getTaskData().currentState === "Follow") { - const leader = this.getUnitByID(unit.getFormationData().leaderID) + if (unit.getData().state === "Follow") { + const leader = this.getUnitByID(unit.getData().leaderID) if (leader && leader.getSelected()) leader.addDestination(latlng); else @@ -276,8 +238,8 @@ export class UnitsManager { var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { const unit = selectedUnits[idx]; - if (unit.getTaskData().currentState === "Follow") { - const leader = this.getUnitByID(unit.getFormationData().leaderID) + if (unit.getData().state === "Follow") { + const leader = this.getUnitByID(unit.getData().leaderID) if (leader && leader.getSelected()) leader.clearDestinations(); else @@ -388,13 +350,13 @@ export class UnitsManager { for (let idx in selectedUnits) { selectedUnits[idx].attackUnit(ID); } - this.#showActionMessage(selectedUnits, `attacking unit ${this.getUnitByID(ID)?.getBaseData().unitName}`); + this.#showActionMessage(selectedUnits, `attacking unit ${this.getUnitByID(ID)?.getData().unitName}`); } selectedUnitsDelete(explosion: boolean = false) { var selectedUnits = this.getSelectedUnits(); /* Can be applied to humans too */ const selectionContainsAHuman = selectedUnits.some( ( unit:Unit ) => { - return unit.getMissionData().flags.Human === true; + return unit.getData().human === true; }); if (selectionContainsAHuman && !confirm( "Your selection includes a human player. Deleting humans causes their vehicle to crash.\n\nAre you sure you want to do this?" ) ) { @@ -455,7 +417,7 @@ export class UnitsManager { } count++; } - this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getBaseData().unitName}`); + this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getData().unitName}`); } selectedUnitsSetHotgroup(hotgroup: number) { @@ -477,7 +439,7 @@ export class UnitsManager { /* Compute the center of the group */ var center = { x: 0, y: 0 }; selectedUnits.forEach((unit: Unit) => { - var mercator = latLngToMercator(unit.getFlightData().latitude, unit.getFlightData().longitude); + var mercator = latLngToMercator(unit.getData().position.lat, unit.getData().position.lng); center.x += mercator.x / selectedUnits.length; center.y += mercator.y / selectedUnits.length; }); @@ -485,7 +447,7 @@ export class UnitsManager { /* Compute the distances from the center of the group */ var unitDestinations: { [key: number]: LatLng } = {}; selectedUnits.forEach((unit: Unit) => { - var mercator = latLngToMercator(unit.getFlightData().latitude, unit.getFlightData().longitude); + var mercator = latLngToMercator(unit.getData().position.lat, unit.getData().position.lat); var distancesFromCenter = { dx: mercator.x - center.x, dy: mercator.y - center.y }; /* Rotate the distance according to the group rotation */ @@ -615,8 +577,8 @@ export class UnitsManager { #showActionMessage(units: Unit[], message: string) { if (units.length == 1) - getInfoPopup().setText(`${units[0].getBaseData().unitName} ${message}`); + getInfoPopup().setText(`${units[0].getData().unitName} ${message}`); else if (units.length > 1) - getInfoPopup().setText(`${units[0].getBaseData().unitName} and ${units.length - 1} other units ${message}`); + getInfoPopup().setText(`${units[0].getData().unitName} and ${units.length - 1} other units ${message}`); } } \ No newline at end of file diff --git a/src/core/include/unit.h b/src/core/include/unit.h index f8f5241d..bdf55afc 100644 --- a/src/core/include/unit.h +++ b/src/core/include/unit.h @@ -59,7 +59,7 @@ namespace Options { namespace DataTypes { struct Ammo { unsigned short quantity = 0; - string name; + char name[32]; unsigned char guidance = 0; unsigned char category = 0; unsigned char missileCategory = 0; @@ -88,6 +88,8 @@ namespace DataTypes { Options::TACAN TACAN; Options::Radio Radio; unsigned short pathLength; + unsigned short ammoLength; + unsigned short contactsLength; unsigned char nameLength; unsigned char unitNameLength; unsigned char groupNameLength; diff --git a/src/core/src/unit.cpp b/src/core/src/unit.cpp index bc99edbd..b5580505 100644 --- a/src/core/src/unit.cpp +++ b/src/core/src/unit.cpp @@ -180,8 +180,13 @@ unsigned int Unit::getUpdateData(char* &data) /* Reserve data for: 1) DataPacket; 2) Active path; + 3) Ammo vector; + 4) Contacts vector; */ - data = (char*)malloc(sizeof(DataTypes::DataPacket) + activePath.size() * sizeof(Coords)); + data = (char*)malloc(sizeof(DataTypes::DataPacket) + + activePath.size() * sizeof(Coords) + + ammo.size() * sizeof(Coords) + + contacts.size() * sizeof(Coords)); unsigned int offset = 0; /* Prepare the data packet and copy it to memory */ @@ -221,6 +226,8 @@ unsigned int Unit::getUpdateData(char* &data) TACAN, radio, activePath.size(), + ammo.size(), + contacts.size(), name.size(), unitName.size(), groupName.size(), @@ -231,14 +238,21 @@ unsigned int Unit::getUpdateData(char* &data) memcpy(data + offset, &dataPacket, sizeof(dataPacket)); offset += sizeof(dataPacket); - /* Prepare the path memory and copy it to memory */ + /* Prepare the path vector and copy it to memory */ std::vector path; for (const Coords& c : activePath) path.push_back(c); - memcpy(data + offset, &path, activePath.size() * sizeof(Coords)); offset += activePath.size() * sizeof(Coords); + /* Copy the ammo vector to memory */ + memcpy(data + offset, &ammo, ammo.size() * sizeof(DataTypes::Ammo)); + offset += ammo.size() * sizeof(DataTypes::Ammo); + + /* Copy the contacts vector to memory */ + memcpy(data + offset, &contacts, contacts.size() * sizeof(DataTypes::Contact)); + offset += contacts.size() * sizeof(DataTypes::Contact); + return offset; }