Completed backend for advanced RTS functions

This commit is contained in:
Pax1601
2023-07-23 22:30:25 +02:00
parent 8ffd5ef972
commit 81871b596b
36 changed files with 338 additions and 216 deletions

View File

@@ -11,24 +11,27 @@ public:
void appendCommand(Command* command);
void execute(lua_State* L);
void handleRequest(string key, json::value value);
void handleRequest(string key, json::value value, string username);
bool checkSpawnPoints(int spawnPoints, string coalition);
void setFrameRate(double newFrameRate) { frameRate = newFrameRate; }
void setRestrictSpawns(bool newRestrictSpawns) { restrictSpawns = newRestrictSpawns; }
void setRestrictToCoalition(bool newRestrictToCoalition) { restrictSpawns = newRestrictToCoalition; }
void setRestrictToCoalition(bool newRestrictToCoalition) { restrictToCoalition = newRestrictToCoalition; }
void setSetupTime(unsigned int newSetupTime) { setupTime = newSetupTime; }
void setBlueSpawnPoints(unsigned int newBlueSpawnPoints) { blueSpawnPoints = newBlueSpawnPoints; }
void setRedSpawnPoints(unsigned int newRedSpawnPoints) { redSpawnPoints = newRedSpawnPoints; }
void setEras(vector<string> newEras) { eras = newEras; }
void setCommandModeOptions(json::value newOptions);
int getFrameRate() { return frameRate; };
int getLoad();
bool getRestrictSpawns() { return restrictSpawns; }
bool getRestrictToCoalition() { return restrictSpawns; }
bool getRestrictToCoalition() { return restrictToCoalition; }
unsigned int getSetupTime() { return setupTime; }
unsigned int getBlueSpawnPoints() { return blueSpawnPoints; }
unsigned int getRedSpawnPoints() { return redSpawnPoints; }
vector<string> getEras() { return eras; }
json::value getCommandModeOptions();
private:
list<Command*> commands;

View File

@@ -24,6 +24,7 @@ private:
void handle_request(http_request request, function<void(json::value const&, json::value&)> action);
void handle_put(http_request request);
string extractUsername(http_request& request);
string extractPassword(http_request& request);
void task();

View File

@@ -67,6 +67,9 @@ extern "C" DllExport int coreFrame(lua_State* L)
if (!initialized)
return (0);
/* Lock for thread safety */
lock_guard<mutex> guard(mutexLock);
frameCounter++;
const std::chrono::duration<double> executionDuration = std::chrono::system_clock::now() - lastExecution;
@@ -91,9 +94,9 @@ extern "C" DllExport int coreUnitsData(lua_State * L)
return (0);
/* Lock for thread safety */
json::value unitsData = json::value::object();
lock_guard<mutex> guard(mutexLock);
json::value unitsData = json::value::object();
lua_getglobal(L, "Olympus");
lua_getfield(L, -1, "unitsData");
luaTableToJSON(L, -1, unitsData);
@@ -115,6 +118,7 @@ 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");
luaTableToJSON(L, -1, missionData);

View File

