Tweaks and implemented workaround to MIST bug

This commit is contained in:
Pax1601 2023-07-12 17:01:03 +02:00
parent b78cd27e4e
commit a949a9bf22
27 changed files with 453 additions and 135 deletions

View File

@ -1,12 +1,12 @@
{
"name": "DCSOlympus",
"version": "v0.3.0-alpha",
"version": "v0.4.0-alpha",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "DCSOlympus",
"version": "v0.3.0-alpha",
"version": "v0.4.0-alpha",
"dependencies": {
"@turf/turf": "^6.5.0",
"@types/formatcoords": "^1.1.0",

View File

@ -2,7 +2,7 @@
"name": "DCSOlympus",
"node-main": "./bin/www",
"main": "http://localhost:3000",
"version": "v0.3.0-alpha",
"version": "v0.4.0-alpha",
"private": true,
"scripts": {
"copy": "copy.bat",

View File

@ -31,7 +31,7 @@ export class MapContextMenu extends ContextMenu {
#navyUnitTypeDropdown: Dropdown;
#navyUnitNameDropdown: Dropdown;
#navyUnitCountDropdown: Dropdown;
#spawnOptions = { role: "", name: "", latlng: new LatLng(0, 0), coalition: "blue", loadout: "", airbaseName: "", altitude: ftToM(20000), count: 1 };
#spawnOptions = { role: "", name: "", latlng: new LatLng(0, 0), coalition: "blue", loadout: "", airbaseName: "", altitude: 0, count: 1 };
#coalitionArea: CoalitionArea | null = null;
constructor(id: string) {
@ -89,7 +89,7 @@ export class MapContextMenu extends ContextMenu {
this.hideSubMenus(e.detail.type);
});
document.addEventListener("contextMenuDeployAircraft", () => {
document.addEventListener("contextMenuDeployAircrafts", () => {
this.hide();
this.#spawnOptions.coalition = getActiveCoalition();
if (this.#spawnOptions) {
@ -103,7 +103,7 @@ export class MapContextMenu extends ContextMenu {
}
});
document.addEventListener("contextMenuDeployHelicopter", () => {
document.addEventListener("contextMenuDeployHelicopters", () => {
this.hide();
this.#spawnOptions.coalition = getActiveCoalition();
if (this.#spawnOptions) {
@ -117,7 +117,7 @@ export class MapContextMenu extends ContextMenu {
}
});
document.addEventListener("contextMenuDeployGroundUnit", () => {
document.addEventListener("contextMenuDeployGroundUnits", () => {
this.hide();
this.#spawnOptions.coalition = getActiveCoalition();
if (this.#spawnOptions) {
@ -222,6 +222,13 @@ export class MapContextMenu extends ContextMenu {
this.#groundUnitCountDropdown.setValue("1");
this.clip();
if (type === "aircraft") {
this.#spawnOptions.altitude = ftToM(this.#aircraftSpawnAltitudeSlider.getValue());
}
else if (type === "helicopter") {
this.#spawnOptions.altitude = ftToM(this.#helicopterSpawnAltitudeSlider.getValue());
}
this.setVisibleSubMenu(type);
}

View File

@ -17,6 +17,8 @@ import { TargetMarker } from "./targetmarker";
import { CoalitionArea } from "./coalitionarea";
import { CoalitionAreaContextMenu } from "../controls/coalitionareacontextmenu";
import { DrawingCursor } from "./drawingcursor";
import { aircraftDatabase } from "../units/aircraftdatabase";
import { groundUnitDatabase } from "../units/groundunitdatabase";
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
@ -422,6 +424,9 @@ export class Map extends L.Map {
#onDoubleClick(e: any) {
this.deselectAllCoalitionAreas();
var db = groundUnitDatabase;
db.generateTestGrid(this.getMouseCoordinates())
}
#onContextMenu(e: any) {

View File

@ -153,7 +153,7 @@ export function spawnAircrafts(units: any, coalition: string, airbaseName: strin
export function spawnHelicopters(units: any, coalition: string, airbaseName: string, immediate: boolean) {
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate };
var data = { "spawnHelicopter": command }
var data = { "spawnHelicopters": command }
POST(data, () => { });
}
@ -191,8 +191,8 @@ export function cloneUnit(ID: number, latlng: LatLng) {
POST(data, () => { });
}
export function deleteUnit(ID: number, explosion: boolean) {
var command = { "ID": ID, "explosion": explosion };
export function deleteUnit(ID: number, explosion: boolean, immediate: boolean) {
var command = { "ID": ID, "explosion": explosion, "immediate": immediate };
var data = { "deleteUnit": command }
POST(data, () => { });
}

View File

@ -3906,6 +3906,10 @@ export class AircraftDatabase extends UnitDatabase {
}
}
}
getCategory() {
return "Aircraft";
}
}
export var aircraftDatabase = new AircraftDatabase();

View File

@ -1537,6 +1537,10 @@ export class GroundUnitDatabase extends UnitDatabase {
}
}
}
getCategory() {
return "GroundUnit";
}
}
export var groundUnitDatabase = new GroundUnitDatabase();

View File

@ -578,6 +578,10 @@ export class HelicopterDatabase extends UnitDatabase {
}
}
}
getCategory() {
return "Helicopter";
}
}
export var helicopterDatabase = new HelicopterDatabase();

View File

@ -631,8 +631,8 @@ export class NavyUnitDatabase extends UnitDatabase {
"range": "",
"filename": ""
},
"Ticonderoga": {
"name": "Ticonderoga",
"TICONDEROG": {
"name": "TICONDEROG",
"type": "Cruiser",
"era": [
"Late Cold War"
@ -941,6 +941,10 @@ export class NavyUnitDatabase extends UnitDatabase {
}
}
}
getCategory() {
return "NavyUnit";
}
}
export var navyUnitDatabase = new NavyUnitDatabase();

View File

@ -646,8 +646,8 @@ export class Unit extends CustomMarker {
setFollowRoads(this.ID, followRoads);
}
delete(explosion: boolean) {
deleteUnit(this.ID, explosion);
delete(explosion: boolean, immediate: boolean) {
deleteUnit(this.ID, explosion, immediate);
}
refuel() {

View File

@ -1,3 +1,6 @@
import { LatLng } from "leaflet";
import { getUnitsManager } from "..";
export class UnitDatabase {
blueprints: { [key: string]: UnitBlueprint } = {};
@ -5,6 +8,10 @@ export class UnitDatabase {
}
getCategory() {
return "";
}
getBlueprints() {
return this.blueprints;
}
@ -143,4 +150,16 @@ export class UnitDatabase {
}
return null;
}
generateTestGrid(initialPosition: LatLng) {
const step = 0.01;
var nUnits = Object.values(this.blueprints).length;
var gridSize = Math.ceil(Math.sqrt(nUnits));
Object.values(this.blueprints).forEach((unitBlueprint: UnitBlueprint, idx: number) => {
var row = Math.floor(idx / gridSize);
var col = idx - row * gridSize;
var location = new LatLng(initialPosition.lat + col * step, initialPosition.lng + row * step)
getUnitsManager().spawnUnit(this.getCategory(), [{unitType: unitBlueprint.name, location: location, altitude: 1000, loadout: ""}]);
})
}
}

View File

@ -1,15 +1,14 @@
import { LatLng, LatLngBounds } from "leaflet";
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler } from "..";
import { getHotgroupPanel, getInfoPopup, getMap } from "..";
import { Unit } from "./unit";
import { cloneUnit, setLastUpdateTime, spawnGroundUnits } from "../server/server";
import { cloneUnit, setLastUpdateTime, spawnAircrafts, spawnGroundUnits } from "../server/server";
import { bearingAndDistanceToLatLng, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils";
import { CoalitionArea } from "../map/coalitionarea";
import { Airbase } from "../missionhandler/airbase";
import { groundUnitDatabase } from "./groundunitdatabase";
import { DataIndexes, HIDE_ALL, IADSDensities, IDLE, MOVE_UNIT } from "../constants/constants";
import { DataExtractor } from "./dataextractor";
import { Contact } from "../@types/unit";
import { citiesDatabase } from "./citiesDatabase";
import { citiesDatabase } from "./citiesdatabase";
export class UnitsManager {
#units: { [ID: number]: Unit };
@ -378,8 +377,12 @@ export class UnitsManager {
return;
}
var immediate = false;
if (selectedUnits.length > 20)
immediate = confirm(`You are trying to delete ${selectedUnits.length} units, do you want to delete them immediately? This may cause lag for players.`)
for (let idx in selectedUnits) {
selectedUnits[idx].delete(explosion);
selectedUnits[idx].delete(explosion, immediate);
}
this.#showActionMessage(selectedUnits, `deleted`);
}
@ -518,7 +521,7 @@ export class UnitsManager {
if (this.#units[idx].getCoalition() !== "neutral" && this.#units[idx].getCoalition() != unit.getCoalition())
{
this.#units[idx].getContacts().forEach((contact: Contact) => {
if (contact.ID == unit.ID && !detectionMethods.includes(contact.detectionMethod))
if (this.#units[idx].getAlive() && contact.ID == unit.ID && !detectionMethods.includes(contact.detectionMethod))
detectionMethods.push(contact.detectionMethod);
});
}
@ -616,6 +619,14 @@ export class UnitsManager {
input.click();
}
spawnUnit(category: string, units: any, coalition: string = "blue", immediate: boolean = true) {
if (category === "Aircraft") {
spawnAircrafts(units, coalition, "", immediate);
} else if (category === "GroundUnit") {
spawnGroundUnits(units, coalition, immediate);
}
}
/***********************************************/
#onKeyUp(event: KeyboardEvent) {
if (!keyEventWasInInput(event) && event.key === "Delete" ) {

View File

@ -4,8 +4,8 @@
<div id="coalition-switch" class="ol-switch ol-coalition-switch"></div>
<button data-coalition="blue" id="aircraft-spawn-button" title="Spawn aircraft" data-on-click="mapContextMenuShow"
data-on-click-params='{ "type": "aircraft" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
<!--<button data-coalition="blue" id="helicopter-spawn-button" title="Spawn helicopter" data-on-click="mapContextMenuShow"
data-on-click-params='{ "type": "helicopter" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/helicopter.svg" inject-svg></button>-->
<button data-coalition="blue" id="helicopter-spawn-button" title="Spawn helicopter" data-on-click="mapContextMenuShow"
data-on-click-params='{ "type": "helicopter" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/helicopter.svg" inject-svg></button>
<button data-coalition="blue" id="groundunit-spawn-button" title="Spawn ground unit" data-on-click="mapContextMenuShow"
data-on-click-params='{ "type": "groundunit" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/groundunit.svg" inject-svg></button>
<button data-coalition="blue" id="coalition-area-button" title="Edit coalition area" data-on-click="editCoalitionArea"
@ -15,9 +15,10 @@
</div>
<div id="more-options-button-bar" class="upper-bar ol-panel hide">
<div id="coalition-switch" class="ol-switch ol-coalition-switch"></div>
<!-- <button data-coalition="blue" id="navyunit-spawn-button" title="Spawn navy unit" data-on-click="mapContextMenuShow"
data-on-click-params='{ "type": "navyunit" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/navyunit.svg" inject-svg></button> -->
<!--
<button data-coalition="blue" id="navyunit-spawn-button" title="Spawn navy unit" data-on-click="mapContextMenuShow"
data-on-click-params='{ "type": "navyunit" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/navyunit.svg" inject-svg></button>
-->
<button data-coalition="blue" id="smoke-spawn-button" title="Spawn smoke" data-on-click="mapContextMenuShow"
data-on-click-params='{ "type": "smoke" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/smoke.svg" inject-svg></button>
<button data-coalition="blue" id="explosion-spawn-button" title="Explosion" data-on-click="mapContextMenuShow"
@ -75,7 +76,7 @@
<div id="aircraft-loadout-list">
</div>
</div>
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployAircraft" disabled>Deploy unit</button>
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployAircrafts" disabled>Deploy unit</button>
</div>
<div id="helicopter-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
<div class="ol-select-container">
@ -129,7 +130,7 @@
<div id="helicopter-loadout-list">
</div>
</div>
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployAircraft" disabled>Deploy unit</button>
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployHelicopters" disabled>Deploy unit</button>
</div>
<div id="groundunit-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
<div class="ol-select-container">
@ -158,7 +159,7 @@
</div>
</div>
</div>
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployGroundUnit" disabled>Deploy unit</button>
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployGroundUnits" disabled>Deploy unit</button>
</div>
<div id="navyunit-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
<div class="ol-select-container">
@ -187,7 +188,7 @@
</div>
</div>
</div>
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployNavyUnit" disabled>Deploy unit</button>
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployNavyUnits" disabled>Deploy unit</button>
</div>
<div id="smoke-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
<button class="smoke-button" title="" data-smoke-color="white" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "white" }'>White smoke</button>

View File

@ -3,7 +3,7 @@
<div id="app-summary">
<h2>DCS Olympus</h2>
<h4>Dynamic Unit Command</h4>
<div class="app-version">Version <span class="app-version-number">v0.3.0</span></div>
<div class="app-version">Version <span class="app-version-number">v0.4.0-alpha</span></div>
</div>
<div id="authentication-form">

View File

@ -6,7 +6,7 @@
<div class="ol-select-options">
<div id="toolbar-summary">
<h3>DCS Olympus</h3>
<div class="accent-green app-version-number">version v0.3.0</div>
<div class="accent-green app-version-number">version v0.4.0-alpha</div>
</div>
<div>
<a href="https://www.discord.com" target="_blank">Discord</a>

View File

@ -1,5 +1,5 @@
#define nwjsFolder "C:\Users\dpass\Documents\nwjs\"
#define version "v0.3.0-alpha"
#define version "v0.4.0-alpha"
[Setup]
AppName=DCS Olympus

View File

@ -15,7 +15,7 @@ declare_plugin(self_ID,
shortName = "Olympus",
fileMenuName = "Olympus",
version = "0.1.1-alpha",
version = "v0.4.0-alpha",
state = "installed",
developerName= "DCS Refugees 767 squadron",
info = _("DCS Olympus is a mod for DCS World. It allows users to spawn, control, task, group, and remove units from a DCS World server using a real-time map interface, similarly to Real Time Strategy games. The user interface also provides useful informations units, like loadouts, fuel, tasking, and so on. In the future, more features for DCS World GCI and JTAC will be available."),

View File

@ -1,6 +1,6 @@
local version = "v0.3.0-alpha"
local version = "v0.4.0-alpha"
local debug = false
local debug = true
Olympus.unitCounter = 1
Olympus.payloadRegistry = {}
@ -10,6 +10,7 @@ Olympus.groupStep = 40
Olympus.OlympusDLL = nil
Olympus.DLLsloaded = false
Olympus.OlympusModPath = os.getenv('DCSOLYMPUS_PATH')..'\\bin\\'
Olympus.log = mist.Logger:new("Olympus", 'info')
function Olympus.debug(message, displayFor)
if debug == true then
@ -252,7 +253,51 @@ function Olympus.move(groupName, lat, lng, altitude, altitudeType, speed, speedT
if groupCon then
groupCon:setTask(missionTask)
end
Olympus.debug("Olympus.move executed successfully on a Aircraft", 2)
Olympus.debug("Olympus.move executed successfully on Aircraft", 2)
elseif category == "Helicopter" then
local startPoint = mist.getLeadPos(group)
local endPoint = coord.LLtoLO(lat, lng, 0)
if altitudeType == "AGL" then
altitude = land.getHeight({x = endPoint.x, y = endPoint.z}) + altitude
end
local path = {}
if taskOptions and taskOptions['id'] == 'Land' then
path = {
[1] = mist.heli.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
[2] = mist.heli.buildWP(endPoint, landing, speed, 0, 'AGL')
}
else
path = {
[1] = mist.heli.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
[2] = mist.heli.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
}
end
-- If a task exists assign it to the controller
if taskOptions then
local task = Olympus.buildEnrouteTask(taskOptions)
if task then
path[1].task = task
path[2].task = task
end
end
-- Assign the mission task to the controller
local missionTask = {
id = 'Mission',
params = {
route = {
points = mist.utils.deepCopy(path),
},
},
}
local groupCon = group:getController()
if groupCon then
groupCon:setTask(missionTask)
end
Olympus.debug("Olympus.move executed successfully on Helicopter", 2)
elseif category == "GroundUnit" then
vars =
{
@ -270,7 +315,17 @@ function Olympus.move(groupName, lat, lng, altitude, altitudeType, speed, speedT
end
mist.groupToRandomPoint(vars)
Olympus.debug("Olympus.move executed succesfully on a ground unit", 2)
Olympus.debug("Olympus.move executed succesfully on GroundUnit", 2)
elseif category == "NavyUnit" then
vars =
{
group = group,
point = coord.LLtoLO(lat, lng, 0),
heading = 0,
speed = speed
}
mist.groupToRandomPoint(vars)
Olympus.debug("Olympus.move executed succesfully on NavyUnit", 2)
else
Olympus.debug("Olympus.move not implemented yet for " .. category, 2)
end
@ -314,12 +369,21 @@ function Olympus.spawnUnits(spawnTable)
if spawnTable.category == 'Aircraft' then
unitTable = Olympus.generateAirUnitsTable(spawnTable.units)
route = Olympus.generateAirUnitsRoute(spawnTable)
category = 'airplane'
category = 'plane'
elseif spawnTable.category == 'Helicopter' then
unitTable = Olympus.generateAirUnitsTable(spawnTable.units)
route = Olympus.generateAirUnitsRoute(spawnTable)
category = 'helicopter'
elseif spawnTable.category == 'GroundUnit' then
unitTable = Olympus.generateGroundUnitsTable(spawnTable.units)
category = 'vehicle'
elseif spawnTable.category == 'NavyUnit' then
unitTable = Olympus.generateNavyUnitsTable(spawnTable.units)
category = 'ship'
end
Olympus.debug(Olympus.serializeTable(unitTable), 5)
local countryID = Olympus.getCountryIDByCoalition(spawnTable.coalition)
local vars =
{
@ -336,37 +400,6 @@ function Olympus.spawnUnits(spawnTable)
Olympus.debug("Olympus.spawnUnits completed succesfully", 2)
end
-- Generates ground units table, either single or from template
function Olympus.generateGroundUnitsTable(units)
local unitTable = {}
for idx, unit in pairs(units) do
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
if Olympus.hasKey(templates, unit.unitType) then
for idx, value in pairs(templates[unit.unitType].units) do
unitTable[#unitTable + 1] =
{
["type"] = value.name,
["x"] = spawnLocation.x + value.dx,
["y"] = spawnLocation.z + value.dy,
["heading"] = 0,
["skill"] = "High"
}
end
else
unitTable[#unitTable + 1] =
{
["type"] = unit.unitType,
["x"] = spawnLocation.x,
["y"] = spawnLocation.z,
["heading"] = 0,
["skill"] = "High"
}
end
end
return unitTable
end
-- Generates unit table for a air unit.
function Olympus.generateAirUnitsTable(units)
local unitTable = {}
@ -456,6 +489,74 @@ function Olympus.generateAirUnitsRoute(spawnTable)
return route
end
-- Generates ground units table, either single or from template
function Olympus.generateGroundUnitsTable(units)
local unitTable = {}
for idx, unit in pairs(units) do
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
if Olympus.hasKey(templates, unit.unitType) then
for idx, value in pairs(templates[unit.unitType].units) do
unitTable[#unitTable + 1] =
{
["type"] = value.name,
["x"] = spawnLocation.x + value.dx,
["y"] = spawnLocation.z + value.dy,
["heading"] = 0,
["skill"] = "High",
["name"] = "GroundUnit-" .. Olympus.unitCounter .. "-" .. #unitTable + 1
}
end
else
unitTable[#unitTable + 1] =
{
["type"] = unit.unitType,
["x"] = spawnLocation.x,
["y"] = spawnLocation.z,
["heading"] = 0,
["skill"] = "High",
["name"] = "GroundUnit-" .. Olympus.unitCounter .. "-" .. #unitTable + 1
}
end
end
return unitTable
end
-- Generates navy units table, either single or from template
function Olympus.generateNavyUnitsTable(units)
local unitTable = {}
for idx, unit in pairs(units) do
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
if Olympus.hasKey(templates, unit.unitType) then
for idx, value in pairs(templates[unit.unitType].units) do
unitTable[#unitTable + 1] =
{
["type"] = value.name,
["x"] = spawnLocation.x + value.dx,
["y"] = spawnLocation.z + value.dy,
["heading"] = 0,
["skill"] = "High",
["name"] = "NavyUnit-" .. Olympus.unitCounter .. "-" .. #unitTable + 1,
["transportable"] = { ["randomTransportable"] = false }
}
end
else
unitTable[#unitTable + 1] =
{
["type"] = unit.unitType,
["x"] = spawnLocation.x,
["y"] = spawnLocation.z,
["heading"] = 0,
["skill"] = "High",
["name"] = "NavyUnit-" .. Olympus.unitCounter .. "-" .. #unitTable + 1,
["transportable"] = { ["randomTransportable"] = false }
}
end
end
return unitTable
end
-- Clones a unit by ID. Will clone the unit with the same original payload as the source unit. TODO: only works on Olympus unit not ME units.
function Olympus.clone(ID, lat, lng, category)
Olympus.debug("Olympus.clone " .. ID .. ", " .. category, 2)

View File

@ -1,4 +1,4 @@
local version = 'v0.3.0-alpha'
local version = 'v0.4.0-alpha'
Olympus = {}
Olympus.OlympusDLL = nil

View File

@ -163,7 +163,7 @@ public:
priority = immediate? CommandPriority::IMMEDIATE: CommandPriority::LOW;
};
virtual string getString(lua_State* L);
virtual unsigned int getLoad() { return 100 * !immediate; }
virtual unsigned int getLoad() { return immediate? 1: 100; }
private:
const string coalition;
@ -172,7 +172,29 @@ private:
const bool immediate;
};
/* Spawn air unit command */
/* Spawn navy unit command */
class SpawnNavyUnits : public Command
{
public:
SpawnNavyUnits(string coalition, vector<string> unitTypes, vector<Coords> locations, bool immediate) :
coalition(coalition),
unitTypes(unitTypes),
locations(locations),
immediate(immediate)
{
priority = immediate ? CommandPriority::IMMEDIATE : CommandPriority::LOW;
};
virtual string getString(lua_State* L);
virtual unsigned int getLoad() { return immediate ? 1 : 100; }
private:
const string coalition;
const vector<string> unitTypes;
const vector<Coords> locations;
const bool immediate;
};
/* Spawn aircraft command */
class SpawnAircrafts : public Command
{
public:
@ -187,7 +209,34 @@ public:
priority = immediate ? CommandPriority::IMMEDIATE : CommandPriority::LOW;
};
virtual string getString(lua_State* L);
virtual unsigned int getLoad() { return 100 * !immediate; }
virtual unsigned int getLoad() { return immediate ? 1 : 100; }
private:
const string coalition;
const vector<string> unitTypes;
const vector<Coords> locations;
const vector<string> loadouts;
const string airbaseName;
const bool immediate;
};
/* Spawn helicopter command */
class SpawnHelicopters : public Command
{
public:
SpawnHelicopters(string coalition, vector<string> unitTypes, vector<Coords> locations, vector<string> loadouts, string airbaseName, bool immediate) :
coalition(coalition),
unitTypes(unitTypes),
locations(locations),
loadouts(loadouts),
airbaseName(airbaseName),
immediate(immediate)
{
priority = immediate ? CommandPriority::IMMEDIATE : CommandPriority::LOW;
};
virtual string getString(lua_State* L);
virtual unsigned int getLoad() { return immediate ? 1 : 100; }
private:
const string coalition;
@ -220,18 +269,21 @@ private:
class Delete : public Command
{
public:
Delete(unsigned int ID, bool explosion) :
Delete(unsigned int ID, bool explosion, bool immediate ) :
ID(ID),
explosion(explosion)
explosion(explosion),
immediate(immediate)
{
priority = CommandPriority::HIGH;
immediate = immediate;
};
virtual string getString(lua_State* L);
virtual unsigned int getLoad() { return 20; }
virtual unsigned int getLoad() { return immediate? 1: 20; }
private:
const unsigned int ID;
const bool explosion;
const bool immediate;
};
/* SetTask command */

View File

@ -10,6 +10,7 @@ public:
~Scheduler();
void appendCommand(Command* command);
int getCurrentLoad();
void execute(lua_State* L);
void handleRequest(string key, json::value value);

View File

@ -21,7 +21,7 @@ public:
void updateMissionData(json::value missionData);
void runAILoop();
string getUnitData(stringstream &ss, unsigned long long time);
void deleteUnit(unsigned int ID, bool explosion);
void deleteUnit(unsigned int ID, bool explosion, bool immediate);
void acquireControl(unsigned int ID);
private:

View File

@ -60,6 +60,30 @@ string SpawnGroundUnits::getString(lua_State* L)
return commandSS.str();
}
/* Spawn ground units command */
string SpawnNavyUnits::getString(lua_State* L)
{
if (unitTypes.size() != locations.size()) return "";
std::ostringstream unitsSS;
unitsSS.precision(10);
for (int i = 0; i < unitTypes.size(); i++) {
unitsSS << "[" << i + 1 << "] = {"
<< "unitType = " << "\"" << unitTypes[i] << "\"" << ", "
<< "lat = " << locations[i].lat << ", "
<< "lng = " << locations[i].lng << "},";
}
std::ostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.spawnUnits, {"
<< "category = " << "\"" << "NavyUnit" << "\"" << ", "
<< "coalition = " << "\"" << coalition << "\"" << ", "
<< "units = " << "{" << unitsSS.str() << "}" << "}";
return commandSS.str();
}
/* Spawn aircrafts command */
string SpawnAircrafts::getString(lua_State* L)
{
@ -86,6 +110,34 @@ string SpawnAircrafts::getString(lua_State* L)
return commandSS.str();
}
/* Spawn helicopters command */
string SpawnHelicopters::getString(lua_State* L)
{
if (unitTypes.size() != locations.size() || unitTypes.size() != loadouts.size()) return "";
std::ostringstream unitsSS;
unitsSS.precision(10);
for (int i = 0; i < unitTypes.size(); i++) {
unitsSS << "[" << i + 1 << "] = {"
<< "unitType = " << "\"" << unitTypes[i] << "\"" << ", "
<< "lat = " << locations[i].lat << ", "
<< "lng = " << locations[i].lng << ", "
<< "alt = " << locations[i].alt << ", "
<< "loadout = \"" << loadouts[i] << "\"" << "},";
}
std::ostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.spawnUnits, {"
<< "category = " << "\"" << "Helicopter" << "\"" << ", "
<< "coalition = " << "\"" << coalition << "\"" << ", "
<< "airbaseName = \"" << airbaseName << "\", "
<< "units = " << "{" << unitsSS.str() << "}" << "}";
return commandSS.str();
}
/* Clone unit command */
string Clone::getString(lua_State* L)
{

View File

@ -9,7 +9,8 @@
#include <chrono>
using namespace std::chrono;
auto before = std::chrono::system_clock::now();
auto lastUpdate = std::chrono::system_clock::now();
auto lastExecution = std::chrono::system_clock::now();
/* Singleton objects */
UnitsManager* unitsManager = nullptr;
@ -72,27 +73,33 @@ extern "C" DllExport int coreFrame(lua_State* L)
frameCounter++;
/* Slow down the update rate if the frameRate is very low since it means DCS is struggling to keep up */
const std::chrono::duration<double> duration = std::chrono::system_clock::now() - before;
if (duration.count() > UPDATE_TIME_INTERVAL * (60.0 / frameRate))
const std::chrono::duration<double> updateDuration = std::chrono::system_clock::now() - lastUpdate;
double updateTimeInterval = max(UPDATE_TIME_INTERVAL, UPDATE_TIME_INTERVAL * (60.0 / frameRate));
if (updateDuration.count() > updateTimeInterval)
{
/* Lock for thread safety */
lock_guard<mutex> guard(mutexLock);
milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
if (duration.count() > 0)
frameRate = frameCounter / duration.count();
if (updateDuration.count() > 0)
frameRate = frameCounter / updateDuration.count();
frameCounter = 0;
if (unitsManager != nullptr) {
unitsManager->updateExportData(L, duration.count());
unitsManager->updateExportData(L, updateDuration.count());
unitsManager->runAILoop();
}
before = std::chrono::system_clock::now();
lastUpdate = std::chrono::system_clock::now();
}
if (scheduler != nullptr)
scheduler->execute(L);
const std::chrono::duration<double> executionDuration = std::chrono::system_clock::now() - lastExecution;
double executionTimeInterval = max(EXECUTION_TIME_INTERVAL, EXECUTION_TIME_INTERVAL * (60.0 / frameRate));
if (executionDuration.count() > executionTimeInterval) {
if (scheduler != nullptr)
scheduler->execute(L);
lastExecution = std::chrono::system_clock::now();
}
return(0);
}
@ -104,28 +111,19 @@ extern "C" DllExport int coreMissionData(lua_State * L)
/* Lock for thread safety */
lock_guard<mutex> guard(mutexLock);
lua_getglobal(L, "Olympus");
lua_getfield(L, -1, "missionData");
json::value missionData = luaTableToJSON(L, -1);
try
{
lua_getglobal(L, "Olympus");
lua_getfield(L, -1, "missionData");
json::value missionData = luaTableToJSON(L, -1);
if (missionData.has_object_field(L"unitsData"))
unitsManager->updateMissionData(missionData[L"unitsData"]);
if (missionData.has_object_field(L"airbases"))
airbases = missionData[L"airbases"];
if (missionData.has_object_field(L"bullseyes"))
bullseyes = missionData[L"bullseyes"];
if (missionData.has_object_field(L"mission"))
mission = missionData[L"mission"];
if (missionData.has_object_field(L"unitsData")) {
unitsManager->updateMissionData(missionData[L"unitsData"]);
}
catch (exception const& e)
{
log(e.what());
}
if (missionData.has_object_field(L"airbases"))
airbases = missionData[L"airbases"];
if (missionData.has_object_field(L"bullseyes"))
bullseyes = missionData[L"bullseyes"];
if (missionData.has_object_field(L"mission"))
mission = missionData[L"mission"];
return(0);
}

View File

@ -23,6 +23,15 @@ void Scheduler::appendCommand(Command* command)
commands.push_back(command);
}
int Scheduler::getCurrentLoad()
{
int currentLoad = 0;
for (auto command : commands) {
currentLoad += command->getLoad();
}
return currentLoad;
}
void Scheduler::execute(lua_State* L)
{
/* Decrease the active computation load. New commands can be sent only if the load has reached 0.
@ -42,7 +51,7 @@ void Scheduler::execute(lua_State* L)
if (dostring_in(L, "server", (commandString)))
log("Error executing command " + commandString);
else
log("Command '" + commandString + "' executed correctly, current load " + to_string(load));
log("Command '" + commandString + "' executed correctly, current load " + to_string(getCurrentLoad()));
load = command->getLoad();
commands.remove(command);
return;
@ -92,25 +101,6 @@ void Scheduler::handleRequest(string key, json::value value)
Coords loc; loc.lat = lat; loc.lng = lng;
command = dynamic_cast<Command*>(new Smoke(color, loc));
}
else if (key.compare("spawnGroundUnits") == 0)
{
bool immediate = value[L"immediate"].as_bool();
string coalition = to_string(value[L"coalition"]);
vector<string> unitTypes;
vector<Coords> locations;
for (auto unit : value[L"units"].as_array()) {
string unitType = to_string(unit[L"unitType"]);
double lat = unit[L"location"][L"lat"].as_double();
double lng = unit[L"location"][L"lng"].as_double();
Coords location; location.lat = lat; location.lng = lng;
log("Spawning " + coalition + " ground unit of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")");
unitTypes.push_back(unitType);
locations.push_back(location);
}
command = dynamic_cast<Command*>(new SpawnGroundUnits(coalition, unitTypes, locations, immediate));
}
else if (key.compare("spawnAircrafts") == 0)
{
bool immediate = value[L"immediate"].as_bool();
@ -127,15 +117,78 @@ void Scheduler::handleRequest(string key, json::value value)
double alt = unit[L"altitude"].as_double();
Coords location; location.lat = lat; location.lng = lng; location.alt = alt;
string loadout = to_string(unit[L"loadout"]);
log("Spawning " + coalition + " air unit unit of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")");
log("Spawning " + coalition + " aircraft of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")");
unitTypes.push_back(unitType);
locations.push_back(location);
loadouts.push_back(loadout);
}
command = dynamic_cast<Command*>(new SpawnAircrafts(coalition, unitTypes, locations, loadouts, airbaseName, immediate));
}
else if (key.compare("spawnHelicopters") == 0)
{
bool immediate = value[L"immediate"].as_bool();
string coalition = to_string(value[L"coalition"]);
string airbaseName = to_string(value[L"airbaseName"]);
vector<string> unitTypes;
vector<Coords> locations;
vector<string> loadouts;
for (auto unit : value[L"units"].as_array()) {
string unitType = to_string(unit[L"unitType"]);
double lat = unit[L"location"][L"lat"].as_double();
double lng = unit[L"location"][L"lng"].as_double();
double alt = unit[L"altitude"].as_double();
Coords location; location.lat = lat; location.lng = lng; location.alt = alt;
string loadout = to_string(unit[L"loadout"]);
log("Spawning " + coalition + " helicopter of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")");
unitTypes.push_back(unitType);
locations.push_back(location);
loadouts.push_back(loadout);
}
command = dynamic_cast<Command*>(new SpawnHelicopters(coalition, unitTypes, locations, loadouts, airbaseName, immediate));
}
else if (key.compare("spawnGroundUnits") == 0)
{
bool immediate = value[L"immediate"].as_bool();
string coalition = to_string(value[L"coalition"]);
vector<string> unitTypes;
vector<Coords> locations;
for (auto unit : value[L"units"].as_array()) {
string unitType = to_string(unit[L"unitType"]);
double lat = unit[L"location"][L"lat"].as_double();
double lng = unit[L"location"][L"lng"].as_double();
Coords location; location.lat = lat; location.lng = lng;
log("Spawning " + coalition + " GroundUnit of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")");
unitTypes.push_back(unitType);
locations.push_back(location);
}
command = dynamic_cast<Command*>(new SpawnGroundUnits(coalition, unitTypes, locations, immediate));
}
else if (key.compare("spawnNavyUnits") == 0)
{
bool immediate = value[L"immediate"].as_bool();
string coalition = to_string(value[L"coalition"]);
vector<string> unitTypes;
vector<Coords> locations;
for (auto unit : value[L"units"].as_array()) {
string unitType = to_string(unit[L"unitType"]);
double lat = unit[L"location"][L"lat"].as_double();
double lng = unit[L"location"][L"lng"].as_double();
Coords location; location.lat = lat; location.lng = lng;
log("Spawning " + coalition + " NavyUnit of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")");
unitTypes.push_back(unitType);
locations.push_back(location);
}
command = dynamic_cast<Command*>(new SpawnNavyUnits(coalition, unitTypes, locations, immediate));
}
else if (key.compare("attackUnit") == 0)
{
unsigned int ID = value[L"ID"].as_integer();
@ -287,7 +340,8 @@ void Scheduler::handleRequest(string key, json::value value)
{
unsigned int ID = value[L"ID"].as_integer();
bool explosion = value[L"explosion"].as_bool();
unitsManager->deleteUnit(ID, explosion);
bool immediate = value[L"immediate"].as_bool();
unitsManager->deleteUnit(ID, explosion, immediate);
}
else if (key.compare("refuel") == 0)
{
@ -414,7 +468,7 @@ void Scheduler::handleRequest(string key, json::value value)
if (command != nullptr)
{
appendCommand(command);
log("New command appended correctly to stack. Current server load: " + to_string(load));
log("New command appended correctly to stack. Current server load: " + to_string(getCurrentLoad()));
}
}

View File

@ -164,11 +164,11 @@ string UnitsManager::getUnitData(stringstream &ss, unsigned long long time)
return to_base64(ss.str());
}
void UnitsManager::deleteUnit(unsigned int ID, bool explosion)
void UnitsManager::deleteUnit(unsigned int ID, bool explosion, bool immediate)
{
if (getUnit(ID) != nullptr)
{
Command* command = dynamic_cast<Command*>(new Delete(ID, explosion));
Command* command = dynamic_cast<Command*>(new Delete(ID, explosion, immediate));
scheduler->appendCommand(command);
}
}

View File

@ -1,6 +1,6 @@
#pragma once
#define VERSION "v0.2.1"
#define VERSION "v0.4.0-alpha"
#define LOG_NAME "Olympus_log.txt"
#define REST_ADDRESS "http://localhost:30000"
#define REST_URI "olympus"
@ -10,4 +10,5 @@
#define BULLSEYE_URI "bullseyes"
#define MISSION_URI "mission"
#define UPDATE_TIME_INTERVAL 0.25
#define UPDATE_TIME_INTERVAL 0.25
#define EXECUTION_TIME_INTERVAL 0.05