Added new method for handling data

This commit is contained in:
Pax1601 2023-06-21 20:05:41 +02:00
parent 61d6d9f16c
commit 9d0e2239e4
16 changed files with 137 additions and 93 deletions

View File

@ -111,3 +111,5 @@ export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area";
export const COALITIONAREA_INTERACT = "Interact with Coalition Areas"
export const visibilityControls: string[] = ["human", "dcs", "aircraft", "groundunit-sam", "groundunit-other", "navyunit", "airbase"];
export const visibilityControlsTootlips: string[] = ["Toggle human players visibility", "Toggle DCS controlled units visibility", "Toggle aircrafts visibility", "Toggle SAM units visibility", "Toggle ground units (not SAM) visibility", "Toggle navy units visibility", "Toggle airbases visibility"];
export const IADSRoles: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3, "SAM Sites": 0.1, "Radar": 0.05};

View File

@ -1,12 +1,11 @@
import { getMap, getUnitsManager } from "..";
import { IADSRoles } from "../constants/constants";
import { CoalitionArea } from "../map/coalitionarea";
import { ContextMenu } from "./contextmenu";
import { Dropdown } from "./dropdown";
import { Slider } from "./slider";
import { Switch } from "./switch";
const unitRole = ["AAA", "MANPADS", "SAM Sites", "Radar"];
export class CoalitionAreaContextMenu extends ContextMenu {
#coalitionSwitch: Switch;
#coalitionArea: CoalitionArea | null = null;
@ -57,7 +56,7 @@ export class CoalitionAreaContextMenu extends ContextMenu {
})
/* Create the checkboxes to select the unit roles */
this.#iadsRoleDropdown.setOptionsElements(unitRole.map((unitRole: string) => {
this.#iadsRoleDropdown.setOptionsElements(Object.keys(IADSRoles).map((unitRole: string) => {
var div = document.createElement("div");
div.classList.add("ol-checkbox");
var label = document.createElement("label");

View File