@@ -68,12 +68,79 @@ void Scheduler::execute(lua_State* L)
};
}
void Scheduler::handleRequest(string key, json::value value)
void Scheduler::setCommandModeOptions(json::value value) {
if (value.has_boolean_field(L"restrictSpawns"))
setRestrictSpawns(value[L"restrictSpawns"].as_bool());
if (value.has_boolean_field(L"restrictToCoalition"))
setRestrictToCoalition(value[L"restrictToCoalition"].as_bool());
if (value.has_number_field(L"setupTime"))
setSetupTime(value[L"setupTime"].as_number().to_int32());
if (value.has_object_field(L"spawnPoints")) {
if (value[L"spawnPoints"].has_number_field(L"blue"))
setBlueSpawnPoints(value[L"spawnPoints"][L"blue"].as_number().to_int32());
if (value[L"spawnPoints"].has_number_field(L"red"))
setRedSpawnPoints(value[L"spawnPoints"][L"red"].as_number().to_int32());
}
if (value.has_array_field(L"eras")) {
int length = value[L"eras"].as_array().size();
vector<string> newEras;
for (int idx = 0; idx < length; idx++)
newEras.push_back(to_string(value[L"eras"].as_array().at(idx)));
setEras(newEras);
}
}
json::value Scheduler::getCommandModeOptions() {
json::value json = json::value::object();
json[L"restrictSpawns"] = json::value(getRestrictSpawns());
json[L"restrictToCoalition"] = json::value(getRestrictToCoalition());
json[L"setupTime"] = json::value(getSetupTime());
json[L"spawnPoints"] = json::value::object();
json[L"spawnPoints"][L"blue"] = json::value(getBlueSpawnPoints());
json[L"spawnPoints"][L"red"] = json::value(getRedSpawnPoints());
int idx = 0;
json[L"eras"] = json::value::array();
for (string era : getEras())
json[L"eras"][idx++] = json::value(to_wstring(era));
return json;
}
bool Scheduler::checkSpawnPoints(int spawnPoints, string coalition)
{
if (!getRestrictSpawns()) return true;
if (coalition.compare("blue") == 0) {
if (getBlueSpawnPoints() - spawnPoints >= 0) {
setBlueSpawnPoints(getBlueSpawnPoints() - spawnPoints);
return true;
}
else {
log("Not enough blue coalition spawn points available. Available: " + to_string(getBlueSpawnPoints()) + ", required: " + to_string(spawnPoints));
return false;
}
}
else if (coalition.compare("red") == 0) {
if (getRedSpawnPoints() - spawnPoints >= 0) {
setRedSpawnPoints(getRedSpawnPoints() - spawnPoints);
return true;
}
else {
log("Not enough red coalition spawn points available. Available: " + to_string(getRedSpawnPoints()) + ", required: " + to_string(spawnPoints));
return false;
}
}
}
void Scheduler::handleRequest(string key, json::value value, string username)
{
Command* command = nullptr;
log("Received request with ID: " + key);
log(value.serialize());
log(L"Incoming command raw value: " + value.serialize());
if (key.compare("setPath") == 0)
{
unsigned int ID = value[L"ID"].as_integer();
@@ -89,14 +156,13 @@ void Scheduler::handleRequest(string key, json::value value)
string WP = to_string(i);
double lat = path[i][L"lat"].as_double();
double lng = path[i][L"lng"].as_double();
log(unitName + " set path destination " + WP + " (" + to_string(lat) + ", " + to_string(lng) + ")");
Coords dest; dest.lat = lat; dest.lng = lng;
newPath.push_back(dest);
}
unit->setActivePath(newPath);
unit->setState(State::REACH_DESTINATION);
log(unitName + " new path set successfully");
log(username + " updated destination path for unit " + unitName, true);
}
}
else if (key.compare("smoke") == 0)
@@ -104,7 +170,7 @@ void Scheduler::handleRequest(string key, json::value value)
string color = to_string(value[L"color"]);
double lat = value[L"location"][L"lat"].as_double();
double lng = value[L"location"][L"lng"].as_double();
log("Adding " + color + " smoke at (" + to_string(lat) + ", " + to_string(lng) + ")");
log(username + " added a " + color + " smoke at (" + to_string(lat) + ", " + to_string(lng) + ")", true);
Coords loc; loc.lat = lat; loc.lng = lng;
command = dynamic_cast<Command*>(new Smoke(color, loc));
}
@@ -114,6 +180,9 @@ void Scheduler::handleRequest(string key, json::value value)
string coalition = to_string(value[L"coalition"]);
string airbaseName = to_string(value[L"airbaseName"]);
int spawnPoints = value[L"spawnPoints"].as_number().to_int32();
if (!checkSpawnPoints(spawnPoints, coalition)) return;
vector<string> unitTypes;
vector<Coords> locations;
vector<string> loadouts;
@@ -125,10 +194,10 @@ void Scheduler::handleRequest(string key, json::value value)
Coords location; location.lat = lat; location.lng = lng; location.alt = alt;
string loadout = to_string(unit[L"loadout"]);
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);
log(username + " spawned a " + coalition + " " + unitType, true);
}
command = dynamic_cast<Command*>(new SpawnAircrafts(coalition, unitTypes, locations, loadouts, airbaseName, immediate));
@@ -139,6 +208,9 @@ void Scheduler::handleRequest(string key, json::value value)
string coalition = to_string(value[L"coalition"]);
string airbaseName = to_string(value[L"airbaseName"]);
int spawnPoints = value[L"spawnPoints"].as_number().to_int32();
if (!checkSpawnPoints(spawnPoints, coalition)) return;
vector<string> unitTypes;
vector<Coords> locations;
vector<string> loadouts;
@@ -150,10 +222,10 @@ void Scheduler::handleRequest(string key, json::value value)
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);
log(username + " spawned a " + coalition + " " + unitType, true);
}
command = dynamic_cast<Command*>(new SpawnHelicopters(coalition, unitTypes, locations, loadouts, airbaseName, immediate));
@@ -163,6 +235,9 @@ void Scheduler::handleRequest(string key, json::value value)
bool immediate = value[L"immediate"].as_bool();
string coalition = to_string(value[L"coalition"]);
int spawnPoints = value[L"spawnPoints"].as_number().to_int32();
if (!checkSpawnPoints(spawnPoints, coalition)) return;
vector<string> unitTypes;
vector<Coords> locations;
for (auto unit : value[L"units"].as_array()) {
@@ -170,9 +245,10 @@ void Scheduler::handleRequest(string key, json::value value)
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);
log(username + " spawned a " + coalition + " " + unitType, true);
}
command = dynamic_cast<Command*>(new SpawnGroundUnits(coalition, unitTypes, locations, immediate));
@@ -182,6 +258,9 @@ void Scheduler::handleRequest(string key, json::value value)
bool immediate = value[L"immediate"].as_bool();
string coalition = to_string(value[L"coalition"]);
int spawnPoints = value[L"spawnPoints"].as_number().to_int32();
if (!checkSpawnPoints(spawnPoints, coalition)) return;
vector<string> unitTypes;
vector<Coords> locations;
for (auto unit : value[L"units"].as_array()) {
@@ -189,9 +268,10 @@ void Scheduler::handleRequest(string key, json::value value)
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);
log(username + " spawned a " + coalition + " " + unitType, true);
}
command = dynamic_cast<Command*>(new SpawnNavyUnits(coalition, unitTypes, locations, immediate));
@@ -218,7 +298,7 @@ void Scheduler::handleRequest(string key, json::value value)
else
return;
log("Unit " + unitName + " attacking unit " + targetName);
log(username + " tasked unit " + unitName + " to attack unit " + targetName, true);
unit->setTargetID(targetID);
unit->setState(State::ATTACK);
}
@@ -247,7 +327,7 @@ void Scheduler::handleRequest(string key, json::value value)
else
return;
log("Unit " + unitName + " following unit " + leaderName);
log(username + " tasked unit " + unitName + " to follow unit " + leaderName, true);
unit->setFormationOffset(Offset(offsetX, offsetY, offsetZ));
unit->setLeaderID(leaderID);
unit->setState(State::FOLLOW);
@@ -307,7 +387,6 @@ void Scheduler::handleRequest(string key, json::value value)
double lng = value[L"location"][L"lng"].as_double();
Coords loc; loc.lat = lat; loc.lng = lng;
command = dynamic_cast<Command*>(new Clone(ID, loc));
log("Cloning unit " + to_string(ID));
}
else if (key.compare("setROE") == 0)
{
@@ -316,6 +395,7 @@ void Scheduler::handleRequest(string key, json::value value)
Unit* unit = unitsManager->getGroupLeader(ID);
unsigned char ROE = value[L"ROE"].as_number().to_uint32();
unit->setROE(ROE);
log(username + " set unit " + unit->getName() + " ROE to " + to_string(ROE), true);
}
else if (key.compare("setReactionToThreat") == 0)
{
@@ -324,6 +404,7 @@ void Scheduler::handleRequest(string key, json::value value)
Unit* unit = unitsManager->getGroupLeader(ID);
unsigned char reactionToThreat = value[L"reactionToThreat"].as_number().to_uint32();
unit->setReactionToThreat(reactionToThreat);
log(username + " set unit " + unit->getName() + " reaction to threat to " + to_string(reactionToThreat), true);
}
else if (key.compare("setEmissionsCountermeasures") == 0)
{
@@ -332,6 +413,7 @@ void Scheduler::handleRequest(string key, json::value value)
Unit* unit = unitsManager->getGroupLeader(ID);
unsigned char emissionsCountermeasures = value[L"emissionsCountermeasures"].as_number().to_uint32();
unit->setEmissionsCountermeasures(emissionsCountermeasures);
log(username + " set unit " + unit->getName() + " emissions and countermeasures to " + to_string(emissionsCountermeasures), true);
}
else if (key.compare("landAt") == 0)
{
@@ -342,6 +424,7 @@ void Scheduler::handleRequest(string key, json::value value)
double lng = value[L"location"][L"lng"].as_double();
Coords loc; loc.lat = lat; loc.lng = lng;
unit->landAt(loc);
log(username + " tasked unit " + unit->getName() + " to land", true);
}
else if (key.compare("deleteUnit") == 0)
{
@@ -349,6 +432,8 @@ void Scheduler::handleRequest(string key, json::value value)
bool explosion = value[L"explosion"].as_bool();
bool immediate = value[L"immediate"].as_bool();
unitsManager->deleteUnit(ID, explosion, immediate);
Unit* unit = unitsManager->getUnit(ID);
log(username + " deleted unit " + unit->getName(), true);
}
else if (key.compare("refuel") == 0)
{
@@ -356,6 +441,7 @@ void Scheduler::handleRequest(string key, json::value value)
unitsManager->acquireControl(ID);
Unit* unit = unitsManager->getGroupLeader(ID);
unit->setState(State::REFUEL);
log(username + " tasked unit " + unit->getName() + " to refuel", true);
}
else if (key.compare("setAdvancedOptions") == 0)
{
@@ -433,6 +519,7 @@ void Scheduler::handleRequest(string key, json::value value)
Unit* unit = unitsManager->getGroupLeader(ID);
unit->setState(State::BOMB_POINT);
unit->setTargetPosition(loc);
log(username + " tasked unit " + unit->getName() + " to bomb a point", true);
}
else if (key.compare("carpetBomb") == 0)
{
@@ -444,6 +531,7 @@ void Scheduler::handleRequest(string key, json::value value)
Unit* unit = unitsManager->getGroupLeader(ID);
unit->setState(State::CARPET_BOMB);
unit->setTargetPosition(loc);
log(username + " tasked unit " + unit->getName() + " to perform carpet bombing", true);
}
else if (key.compare("bombBuilding") == 0)
{
@@ -466,6 +554,11 @@ void Scheduler::handleRequest(string key, json::value value)
Unit* unit = unitsManager->getGroupLeader(ID);
unit->setState(State::FIRE_AT_AREA);
unit->setTargetPosition(loc);
log(username + " tasked unit " + unit->getName() + " to fire at area", true);
}
else if (key.compare("setCommandModeOptions") == 0) {
setCommandModeOptions(value);
log(username + " updated the Command Mode Options", true);
}
else
{

View File

@@ -94,6 +94,7 @@ void Server::handle_get(http_request request)
if (path.size() > 0)
{
string URI = to_string(path[0]);
/* Units data. This is the only binary format data transmitted, all others are transmitted as text json for simplicity */
if (URI.compare(UNITS_URI) == 0)
{
unsigned long long updateTime = ms.count();
@@ -103,39 +104,36 @@ void Server::handle_get(http_request request)
response.set_body(concurrency::streams::bytestream::open_istream(ss.str()));
}
else {
/* Logs data*/
if (URI.compare(LOGS_URI) == 0)
{
auto logs = json::value::object();
getLogsJSON(logs, time);
answer[L"logs"] = logs;
}
/* Airbases data */
else if (URI.compare(AIRBASES_URI) == 0 && missionData.has_object_field(L"airbases"))
answer[L"airbases"] = missionData[L"airbases"];
/* Bullseyes data */
else if (URI.compare(BULLSEYE_URI) == 0 && missionData.has_object_field(L"bullseyes"))
answer[L"bullseyes"] = missionData[L"bullseyes"];
/* Mission data */
else if (URI.compare(MISSION_URI) == 0 && missionData.has_object_field(L"mission")) {
answer[L"mission"] = missionData[L"mission"];
answer[L"mission"][L"RTSOptions"] = json::value::object();
if (password.compare(gameMasterPassword) == 0)
answer[L"mission"][L"RTSOptions"][L"commandMode"] = json::value(L"Game master");
else if (password.compare(blueCommanderPassword) == 0)
answer[L"mission"][L"RTSOptions"][L"commandMode"] = json::value(L"Blue commander");
else if (password.compare(redCommanderPassword) == 0)
answer[L"mission"][L"RTSOptions"][L"commandMode"] = json::value(L"Red commander");
answer[L"mission"][L"commandModeOptions"] = scheduler->getCommandModeOptions();
answer[L"mission"][L"RTSOptions"][L"restrictSpawns"] = json::value(scheduler->getRestrictSpawns());
answer[L"mission"][L"RTSOptions"][L"restrictToCoalition"] = json::value(scheduler->getRestrictToCoalition());
answer[L"mission"][L"RTSOptions"][L"setupTime"] = json::value(scheduler->getSetupTime());
answer[L"mission"][L"RTSOptions"][L"spawnPoints"] = json::value::object();
answer[L"mission"][L"RTSOptions"][L"spawnPoints"][L"blue"] = json::value(scheduler->getBlueSpawnPoints());
answer[L"mission"][L"RTSOptions"][L"spawnPoints"][L"red"] = json::value(scheduler->getRedSpawnPoints());
int idx = 0;
answer[L"mission"][L"RTSOptions"][L"eras"] = json::value::array();
for (string era : scheduler->getEras())
answer[L"mission"][L"RTSOptions"][L"eras"].as_array()[idx++] = json::value(to_wstring(era));
/* The active mode is determined by the inserted password*/
if (password.compare(gameMasterPassword) == 0)
answer[L"mission"][L"commandModeOptions"][L"commandMode"] = json::value(L"Game master");
else if (password.compare(blueCommanderPassword) == 0)
answer[L"mission"][L"commandModeOptions"][L"commandMode"] = json::value(L"Blue commander");
else if (password.compare(redCommanderPassword) == 0)
answer[L"mission"][L"commandModeOptions"][L"commandMode"] = json::value(L"Red commander");
else
answer[L"mission"][L"commandModeOptions"][L"commandMode"] = json::value(L"Observer");
}
/* Common data */
answer[L"time"] = json::value::string(to_wstring(ms.count()));
answer[L"sessionHash"] = json::value::string(to_wstring(sessionHash));
answer[L"load"] = scheduler->getLoad();
@@ -199,9 +197,10 @@ void Server::handle_request(http_request request, function<void(json::value cons
void Server::handle_put(http_request request)
{
string username = extractUsername(request);
handle_request(
request,
[](json::value const& jvalue, json::value& answer)
[username](json::value const& jvalue, json::value& answer)
{
/* Lock for thread safety */
lock_guard<mutex> guard(mutexLock);
@@ -210,9 +209,10 @@ void Server::handle_put(http_request request)
{
auto key = e.first;
auto value = e.second;
std::exception_ptr eptr;
try {
scheduler->handleRequest(to_string(key), value);
scheduler->handleRequest(to_string(key), value, username);
}
catch (...) {
eptr = std::current_exception(); // capture
@@ -222,6 +222,30 @@ void Server::handle_put(http_request request)
});
}
string Server::extractUsername(http_request& request) {
if (request.headers().has(L"Authorization")) {
string authorization = to_string(request.headers().find(L"Authorization")->second);
string s = "Basic ";
string::size_type i = authorization.find(s);
if (i != std::string::npos)
authorization.erase(i, s.length());
else
return "";
string decoded = from_base64(authorization);
i = decoded.find(":");
if (i != string::npos && i <= decoded.length())
decoded.erase(i, decoded.length() - i);
else
return "";
return decoded;
}
else
return "";
}
string Server::extractPassword(http_request& request) {
if (request.headers().has(L"Authorization")) {
string authorization = to_string(request.headers().find(L"Authorization")->second);

View File

@@ -1,6 +1,6 @@
#pragma once
#include "framework.h"
void DllExport log(const std::string& sMessage);
void DllExport log(const std::wstring& sMessage);
void DllExport log(const std::string& sMessage, bool addToJSON = false);
void DllExport log(const std::wstring& sMessage, bool addToJSON = false);
void DllExport getLogsJSON(json::value& json, unsigned long long time);

View File

@@ -5,8 +5,8 @@
class Logger
{
public:
void log(const string& sMessage);
void log(const wstring& sMessage);
void log(const string& sMessage, bool addToJSON);
void log(const wstring& sMessage, bool addToJSON);
void toJSON(json::value& json, unsigned long long time);
static Logger* GetLogger();

View File

@@ -4,14 +4,14 @@
#define LOGGER Logger::GetLogger()
void log(const string& message)
void log(const string& message, bool addToJSON)
{
LOGGER->log(message);
LOGGER->log(message, addToJSON);
}
void log(const wstring& message)
void log(const wstring& message, bool addToJSON)
{
LOGGER->log(message);
LOGGER->log(message, addToJSON);
}
void getLogsJSON(json::value& json, unsigned long long time)

View File

@@ -47,24 +47,26 @@ void Logger::toJSON(json::value& json, unsigned long long time)
}
}
void Logger::log(const string& message)
void Logger::log(const string& message, bool addToJSON)
{
lock_guard<mutex> guard(mutexLock);
Open();
milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
m_Logfile << CurrentDateTime() << ":\t";
m_Logfile << message << "\n";
m_logs[static_cast<unsigned long long>(ms.count())] = CurrentDateTime() + ": " + message;
if (addToJSON)
m_logs[static_cast<unsigned long long>(ms.count())] = message;
Close();
}
void Logger::log(const wstring& message)
void Logger::log(const wstring& message, bool addToJSON)
{
lock_guard<mutex> guard(mutexLock);
Open();
milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
m_Logfile << CurrentDateTime() << ":\t";
m_Logfile << to_string(message) << "\n";
m_logs[static_cast<unsigned long long>(ms.count())] = CurrentDateTime() + ": " + to_string(message);
if (addToJSON)
m_logs[static_cast<unsigned long long>(ms.count())] = to_string(message);
Close();
}