Started transition to binary data on client side

This commit is contained in:
Pax1601 2023-06-22 22:17:00 +02:00
parent 1d62b4c115
commit 916752301a
12 changed files with 196 additions and 132 deletions

View File

@ -1,5 +1,5 @@
interface UnitsData {
units: {[key: string]: UnitData},
units: string,
sessionHash: string
}

View File

@ -4,6 +4,7 @@ import { UnitDatabase } from "../units/unitdatabase";
import { aircraftDatabase } from "../units/aircraftdatabase";
import { helicopterDatabase } from "../units/helicopterdatabase";
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
import { Buffer } from "buffer";
export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) {
const φ1 = deg2rad(lat1); // φ, λ in radians
@ -247,4 +248,8 @@ export function getUnitDatabaseByCategory(category: string) {
return groundUnitsDatabase;
else
return null;
}
}
export function base64ToBytes(base64: string) {
return Buffer.from(base64, 'base64').buffer;
}

View File

@ -300,7 +300,7 @@ export function startUpdate() {
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
getBullseye((data: BullseyesData) => getMissionData()?.update(data));
getMission((data: any) => { getMissionData()?.update(data) });
getUnits((data: UnitsData) => getUnitsManager()?.update(data), true /* Does a full refresh */);
getUnits((data: UnitsData) => getUnitsManager()?.update(data.units), true /* Does a full refresh */);
requestUpdate();
requestRefresh();
@ -310,7 +310,7 @@ export function requestUpdate() {
/* Main update rate = 250ms is minimum time, equal to server update time. */
getUnits((data: UnitsData) => {
if (!getPaused()) {
getUnitsManager()?.update(data);
getUnitsManager()?.update(data.units);
checkSessionHash(data.sessionHash);
}
}, false);

View File

@ -1,8 +1,8 @@
import { LatLng, LatLngBounds } from "leaflet";
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitDataTable } from "..";
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler } from "..";
import { Unit } from "./unit";
import { cloneUnit, spawnGroundUnit } from "../server/server";
import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polygonArea, randomPointInPoly, randomUnitBlueprintByRole } from "../other/utils";
import { base64ToBytes, 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";
@ -68,9 +68,61 @@ export class UnitsManager {
}
update(data: UnitsData) {
update(data: string) {
var updatedUnits: Unit[] = [];
Object.keys(data.units)
var buffer = base64ToBytes(data);
/*Coords position;
double speed;
double heading;
unsigned short fuel;
double desiredSpeed;
double desiredAltitude;
unsigned int targetID;
Coords targetPosition;
unsigned char state;
unsigned char ROE;
unsigned char reactionToThreat;
unsigned char emissionsCountermeasures;
Options::TACAN TACAN;
Options::Radio Radio;
unsigned short pathLength;
unsigned char nameLength;
unsigned char unitNameLength;
unsigned char groupNameLength;
unsigned char categoryLength;
unsigned char coalitionLength;*/
var offset = 0;
var dataview = new DataView(buffer);
const ID = dataview.getUint32(offset, true); offset += 4;
const bitmask = dataview.getUint32(offset , true); offset += 4;
const alive = bitmask & (1 << 0);
const human = bitmask >> 1 & 1;
const controlled = bitmask >> 2 & 1;
const hasTask = bitmask >> 3 & 1;
const desiredAltitudeType = bitmask >> 16 & 1;
const desiredSpeedType = bitmask >> 17 & 1;
const isTanker = bitmask >> 18 & 1;
const isAWACS = bitmask >> 19 & 1;
const onOff = bitmask >> 20 & 1;
const followRoads = bitmask >> 21 & 1;
const EPLRS = bitmask >> 22 & 1;
const prohibitAA = bitmask >> 23 & 1;
const prohibitAfterburner = bitmask >> 24 & 1;
const prohibitAG = bitmask >> 25 & 1;
const prohibitAirWpn = bitmask >> 26 & 1;
const prohibitJettison = bitmask >> 27 & 1;
const latitude = dataview.getFloat64(offset , true); offset += 8;
const longitude = dataview.getFloat64(offset , true); offset += 8;
const altitude = dataview.getFloat64(offset , true); offset += 8;
const speed = dataview.getFloat64(offset , true); offset += 8;
const heading = dataview.getFloat64(offset , true); offset += 8;
var foo = 12;
/*Object.keys(data.units)
.filter((ID: string) => !(ID in this.#units))
.reduce((timeout: number, ID: string) => {
window.setTimeout(() => {
@ -91,7 +143,7 @@ export class UnitsManager {
this.getSelectedUnits().forEach((unit: Unit) => {
if (!updatedUnits.includes(unit))
unit.setData({})
});
});*/
}
setHiddenType(key: string, value: boolean) {

View File

@ -29,6 +29,7 @@ namespace State
};
};
#pragma pack(push, 1)
namespace Options {
struct TACAN
{
@ -86,8 +87,15 @@ namespace DataTypes {
unsigned char emissionsCountermeasures;
Options::TACAN TACAN;
Options::Radio Radio;
unsigned short pathLength;
unsigned char nameLength;
unsigned char unitNameLength;
unsigned char groupNameLength;
unsigned char categoryLength;
unsigned char coalitionLength;
};
}
#pragma pack(pop)
class Unit
{
@ -102,8 +110,8 @@ public:
void runAILoop();
void updateExportData(json::value json, double dt = 0);
void updateMissionData(json::value json);
DataTypes::DataPacket getDataPacket();
string getData(bool refresh);
unsigned int getUpdateData(char* &data);
void getData(stringstream &ss, bool refresh);
virtual string getCategory() { return "No category"; };
/********** Base data **********/

View File

@ -68,7 +68,7 @@ void AirUnit::setState(unsigned char newState)
case State::ATTACK: {
if (isTargetAlive()) {
Unit* target = unitsManager->getUnit(targetID);
Coords targetPosition = Coords(target->getLatitude(), target->getLongitude(), 0);
Coords targetPosition = Coords(target->getPosition().lat, target->getPosition().lng, 0);
clearActivePath();
pushActivePathFront(targetPosition);
resetActiveDestination();

View File

@ -79,18 +79,19 @@ extern "C" DllExport int coreFrame(lua_State* L)
lock_guard<mutex> guard(mutexLock);
milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
frameRate = frameCounter / duration.count();
if (duration.count() > 0)
frameRate = frameCounter / duration.count();
frameCounter = 0;
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);
}

View File

@ -71,6 +71,7 @@ void Server::handle_get(http_request request)
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)))
{
std::exception_ptr eptr;

View File

@ -6,9 +6,6 @@
#include "defines.h"
#include "unitsmanager.h"
#include "base64.hpp"
using namespace base64;
#include <chrono>
using namespace std::chrono;
@ -54,30 +51,26 @@ void Unit::initialize(json::value json)
void Unit::setDefaults(bool force)
{
const bool isUnitControlledByOlympus = getControlled();
const bool isUnitAlive = getAlive();
const bool isUnitLeader = unitsManager->isUnitGroupLeader(this);
const bool isUnitLeaderOfAGroupWithOtherUnits = unitsManager->isUnitInGroup(this) && unitsManager->isUnitGroupLeader(this);
if (!getControlled()) return;
if (!unitsManager->isUnitGroupLeader(this)) return;
if (!(getAlive() || unitsManager->isUnitInGroup(this) && unitsManager->isUnitGroupLeader(this))) return;
if (getHuman()) return;
if (isUnitControlledByOlympus && (isUnitAlive || isUnitLeaderOfAGroupWithOtherUnits) && isUnitLeader && !human) {
/* Set the default IDLE state */
setState(State::IDLE);
/* Set the default IDLE state */
setState(State::IDLE);
/* Set desired altitude to be equal to current altitude so the unit does not climb/descend after spawn */
setDesiredAltitude(position.alt);
/* Set desired altitude to be equal to current altitude so the unit does not climb/descend after spawn */
setDesiredAltitude(position.alt);
/* Set the default options (these are all defaults so will only affect the export data, no DCS command will be sent) */
setROE(ROE::OPEN_FIRE_WEAPON_FREE, force);
setReactionToThreat(ReactionToThreat::EVADE_FIRE, force);
setEmissionsCountermeasures(EmissionCountermeasure::DEFEND, force);
strcpy_s(TACAN.callsign, 4, "TKR");
setTACAN(TACAN, force);
setRadio(radio, force);
setEPLRS(EPLRS, force);
setGeneralSettings(generalSettings, force);
setOnOff(onOff);
setFollowRoads(followRoads);
}
/* Set the default options */
setROE(ROE::OPEN_FIRE_WEAPON_FREE, force);
setReactionToThreat(ReactionToThreat::EVADE_FIRE, force);
setEmissionsCountermeasures(EmissionCountermeasure::DEFEND, force);
strcpy_s(TACAN.callsign, 4, "TKR");
setTACAN(TACAN, force);
setRadio(radio, force);
setEPLRS(EPLRS, force);
setGeneralSettings(generalSettings, force);
}
void Unit::runAILoop() {
@ -107,8 +100,7 @@ void Unit::updateExportData(json::value json, double dt)
if (dt > 0)
setSpeed(getSpeed() * 0.95 + (dist / dt) * 0.05);
}
oldPosition = position;
if (json.has_string_field(L"Name"))
setName(to_string(json[L"Name"]));
if (json.has_string_field(L"UnitName"))
@ -136,53 +128,63 @@ void Unit::updateExportData(json::value json, double dt)
/* All units which contain the name "Olympus" are automatically under AI control */
if (getUnitName().find("Olympus") != string::npos)
setControlled(true);
oldPosition = position;
}
void Unit::updateMissionData(json::value json)
{
if (json.has_number_field(L"fuel"))
setFuel(short(json[L"fuel"].as_number().to_double() * 100));
if (json.has_object_field(L"ammo")) {
vector<DataTypes::Ammo> ammo;
for (auto const& el : json[L"ammo"].as_object()) {
DataTypes::Ammo ammoItem;
auto ammoJson = el.second;
ammoItem.quantity = ammoJson[L"count"].as_number().to_uint32();
ammoItem.name = to_string(ammoJson[L"desc"][L"displayName"]);
ammoItem.guidance = ammoJson[L"desc"][L"guidance"].as_number().to_uint32();
ammoItem.category = ammoJson[L"desc"][L"category"].as_number().to_uint32();
ammoItem.missileCategory = ammoJson[L"desc"][L"missileCategory"].as_number().to_uint32();
ammo.push_back(ammoItem);
}
setAmmo(ammo);
}
if (json.has_object_field(L"contacts")) {
vector<DataTypes::Contact> contacts;
for (auto const& el : json[L"ammo"].as_object()) {
DataTypes::Contact contactItem;
auto contactJson = el.second;
contactItem.ID = contactJson[L"object"][L"id_"].as_number().to_uint32();
string detectionMethod = to_string(contactJson[L"detectionMethod"]);
if (detectionMethod.compare("VISUAL")) contactItem.detectionMethod = 1;
else if (detectionMethod.compare("OPTIC")) contactItem.detectionMethod = 2;
else if (detectionMethod.compare("RADAR")) contactItem.detectionMethod = 4;
else if (detectionMethod.compare("IRST")) contactItem.detectionMethod = 8;
else if (detectionMethod.compare("RWR")) contactItem.detectionMethod = 16;
else if (detectionMethod.compare("DLINK")) contactItem.detectionMethod = 32;
contacts.push_back(contactItem);
}
setContacts(contacts);
}
//if (json.has_number_field(L"fuel"))
// setFuel(short(json[L"fuel"].as_number().to_double() * 100));
//
//if (json.has_object_field(L"ammo")) {
// vector<DataTypes::Ammo> ammo;
// for (auto const& el : json[L"ammo"].as_object()) {
// DataTypes::Ammo ammoItem;
// auto ammoJson = el.second;
// ammoItem.quantity = ammoJson[L"count"].as_number().to_uint32();
// ammoItem.name = to_string(ammoJson[L"desc"][L"displayName"]);
// ammoItem.guidance = ammoJson[L"desc"][L"guidance"].as_number().to_uint32();
// ammoItem.category = ammoJson[L"desc"][L"category"].as_number().to_uint32();
// ammoItem.missileCategory = ammoJson[L"desc"][L"missileCategory"].as_number().to_uint32();
// ammo.push_back(ammoItem);
// }
// setAmmo(ammo);
//}
//
//if (json.has_object_field(L"contacts")) {
// vector<DataTypes::Contact> contacts;
// for (auto const& el : json[L"ammo"].as_object()) {
// DataTypes::Contact contactItem;
// auto contactJson = el.second;
// contactItem.ID = contactJson[L"object"][L"id_"].as_number().to_uint32();
//
// string detectionMethod = to_string(contactJson[L"detectionMethod"]);
// if (detectionMethod.compare("VISUAL")) contactItem.detectionMethod = 1;
// else if (detectionMethod.compare("OPTIC")) contactItem.detectionMethod = 2;
// else if (detectionMethod.compare("RADAR")) contactItem.detectionMethod = 4;
// else if (detectionMethod.compare("IRST")) contactItem.detectionMethod = 8;
// else if (detectionMethod.compare("RWR")) contactItem.detectionMethod = 16;
// else if (detectionMethod.compare("DLINK")) contactItem.detectionMethod = 32;
// contacts.push_back(contactItem);
// }
// setContacts(contacts);
//}
if (json.has_boolean_field(L"hasTask"))
setHasTask(json[L"hasTask"].as_bool());
}
DataTypes::DataPacket Unit::getDataPacket()
unsigned int Unit::getUpdateData(char* &data)
{
/* Reserve data for:
1) DataPacket;
2) Active path;
*/
data = (char*)malloc(sizeof(DataTypes::DataPacket) + activePath.size() * sizeof(Coords));
unsigned int offset = 0;
/* Prepare the data packet and copy it to memory */
unsigned int bitmask = 0;
bitmask |= alive << 0;
bitmask |= human << 1;
@ -192,16 +194,16 @@ DataTypes::DataPacket Unit::getDataPacket()
bitmask |= desiredSpeedType << 17;
bitmask |= isTanker << 18;
bitmask |= isAWACS << 19;
bitmask |= onOff << 19;
bitmask |= followRoads << 19;
bitmask |= EPLRS << 20;
bitmask |= generalSettings.prohibitAA << 21;
bitmask |= generalSettings.prohibitAfterburner << 22;
bitmask |= generalSettings.prohibitAG << 23;
bitmask |= generalSettings.prohibitAirWpn << 24;
bitmask |= generalSettings.prohibitJettison << 25;
bitmask |= onOff << 20;
bitmask |= followRoads << 21;
bitmask |= EPLRS << 22;
bitmask |= generalSettings.prohibitAA << 23;
bitmask |= generalSettings.prohibitAfterburner << 24;
bitmask |= generalSettings.prohibitAG << 25;
bitmask |= generalSettings.prohibitAirWpn << 26;
bitmask |= generalSettings.prohibitJettison << 27;
DataTypes::DataPacket datapacket{
DataTypes::DataPacket dataPacket{
ID,
bitmask,
position,
@ -217,37 +219,15 @@ DataTypes::DataPacket Unit::getDataPacket()
reactionToThreat,
emissionsCountermeasures,
TACAN,
radio
radio,
activePath.size(),
name.size(),
unitName.size(),
groupName.size(),
getCategory().size(),
coalition.size()
};
return datapacket;
}
string Unit::getData(bool refresh)
{
/* Prepare the data in a stringstream */
stringstream ss;
/* Reserve data for:
1) DataPacket;
2) Length of active path;
3) Active path;
*/
char* data = (char*)malloc(sizeof(DataTypes::DataPacket) + sizeof(unsigned short) + activePath.size() * sizeof(Coords));
unsigned int offset = 0;
/* Prepare the data packet and copy it to memory */
DataTypes::DataPacket dataPacket;
/* If the unit is in a group, get the datapacket from the group leader and only replace the position, speed and heading */
if (unitsManager->isUnitInGroup(this) && !unitsManager->isUnitGroupLeader(this)) {
dataPacket = unitsManager->getGroupLeader(this)->getDataPacket();
dataPacket.position = position;
dataPacket.speed = speed;
dataPacket.heading = heading;
}
else
dataPacket = getDataPacket();
memcpy(data + offset, &dataPacket, sizeof(dataPacket));
offset += sizeof(dataPacket);
@ -255,14 +235,29 @@ string Unit::getData(bool refresh)
std::vector<Coords> path;
for (const Coords& c : activePath)
path.push_back(c);
unsigned short pathLength = activePath.size();
memcpy(data + offset, &pathLength, sizeof(unsigned short));
offset += sizeof(unsigned short);
memcpy(data + offset, &path, activePath.size() * sizeof(Coords));
offset += sizeof(unsigned short);
offset += activePath.size() * sizeof(Coords);
ss << to_base64(data, offset);
return offset;
}
void Unit::getData(stringstream &ss, bool refresh)
{
char* data;
unsigned int size = getUpdateData(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 */
if (unitsManager->isUnitInGroup(this) && !unitsManager->isUnitGroupLeader(this)) {
DataTypes::DataPacket* p = (DataTypes::DataPacket*)data;
p->position = position;
p->speed = speed;
p->heading = heading;
}
ss.write(data, size);
delete data;
if (refresh) {
ss << name;
@ -271,8 +266,6 @@ string Unit::getData(bool refresh)
ss << getCategory();
ss << coalition;
}
return ss.str();
}
void Unit::setActivePath(list<Coords> newPath)
@ -397,7 +390,7 @@ void Unit::setROE(unsigned char newROE, bool force)
{
if (ROE != newROE || force) {
ROE = newROE;
Command* command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::ROE, ROE));
Command* command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::ROE, static_cast<unsigned int>(ROE)));
scheduler->appendCommand(command);
}
}
@ -407,7 +400,7 @@ void Unit::setReactionToThreat(unsigned char newReactionToThreat, bool force)
if (reactionToThreat != newReactionToThreat || force) {
reactionToThreat = newReactionToThreat;
Command* command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::REACTION_ON_THREAT, reactionToThreat));
Command* command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::REACTION_ON_THREAT, static_cast<unsigned int>(reactionToThreat)));
scheduler->appendCommand(command);
}
}