@ -66,11 +66,10 @@ export class CoalitionArea extends Polygon {
this.setOpacity(interactive? 1: 0.5);
this.options.interactive = interactive;
if (interactive) {
if (interactive)
DomUtil.addClass(this.getElement() as HTMLElement, 'leaflet-interactive');
} else {
DomUtil.removeClass(this.getElement() as HTMLElement, 'leaflet-interactive');
}
else
DomUtil.removeClass(this.getElement() as HTMLElement, 'leaflet-interactive');
}
addTemporaryLatLng(latlng: LatLng) {

View File

@ -396,18 +396,18 @@ export class Map extends L.Map {
removeTemporaryMarker(latlng: L.LatLng) {
// TODO something more refined than this
var d: number | null = null;
var dist: number | null = null;
var closest: L.Marker | null = null;
var i: number = 0;
this.#temporaryMarkers.forEach((marker: L.Marker, idx: number) => {
var t = latlng.distanceTo(marker.getLatLng());
if (d == null || t < d) {
d = t;
if (dist == null || t < dist) {
dist = t;
closest = marker;
i = idx;
}
});
if (closest) {
if (closest && dist != null && dist < 100) {
this.removeLayer(closest);
this.#temporaryMarkers.splice(i, 1);
}
@ -582,29 +582,40 @@ export class Map extends L.Map {
document.getElementById(this.#ID)?.classList.add("hidden-cursor");
}
#showDestinationCursors() {
#showDestinationCursors(singleCursor: boolean) {
/* Don't create the cursors if there already are the correct number of them available */
if (getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length != this.#destinationPreviewCursors.length) {
if (singleCursor || getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length != this.#destinationPreviewCursors.length) {
/* Reset the cursors to start from a clean condition */
this.#hideDestinationCursors();
if (getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length > 0) {
/* Create the cursors. If a group is selected only one cursor is shown for it, because you can't control single units in a group */
this.#destinationPreviewCursors = getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).map((unit: Unit) => {
if (singleCursor) {
var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false });
marker.addTo(this);
return marker;
})
this.#destinationPreviewCursors = [marker];
}
else {
/* Create the cursors. If a group is selected only one cursor is shown for it, because you can't control single units in a group */
this.#destinationPreviewCursors = getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).map((unit: Unit) => {
var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false });
marker.addTo(this);
return marker;
});
}
}
}
}
#updateDestinationCursors(e: any) {
const groupLatLng = this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates();
Object.values(getUnitsManager().selectedUnitsComputeGroupDestination(groupLatLng, this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
if (idx < this.#destinationPreviewCursors.length)
this.#destinationPreviewCursors[idx].setLatLng(e.originalEvent.shiftKey ? latlng : this.getMouseCoordinates());
})
if (this.#destinationPreviewCursors.length == 1)
this.#destinationPreviewCursors[0].setLatLng(this.getMouseCoordinates());
else {
Object.values(getUnitsManager().selectedUnitsComputeGroupDestination(groupLatLng, this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
if (idx < this.#destinationPreviewCursors.length)
this.#destinationPreviewCursors[idx].setLatLng(e.originalEvent.shiftKey ? latlng : this.getMouseCoordinates());
})
};
}
#hideDestinationCursors() {
@ -660,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();
else if (this.#state === MOVE_UNIT) this.#showDestinationCursors(!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();
}

View File

@ -321,22 +321,19 @@ export function requestUpdate() {
export function requestRefresh() {
/* Main refresh rate = 5000ms. */
getUnits((data: UnitsData) => {
if (!getPaused()) {
getUnitsManager()?.update(data);
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
getBullseye((data: BullseyesData) => getMissionData()?.update(data));
getMission((data: any) => {
getMissionData()?.update(data)
});
// Update the list of existing units
getUnitDataTable()?.update();
if (!getPaused()) {
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
getBullseye((data: BullseyesData) => getMissionData()?.update(data));
getMission((data: any) => {
checkSessionHash(data.sessionHash);
}
}, true);
window.setTimeout(() => requestRefresh(), 5000);
getMissionData()?.update(data)
});
// Update the list of existing units
getUnitDataTable()?.update();
}
window.setTimeout(() => requestRefresh(), 1000);
}
export function checkSessionHash(newSessionHash: string) {

View File

@ -6,7 +6,7 @@ import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng,
import { CoalitionArea } from "../map/coalitionarea";
import { Airbase } from "../missionhandler/airbase";
import { groundUnitsDatabase } from "./groundunitsdatabase";
import { IDLE, MOVE_UNIT } from "../constants/constants";
import { IADSRoles, IDLE, MOVE_UNIT } from "../constants/constants";
export class UnitsManager {
#units: { [ID: number]: Unit };
@ -516,9 +516,9 @@ export class UnitsManager {
if (distance > maxDistance) maxDistance = distance;
});
const probability = Math.pow(1 - minDistance / 50e3, 5);
const role = activeRoles[Math.floor(Math.random() * activeRoles.length)];
const probability = Math.pow(1 - minDistance / 50e3, 5) * IADSRoles[role];
if (Math.random() < probability){
const role = activeRoles[Math.floor(Math.random() * activeRoles.length)];
const unitBlueprint = randomUnitBlueprintByRole(groundUnitsDatabase, role);
const spawnOptions = {role: role, latlng: latlng, name: unitBlueprint.name, coalition: coalitionArea.getCoalition(), immediate: true};
spawnGroundUnit(spawnOptions);

View File

@ -1,6 +1,6 @@
local version = "v0.3.0-alpha"
local debug = true
local debug = false
Olympus.unitCounter = 1
Olympus.payloadRegistry = {}
@ -343,7 +343,7 @@ function Olympus.spawnGroundUnit(coalition, unitType, lat, lng)
units = unitTable,
country = countryID,
category = 'vehicle',
name = "Olympus-" .. Olympus.unitCounter,
name = "Ground-" .. Olympus.unitCounter,
}
mist.dynAdd(vars)
Olympus.unitCounter = Olympus.unitCounter + 1

View File

@ -5,7 +5,7 @@
#include "logger.h"
namespace CommandPriority {
enum CommandPriorities { LOW, MEDIUM, HIGH };
enum CommandPriorities { LOW, MEDIUM, HIGH, IMMEDIATE };
};
namespace SetCommandType {
@ -151,7 +151,7 @@ public:
location(location),
immediate(immediate)
{
priority = CommandPriority::LOW;
priority = immediate? CommandPriority::IMMEDIATE: CommandPriority::LOW;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 100 * !immediate; }
@ -175,7 +175,7 @@ public:
airbaseName(airbaseName),
immediate(immediate)
{
priority = CommandPriority::LOW;
priority = immediate ? CommandPriority::IMMEDIATE : CommandPriority::LOW;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 100 * !immediate; }

View File

@ -15,6 +15,8 @@ public:
void start(lua_State* L);
void stop(lua_State* L);
json::value& getUpdateJson() { return updateJson; }
json::value& getRefreshJson() { return refreshJson; }
private:
std::thread* serverThread;
@ -27,6 +29,8 @@ private:
void task();
atomic<bool> runListener;
json::value updateJson;
json::value refreshJson;
wstring password = L"";
};

View File

@ -65,7 +65,7 @@ public:
void setDefaults(bool force = false);
int getID() { return ID; }
void runAILoop();
void updateExportData(json::value json);
void updateExportData(json::value json, double dt = 0);
void updateMissionData(json::value json);
json::value getData(long long time, bool getAll = false);
virtual wstring getCategory() { return L"No category"; };

View File

@ -10,16 +10,18 @@ public:
UnitsManager(lua_State* L);
~UnitsManager();
map<int, Unit*>& getUnits() { return units; };
Unit* getUnit(int ID);
bool isUnitInGroup(Unit* unit);
bool isUnitGroupLeader(Unit* unit);
Unit* getGroupLeader(int ID);
Unit* getGroupLeader(Unit* unit);
vector<Unit*> getGroupMembers(wstring groupName);
void updateExportData(lua_State* L);
void updateExportData(lua_State* L, double dt = 0);
void updateMissionData(json::value missionData);
void runAILoop();
void getData(json::value& answer, long long time);
void getUnitData(json::value& answer, long long time);
void appendUnitData(int ID, json::value& answer, long long time);
void deleteUnit(int ID, bool explosion);
void acquireControl(int ID);

View File

@ -6,6 +6,8 @@
#include "scheduler.h"
#include "scriptLoader.h"
#include "luatools.h"
#include <chrono>
using namespace std::chrono;
auto before = std::chrono::system_clock::now();
UnitsManager* unitsManager = nullptr;
@ -17,6 +19,10 @@ json::value mission;
mutex mutexLock;
bool initialized = false;
string sessionHash;
int lastUpdateIndex = 0;
int frameCounter = 0;
double frameRate = 30;
long long lastUpdateTime = 0;
/* Called when DCS simulation stops. All singleton instances are deleted. */
extern "C" DllExport int coreDeinit(lua_State* L)
@ -61,21 +67,44 @@ extern "C" DllExport int coreFrame(lua_State* L)
/* Lock for thread safety */
lock_guard<mutex> guard(mutexLock);
frameCounter++;
const std::chrono::duration<double> duration = std::chrono::system_clock::now() - before;
/* TODO make intervals editable */
if (duration.count() > UPDATE_TIME_INTERVAL)
if (unitsManager != nullptr) {
// TODO put in a function
vector<int> IDs;
for (auto iter = unitsManager->getUnits().begin(); iter != unitsManager->getUnits().end(); ++iter)
IDs.push_back(iter->first);
int updateChunk = 20;
int finalUpdateIndex = lastUpdateIndex + updateChunk;
/* Get all the new data (with some margin) */
while (lastUpdateIndex < unitsManager->getUnits().size() && lastUpdateIndex <= finalUpdateIndex)
unitsManager->appendUnitData(IDs[lastUpdateIndex++], server->getUpdateJson(), lastUpdateTime - 1000);
}
if (duration.count() > UPDATE_TIME_INTERVAL && lastUpdateIndex == unitsManager->getUnits().size())
{
milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
lastUpdateTime = ms.count();
frameRate = frameCounter / duration.count();
frameCounter = 0;
if (unitsManager != nullptr)
{
unitsManager->updateExportData(L);
unitsManager->runAILoop();
}
unitsManager->updateExportData(L, duration.count());
before = std::chrono::system_clock::now();
/* Restart the update counter */
lastUpdateIndex = 0;
}
if (scheduler != nullptr)
scheduler->execute(L);
if (duration.count() > UPDATE_TIME_INTERVAL && unitsManager != nullptr)
unitsManager->runAILoop();
return(0);
}

View File

@ -32,7 +32,7 @@ void Scheduler::execute(lua_State* L)
return;
}
int priority = CommandPriority::HIGH;
int priority = CommandPriority::IMMEDIATE;
while (priority >= CommandPriority::LOW)
{
for (auto command : commands)

View File

@ -36,7 +36,8 @@ Server::Server(lua_State* L):
serverThread(nullptr),
runListener(true)
{
refreshJson = json::value::object();
updateJson = json::value::object();
}
void Server::start(lua_State* L)
@ -93,7 +94,10 @@ void Server::handle_get(http_request request)
time = 0;
}
}
unitsManager->getData(answer, time);
if (time == 0)
unitsManager->getUnitData(answer, 0);
else
answer[L"units"] = updateJson;
}
else if (path[0] == LOGS_URI)
{

View File

@ -92,31 +92,31 @@ void Unit::addMeasure(wstring key, json::value value)
}
void Unit::runAILoop() {
/* If the unit is alive and it is not a human, run the AI Loop that performs the requested commands and instructions (moving, attacking, etc) */
const bool isUnitControlledByOlympus = getControlled();
const bool isUnitAlive = getAlive();
const bool isUnitLeader = unitsManager->isUnitGroupLeader(this);
const bool isUnitLeaderOfAGroupWithOtherUnits = unitsManager->isUnitInGroup(this) && unitsManager->isUnitGroupLeader(this);
const bool isUnitHuman = getFlags()[L"Human"].as_bool();
/* If the unit is alive, controlled and it is not a human, run the AI Loop that performs the requested commands and instructions (moving, attacking, etc) */
if (!getControlled()) return;
if (!unitsManager->isUnitGroupLeader(this)) return;
if (getFlags()[L"Human"].as_bool()) return;
// Keep running the AI loop even if the unit is dead if it is the leader of a group which has other members in it
if (isUnitControlledByOlympus && (isUnitAlive || isUnitLeaderOfAGroupWithOtherUnits) && isUnitLeader && !isUnitHuman)
{
if (checkTaskFailed() && state != State::IDLE && State::LAND)
setState(State::IDLE);
const bool isUnitAlive = getAlive();
const bool isUnitLeaderOfAGroupWithOtherUnits = unitsManager->isUnitInGroup(this) && unitsManager->isUnitGroupLeader(this);
if (!(isUnitAlive || isUnitLeaderOfAGroupWithOtherUnits)) return;
if (checkTaskFailed() && state != State::IDLE && State::LAND)
setState(State::IDLE);
AIloop();
}
AIloop();
}
void Unit::updateExportData(json::value json)
void Unit::updateExportData(json::value json, double dt)
{
/* Compute speed (loGetWorldObjects does not provide speed, we compute it for better performance instead of relying on many lua calls) */
if (oldPosition != NULL)
{
double dist = 0;
Geodesic::WGS84().Inverse(latitude, longitude, oldPosition.lat, oldPosition.lng, dist);
setSpeed(getSpeed() * 0.95 + (dist / UPDATE_TIME_INTERVAL) * 0.05);
if (dt > 0)
setSpeed(getSpeed() * 0.95 + (dist / dt) * 0.05);
}
oldPosition = Coords(latitude, longitude, altitude);
@ -160,7 +160,7 @@ void Unit::updateMissionData(json::value json)
setHasTask(json[L"hasTask"].as_bool());
}
json::value Unit::getData(long long time, bool sendAll)
json::value Unit::getData(long long time, bool getAll)
{
auto json = json::value::object();
@ -178,7 +178,7 @@ json::value Unit::getData(long long time, bool sendAll)
if (json[L"baseData"].size() == 0)
json.erase(L"baseData");
if (alive || sendAll) {
if (alive || getAll) {
/********** Flight data **********/
json[L"flightData"] = json::value::object();
for (auto key : { L"latitude", L"longitude", L"altitude", L"speed", L"heading" })

View File

@ -59,18 +59,11 @@ Unit* UnitsManager::getGroupLeader(Unit* unit)
if (unit != nullptr) {
wstring groupName = unit->getGroupName();
/* Get the unit IDs in order */
std::vector<int> keys;
for (auto const& p : units)
keys.push_back(p.first);
sort(keys.begin(), keys.end());
/* Find the first unit that has the same groupName */
for (auto const& tempID : keys)
for (auto const& p : units)
{
Unit* tempUnit = getUnit(tempID);
if (tempUnit != nullptr && tempUnit->getGroupName().compare(groupName) == 0)
return tempUnit;
if (p.second->getGroupName().compare(groupName) == 0)
return p.second;
}
}
return nullptr;
@ -93,7 +86,7 @@ Unit* UnitsManager::getGroupLeader(int ID)
return getGroupLeader(unit);
}
void UnitsManager::updateExportData(lua_State* L)
void UnitsManager::updateExportData(lua_State* L, double dt)
{
map<int, json::value> unitJSONs = getAllUnits(L);
@ -132,15 +125,13 @@ void UnitsManager::updateExportData(lua_State* L)
else {
/* Update the unit if present*/
if (units.count(ID) != 0)
units[ID]->updateExportData(p.second);
units[ID]->updateExportData(p.second, dt);
}
}
/* Set the units that are not present in the JSON as dead (probably have been destroyed) */
for (auto const& unit : units)
{
unit.second->setAlive(unitJSONs.find(unit.first) != unitJSONs.end());
}
}
void UnitsManager::updateMissionData(json::value missionData)
@ -150,32 +141,38 @@ void UnitsManager::updateMissionData(json::value missionData)
{
int ID = p.first;
if (missionData.has_field(to_wstring(ID)))
{
p.second->updateMissionData(missionData[to_wstring(ID)]);
}
}
}
void UnitsManager::runAILoop() {
/* Run the AI Loop on all units */
for (auto const& unit : units)
{
unit.second->runAILoop();
}
}
void UnitsManager::getData(json::value& answer, long long time)
void UnitsManager::getUnitData(json::value& answer, long long time)
{
auto unitsJson = json::value::object();
for (auto const& p : units)
{
auto unitJson = p.second->getData(time);
if (unitJson.size() > 0)
unitsJson[to_wstring(p.first)] = p.second->getData(time);
unitsJson[to_wstring(p.first)] = unitJson;
}
answer[L"units"] = unitsJson;
}
void UnitsManager::appendUnitData(int ID, json::value& answer, long long time)
{
Unit* unit = getUnit(ID);
if (unit != nullptr) {
auto unitJson = unit->getData(time);
if (unitJson.size() > 0)
answer[to_wstring(ID)] = unitJson;
}
}
void UnitsManager::deleteUnit(int ID, bool explosion)
{
if (getUnit(ID) != nullptr)