diff --git a/client/src/@types/unit.d.ts b/client/src/@types/unit.d.ts index 0c7d400a..703b8852 100644 --- a/client/src/@types/unit.d.ts +++ b/client/src/@types/unit.d.ts @@ -52,8 +52,8 @@ interface UnitData { human: boolean, controlled: boolean, hasTask: boolean, - desiredAltitudeType: boolean, - desiredSpeedType: boolean, + desiredAltitudeType: string, + desiredSpeedType: string, isTanker: boolean, isAWACS: boolean, onOff: boolean, diff --git a/client/src/map/map.ts b/client/src/map/map.ts index 30c0cc0c..c81c2e82 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -671,7 +671,7 @@ export class Map extends L.Map { /* Show the active cursor depending on the active state */ if (this.#state === IDLE || this.#state === COALITIONAREA_INTERACT) this.#showDefaultCursor(); - else if (this.#state === MOVE_UNIT) this.#showDestinationCursors(!e.originalEvent.shiftKey); + else if (this.#state === MOVE_UNIT) this.#showDestinationCursors(e && e.originaleEvent && !e.originalEvent.shiftKey); else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#showTargetCursor(); else if (this.#state === COALITIONAREA_DRAW_POLYGON) this.#showDrawingCursor(); } diff --git a/client/src/server/server.ts b/client/src/server/server.ts index ca392e5f..533053fe 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -2,6 +2,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'; +import { ROEs, emissionsCountermeasures, reactionsToThreat } from '../constants/constants'; var connected: boolean = false; var paused: boolean = false; @@ -38,19 +39,21 @@ export function GET(callback: CallableFunction, uri: string, options?: { time?: if (options?.time != undefined) optionsString = `time=${options.time}`; - xmlHttp.open("GET", `${demoEnabled ? DEMO_ADDRESS : REST_ADDRESS}/${uri}${optionsString ? `?${optionsString}` : ''}`, true); if (credentials) xmlHttp.setRequestHeader("Authorization", "Basic " + credentials); + + if (uri === UNITS_URI) + xmlHttp.responseType = "arraybuffer"; + xmlHttp.onload = function (e) { if (xmlHttp.status == 200) { - var data = JSON.parse(xmlHttp.responseText); - if (uri !== UNITS_URI || (options?.time == 0) || parseInt(data.time) > lastUpdateTime) { + setConnected(true); + if (xmlHttp.responseType == 'arraybuffer') + callback(xmlHttp.response); + else { + var data = JSON.parse(xmlHttp.responseText); callback(data); - lastUpdateTime = parseInt(data.time); - if (isNaN(lastUpdateTime)) - lastUpdateTime = 0; - setConnected(true); } } else if (xmlHttp.status == 401) { console.error("Incorrect username/password"); @@ -96,6 +99,10 @@ export function setAddress(address: string, port: number) { console.log(`Setting REST address to ${REST_ADDRESS}`) } +export function setLastUpdateTime(newLastUpdateTime: number) { + lastUpdateTime = newLastUpdateTime; +} + export function getAirbases(callback: CallableFunction) { GET(callback, AIRBASES_URI); } @@ -223,19 +230,19 @@ export function createFormation(ID: number, isLeader: boolean, wingmenIDs: numbe } export function setROE(ID: number, ROE: string) { - var command = { "ID": ID, "ROE": ROE } + var command = { "ID": ID, "ROE": ROEs.indexOf(ROE) } var data = { "setROE": command } POST(data, () => { }); } export function setReactionToThreat(ID: number, reactionToThreat: string) { - var command = { "ID": ID, "reactionToThreat": reactionToThreat } + var command = { "ID": ID, "reactionToThreat": reactionsToThreat.indexOf(reactionToThreat) } var data = { "setReactionToThreat": command } POST(data, () => { }); } export function setEmissionsCountermeasures(ID: number, emissionCountermeasure: string) { - var command = { "ID": ID, "emissionsCountermeasures": emissionCountermeasure } + var command = { "ID": ID, "emissionsCountermeasures": emissionsCountermeasures.indexOf(emissionCountermeasure) } var data = { "setEmissionsCountermeasures": command } POST(data, () => { }); } @@ -300,8 +307,11 @@ export function startUpdate() { /* On the first connection, force request of full data */ getAirbases((data: AirbasesData) => getMissionData()?.update(data)); getBullseye((data: BullseyesData) => getMissionData()?.update(data)); - getMission((data: any) => { getMissionData()?.update(data) }); - getUnits((data: UnitsData) => getUnitsManager()?.update(data.units), true /* Does a full refresh */); + getMission((data: any) => { + getMissionData()?.update(data); + checkSessionHash(data.sessionHash); + }); + getUnits((buffer: ArrayBuffer) => getUnitsManager()?.update(buffer), true /* Does a full refresh */); requestUpdate(); requestRefresh(); @@ -309,10 +319,9 @@ export function startUpdate() { export function requestUpdate() { /* Main update rate = 250ms is minimum time, equal to server update time. */ - getUnits((data: UnitsData) => { + getUnits((buffer: ArrayBuffer) => { if (!getPaused()) { - getUnitsManager()?.update(data.units); - checkSessionHash(data.sessionHash); + getUnitsManager()?.update(buffer); } }, false); window.setTimeout(() => requestUpdate(), getConnected() ? 250 : 1000); diff --git a/client/src/units/dataextractor.ts b/client/src/units/dataextractor.ts index 8b45a504..d3265a9a 100644 --- a/client/src/units/dataextractor.ts +++ b/client/src/units/dataextractor.ts @@ -17,85 +17,81 @@ export class DataExtractor { extractData(offset: number) { this.#offset = offset; - const ID = this.#extractUInt32(); - const bitmask = this.#extractUInt32(); + 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), + 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)? "AGL": "ASL", + desiredSpeedType: this.extractFromBitmask(bitmask, 17)? "GS": "CAS", + 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), + 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() + 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, + speed: this.extractFloat64(), + heading: this.extractFloat64(), + fuel: this.extractUInt16(), + desiredSpeed: this.extractFloat64(), + desiredAltitude: this.extractFloat64(), + leaderID: this.extractUInt32(), + targetID: this.extractUInt32(), targetPosition: new LatLng( - this.#extractFloat64(), - this.#extractFloat64(), - this.#extractFloat64() + 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()), + state: this.#getState(this.extractUInt8()), + ROE: this.#getROE(this.extractUInt8()), + reactionToThreat: this.#getReactionToThreat(this.extractUInt8()), + emissionsCountermeasures: this.#getEmissionCountermeasure(this.extractUInt8()), + coalition: this.#getCoalition(this.extractUInt8()), TACAN: { - isOn: this.#extractBool(), - channel: this.#extractUint8(), - XY: this.#extractChar(), - callsign: this.#extractString(4) + isOn: this.extractBool(), + channel: this.extractUInt8(), + XY: this.extractChar(), + callsign: this.extractString(4) }, radio: { - frequency: this.#extractUInt32(), - callsign: this.#extractUint8(), - callsignNumber: this.#extractUint8() + frequency: this.extractUInt32(), + callsign: this.extractUInt8(), + callsignNumber: this.extractUInt8() }, activePath: [], ammo: [], contacts: [], + task: "", 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(); + const pathLength = this.extractUInt16(); + const ammoLength = this.extractUInt16(); + const contactsLength = this.extractUInt16(); + const taskLength = this.extractUInt8(); if (pathLength > 0) { unitData.activePath = []; for (let idx = 0; idx < pathLength; idx++) { - unitData.activePath.push(new LatLng(this.#extractFloat64(), this.#extractFloat64(), this.#extractFloat64())); + unitData.activePath.push(new LatLng(this.extractFloat64(), this.extractFloat64(), this.extractFloat64())); } } @@ -103,11 +99,11 @@ export class DataExtractor { 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() + quantity: this.extractUInt16(), + name: this.extractString(32), + guidance: this.extractUInt8(), + category: this.extractUInt8(), + missileCategory: this.extractUInt8() }); } } @@ -116,77 +112,92 @@ export class DataExtractor { unitData.contacts = []; for (let idx = 0; idx < pathLength; idx++) { unitData.contacts.push({ - ID: this.#extractUInt32(), - detectionMethod: this.#extractUint8() + ID: this.extractUInt32(), + detectionMethod: this.extractUInt8() }); } } + + if (taskLength > 0) { + unitData.task = this.extractString(taskLength); + } + const nameLength = this.extractUInt16(); + const unitNameLength = this.extractUInt16(); + const groupNameLength = this.extractUInt16(); + const categoryLength = this.extractUInt16(); + if (nameLength > 0) { - unitData.name = this.#extractString(nameLength); + unitData.name = this.extractString(nameLength); } if (unitNameLength > 0) { - unitData.unitName = this.#extractString(unitNameLength); + unitData.unitName = this.extractString(unitNameLength); } if (groupNameLength > 0) { - unitData.groupName = this.#extractString(groupNameLength); + unitData.groupName = this.extractString(groupNameLength); } if (categoryLength > 0) { - unitData.category = this.#extractString(categoryLength); - } - - if (coalitionLength > 0) { - unitData.coalition = this.#extractString(coalitionLength); + unitData.category = this.extractString(categoryLength); } return {data: unitData, offset: this.#offset}; } - #extractBool() { + extractBool() { const value = this.#dataview.getUint8(this.#offset); this.#offset += 1; return value > 0; } - #extractUint8() { + extractUInt8() { const value = this.#dataview.getUint8(this.#offset); this.#offset += 1; return value; } - #extractUInt16() { - const value = this.#dataview.getUint16(this.#offset); + extractUInt16() { + const value = this.#dataview.getUint16(this.#offset, true); this.#offset += 2; return value; } - #extractUInt32() { - const value = this.#dataview.getUint32(this.#offset); + extractUInt32() { + const value = this.#dataview.getUint32(this.#offset, true); this.#offset += 4; return value; } - #extractFloat64() { - const value = this.#dataview.getFloat64(this.#offset); + extractUInt64() { + const value = this.#dataview.getBigUint64(this.#offset, true); this.#offset += 8; return value; } - #extractFromBitmask(bitmask: number, position: number) { - return (bitmask >> position & 1) > 0; + extractFloat64() { + const value = this.#dataview.getFloat64(this.#offset, true); + this.#offset += 8; + return value; } - #extractString(length: number) { - const value = this.#decoder.decode(this.#buffer.slice(this.#offset, length)); + extractFromBitmask(bitmask: number, position: number) { + return ((bitmask >> position) & 1) > 0; + } + + extractString(length: number) { + const value = this.#decoder.decode(this.#buffer.slice(this.#offset, this.#offset +length)); this.#offset += length; return value; } - #extractChar() { - return this.#extractString(1); + extractChar() { + return this.extractString(1); + } + + getOffset() { + return this.#offset; } #getState(state: number) { @@ -216,4 +227,13 @@ export class DataExtractor { else return emissionsCountermeasures[0]; } + + #getCoalition(coalitionID: number) { + switch (coalitionID){ + case 0: return "neutral"; + case 1: return "red"; + case 2: return "blue"; + } + return ""; + } } \ No newline at end of file diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 4d2416ce..c71d0d3f 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -24,8 +24,8 @@ export class Unit extends CustomMarker { human: false, controlled: false, hasTask: false, - desiredAltitudeType: false, - desiredSpeedType: false, + desiredAltitudeType: "AGL", + desiredSpeedType: "GS", isTanker: false, isAWACS: false, onOff: false, @@ -222,7 +222,6 @@ export class Unit extends CustomMarker { var updateMarker = positionChanged || headingChanged || aliveChanged || stateChanged || controlledChanged || !getMap().hasLayer(this); /* Assign the data */ - /* TODO Allow for partial updates */ this.#data = data; /* Fire an event when a unit dies */ @@ -406,10 +405,10 @@ export class Unit extends CustomMarker { var path: any = {}; if (this.getData().activePath.length > 0) { path = this.getData().activePath; - path[(Object.keys(path).length + 1).toString()] = latlng; + path[(Object.keys(path).length).toString()] = latlng; } else { - path = { "1": latlng }; + path = [latlng]; } addDestination(this.ID, path); } @@ -779,7 +778,7 @@ export class Unit extends CustomMarker { /* Update the position of the existing markers (to avoid creating markers uselessly) */ for (let WP in this.getData().activePath) { var destination = this.getData().activePath[WP]; - this.#pathMarkers[parseInt(WP) - 1].setLatLng([destination.lat, destination.lng]); + this.#pathMarkers[parseInt(WP)].setLatLng([destination.lat, destination.lng]); points.push(new LatLng(destination.lat, destination.lng)); this.#pathPolyline.setLatLngs(points); } diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 2f56b189..c9124df6 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -1,8 +1,8 @@ import { LatLng, LatLngBounds } from "leaflet"; import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler } from ".."; import { Unit } from "./unit"; -import { cloneUnit, spawnGroundUnit } from "../server/server"; -import { base64ToBytes, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polygonArea, randomPointInPoly, randomUnitBlueprintByRole } from "../other/utils"; +import { cloneUnit, setLastUpdateTime, spawnGroundUnit } from "../server/server"; +import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polygonArea, randomPointInPoly, randomUnitBlueprintByRole } from "../other/utils"; import { CoalitionArea } from "../map/coalitionarea"; import { Airbase } from "../missionhandler/airbase"; import { groundUnitsDatabase } from "./groundunitsdatabase"; @@ -70,13 +70,13 @@ export class UnitsManager { } - update(encodedData: string) { + update(buffer: ArrayBuffer) { var updatedUnits: Unit[] = []; - var buffer = base64ToBytes(encodedData); var dataExtractor = new DataExtractor(buffer); var data: {[key: string]: UnitData} = {}; - var offset = 0; + var updateTime = Number(dataExtractor.extractUInt64()); + var offset = dataExtractor.getOffset(); while (offset < buffer.byteLength) { const result = dataExtractor.extractData(offset); data[result.data.ID] = result.data; @@ -106,6 +106,8 @@ export class UnitsManager { // if (!updatedUnits.includes(unit)) // unit.setData(null); //}); + + setLastUpdateTime(updateTime); } setHiddenType(key: string, value: boolean) { diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index 50772ef6..7f086d6e 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -1,6 +1,6 @@ local version = "v0.3.0-alpha" -local debug = false +local debug = FALSE Olympus.unitCounter = 1 Olympus.payloadRegistry = {} diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json deleted file mode 100644 index e8ff2462..00000000 --- a/src/.vscode/settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "files.associations": { - "*.ejs": "html", - "xstring": "cpp", - "vector": "cpp", - "list": "cpp" - } -} \ No newline at end of file diff --git a/src/core/include/unit.h b/src/core/include/unit.h index bdf55afc..2022b0d4 100644 --- a/src/core/include/unit.h +++ b/src/core/include/unit.h @@ -7,6 +7,9 @@ #include "logger.h" #include "commands.h" +#include +using namespace std::chrono; + #define TASK_CHECK_INIT_VALUE 10 namespace State @@ -79,8 +82,10 @@ namespace DataTypes { unsigned short fuel; double desiredSpeed; double desiredAltitude; + unsigned int leaderID; unsigned int targetID; Coords targetPosition; + unsigned char coalition; unsigned char state; unsigned char ROE; unsigned char reactionToThreat; @@ -90,11 +95,7 @@ namespace DataTypes { unsigned short pathLength; unsigned short ammoLength; unsigned short contactsLength; - unsigned char nameLength; - unsigned char unitNameLength; - unsigned char groupNameLength; - unsigned char categoryLength; - unsigned char coalitionLength; + unsigned char taskLength; }; } #pragma pack(pop) @@ -112,18 +113,18 @@ public: void runAILoop(); void updateExportData(json::value json, double dt = 0); void updateMissionData(json::value json); - unsigned int getUpdateData(char* &data); - void getData(stringstream &ss, bool refresh); + unsigned int getDataPacket(char* &data); + void getData(stringstream &ss, unsigned long long time, bool refresh); virtual string getCategory() { return "No category"; }; /********** Base data **********/ - void setControlled(bool newControlled) { controlled = newControlled; } - void setName(string newName) { name = newName; } - void setUnitName(string newUnitName) { unitName = newUnitName; } - void setGroupName(string newGroupName) { groupName = newGroupName; } - void setAlive(bool newAlive) { alive = newAlive; } - void setCountry(unsigned int newCountry) { country = newCountry; } - void setHuman(bool newHuman) { human = newHuman; } + void setControlled(bool newValue) { updateValue(controlled, newValue); } + void setName(string newValue) { updateValue(name, newValue); } + void setUnitName(string newValue) { updateValue(unitName, newValue); } + void setGroupName(string newValue) { updateValue(groupName, newValue); } + void setAlive(bool newValue) { updateValue(alive, newValue); } + void setCountry(unsigned int newValue) { updateValue(country, newValue); } + void setHuman(bool newValue) { updateValue(human, newValue); } bool getControlled() { return controlled; } string getName() { return name; } @@ -134,51 +135,50 @@ public: bool getHuman() { return human; } /********** Flight data **********/ - void setPosition(Coords newPosition) { position = newPosition; } - void setHeading(double newHeading) {heading = newHeading; } - void setSpeed(double newSpeed) {speed = newSpeed; } + void setPosition(Coords newValue) { updateValue(position, newValue); } + void setHeading(double newValue) { updateValue(heading, newValue); } + void setSpeed(double newValue) { updateValue(speed, newValue); } Coords getPosition() { return position; } double getHeading() { return heading; } double getSpeed() { return speed; } /********** Mission data **********/ - void setFuel(short newFuel) { fuel = newFuel; } + void setFuel(unsigned short newValue) { updateValue(fuel, newValue); } void setAmmo(vector newAmmo) { ammo = newAmmo; } - void setContacts(vector newContacts) {contacts = newContacts; } - void setHasTask(bool newHasTask); - void setCoalitionID(unsigned int newCoalitionID); + void setContacts(vector newContacts) { contacts = newContacts; } + void setHasTask(bool newValue) { updateValue(hasTask, newValue); } + void setCoalition(unsigned char newValue) { updateValue(coalition, newValue);} double getFuel() { return fuel; } vector getAmmo() { return ammo; } vector getTargets() { return contacts; } bool getHasTask() { return hasTask; } - string getCoalition() { return coalition; } - unsigned int getCoalitionID(); + unsigned int getCoalition() { return coalition; } /********** Formation data **********/ - void setLeaderID(unsigned int newLeaderID) { leaderID = newLeaderID; } + void setLeaderID(unsigned int newValue) { updateValue(leaderID, newValue); } void setFormationOffset(Offset formationOffset); unsigned int getLeaderID() { return leaderID; } Offset getFormationoffset() { return formationOffset; } /********** Task data **********/ - void setCurrentTask(string newCurrentTask) { currentTask = newCurrentTask; } - void setDesiredSpeed(double newDesiredSpeed); - void setDesiredAltitude(double newDesiredAltitude); - void setDesiredSpeedType(string newDesiredSpeedType); - void setDesiredAltitudeType(string newDesiredAltitudeType); - void setActiveDestination(Coords newActiveDestination) { activeDestination = newActiveDestination; } - void setActivePath(list newActivePath); - void setTargetID(unsigned int newTargetID) { targetID = newTargetID; } - void setTargetPosition(Coords newTargetPosition); - void setIsTanker(bool newIsTanker); - void setIsAWACS(bool newIsAWACS); - virtual void setOnOff(bool newOnOff) { onOff = newOnOff; }; - virtual void setFollowRoads(bool newFollowRoads) { followRoads = newFollowRoads; }; + void setTask(string newValue) { updateValue(task, newValue); } + void setDesiredSpeed(double newValue); + void setDesiredAltitude(double newValue); + void setDesiredSpeedType(string newValue); + void setDesiredAltitudeType(string newValue); + void setActiveDestination(Coords newValue) { updateValue(activeDestination, newValue); } + void setActivePath(list newValue); + void setTargetID(unsigned int newValue) { updateValue(targetID, newValue); } + void setTargetPosition(Coords newValue) { updateValue(targetPosition, newValue); } + void setIsTanker(bool newValue); + void setIsAWACS(bool newValue); + virtual void setOnOff(bool newValue) { updateValue(onOff, newValue); }; + virtual void setFollowRoads(bool newValue) { updateValue(followRoads, newValue); }; - string getCurrentTask() { return currentTask; } + string getTask() { return task; } virtual double getDesiredSpeed() { return desiredSpeed; }; virtual double getDesiredAltitude() { return desiredAltitude; }; virtual bool getDesiredSpeedType() { return desiredSpeedType; }; @@ -193,13 +193,13 @@ public: bool getFollowRoads() { return followRoads; }; /********** Options data **********/ - void setROE(unsigned char newROE, bool force = false); - void setReactionToThreat(unsigned char newReactionToThreat, bool force = false); - void setEmissionsCountermeasures(unsigned char newEmissionsCountermeasures, bool force = false); - void setTACAN(Options::TACAN newTACAN, bool force = false); - void setRadio(Options::Radio newradio, bool force = false); - void setGeneralSettings(Options::GeneralSettings newGeneralSettings, bool force = false); - void setEPLRS(bool newEPLRS, bool force = false); + void setROE(unsigned char newValue, bool force = false); + void setReactionToThreat(unsigned char newValue, bool force = false); + void setEmissionsCountermeasures(unsigned char newValue, bool force = false); + void setTACAN(Options::TACAN newValue, bool force = false); + void setRadio(Options::Radio newValue, bool force = false); + void setGeneralSettings(Options::GeneralSettings newValue, bool force = false); + void setEPLRS(bool newValue, bool force = false); unsigned char getROE() { return ROE; } unsigned char getReactionToThreat() { return reactionToThreat; } @@ -220,6 +220,8 @@ public: void pushActivePathFront(Coords newActivePathFront); void pushActivePathBack(Coords newActivePathBack); void popActivePathFront(); + void triggerUpdate() { lastUpdateTime = duration_cast(system_clock::now().time_since_epoch()).count(); } + unsigned long long getLastUpdateTime() { return lastUpdateTime; }; protected: unsigned int ID; @@ -247,18 +249,18 @@ protected: vector ammo; vector contacts; bool hasTask = false; - string coalition = ""; + unsigned char coalition; /********** Formation data **********/ unsigned int leaderID = NULL; Offset formationOffset = Offset(NULL); /********** Task data **********/ - string currentTask = ""; + string task = ""; double desiredSpeed = 0; double desiredAltitude = 0; - bool desiredSpeedType = 0; - bool desiredAltitudeType = 0; + bool desiredSpeedType = 1; + bool desiredAltitudeType = 1; list activePath; Coords activeDestination = Coords(NULL); unsigned int targetID = NULL; @@ -277,10 +279,14 @@ protected: Options::GeneralSettings generalSettings; bool EPLRS = false; + /********** Data packet **********/ + DataTypes::DataPacket dataPacket; + /********** State machine **********/ unsigned char state = State::NONE; /********** Other **********/ + unsigned long long lastUpdateTime = 0; Coords oldPosition = Coords(0); // Used to approximate speed /********** Functions **********/ @@ -295,4 +301,14 @@ protected: void goToDestination(string enrouteTask = "nil"); bool checkTaskFailed(); void resetTaskFailedCounter(); + + template + void updateValue(T& value, T& newValue) + { + if (newValue != value) + { + triggerUpdate(); + *(&value) = newValue; + } + } }; diff --git a/src/core/include/unitsmanager.h b/src/core/include/unitsmanager.h index b2233183..081545a9 100644 --- a/src/core/include/unitsmanager.h +++ b/src/core/include/unitsmanager.h @@ -20,7 +20,7 @@ public: void updateExportData(lua_State* L, double dt = 0); void updateMissionData(json::value missionData); void runAILoop(); - string getUnitData(bool refresh); + string getUnitData(stringstream &ss, unsigned long long time, bool refresh); void deleteUnit(unsigned int ID, bool explosion); void acquireControl(unsigned int ID); diff --git a/src/core/src/airunit.cpp b/src/core/src/airunit.cpp index fe0b9977..85eb71fe 100644 --- a/src/core/src/airunit.cpp +++ b/src/core/src/airunit.cpp @@ -120,7 +120,7 @@ void AirUnit::AIloop() /* State machine */ switch (state) { case State::IDLE: { - currentTask = "Idle"; + setTask("Idle"); if (!getHasTask()) { @@ -147,17 +147,17 @@ void AirUnit::AIloop() if (isTanker) { enrouteTask = "{ id = 'Tanker' }"; - currentTask = "Tanker"; + setTask("Tanker"); } else if (isAWACS) { enrouteTask = "{ id = 'AWACS' }"; - currentTask = "AWACS"; + setTask("AWACS"); } else { enrouteTask = "nil"; - currentTask = "Reaching destination"; + setTask("Reaching destination"); } if (activeDestination == NULL || !getHasTask()) @@ -179,7 +179,7 @@ void AirUnit::AIloop() } case State::LAND: { string enrouteTask = "{ id = 'Land' }"; - currentTask = "Landing"; + setTask("Landing"); if (activeDestination == NULL) { @@ -203,7 +203,7 @@ void AirUnit::AIloop() << "targetID = " << targetID << "," << "}"; string enrouteTask = enrouteTaskSS.str(); - currentTask = "Attacking " + getTargetName(); + setTask("Attacking " + getTargetName()); if (!getHasTask()) { @@ -223,7 +223,7 @@ void AirUnit::AIloop() break; } - currentTask = "Following " + getTargetName(); + setTask("Following " + getTargetName()); Unit* leader = unitsManager->getUnit(leaderID); if (!getHasTask()) { @@ -247,7 +247,7 @@ void AirUnit::AIloop() break; } case State::REFUEL: { - currentTask = "Refueling"; + setTask("Refueling"); if (!getHasTask()) { if (fuel <= initialFuel) { @@ -265,7 +265,7 @@ void AirUnit::AIloop() } } case State::BOMB_POINT: { - currentTask = "Bombing point"; + setTask("Bombing point"); if (!getHasTask()) { std::ostringstream taskSS; @@ -276,7 +276,7 @@ void AirUnit::AIloop() } } case State::CARPET_BOMB: { - currentTask = "Carpet bombing"; + setTask("Carpet bombing"); if (!getHasTask()) { std::ostringstream taskSS; @@ -288,7 +288,7 @@ void AirUnit::AIloop() break; } case State::BOMB_BUILDING: { - currentTask = "Bombing building"; + setTask("Bombing building"); if (!getHasTask()) { std::ostringstream taskSS; diff --git a/src/core/src/core.cpp b/src/core/src/core.cpp index 4f75b5ec..9dcc5a4b 100644 --- a/src/core/src/core.cpp +++ b/src/core/src/core.cpp @@ -85,13 +85,14 @@ extern "C" DllExport int coreFrame(lua_State* L) if (unitsManager != nullptr) { unitsManager->updateExportData(L, duration.count()); - //unitsManager->runAILoop(); + unitsManager->runAILoop(); + } before = std::chrono::system_clock::now(); } if (scheduler != nullptr) - //scheduler->execute(L); + scheduler->execute(L); return(0); } diff --git a/src/core/src/groundunit.cpp b/src/core/src/groundunit.cpp index f28e8a18..dc61510c 100644 --- a/src/core/src/groundunit.cpp +++ b/src/core/src/groundunit.cpp @@ -71,7 +71,7 @@ void GroundUnit::AIloop() { switch (state) { case State::IDLE: { - currentTask = "Idle"; + setTask("Idle"); if (getHasTask()) resetTask(); break; @@ -102,7 +102,7 @@ void GroundUnit::AIloop() break; } case State::FIRE_AT_AREA: { - currentTask = "Firing at area"; + setTask("Firing at area"); if (!getHasTask()) { std::ostringstream taskSS; diff --git a/src/core/src/scheduler.cpp b/src/core/src/scheduler.cpp index e070414a..98c855ff 100644 --- a/src/core/src/scheduler.cpp +++ b/src/core/src/scheduler.cpp @@ -7,7 +7,7 @@ extern UnitsManager* unitsManager; -Scheduler::Scheduler(lua_State* L): +Scheduler::Scheduler(lua_State* L) : load(0) { LogInfo(L, "Scheduler constructor called successfully"); @@ -25,16 +25,15 @@ void Scheduler::appendCommand(Command* command) void Scheduler::execute(lua_State* L) { - /* Decrease the active computation load. New commands can be sent only if the load has reached 0. + /* Decrease the active computation load. New commands can be sent only if the load has reached 0. This is needed to avoid server lag. */ if (load > 0) { load--; return; } - unsigned int priority = CommandPriority::IMMEDIATE; - while (priority >= CommandPriority::LOW) - { + int priority = CommandPriority::IMMEDIATE; + while (priority >= CommandPriority::LOW) { for (auto command : commands) { if (command->getPriority() == priority) @@ -42,13 +41,15 @@ void Scheduler::execute(lua_State* L) string commandString = "Olympus.protectedCall(" + command->getString(L) + ")"; if (dostring_in(L, "server", (commandString))) log("Error executing command " + commandString); + else + log("Command '" + commandString + "' executed correctly, current load " + to_string(load)); load = command->getLoad(); commands.remove(command); return; } } priority--; - } + }; } void Scheduler::handleRequest(string key, json::value value) @@ -66,11 +67,11 @@ void Scheduler::handleRequest(string key, json::value value) string unitName = unit->getUnitName(); json::value path = value[L"path"]; list newPath; - for (unsigned int i = 1; i <= path.as_object().size(); i++) + for (unsigned int i = 0; i < path.as_array().size(); i++) { string WP = to_string(i); - double lat = path[to_wstring(i)][L"lat"].as_double(); - double lng = path[to_wstring(i)][L"lng"].as_double(); + double lat = path[i][L"lat"].as_double(); + double lng = path[i][L"lng"].as_double(); log(unitName + " set path destination " + WP + " (" + to_string(lat) + ", " + to_string(lng) + ")"); Coords dest; dest.lat = lat; dest.lng = lng; newPath.push_back(dest); @@ -126,7 +127,7 @@ void Scheduler::handleRequest(string key, json::value value) string unitName; string targetName; - + if (unit != nullptr) unitName = unit->getUnitName(); else @@ -197,11 +198,11 @@ void Scheduler::handleRequest(string key, json::value value) } else if (key.compare("setSpeedType") == 0) { - unsigned int ID = value[L"ID"].as_integer(); - unitsManager->acquireControl(ID); - Unit* unit = unitsManager->getGroupLeader(ID); - if (unit != nullptr) - unit->setDesiredSpeedType(to_string(value[L"speedType"])); + unsigned int ID = value[L"ID"].as_integer(); + unitsManager->acquireControl(ID); + Unit* unit = unitsManager->getGroupLeader(ID); + if (unit != nullptr) + unit->setDesiredSpeedType(to_string(value[L"speedType"])); } else if (key.compare("setAltitude") == 0) { @@ -233,7 +234,7 @@ void Scheduler::handleRequest(string key, json::value value) unsigned int ID = value[L"ID"].as_integer(); unitsManager->acquireControl(ID); Unit* unit = unitsManager->getGroupLeader(ID); - unsigned char ROE = value[L"ROE"].as_number().is_uint32(); + unsigned char ROE = value[L"ROE"].as_number().to_uint32(); unit->setROE(ROE); } else if (key.compare("setReactionToThreat") == 0) @@ -241,7 +242,7 @@ void Scheduler::handleRequest(string key, json::value value) unsigned int ID = value[L"ID"].as_integer(); unitsManager->acquireControl(ID); Unit* unit = unitsManager->getGroupLeader(ID); - unsigned char reactionToThreat = value[L"reactionToThreat"].as_number().is_uint32(); + unsigned char reactionToThreat = value[L"reactionToThreat"].as_number().to_uint32(); unit->setReactionToThreat(reactionToThreat); } else if (key.compare("setEmissionsCountermeasures") == 0) @@ -249,7 +250,7 @@ void Scheduler::handleRequest(string key, json::value value) unsigned int ID = value[L"ID"].as_integer(); unitsManager->acquireControl(ID); Unit* unit = unitsManager->getGroupLeader(ID); - unsigned char emissionsCountermeasures = value[L"emissionsCountermeasures"].as_number().is_uint32(); + unsigned char emissionsCountermeasures = value[L"emissionsCountermeasures"].as_number().to_uint32(); unit->setEmissionsCountermeasures(emissionsCountermeasures); } else if (key.compare("landAt") == 0) @@ -317,7 +318,7 @@ void Scheduler::handleRequest(string key, json::value value) } } else if (key.compare("setFollowRoads") == 0) - { + { unsigned int ID = value[L"ID"].as_integer(); unitsManager->acquireControl(ID); bool followRoads = value[L"followRoads"].as_bool(); @@ -325,7 +326,7 @@ void Scheduler::handleRequest(string key, json::value value) unit->setFollowRoads(followRoads); } else if (key.compare("setOnOff") == 0) - { + { unsigned int ID = value[L"ID"].as_integer(); unitsManager->acquireControl(ID); bool onOff = value[L"onOff"].as_bool(); @@ -389,10 +390,11 @@ void Scheduler::handleRequest(string key, json::value value) { log("Unknown command: " + key); } - + if (command != nullptr) { appendCommand(command); + log("New command appended correctly to stack. Current server load: " + to_string(load)); } } diff --git a/src/core/src/server.cpp b/src/core/src/server.cpp index 086882d7..68398d1d 100644 --- a/src/core/src/server.cpp +++ b/src/core/src/server.cpp @@ -69,10 +69,11 @@ void Server::handle_get(http_request request) /* Lock for thread safety */ lock_guard guard(mutexLock); + milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); + http_response response(status_codes::OK); string authorization = to_base64("admin:" + password); - log(authorization); - if (password == "" || (request.headers().has(L"Authorization") && request.headers().find(L"Authorization")->second == L"Basic " + to_wstring(authorization))) + if (password.length() == 0 || (request.headers().has(L"Authorization") && request.headers().find(L"Authorization")->second.compare(L"Basic " + to_wstring(authorization)) == 0)) { std::exception_ptr eptr; try { @@ -96,28 +97,34 @@ void Server::handle_get(http_request request) } } - // TODO would be nice to optimize this - answer[L"units"] = json::value(to_wstring(unitsManager->getUnitData(time == 0))); - } - else if (URI.compare(LOGS_URI) == 0) - { - auto logs = json::value::object(); - getLogsJSON(logs, 100); // By reference, for thread safety. Get the last 100 log entries - answer[L"logs"] = logs; - } - else if (URI.compare(AIRBASES_URI) == 0) - answer[L"airbases"] = airbases; - else if (URI.compare(BULLSEYE_URI) == 0) - answer[L"bullseyes"] = bullseyes; - else if (URI.compare(MISSION_URI) == 0) - answer[L"mission"] = mission; + bool refresh = (time == 0); + unsigned long long updateTime = ms.count(); - milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); - answer[L"time"] = json::value::string(to_wstring(ms.count())); - answer[L"sessionHash"] = json::value::string(to_wstring(sessionHash)); + stringstream ss; + ss.write((char*)&updateTime, sizeof(updateTime)); + unitsManager->getUnitData(ss, time, refresh); + response.set_body(concurrency::streams::bytestream::open_istream(ss.str())); + } + else { + if (URI.compare(LOGS_URI) == 0) + { + auto logs = json::value::object(); + getLogsJSON(logs, 100); // By reference, for thread safety. Get the last 100 log entries + answer[L"logs"] = logs; + } + else if (URI.compare(AIRBASES_URI) == 0) + answer[L"airbases"] = airbases; + else if (URI.compare(BULLSEYE_URI) == 0) + answer[L"bullseyes"] = bullseyes; + else if (URI.compare(MISSION_URI) == 0) + answer[L"mission"] = mission; + + + answer[L"time"] = json::value::string(to_wstring(ms.count())); + answer[L"sessionHash"] = json::value::string(to_wstring(sessionHash)); + response.set_body(answer); + } } - - response.set_body(answer); } catch (...) { eptr = std::current_exception(); // capture @@ -140,7 +147,7 @@ void Server::handle_request(http_request request, functionsecond.compare(L"Basic " + to_wstring(authorization)))) + if (password.length() == 0 || (request.headers().has(L"Authorization") && request.headers().find(L"Authorization")->second.compare(L"Basic " + to_wstring(authorization)) == 0)) { auto answer = json::value::object(); request.extract_json().then([&answer, &action](pplx::task task) diff --git a/src/core/src/unit.cpp b/src/core/src/unit.cpp index b5580505..3d2650b2 100644 --- a/src/core/src/unit.cpp +++ b/src/core/src/unit.cpp @@ -45,6 +45,24 @@ Unit::~Unit() void Unit::initialize(json::value json) { + if (json.has_string_field(L"Name")) + setName(to_string(json[L"Name"])); + if (json.has_string_field(L"UnitName")) + setUnitName(to_string(json[L"UnitName"])); + if (json.has_string_field(L"GroupName")) + setGroupName(to_string(json[L"GroupName"])); + if (json.has_number_field(L"Country")) + setCountry(json[L"Country"].as_number().to_int32()); + if (json.has_number_field(L"CoalitionID")) + setCoalition(json[L"CoalitionID"].as_number().to_int32()); + + if (json.has_object_field(L"Flags")) + setHuman(json[L"Flags"][L"Human"].as_bool()); + + /* All units which contain the name "Olympus" are automatically under AI control */ + if (getUnitName().find("Olympus") != string::npos) + setControlled(true); + updateExportData(json); setDefaults(); } @@ -92,6 +110,21 @@ void Unit::runAILoop() { void Unit::updateExportData(json::value json, double dt) { + Coords newPosition = Coords(NULL); + double newHeading = 0; + double newSpeed = 0; + + if (json.has_object_field(L"LatLongAlt")) + { + setPosition({ + json[L"LatLongAlt"][L"Lat"].as_number().to_double(), + json[L"LatLongAlt"][L"Long"].as_number().to_double(), + json[L"LatLongAlt"][L"Alt"].as_number().to_double() + }); + } + if (json.has_number_field(L"Heading")) + setHeading(json[L"Heading"].as_number().to_double()); + /* Compute speed (loGetWorldObjects does not provide speed, we compute it for better performance instead of relying on many lua calls) */ if (oldPosition != NULL) { @@ -100,35 +133,7 @@ void Unit::updateExportData(json::value json, double dt) if (dt > 0) setSpeed(getSpeed() * 0.95 + (dist / dt) * 0.05); } - - if (json.has_string_field(L"Name")) - setName(to_string(json[L"Name"])); - if (json.has_string_field(L"UnitName")) - setUnitName(to_string(json[L"UnitName"])); - if (json.has_string_field(L"GroupName")) - setGroupName(to_string(json[L"GroupName"])); - if (json.has_number_field(L"Country")) - setCountry(json[L"Country"].as_number().to_int32()); - if (json.has_number_field(L"CoalitionID")) - setCoalitionID(json[L"CoalitionID"].as_number().to_int32()); - if (json.has_object_field(L"LatLongAlt")) - { - Coords position = { - json[L"LatLongAlt"][L"Lat"].as_number().to_double(), - json[L"LatLongAlt"][L"Long"].as_number().to_double(), - json[L"LatLongAlt"][L"Alt"].as_number().to_double() - }; - setPosition(position); - } - if (json.has_number_field(L"Heading")) - setHeading(json[L"Heading"].as_number().to_double()); - if (json.has_object_field(L"Flags")) - setHuman(json[L"Flags"][L"Human"].as_bool()); - - /* All units which contain the name "Olympus" are automatically under AI control */ - if (getUnitName().find("Olympus") != string::npos) - setControlled(true); - + oldPosition = position; } @@ -175,7 +180,7 @@ void Unit::updateMissionData(json::value json) setHasTask(json[L"hasTask"].as_bool()); } -unsigned int Unit::getUpdateData(char* &data) +unsigned int Unit::getDataPacket(char* &data) { /* Reserve data for: 1) DataPacket; @@ -217,34 +222,31 @@ unsigned int Unit::getUpdateData(char* &data) fuel, desiredSpeed, desiredAltitude, + leaderID, targetID, targetPosition, state, ROE, reactionToThreat, emissionsCountermeasures, + coalition, TACAN, radio, activePath.size(), ammo.size(), contacts.size(), - name.size(), - unitName.size(), - groupName.size(), - getCategory().size(), - coalition.size() + task.size() }; memcpy(data + offset, &dataPacket, sizeof(dataPacket)); offset += sizeof(dataPacket); /* 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); - + for (const Coords& c : activePath) { + memcpy(data + offset, &c, sizeof(Coords)); + offset += sizeof(Coords); + } + /* Copy the ammo vector to memory */ memcpy(data + offset, &ammo, ammo.size() * sizeof(DataTypes::Ammo)); offset += ammo.size() * sizeof(DataTypes::Ammo); @@ -256,10 +258,12 @@ unsigned int Unit::getUpdateData(char* &data) return offset; } -void Unit::getData(stringstream &ss, bool refresh) +void Unit::getData(stringstream &ss, unsigned long long time, bool refresh) { + if (time > lastUpdateTime && !refresh) return; + char* data; - unsigned int size = getUpdateData(data); + unsigned int size = getDataPacket(data); /* Prepare the data packet and copy it to memory */ /* If the unit is in a group, get the update data from the group leader and only replace the position, speed and heading */ @@ -271,15 +275,25 @@ void Unit::getData(stringstream &ss, bool refresh) } ss.write(data, size); - delete data; + ss << task; - if (refresh) { - ss << name; - ss << unitName; - ss << groupName; - ss << getCategory(); - ss << coalition; - } + unsigned short nameLength = name.length(); + unsigned short unitNameLength = unitName.length(); + unsigned short groupNameLength = groupName.length(); + unsigned short categoryLength = getCategory().length(); + + ss.write((char*)&nameLength, sizeof(nameLength)); + ss.write((char*)&unitNameLength, sizeof(unitNameLength)); + ss.write((char*)&groupNameLength, sizeof(groupNameLength)); + ss.write((char*)&categoryLength, sizeof(categoryLength)); + + ss << name; + ss << unitName; + ss << groupName; + ss << getCategory(); + + + delete data; } void Unit::setActivePath(list newPath) @@ -315,26 +329,6 @@ void Unit::popActivePathFront() setActivePath(path); } -void Unit::setCoalitionID(unsigned int newCoalitionID) -{ - if (newCoalitionID == 0) - coalition = "neutral"; - else if (newCoalitionID == 1) - coalition = "red"; - else - coalition = "blue"; -} - -unsigned int Unit::getCoalitionID() -{ - if (coalition == "neutral") - return 0; - else if (coalition == "red") - return 1; - else - return 2; -} - string Unit::getTargetName() { if (isTargetAlive()) @@ -398,6 +392,8 @@ void Unit::setFormationOffset(Offset newFormationOffset) { formationOffset = newFormationOffset; resetTask(); + + triggerUpdate(); } void Unit::setROE(unsigned char newROE, bool force) @@ -406,6 +402,8 @@ void Unit::setROE(unsigned char newROE, bool force) ROE = newROE; Command* command = dynamic_cast(new SetOption(groupName, SetCommandType::ROE, static_cast(ROE))); scheduler->appendCommand(command); + + triggerUpdate(); } } @@ -416,6 +414,8 @@ void Unit::setReactionToThreat(unsigned char newReactionToThreat, bool force) Command* command = dynamic_cast(new SetOption(groupName, SetCommandType::REACTION_ON_THREAT, static_cast(reactionToThreat))); scheduler->appendCommand(command); + + triggerUpdate(); } } @@ -464,6 +464,8 @@ void Unit::setEmissionsCountermeasures(unsigned char newEmissionsCountermeasures command = dynamic_cast(new SetOption(groupName, SetCommandType::ECM_USING, ECMEnum)); scheduler->appendCommand(command); + + triggerUpdate(); } } @@ -476,15 +478,23 @@ void Unit::landAt(Coords loc) void Unit::setIsTanker(bool newIsTanker) { - isTanker = newIsTanker; - resetTask(); + if (isTanker != newIsTanker) { + isTanker = newIsTanker; + resetTask(); + + triggerUpdate(); + } } void Unit::setIsAWACS(bool newIsAWACS) { - isAWACS = newIsAWACS; - resetTask(); - setEPLRS(isAWACS); + if (isAWACS != newIsAWACS) { + isAWACS = newIsAWACS; + resetTask(); + setEPLRS(isAWACS); + + triggerUpdate(); + } } void Unit::setTACAN(Options::TACAN newTACAN, bool force) @@ -517,6 +527,8 @@ void Unit::setTACAN(Options::TACAN newTACAN, bool force) Command* command = dynamic_cast(new SetCommand(groupName, commandSS.str())); scheduler->appendCommand(command); } + + triggerUpdate(); } } @@ -551,6 +563,8 @@ void Unit::setRadio(Options::Radio newRadio, bool force) << "}"; command = dynamic_cast(new SetCommand(groupName, commandSS.str())); scheduler->appendCommand(command); + + triggerUpdate(); } } @@ -590,6 +604,8 @@ void Unit::setGeneralSettings(Options::GeneralSettings newGeneralSettings, bool scheduler->appendCommand(command); command = dynamic_cast(new SetOption(groupName, SetCommandType::ENGAGE_AIR_WEAPONS, !generalSettings.prohibitAirWpn)); scheduler->appendCommand(command); + + triggerUpdate(); } } @@ -600,6 +616,8 @@ void Unit::setDesiredSpeed(double newDesiredSpeed) resetTask(); else goToDestination(); /* Send the command to reach the destination */ + + triggerUpdate(); } void Unit::setDesiredAltitude(double newDesiredAltitude) @@ -609,6 +627,8 @@ void Unit::setDesiredAltitude(double newDesiredAltitude) resetTask(); else goToDestination(); /* Send the command to reach the destination */ + + triggerUpdate(); } void Unit::setDesiredSpeedType(string newDesiredSpeedType) @@ -618,6 +638,8 @@ void Unit::setDesiredSpeedType(string newDesiredSpeedType) resetTask(); else goToDestination(); /* Send the command to reach the destination */ + + triggerUpdate(); } void Unit::setDesiredAltitudeType(string newDesiredAltitudeType) @@ -627,6 +649,8 @@ void Unit::setDesiredAltitudeType(string newDesiredAltitudeType) resetTask(); else goToDestination(); /* Send the command to reach the destination */ + + triggerUpdate(); } void Unit::goToDestination(string enrouteTask) @@ -669,12 +693,16 @@ bool Unit::setActiveDestination() { activeDestination = activePath.front(); log(unitName + " active destination set to queue front"); + + triggerUpdate(); return true; } else { activeDestination = Coords(0); log(unitName + " active destination set to NULL"); + + triggerUpdate(); return false; } } @@ -695,11 +723,6 @@ bool Unit::updateActivePath(bool looping) } } -void Unit::setTargetPosition(Coords newTargetPosition) -{ - targetPosition = newTargetPosition; -} - bool Unit::checkTaskFailed() { if (getHasTask()) @@ -714,8 +737,3 @@ bool Unit::checkTaskFailed() void Unit::resetTaskFailedCounter() { taskCheckCounter = TASK_CHECK_INIT_VALUE; } - -void Unit::setHasTask(bool newHasTask) -{ - hasTask = newHasTask; -} \ No newline at end of file diff --git a/src/core/src/unitsmanager.cpp b/src/core/src/unitsmanager.cpp index 66a976b5..9f4a30c3 100644 --- a/src/core/src/unitsmanager.cpp +++ b/src/core/src/unitsmanager.cpp @@ -154,11 +154,10 @@ void UnitsManager::runAILoop() { unit.second->runAILoop(); } -string UnitsManager::getUnitData(bool refresh) +string UnitsManager::getUnitData(stringstream &ss, unsigned long long time, bool refresh) { - stringstream ss; for (auto const& p : units) - p.second->getData(ss, refresh); + p.second->getData(ss, time, refresh); return to_base64(ss.str()); } diff --git a/src/utils/include/utils.h b/src/utils/include/utils.h index 3ab1a636..16b16c68 100644 --- a/src/utils/include/utils.h +++ b/src/utils/include/utils.h @@ -1,6 +1,7 @@ #pragma once #include "framework.h" +#pragma pack(push, 1) struct Coords { double lat = 0; double lng = 0; @@ -12,6 +13,7 @@ struct Offset { double y = 0; double z = 0; }; +#pragma pack(pop) // Get current date/time, format is YYYY-MM-DD.HH:mm:ss const DllExport std::string CurrentDateTime();