diff --git a/client/demo.js b/client/demo.js index 0dfe0aee..2f05382f 100644 --- a/client/demo.js +++ b/client/demo.js @@ -112,6 +112,8 @@ class DemoDataGenerator { app.get('/demo/bullseyes', (req, res) => this.bullseyes(req, res)); app.get('/demo/airbases', (req, res) => this.airbases(req, res)); app.get('/demo/mission', (req, res) => this.mission(req, res)); + app.get('/demo/commands', (req, res) => this.command(req, res)); + app.put('/demo', (req, res) => this.put(req, res)); app.use('/demo', basicAuth({ users: { @@ -457,7 +459,17 @@ class DemoDataGenerator { } res.send(JSON.stringify(ret)); } + + command(req, res) { + var ret = {commandExecuted: Math.random() > 0.5}; + res.send(JSON.stringify(ret)); + } + put(req, res) { + var ret = {commandHash: Math.random().toString(36).slice(2, 19)} + res.send(JSON.stringify(ret)); + } + } module.exports = DemoDataGenerator; \ No newline at end of file diff --git a/client/src/@types/server.d.ts b/client/src/@types/server.d.ts index 96701f8f..851bfd53 100644 --- a/client/src/@types/server.d.ts +++ b/client/src/@types/server.d.ts @@ -44,4 +44,9 @@ interface LogData { logs: {[key: string]: string}, sessionHash: string; time: number; +} + +interface ServerRequestOptions { + time?: number; + commandHash?: string; } \ No newline at end of file diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index 1013f6e3..d1124434 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -1,5 +1,13 @@ import { LatLng, LatLngBounds } from "leaflet"; +export const UNITS_URI = "units"; +export const WEAPONS_URI = "weapons"; +export const LOGS_URI = "logs"; +export const AIRBASES_URI = "airbases"; +export const BULLSEYE_URI = "bullseyes"; +export const MISSION_URI = "mission"; +export const COMMANDS_URI = "commands"; + export const NONE = "None"; export const GAME_MASTER = "Game master"; export const BLUE_COMMANDER = "Blue commander"; diff --git a/client/src/controls/unitspawnmenu.ts b/client/src/controls/unitspawnmenu.ts index 16096d8c..5a6a64cc 100644 --- a/client/src/controls/unitspawnmenu.ts +++ b/client/src/controls/unitspawnmenu.ts @@ -412,10 +412,13 @@ export class AircraftSpawnMenu extends UnitSpawnMenu { for (let i = 1; i < unitsCount + 1; i++) { units.push(unitTable); } - if (getUnitsManager().spawnUnits("Aircraft", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country)) { - getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition()); - getMap().getMapContextMenu().hide(); - } + + getUnitsManager().spawnUnits("Aircraft", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => { + if (res.commandHash !== undefined) + getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash); + }); + + getMap().getMapContextMenu().hide(); } } } @@ -447,10 +450,13 @@ export class HelicopterSpawnMenu extends UnitSpawnMenu { for (let i = 1; i < unitsCount + 1; i++) { units.push(unitTable); } - if (getUnitsManager().spawnUnits("Helicopter", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country)) { - getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition()); - getMap().getMapContextMenu().hide(); - } + + getUnitsManager().spawnUnits("Helicopter", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => { + if (res.commandHash !== undefined) + getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash); + }); + + getMap().getMapContextMenu().hide(); } } } @@ -481,10 +487,13 @@ export class GroundUnitSpawnMenu extends UnitSpawnMenu { units.push(JSON.parse(JSON.stringify(unitTable))); unitTable.location.lat += 0.0001; } - if (getUnitsManager().spawnUnits("GroundUnit", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country)) { - getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition()); - getMap().getMapContextMenu().hide(); - } + + getUnitsManager().spawnUnits("GroundUnit", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => { + if (res.commandHash !== undefined) + getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash); + }); + + getMap().getMapContextMenu().hide(); } } } @@ -515,10 +524,13 @@ export class NavyUnitSpawnMenu extends UnitSpawnMenu { units.push(JSON.parse(JSON.stringify(unitTable))); unitTable.location.lat += 0.0001; } - if (getUnitsManager().spawnUnits("NavyUnit", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country)) { - getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition()); - getMap().getMapContextMenu().hide(); - } + + getUnitsManager().spawnUnits("NavyUnit", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => { + if (res.commandHash !== undefined) + getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash); + }); + + getMap().getMapContextMenu().hide(); } } } \ No newline at end of file diff --git a/client/src/map/map.ts b/client/src/map/map.ts index 89d613a2..345d6a82 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -425,8 +425,8 @@ export class Map extends L.Map { } } - addTemporaryMarker(latlng: L.LatLng, name: string, coalition: string) { - var marker = new TemporaryUnitMarker(latlng, name, coalition); + addTemporaryMarker(latlng: L.LatLng, name: string, coalition: string, commandHash?: string) { + var marker = new TemporaryUnitMarker(latlng, name, coalition, commandHash); marker.addTo(this); this.#temporaryMarkers.push(marker); } diff --git a/client/src/map/temporaryunitmarker.ts b/client/src/map/temporaryunitmarker.ts index 81136e6a..ffda032a 100644 --- a/client/src/map/temporaryunitmarker.ts +++ b/client/src/map/temporaryunitmarker.ts @@ -2,15 +2,33 @@ import { CustomMarker } from "./custommarker"; import { DivIcon, LatLng } from "leaflet"; import { SVGInjector } from "@tanem/svg-injector"; import { getMarkerCategoryByName, getUnitDatabaseByCategory } from "../other/utils"; +import { isCommandExecuted } from "../server/server"; +import { getMap } from ".."; export class TemporaryUnitMarker extends CustomMarker { #name: string; #coalition: string; + #commandHash: string|undefined = undefined; + #timer: number = 0; - constructor(latlng: LatLng, name: string, coalition: string) { + constructor(latlng: LatLng, name: string, coalition: string, commandHash?: string) { super(latlng, {interactive: false}); this.#name = name; this.#coalition = coalition; + this.#commandHash = commandHash; + + if (this.#commandHash !== undefined) { + this.#timer = window.setInterval(() => { + if (this.#commandHash !== undefined) { + isCommandExecuted((res: any) => { + if (res.commandExecuted) { + this.removeFrom(getMap()); + window.clearInterval(this.#timer); + } + }, this.#commandHash) + } + }, 1000); + } } createIcon() { diff --git a/client/src/server/server.ts b/client/src/server/server.ts index 3049fd93..189a877a 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -1,19 +1,13 @@ import { LatLng } from 'leaflet'; import { getConnectionStatusPanel, getInfoPopup, getLogPanel, getMissionHandler, getServerStatusPanel, getUnitsManager, getWeaponsManager, setLoginStatus } from '..'; import { GeneralSettings, Radio, TACAN } from '../@types/unit'; -import { NONE, ROEs, emissionsCountermeasures, reactionsToThreat } from '../constants/constants'; +import { AIRBASES_URI, BULLSEYE_URI, COMMANDS_URI, LOGS_URI, MISSION_URI, NONE, ROEs, UNITS_URI, WEAPONS_URI, emissionsCountermeasures, reactionsToThreat } from '../constants/constants'; var connected: boolean = false; var paused: boolean = false; var REST_ADDRESS = "http://localhost:30000/olympus"; var DEMO_ADDRESS = window.location.href + "demo"; -const UNITS_URI = "units"; -const WEAPONS_URI = "weapons"; -const LOGS_URI = "logs"; -const AIRBASES_URI = "airbases"; -const BULLSEYE_URI = "bullseyes"; -const MISSION_URI = "mission"; var username = ""; var password = ""; @@ -38,13 +32,15 @@ export function setCredentials(newUsername: string, newPassword: string) { password = newPassword; } -export function GET(callback: CallableFunction, uri: string, options?: { time?: number }, responseType?: string) { +export function GET(callback: CallableFunction, uri: string, options?: ServerRequestOptions, responseType?: string) { var xmlHttp = new XMLHttpRequest(); /* Assemble the request options string */ var optionsString = ''; if (options?.time != undefined) optionsString = `time=${options.time}`; + if (options?.commandHash != undefined) + optionsString = `commandHash=${options.commandHash}`; /* On the connection */ xmlHttp.open("GET", `${demoEnabled ? DEMO_ADDRESS : REST_ADDRESS}/${uri}${optionsString ? `?${optionsString}` : ''}`, true); @@ -92,8 +88,9 @@ export function POST(request: object, callback: CallableFunction) { xmlHttp.setRequestHeader("Content-Type", "application/json"); if (username && password) xmlHttp.setRequestHeader("Authorization", "Basic " + btoa(`${username}:${password}`)); - xmlHttp.onreadystatechange = () => { - callback(); + xmlHttp.onload = (res: any) => { + var res = JSON.parse(xmlHttp.responseText); + callback(res); }; xmlHttp.send(JSON.stringify(request)); } @@ -140,185 +137,189 @@ export function getWeapons(callback: CallableFunction, refresh: boolean = false) GET(callback, WEAPONS_URI, { time: refresh ? 0 : lastUpdateTimes[WEAPONS_URI] }, 'arraybuffer'); } -export function addDestination(ID: number, path: any) { +export function isCommandExecuted(callback: CallableFunction, commandHash: string) { + GET(callback, COMMANDS_URI, { commandHash: commandHash}); +} + +export function addDestination(ID: number, path: any, callback: CallableFunction = () => {}) { var command = { "ID": ID, "path": path } var data = { "setPath": command } - POST(data, () => { }); + POST(data, callback); } -export function spawnSmoke(color: string, latlng: LatLng) { +export function spawnSmoke(color: string, latlng: LatLng, callback: CallableFunction = () => {}) { var command = { "color": color, "location": latlng }; var data = { "smoke": command } - POST(data, () => { }); + POST(data, callback); } -export function spawnExplosion(intensity: number, latlng: LatLng) { +export function spawnExplosion(intensity: number, latlng: LatLng, callback: CallableFunction = () => {}) { var command = { "intensity": intensity, "location": latlng }; var data = { "explosion": command } - POST(data, () => { }); + POST(data, callback); } -export function spawnAircrafts(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number) { +export function spawnAircrafts(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) { var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "country": country, "immediate": immediate, "spawnPoints": spawnPoints }; var data = { "spawnAircrafts": command } - POST(data, () => { }); + POST(data, callback); } -export function spawnHelicopters(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number) { +export function spawnHelicopters(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) { var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "country": country, "immediate": immediate, "spawnPoints": spawnPoints }; var data = { "spawnHelicopters": command } - POST(data, () => { }); + POST(data, callback); } -export function spawnGroundUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number) { +export function spawnGroundUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) { var command = { "units": units, "coalition": coalition, "country": country, "immediate": immediate, "spawnPoints": spawnPoints };; var data = { "spawnGroundUnits": command } - POST(data, () => { }); + POST(data, callback); } -export function spawnNavyUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number) { +export function spawnNavyUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) { var command = { "units": units, "coalition": coalition, "country": country, "immediate": immediate, "spawnPoints": spawnPoints }; var data = { "spawnNavyUnits": command } - POST(data, () => { }); + POST(data, callback); } -export function attackUnit(ID: number, targetID: number) { +export function attackUnit(ID: number, targetID: number, callback: CallableFunction = () => {}) { var command = { "ID": ID, "targetID": targetID }; var data = { "attackUnit": command } - POST(data, () => { }); + POST(data, callback); } -export function followUnit(ID: number, targetID: number, offset: { "x": number, "y": number, "z": number }) { +export function followUnit(ID: number, targetID: number, offset: { "x": number, "y": number, "z": number }, callback: CallableFunction = () => {}) { // X: front-rear, positive front // Y: top-bottom, positive bottom // Z: left-right, positive right var command = { "ID": ID, "targetID": targetID, "offsetX": offset["x"], "offsetY": offset["y"], "offsetZ": offset["z"] }; var data = { "followUnit": command } - POST(data, () => { }); + POST(data, callback); } -export function cloneUnits(units: {ID: number, location: LatLng}[]) { +export function cloneUnits(units: {ID: number, location: LatLng}[], callback: CallableFunction = () => {}) { var command = { "units": units }; var data = { "cloneUnit": command } - POST(data, () => { }); + POST(data, callback); } -export function deleteUnit(ID: number, explosion: boolean, immediate: boolean) { +export function deleteUnit(ID: number, explosion: boolean, immediate: boolean, callback: CallableFunction = () => {}) { var command = { "ID": ID, "explosion": explosion, "immediate": immediate }; var data = { "deleteUnit": command } - POST(data, () => { }); + POST(data, callback); } -export function landAt(ID: number, latlng: LatLng) { +export function landAt(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) { var command = { "ID": ID, "location": latlng }; var data = { "landAt": command } - POST(data, () => { }); + POST(data, callback); } -export function changeSpeed(ID: number, speedChange: string) { +export function changeSpeed(ID: number, speedChange: string, callback: CallableFunction = () => {}) { var command = { "ID": ID, "change": speedChange } var data = { "changeSpeed": command } - POST(data, () => { }); + POST(data, callback); } -export function setSpeed(ID: number, speed: number) { +export function setSpeed(ID: number, speed: number, callback: CallableFunction = () => {}) { var command = { "ID": ID, "speed": speed } var data = { "setSpeed": command } - POST(data, () => { }); + POST(data, callback); } -export function setSpeedType(ID: number, speedType: string) { +export function setSpeedType(ID: number, speedType: string, callback: CallableFunction = () => {}) { var command = { "ID": ID, "speedType": speedType } var data = { "setSpeedType": command } - POST(data, () => { }); + POST(data, callback); } -export function changeAltitude(ID: number, altitudeChange: string) { +export function changeAltitude(ID: number, altitudeChange: string, callback: CallableFunction = () => {}) { var command = { "ID": ID, "change": altitudeChange } var data = { "changeAltitude": command } - POST(data, () => { }); + POST(data, callback); } -export function setAltitudeType(ID: number, altitudeType: string) { +export function setAltitudeType(ID: number, altitudeType: string, callback: CallableFunction = () => {}) { var command = { "ID": ID, "altitudeType": altitudeType } var data = { "setAltitudeType": command } - POST(data, () => { }); + POST(data, callback); } -export function setAltitude(ID: number, altitude: number) { +export function setAltitude(ID: number, altitude: number, callback: CallableFunction = () => {}) { var command = { "ID": ID, "altitude": altitude } var data = { "setAltitude": command } - POST(data, () => { }); + POST(data, callback); } -export function createFormation(ID: number, isLeader: boolean, wingmenIDs: number[]) { +export function createFormation(ID: number, isLeader: boolean, wingmenIDs: number[], callback: CallableFunction = () => {}) { var command = { "ID": ID, "wingmenIDs": wingmenIDs, "isLeader": isLeader } var data = { "setLeader": command } - POST(data, () => { }); + POST(data, callback); } -export function setROE(ID: number, ROE: string) { +export function setROE(ID: number, ROE: string, callback: CallableFunction = () => {}) { var command = { "ID": ID, "ROE": ROEs.indexOf(ROE) } var data = { "setROE": command } - POST(data, () => { }); + POST(data, callback); } -export function setReactionToThreat(ID: number, reactionToThreat: string) { +export function setReactionToThreat(ID: number, reactionToThreat: string, callback: CallableFunction = () => {}) { var command = { "ID": ID, "reactionToThreat": reactionsToThreat.indexOf(reactionToThreat) } var data = { "setReactionToThreat": command } - POST(data, () => { }); + POST(data, callback); } -export function setEmissionsCountermeasures(ID: number, emissionCountermeasure: string) { +export function setEmissionsCountermeasures(ID: number, emissionCountermeasure: string, callback: CallableFunction = () => {}) { var command = { "ID": ID, "emissionsCountermeasures": emissionsCountermeasures.indexOf(emissionCountermeasure) } var data = { "setEmissionsCountermeasures": command } - POST(data, () => { }); + POST(data, callback); } -export function setOnOff(ID: number, onOff: boolean) { +export function setOnOff(ID: number, onOff: boolean, callback: CallableFunction = () => {}) { var command = { "ID": ID, "onOff": onOff } var data = { "setOnOff": command } - POST(data, () => { }); + POST(data, callback); } -export function setFollowRoads(ID: number, followRoads: boolean) { +export function setFollowRoads(ID: number, followRoads: boolean, callback: CallableFunction = () => {}) { var command = { "ID": ID, "followRoads": followRoads } var data = { "setFollowRoads": command } - POST(data, () => { }); + POST(data, callback); } -export function refuel(ID: number) { +export function refuel(ID: number, callback: CallableFunction = () => {}) { var command = { "ID": ID }; var data = { "refuel": command } - POST(data, () => { }); + POST(data, callback); } -export function bombPoint(ID: number, latlng: LatLng) { +export function bombPoint(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) { var command = { "ID": ID, "location": latlng } var data = { "bombPoint": command } - POST(data, () => { }); + POST(data, callback); } -export function carpetBomb(ID: number, latlng: LatLng) { +export function carpetBomb(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) { var command = { "ID": ID, "location": latlng } var data = { "carpetBomb": command } - POST(data, () => { }); + POST(data, callback); } -export function bombBuilding(ID: number, latlng: LatLng) { +export function bombBuilding(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) { var command = { "ID": ID, "location": latlng } var data = { "bombBuilding": command } - POST(data, () => { }); + POST(data, callback); } -export function fireAtArea(ID: number, latlng: LatLng) { +export function fireAtArea(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) { var command = { "ID": ID, "location": latlng } var data = { "fireAtArea": command } - POST(data, () => { }); + POST(data, callback); } -export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings) { +export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings, callback: CallableFunction = () => {}) { var command = { "ID": ID, "isTanker": isTanker, @@ -329,10 +330,10 @@ export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolea }; var data = { "setAdvancedOptions": command }; - POST(data, () => { }); + POST(data, callback); } -export function setCommandModeOptions(restrictSpawns: boolean, restrictToCoalition: boolean, spawnPoints: {blue: number, red: number}, eras: string[], setupTime: number) { +export function setCommandModeOptions(restrictSpawns: boolean, restrictToCoalition: boolean, spawnPoints: {blue: number, red: number}, eras: string[], setupTime: number, callback: CallableFunction = () => {}) { var command = { "restrictSpawns": restrictSpawns, "restrictToCoalition": restrictToCoalition, @@ -342,7 +343,7 @@ export function setCommandModeOptions(restrictSpawns: boolean, restrictToCoaliti }; var data = { "setCommandModeOptions": command }; - POST(data, () => { }); + POST(data, callback); } export function startUpdate() { diff --git a/client/src/unit/unitsmanager.ts b/client/src/unit/unitsmanager.ts index e9bc9b92..c88e3df6 100644 --- a/client/src/unit/unitsmanager.ts +++ b/client/src/unit/unitsmanager.ts @@ -686,40 +686,46 @@ export class UnitsManager { input.click(); } - spawnUnits(category: string, units: any, coalition: string = "blue", immediate: boolean = true, airbase: string = "", country: string = "") { + spawnUnits(category: string, units: any, coalition: string = "blue", immediate: boolean = true, airbase: string = "", country: string = "", callback: CallableFunction = () => {}) { var spawnPoints = 0; + var spawnFunction = () => {}; + var spawnsRestricted = getMissionHandler().getCommandModeOptions().restrictSpawns && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER; + if (category === "Aircraft") { - if (airbase == "" && getMissionHandler().getCommandModeOptions().restrictSpawns && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) { + if (airbase == "" && spawnsRestricted) { getInfoPopup().setText("Aircrafts can be air spawned during the SETUP phase only"); return false; } - spawnPoints = units.reduce((points: number, unit: any) => { return points + aircraftDatabase.getSpawnPointsByName(unit.unitType) }, 0); - spawnAircrafts(units, coalition, airbase, country, immediate, spawnPoints); + spawnPoints = units.reduce((points: number, unit: any) => {return points + aircraftDatabase.getSpawnPointsByName(unit.unitType)}, 0); + spawnFunction = () => spawnAircrafts(units, coalition, airbase, country, immediate, spawnPoints, callback); } else if (category === "Helicopter") { - if (airbase == "" && getMissionHandler().getCommandModeOptions().restrictSpawns && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) { + if (airbase == "" && spawnsRestricted) { getInfoPopup().setText("Helicopters can be air spawned during the SETUP phase only"); return false; } - spawnPoints = units.reduce((points: number, unit: any) => { return points + helicopterDatabase.getSpawnPointsByName(unit.unitType) }, 0); - spawnHelicopters(units, coalition, airbase, country, immediate, spawnPoints); + spawnPoints = units.reduce((points: number, unit: any) => {return points + helicopterDatabase.getSpawnPointsByName(unit.unitType)}, 0); + spawnFunction = () => spawnHelicopters(units, coalition, airbase, country, immediate, spawnPoints, callback); + } else if (category === "GroundUnit") { - if (getMissionHandler().getCommandModeOptions().restrictSpawns && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) { + if (spawnsRestricted) { getInfoPopup().setText("Ground units can be spawned during the SETUP phase only"); return false; } - spawnPoints = units.reduce((points: number, unit: any) => { return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType) }, 0); - spawnGroundUnits(units, coalition, country, immediate, spawnPoints); + spawnPoints = units.reduce((points: number, unit: any) => {return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0); + spawnFunction = () => spawnGroundUnits(units, coalition, country, immediate, spawnPoints, callback); + } else if (category === "NavyUnit") { - if (getMissionHandler().getCommandModeOptions().restrictSpawns && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) { + if (spawnsRestricted) { getInfoPopup().setText("Navy units can be spawned during the SETUP phase only"); return false; } - spawnPoints = units.reduce((points: number, unit: any) => { return points + navyUnitDatabase.getSpawnPointsByName(unit.unitType) }, 0); - spawnNavyUnits(units, coalition, country, immediate, spawnPoints); + spawnPoints = units.reduce((points: number, unit: any) => {return points + navyUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0); + spawnFunction = () => spawnNavyUnits(units, coalition, country, immediate, spawnPoints, callback); } if (spawnPoints <= getMissionHandler().getAvailableSpawnPoints()) { getMissionHandler().setSpentSpawnPoints(spawnPoints); + spawnFunction(); return true; } else { getInfoPopup().setText("Not enough spawn points available!"); diff --git a/src/core/include/commands.h b/src/core/include/commands.h index 940dcf28..cde73416 100644 --- a/src/core/include/commands.h +++ b/src/core/include/commands.h @@ -98,9 +98,11 @@ public: unsigned int getPriority() { return priority; } virtual string getString() = 0; virtual unsigned int getLoad() = 0; + const string getHash() { return hash; } protected: unsigned int priority = CommandPriority::LOW; + const string hash = random_string(16); }; /* Simple low priority move command (from user click) */ diff --git a/src/core/include/scheduler.h b/src/core/include/scheduler.h index 3cd2fe30..37acdffe 100644 --- a/src/core/include/scheduler.h +++ b/src/core/include/scheduler.h @@ -11,9 +11,10 @@ public: void appendCommand(Command* command); void execute(lua_State* L); - void handleRequest(string key, json::value value, string username); + void handleRequest(string key, json::value value, string username, json::value& answer); bool checkSpawnPoints(int spawnPoints, string coalition); - + bool isCommandExecuted(string commandHash) { return (find(executedCommandsHashes.begin(), executedCommandsHashes.end(), commandHash) != executedCommandsHashes.end()); } + void setFrameRate(double newFrameRate) { frameRate = newFrameRate; } void setRestrictSpawns(bool newRestrictSpawns) { restrictSpawns = newRestrictSpawns; } void setRestrictToCoalition(bool newRestrictToCoalition) { restrictToCoalition = newRestrictToCoalition; } @@ -35,6 +36,7 @@ public: private: list commands; + list executedCommandsHashes; unsigned int load; double frameRate; diff --git a/src/core/src/scheduler.cpp b/src/core/src/scheduler.cpp index ca65688a..cd3f8101 100644 --- a/src/core/src/scheduler.cpp +++ b/src/core/src/scheduler.cpp @@ -60,6 +60,7 @@ void Scheduler::execute(lua_State* L) log("Command '" + commandString + "' executed correctly, current load " + to_string(getLoad())); load = command->getLoad(); commands.remove(command); + executedCommandsHashes.push_back(command->getHash()); delete command; return; } @@ -134,7 +135,7 @@ bool Scheduler::checkSpawnPoints(int spawnPoints, string coalition) } } -void Scheduler::handleRequest(string key, json::value value, string username) +void Scheduler::handleRequest(string key, json::value value, string username, json::value& answer) { Command* command = nullptr; @@ -572,6 +573,7 @@ void Scheduler::handleRequest(string key, json::value value, string username) { appendCommand(command); log("New command appended correctly to stack. Current server load: " + to_string(getLoad())); + answer[L"commandHash"] = json::value(to_wstring(command->getHash())); } } diff --git a/src/core/src/server.cpp b/src/core/src/server.cpp index b707919f..567d5fba 100644 --- a/src/core/src/server.cpp +++ b/src/core/src/server.cpp @@ -142,6 +142,9 @@ void Server::handle_get(http_request request) else answer[L"mission"][L"commandModeOptions"][L"commandMode"] = json::value(L"Observer"); } + else if (URI.compare(COMMANDS_URI) && query.find(L"commandHash") != query.end()) { + answer[L"commandExectued"] = json::value(scheduler->isCommandExecuted(to_string(query[L"commandHash"]))); + } /* Common data */ answer[L"time"] = json::value::string(to_wstring(ms.count())); @@ -222,7 +225,7 @@ void Server::handle_put(http_request request) std::exception_ptr eptr; try { - scheduler->handleRequest(to_string(key), value, username); + scheduler->handleRequest(to_string(key), value, username, answer); } catch (...) { eptr = std::current_exception(); // capture diff --git a/src/shared/include/defines.h b/src/shared/include/defines.h index 29d51209..e8f8fc14 100644 --- a/src/shared/include/defines.h +++ b/src/shared/include/defines.h @@ -10,5 +10,6 @@ #define AIRBASES_URI "airbases" #define BULLSEYE_URI "bullseyes" #define MISSION_URI "mission" +#define COMMANDS_URI "commands" #define FRAMERATE_TIME_INTERVAL 0.05 \ No newline at end of file