View File

@ -10,6 +10,9 @@
#include "commands.h"
#include "scheduler.h"
#include "base64.hpp"
using namespace base64;
extern Scheduler* scheduler;
UnitsManager::UnitsManager(lua_State* L)
@ -155,8 +158,8 @@ string UnitsManager::getUnitData(bool refresh)
{
stringstream ss;
for (auto const& p : units)
ss << p.second->getData(refresh);
return ss.str();
p.second->getData(ss, refresh);
return to_base64(ss.str());
}
void UnitsManager::deleteUnit(unsigned int ID, bool explosion)

View File

@ -20,7 +20,7 @@ std::wstring to_wstring(const std::string& str)
return wstrTo;
}
std::string to_string(json::value value) {
std::string to_string(json::value& value) {
return to_string(value.as_string());
}

View File

@ -12,10 +12,7 @@ namespace base64 {
"0123456789+/";
return base64_chars;
}
inline std::string to_base64(std::string const& data) {
return to_base64(data.c_str(), data.length());
}
inline std::string to_base64(const char* data, size_t size) {
int counter = 0;
uint32_t bit_stream = 0;
@ -52,6 +49,10 @@ namespace base64 {
return encoded;
}
inline std::string to_base64(std::string const& data) {
return to_base64(data.c_str(), data.length());
}
inline std::string from_base64(std::string const& data) {
int counter = 0;
uint32_t bit_stream = 0;