diff --git a/CMakeLists.txt b/CMakeLists.txt index f4a05c7..569e25e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,8 +24,10 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${PROJECT_BINARY_DIR}/bin) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${PROJECT_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${PROJECT_BINARY_DIR}/bin) -# Build with c++14 support, required by sc2api. -set(CMAKE_CXX_STANDARD 14) +# Minimum c++14 support, required by sc2api. +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") # Allow creating filters for projects in visual studio. set_property(GLOBAL PROPERTY USE_FOLDERS ON) diff --git a/cpp-sc2 b/cpp-sc2 index afd2830..f6b6213 160000 --- a/cpp-sc2 +++ b/cpp-sc2 @@ -1 +1 @@ -Subproject commit afd2830714cdb2dd7e893f1b0512ea8fc0a4ecc1 +Subproject commit f6b621308ad11c405d7347de68ad74240127a3d9 diff --git a/src/sc2laddercore/AgentsConfig.cpp b/src/sc2laddercore/AgentsConfig.cpp index e35a079..e5a9cdc 100644 --- a/src/sc2laddercore/AgentsConfig.cpp +++ b/src/sc2laddercore/AgentsConfig.cpp @@ -3,7 +3,6 @@ #include "sc2utils/sc2_manage_process.h" #include "sc2utils/sc2_scan_directory.h" #define RAPIDJSON_HAS_STDSTRING 1 -#include "rapidjson.h" #include "document.h" #include @@ -96,12 +95,7 @@ void AgentsConfig::LoadAgents(const std::string &BaseDirectory, const std::strin } if (val.HasMember("RootPath") && val["RootPath"].IsString()) { - NewBot.RootPath = BaseDirectory; - if (NewBot.RootPath.back() != '/') - { - NewBot.RootPath += '/'; - } - NewBot.RootPath = NewBot.RootPath + val["RootPath"].GetString(); + NewBot.RootPath = BaseDirectory + val["RootPath"].GetString(); if (NewBot.RootPath.back() != '/') { NewBot.RootPath += '/'; @@ -146,13 +140,14 @@ void AgentsConfig::LoadAgents(const std::string &BaseDirectory, const std::strin NewBot.PlayerId = PlayerIds->GetStringValue(NewBot.BotName); if (NewBot.PlayerId.empty()) { - NewBot.PlayerId = GerneratePlayerId(PLAYER_ID_LENGTH); + NewBot.PlayerId = GeneratePlayerId(PLAYER_ID_LENGTH); PlayerIds->AddValue(NewBot.BotName, NewBot.PlayerId); PlayerIds->WriteConfig(); } } - std::string OutCmdLine = ""; + std::string OutCmdLine; + switch (NewBot.Type) { case Python: @@ -222,12 +217,9 @@ void AgentsConfig::LoadAgents(const std::string &BaseDirectory, const std::strin void AgentsConfig::SaveBotConfig(const BotConfig& Agent) { BotConfig SavedBot; - if (FindBot(Agent.BotName, SavedBot)) - { + if (FindBot(Agent.BotName, SavedBot)) { BotConfigs[Agent.BotName] = Agent; - } - else - { + } else { BotConfigs.insert(std::make_pair(std::string(Agent.BotName), Agent)); } } @@ -241,69 +233,12 @@ bool AgentsConfig::FindBot(const std::string &BotName, BotConfig &ReturnBot) ReturnBot = ThisBot->second; return true; } - return false; -} -bool AgentsConfig::CheckDiactivatedBots() -{ - std::string BotCheckLocation = Config->GetStringValue("BotInfoLocation"); - if (BotCheckLocation.empty()) - { - return false; - } - std::vector arguments; - std::string result = PerformRestRequest(BotCheckLocation, arguments); - if (result.empty()) - { - return false; - } - rapidjson::Document doc; - bool parsingFailed = doc.Parse(result.c_str()).HasParseError(); - if (parsingFailed) - { - std::cerr << "Unable to parse incoming bot config: " << BotCheckLocation << std::endl; - return false; - } - if (doc.HasMember("Bots") && doc["Bots"].IsArray()) - { - const rapidjson::Value & Units = doc["Bots"]; - for (const auto& val : Units.GetArray()) - { - if (val.HasMember("name") && val["name"].IsString()) - { - auto ThisBot = BotConfigs.find(val["name"].GetString()); - if (ThisBot != BotConfigs.end()) - { - if (val.HasMember("deactivated") && val.HasMember("deleted") && val["deactivated"].IsBool() && val["deleted"].IsBool()) - { - if ((val["deactivated"].GetBool() || val["deleted"].GetBool()) && ThisBot->second.Enabled) - { - // Set bot to disabled - PrintThread{} << "Deactivating bot " << ThisBot->second.BotName << std::endl; - ThisBot->second.Enabled = false; - - } - else if (val["deactivated"].GetBool() == false && val["deleted"].GetBool() == false && ThisBot->second.Enabled == false) - { - // reenable a bot - PrintThread{} << "Activating bot " << ThisBot->second.BotName; - ThisBot->second.Enabled = true; - } - } - if (val.HasMember("elo") && val["elo"].IsString()) - { - ThisBot->second.ELO = std::stoi(val["elo"].GetString()); - } - } - - } - } - return true; - } return false; } -std::string AgentsConfig::GerneratePlayerId(size_t Length) + +std::string AgentsConfig::GeneratePlayerId(size_t Length) { static const char hexdigit[16] = { '0', '1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; std::string outstring; @@ -319,3 +254,12 @@ std::string AgentsConfig::GerneratePlayerId(size_t Length) } return outstring; } + +std::vector AgentsConfig::Bots() { + std::vector bots = std::vector(); + for (const auto &Bot : this->BotConfigs) { + bots.push_back(Bot.second); + } + + return bots; +} diff --git a/src/sc2laddercore/AgentsConfig.h b/src/sc2laddercore/AgentsConfig.h index d59977e..776ffae 100644 --- a/src/sc2laddercore/AgentsConfig.h +++ b/src/sc2laddercore/AgentsConfig.h @@ -5,6 +5,7 @@ #include "Types.h" #include "LadderConfig.h" + #define PLAYER_ID_LENGTH 16 class AgentsConfig @@ -16,8 +17,7 @@ class AgentsConfig void ReadBotDirectories(const std::string &BaseDirectory); bool FindBot(const std::string &BotName, BotConfig &ReturnBot); - - bool CheckDiactivatedBots(); + std::vector Bots(); std::map BotConfigs; @@ -25,7 +25,5 @@ class AgentsConfig LadderConfig *Config; LadderConfig *PlayerIds; bool EnablePlayerIds; - std::string GerneratePlayerId(size_t Length); - - + static std::string GeneratePlayerId(size_t Length); }; diff --git a/src/sc2laddercore/LadderGame.cpp b/src/sc2laddercore/LadderGame.cpp index c22a27c..4b90d39 100644 --- a/src/sc2laddercore/LadderGame.cpp +++ b/src/sc2laddercore/LadderGame.cpp @@ -1,39 +1,27 @@ #include "LadderGame.h" -#include -#include - -#include #include #include -#include -#include #include #include #include #include #include +#include -#include "sc2lib/sc2_lib.h" -#include "sc2api/sc2_api.h" -#include "sc2api/sc2_interfaces.h" #include "sc2api/sc2_score.h" -#include "sc2api/sc2_map_info.h" #include "sc2utils/sc2_manage_process.h" #include "sc2api/sc2_game_settings.h" -#include "sc2api/sc2_proto_interface.h" -#include "sc2api/sc2_proto_to_pods.h" -#include "s2clientprotocol/sc2api.pb.h" -#include "sc2api/sc2_server.h" -#include "sc2api/sc2_connection.h" #include "sc2api/sc2_args.h" -#include "sc2api/sc2_client.h" -#include "civetweb.h" #include "Types.h" #include "Tools.h" #include "Proxy.h" +static ResultType getEndResultFromProxyResults(const std::vector &exitCases); + +namespace fs = std::filesystem; + LadderGame::LadderGame(int InCoordinatorArgc, char** InCoordinatorArgv, LadderConfig *InConfig) : CoordinatorArgc(InCoordinatorArgc) @@ -47,123 +35,230 @@ LadderGame::LadderGame(int InCoordinatorArgc, char** InCoordinatorArgv, LadderCo RealTime = Config->GetBoolValue("RealTimeMode"); } -void LadderGame::LogStartGame(const BotConfig &Bot1, const BotConfig &Bot2) -{ +void LadderGame::LogStartGame(const std::vector& Agents) { std::time_t t = std::time(nullptr); std::tm tm = *std::localtime(&t); - // Why do we log this in stderr ? - std::string Bot1Filename = Bot1.RootPath + "/data/stderr.log"; - std::string Bot2Filename = Bot2.RootPath + "/data/stderr.log"; - std::ofstream outfile; - outfile.open(Bot1Filename, std::ios_base::app); - outfile << std::endl << std::put_time(&tm, "%d-%m-%Y %H-%M-%S") << ": " << "Starting game vs " << Bot2.BotName << std::endl; - outfile.close(); - outfile.open(Bot2Filename, std::ios_base::app); - outfile << std::endl << std::put_time(&tm, "%d-%m-%Y %H-%M-%S") << ": " << "Starting game vs " << Bot1.BotName << std::endl; - outfile.close(); + + auto startTime = std::put_time(&tm, "%d-%m-%Y %H-%M-%S"); + + for(const auto & Bot : Agents) { + fs::path BotRoot(Bot.RootPath); + BotRoot = BotRoot / "data" / ("stderr-" + Bot.PlayerId + ".log"); + + std::ofstream outfile; + + outfile.open(BotRoot.string(), std::ios_base::app); + outfile << std::endl << startTime << ": " << "Starting game vs " + << Bot.BotName << std::endl; + + outfile.close(); + } } -GameResult LadderGame::StartGame(const BotConfig &Agent1, const BotConfig &Agent2, const std::string &Map) -{ - LogStartGame(Agent1, Agent2); - // Proxy init - Proxy proxyBot1(MaxGameTime, MaxRealGameTime, Agent1); - Proxy proxyBot2(MaxGameTime, MaxRealGameTime, Agent2); - - // Start the SC2 instances - sc2::ProcessSettings process_settings; - sc2::GameSettings game_settings; - sc2::ParseSettings(CoordinatorArgc, CoordinatorArgv, process_settings, game_settings); - constexpr int portServerBot1 = 5677; - constexpr int portServerBot2 = 5678; - constexpr int portClientBot1 = 5679; - constexpr int portClientBot2 = 5680; - PrintThread {} << "Starting the StarCraft II clients." << std::endl; - proxyBot1.startSC2Instance(process_settings, portServerBot1, portClientBot1); - proxyBot2.startSC2Instance(process_settings, portServerBot2, portClientBot2); - const bool startSC2InstanceSuccessful1 = proxyBot1.ConnectToSC2Instance(process_settings, portServerBot1, portClientBot1); - const bool startSC2InstanceSuccessful2 = proxyBot2.ConnectToSC2Instance(process_settings, portServerBot2, portClientBot2); - if (!startSC2InstanceSuccessful1 || !startSC2InstanceSuccessful2) - { - PrintThread {} << "Failed to start the StarCraft II clients." << std::endl; - return GameResult(); +GameResult LadderGame::StartGame(const std::vector &Agents, const std::string &Map) { + if(Agents.empty()) { + return {}; } - // Setup map - PrintThread {} << "Creating the game on " << Map << "." << std::endl; - const bool setupGameSuccessful1 = proxyBot1.setupGame(process_settings, Map, RealTime, Agent1.Race, Agent2.Race); - const bool setupGameSuccessful2 = proxyBot2.setupGame(process_settings, Map, RealTime, Agent1.Race, Agent2.Race); - if (!setupGameSuccessful1 || !setupGameSuccessful2) - { - PrintThread {} << "Failed to create the game." << std::endl; - return GameResult(); + + LogStartGame(Agents); + + // prepare races array + std::vector races = std::vector(); + for (const auto & agent : Agents) { + races.push_back(agent.Race); } - // Start the bots - PrintThread {} << "Starting the bots " << Agent1.BotName << " and " << Agent2.BotName << "." << std::endl; - const bool startBotSuccessful1 = proxyBot1.startBot(portServerBot1, PORT_START, Agent2.PlayerId); - const bool startBotSuccessful2 = proxyBot2.startBot(portServerBot2, PORT_START, Agent1.PlayerId); - if (!startBotSuccessful1) - { - PrintThread {} << "Failed to start " << Agent1.BotName << "." << std::endl; + sc2::ProcessSettings processSettings; + sc2::GameSettings gameSettings; + + sc2::ParseSettings(CoordinatorArgc, CoordinatorArgv, + processSettings, + gameSettings); + + std::vector proxies = std::vector(); + + for (const auto & Agent : Agents) { + auto *proxyBot = new Proxy(MaxGameTime, + MaxRealGameTime, Agent); + proxies.push_back(proxyBot); } - if (!startBotSuccessful2) - { - PrintThread {} << "Failed to start " << Agent2.BotName << "." << std::endl; + + // start sc2 instances + int agentIndex = 0; + for (const auto proxy : proxies) { + int portServer = PORT_START_BOT + agentIndex * 2; + int portClient = PORT_START_BOT + agentIndex * 2 + 1; + + PrintThread{} << "Starting StarCraft II instance for bot " + << Agents[agentIndex].BotName << std::endl; + proxy->startSC2Instance(processSettings, portServer, portClient); + + agentIndex++; } - if (!startBotSuccessful1 || !startBotSuccessful2) - { - return GameResult(); + + // connecting to sc2 instances + agentIndex = 0; + for (const auto proxy : proxies) { + int portServer = PORT_START_BOT + agentIndex * 2; + int portClient = PORT_START_BOT + agentIndex * 2 + 1; + + PrintThread{} << "Connecting " << Agents[agentIndex].PlayerId + << " bot to StarCraft II instance" << std::endl; + + const bool startSC2InstanceSuccessful = proxy->ConnectToSC2Instance( + processSettings, portServer, portClient + ); + + if (!startSC2InstanceSuccessful) { + PrintThread {} << "Failed to start the StarCraft II clients" << std::endl; + return {}; + } + + agentIndex++; } - // Start the match - PrintThread {} << "Starting the match." << std::endl; - proxyBot1.startGame(); - proxyBot2.startGame(); + // setup game + agentIndex = 0; + for (const auto proxy : proxies) { + PrintThread{} << "Setup game for " << Agents[agentIndex].PlayerId + << " bot" << std::endl; + const bool setupGameSuccessful = proxy->setupGame(processSettings, Map, + RealTime, + races); + if (!setupGameSuccessful) { + PrintThread {} << "Failed to create the game" << std::endl; + return {}; + } - // Check from time to time if the match finished - while (!proxyBot1.gameFinished() || !proxyBot2.gameFinished()) - { - sc2::SleepFor(1000); + agentIndex++; } + agentIndex = 0; + for (const auto proxy : proxies) { + int portServer = PORT_START_BOT + agentIndex * 2; + + PrintThread{} << "Starting " << Agents[agentIndex].PlayerId + << " bot" << std::endl; + const bool startBotSuccessful = proxy->startBot(portServer, + PORT_START, + Agents[agentIndex].PlayerId); + if (!startBotSuccessful) { + PrintThread {} << "Failed to start " << Agents[agentIndex].BotName + << std::endl; + return {}; + } + + agentIndex++; + } + + agentIndex = 0; + for (const auto proxy : proxies) { + PrintThread{} << "Starting " << Agents[agentIndex].PlayerId + << " bot game" << std::endl; + proxy->startGame(); + + agentIndex++; + } + + // god, send us streams... + bool gameFinished; + do { + gameFinished = proxies[0]->gameFinished(); + + if (proxies.size() == 1) { + continue; + } + + for (int i = 1; i < proxies.size(); i++) { + gameFinished = gameFinished && proxies[i]->gameFinished(); + if (!gameFinished) { + break; + } + } + + if (!gameFinished) { + sc2::SleepFor(1000); + } + } while (!gameFinished); + std::string replayDir = Config->GetStringValue("LocalReplayDirectory"); - if (replayDir.back() != '/') - { - replayDir += "/"; + fs::path replay(replayDir); + replay = replay / (RemoveMapExtension(Map) + ".SC2Replay"); + + // save replay + bool saveResult = false; + for (const auto proxy : proxies) { + saveResult = proxy->saveReplay(replay.string()); + + if(saveResult) { + break; + } } - std::string replayFile = replayDir + Agent1.BotName + "v" + Agent2.BotName + "-" + RemoveMapExtension(Map) + ".SC2Replay"; - replayFile.erase(remove_if(replayFile.begin(), replayFile.end(), isspace), replayFile.end()); - if (!(proxyBot1.saveReplay(replayFile) || proxyBot2.saveReplay(replayFile))) - { - PrintThread{} << "Saving replay failed." << std::endl; + + if (!saveResult) { + PrintThread{} << "Saving replay failed" << std::endl; } - ChangeBotNames(replayFile, Agent1.BotName, Agent2.BotName); + // save result GameResult Result; - const auto resultBot1 = proxyBot1.getResult(); - const auto resultBot2 = proxyBot2.getResult(); + // todo unify structures - relying on order and different list sizes is not so good + agentIndex = 0; + std::vector exitCases = std::vector(); + std::vector avgFrames = std::vector(); + + for (const auto & proxy : proxies) { + auto result = proxy->getResult(); + exitCases.push_back(result); + + float avgFrame = proxy->stats().avgLoopDuration; + avgFrames.push_back(avgFrame); - Result.Result = getEndResultFromProxyResults(resultBot1, resultBot2); - Result.Bot1AvgFrame = proxyBot1.stats().avgLoopDuration; - Result.Bot2AvgFrame = proxyBot2.stats().avgLoopDuration; - Result.GameLoop = proxyBot1.stats().gameLoops; + if (result == ExitCase::GameEndVictory) { + Result.Winner = Agents[agentIndex].BotName; + } + + agentIndex++; + } + + Result.AvgFrames = avgFrames; + Result.Result = getEndResultFromProxyResults(exitCases); + Result.GameLoop = proxies[0]->stats().gameLoops; std::time_t t = std::time(nullptr); std::tm tm = *std::gmtime(&t); std::ostringstream oss; - oss << std::put_time(&tm, "%d-%m-%Y %H-%M-%S") <<"UTC"; + oss << std::put_time(&tm, "%d-%m-%Y %H-%M-%S") << "UTC"; Result.TimeStamp = oss.str(); + + // delete proxy objects + for (const auto proxy : proxies) { + delete proxy; + } + return Result; } +static ResultType getEndResultFromProxyResults(const std::vector &exitCases) { + for (const auto & exitCase : exitCases) { + if (exitCase == ExitCase::BotCrashed || + exitCase == ExitCase::BotStepTimeout || + exitCase == ExitCase::Error) { + return ResultType::Error; + } -void LadderGame::ChangeBotNames(const std::string &ReplayFile, const std::string &Bot1Name, const std::string &Bot2Name) -{ - std::string CmdLine = Config->GetStringValue("ReplayBotRenameProgram"); - if (CmdLine.size() > 0) - { - CmdLine = CmdLine + " " + ReplayFile + " " + FIRST_PLAYER_NAME + " " + Bot1Name + " " + SECOND_PLAYER_NAME + " " + Bot2Name; - StartExternalProcess(CmdLine); + if (exitCase == ExitCase::GameTimeOver) { + return ResultType::Timeout; + } + + if (exitCase == ExitCase::GameEndVictory) { + return ResultType::Win; + } } + + return ResultType::Timeout; } + + + + diff --git a/src/sc2laddercore/LadderGame.h b/src/sc2laddercore/LadderGame.h index 3a4cff5..ffa41d0 100644 --- a/src/sc2laddercore/LadderGame.h +++ b/src/sc2laddercore/LadderGame.h @@ -2,22 +2,19 @@ #include "Types.h" #include "LadderConfig.h" +#define PORT_START_BOT 5677 #define PORT_START 5690 -#define FIRST_PLAYER_NAME "foo5679" -#define SECOND_PLAYER_NAME "foo5680" - class LadderGame { public: LadderGame(int InCoordinatorArgc, char** InCoordinatorArgv, LadderConfig *InConfig); - GameResult StartGame(const BotConfig & Agent1, const BotConfig & Agent2, const std::string & Map); + GameResult StartGame(const std::vector & Agents, const std::string & Map); private: - void LogStartGame(const BotConfig & Bot1, const BotConfig & Bot2); - void ChangeBotNames(const std::string &ReplayFile, const std::string &Bot1Name, const std::string &Bot2Name); + static void LogStartGame(const std::vector& Agents); int CoordinatorArgc; char** CoordinatorArgv; @@ -25,4 +22,6 @@ class LadderGame uint32_t MaxGameTime{0U}; uint32_t MaxRealGameTime{0U}; bool RealTime{false}; + + }; diff --git a/src/sc2laddercore/LadderManager.cpp b/src/sc2laddercore/LadderManager.cpp index 0c27a00..1d42518 100644 --- a/src/sc2laddercore/LadderManager.cpp +++ b/src/sc2laddercore/LadderManager.cpp @@ -1,28 +1,15 @@ -#include "sc2lib/sc2_lib.h" -#include "sc2api/sc2_api.h" -#include "sc2api/sc2_interfaces.h" #include "sc2api/sc2_score.h" -#include "sc2api/sc2_map_info.h" #include "sc2utils/sc2_manage_process.h" #include "sc2api/sc2_game_settings.h" -#include "sc2api/sc2_proto_interface.h" -#include "sc2api/sc2_interfaces.h" -#include "sc2api/sc2_proto_to_pods.h" -#include "s2clientprotocol/sc2api.pb.h" -#include "sc2api/sc2_server.h" -#include "sc2api/sc2_connection.h" #include "sc2api/sc2_args.h" -#include "sc2api/sc2_client.h" -#include "sc2api/sc2_proto_to_pods.h" #include "civetweb.h" #include +#include #define RAPIDJSON_HAS_STDSTRING 1 -#include "rapidjson.h" #include "document.h" #include "ostreamwrapper.h" -#include "writer.h" #include "prettywriter.h" #include #include @@ -31,9 +18,7 @@ #include #include #include -#include -#include -#include +#include #include #include "Types.h" #include "LadderConfig.h" @@ -89,11 +74,7 @@ bool LadderManager::LoadSetup() return false; } - std::string EnableReplayUploadString = Config->GetStringValue("EnableReplayUpload"); - if (EnableReplayUploadString == "True") - { - EnableReplayUploads = true; - } + EnableReplayUploads = Config->GetBoolValue("EnableReplayUpload"); ResultsLogFile = Config->GetStringValue("ResultsLogFile"); ServerUsername = Config->GetStringValue("ServerUsername"); @@ -115,8 +96,11 @@ bool LadderManager::LoadSetup() return true; } -void LadderManager::SaveJsonResult(const BotConfig &Bot1, const BotConfig &Bot2, const std::string &Map, GameResult Result) -{ +void +LadderManager::SaveJsonResult(const std::vector &Agents, + const std::string &Map, + const GameResult &Result) { + rapidjson::Document ResultsDoc; rapidjson::Document OriginalResults; rapidjson::Document::AllocatorType& alloc = ResultsDoc.GetAllocator(); @@ -141,19 +125,19 @@ void LadderManager::SaveJsonResult(const BotConfig &Bot1, const BotConfig &Bot2, } rapidjson::Value NewResult(rapidjson::kObjectType); - NewResult.AddMember("Bot1", Bot1.BotName, ResultsDoc.GetAllocator()); - NewResult.AddMember("Bot2", Bot2.BotName, alloc); - switch (Result.Result) - { - case ResultType::Player1Win: - case ResultType::Player2Crash: - case ResultType::Player2TimeOut: - NewResult.AddMember("Winner", Bot1.BotName, alloc); - break; - case ResultType::Player2Win: - case ResultType::Player1Crash: - case ResultType::Player1TimeOut: - NewResult.AddMember("Winner", Bot2.BotName, alloc); + + int agentIndex = 0; + for (const auto & Agent : Agents) { + std::string fieldName = "Bot" + std::to_string(agentIndex + 1); + + NewResult.AddMember(rapidjson::StringRef(fieldName), + Agent.BotName, alloc); + agentIndex++; + } + + switch (Result.Result) { + case ResultType::Win: + NewResult.AddMember("Winner", Result.Winner, alloc); break; case ResultType::Tie: case ResultType::Timeout: @@ -170,8 +154,10 @@ void LadderManager::SaveJsonResult(const BotConfig &Bot1, const BotConfig &Bot2, NewResult.AddMember("Result", GetResultType(Result.Result), alloc); NewResult.AddMember("GameTime", Result.GameLoop, alloc); NewResult.AddMember("TimeStamp", Result.TimeStamp, alloc); + ResultsArray.PushBack(NewResult, alloc); ResultsDoc.AddMember("Results", ResultsArray, alloc); + std::ofstream ofs(ResultsLogFile.c_str()); rapidjson::OStreamWrapper osw(ofs); rapidjson::PrettyWriter writer(osw); @@ -182,28 +168,29 @@ bool LadderManager::UploadCmdLine(GameResult result, const Matchup &ThisMatch, c { std::string ReplayDir = Config->GetStringValue("LocalReplayDirectory"); std::string RawMapName = RemoveMapExtension(ThisMatch.Map); - std::string ReplayFile; - ReplayFile = ThisMatch.Agent1.BotName + "v" + ThisMatch.Agent2.BotName + "-" + RawMapName + ".Sc2Replay"; + std::string ReplayFile = ThisMatch.ReplayName() + ".Sc2Replay"; ReplayFile.erase(remove_if(ReplayFile.begin(), ReplayFile.end(), isspace), ReplayFile.end()); std::string ReplayLoc = ReplayDir + ReplayFile; std::vector arguments; std::string argument = " -b cookies.txt"; arguments.push_back(argument); - argument = " -F Username=" + ServerUsername; + argument = " -F Username=" + ServerUsername; arguments.push_back(argument); argument = " -F Password=" + ServerPassword; arguments.push_back(argument); - argument = " -F Bot1Name=" + ThisMatch.Agent1.BotName; - arguments.push_back(argument); - argument = " -F Bot1Race=" + std::to_string((int)ThisMatch.Agent1.Race); - arguments.push_back(argument); - argument = " -F Bot2Name=" + ThisMatch.Agent2.BotName; - arguments.push_back(argument); - argument = " -F Bot1AvgFrame=" + std::to_string(result.Bot1AvgFrame); - arguments.push_back(argument); - argument = " -F Bot2AvgFrame=" + std::to_string(result.Bot2AvgFrame); - arguments.push_back(argument); + + int botCounter = 0; + for (const BotConfig &bot : ThisMatch.Agents) { // todo use cpp23 enumerate (if it happen...) + argument = " -F Bot" + std::to_string(botCounter + 1) + "Name=" + bot.BotName; + arguments.push_back(argument); + + // todo update avgframe fields + argument = " -F Bot" + std::to_string(botCounter + 1) + "AvgFrame=" + std::to_string(result.AvgFrames[botCounter]); + arguments.push_back(argument); + botCounter += 1; + } + argument = " -F Frames=" + std::to_string(result.GameLoop); arguments.push_back(argument); argument = " -F Map=" + RawMapName; @@ -213,6 +200,7 @@ bool LadderManager::UploadCmdLine(GameResult result, const Matchup &ThisMatch, c argument = " -F replayfile=@" + ReplayLoc; arguments.push_back(argument); PerformRestRequest(UploadResultLocation, arguments); + return true; } @@ -230,38 +218,6 @@ bool LadderManager::LoginToServer() return true; } -bool LadderManager::IsBotEnabled(std::string BotName) -{ - BotConfig ThisBot; - if (AgentConfig->FindBot(BotName, ThisBot)) - { - return ThisBot.Enabled; - - } - return false; -} -bool LadderManager::IsInsideEloRange(std::string Bot1Name, std::string Bot2Name) -{ - if (MaxEloDiff == 0) - { - return true; - } - BotConfig Bot1, Bot2; - if (AgentConfig->FindBot(Bot1Name, Bot1) && AgentConfig->FindBot(Bot2Name, Bot2)) - { - int32_t EloDiff = abs(Bot1.ELO - Bot2.ELO); - PrintThread{} << Bot1Name << " ELO: " << Bot1.ELO << " | " << Bot2Name << " ELO: " << Bot2.ELO << " | Diff: " << EloDiff << std::endl; - - if (Bot1.ELO > 0 && Bot2.ELO > 0 && abs(EloDiff) > MaxEloDiff) - { - return false; - } - return true; - - } - return true; -} - bool LadderManager::DownloadBot(const std::string& BotName, const std::string& Checksum, bool Data) { constexpr int DownloadRetrys = 3; @@ -293,7 +249,7 @@ bool LadderManager::DownloadBot(const std::string& BotName, const std::string& C std::string BotMd5 = GenerateMD5(BotZipLocation); PrintThread{} << "Download checksum: " << Checksum << " Bot checksum: " << BotMd5 << std::endl; - if (BotMd5.compare(Checksum) == 0) + if (BotMd5 == Checksum) { UnzipArchive(BotZipLocation, RootPath); remove(BotZipLocation.c_str()); @@ -370,22 +326,22 @@ bool LadderManager::UploadBot(const BotConfig &bot, bool Data) bool LadderManager::GetBot(BotConfig& Agent, const std::string& BotChecksum, const std::string& DataChecksum) { - if (BotChecksum != "" && BotChecksum != Agent.CheckSum) + if (!BotChecksum.empty() && BotChecksum != Agent.CheckSum) { if (!DownloadBot(Agent.BotName, BotChecksum, false)) { PrintThread{} << "Bot download failed, skipping game" << std::endl; - LogNetworkFailiure(Agent.BotName, "Download"); + LogNetworkFailure(Agent.BotName, "Download"); Agent.CheckSum = ""; return false; } } - if (DataChecksum != "") + if (!DataChecksum.empty()) { if (!DownloadBot(Agent.BotName, DataChecksum, true)) { PrintThread{} << "Bot data download failed, skipping game" << std::endl; - LogNetworkFailiure(Agent.BotName, "Download Data"); + LogNetworkFailure(Agent.BotName, "Download Data"); return false; } } @@ -397,35 +353,43 @@ bool LadderManager::GetBot(BotConfig& Agent, const std::string& BotChecksum, con return true; } -bool LadderManager::ConfgureBot(BotConfig& Agent, const std::string& BotId, const std::string& Checksum, const std::string& DataChecksum) -{ - if (Config->GetStringValue("BotDownloadPath") != "") - { - if (Checksum == "" ) - { - PrintThread{} << "No bot checksum found. skipping game" << std::endl; +bool LadderManager::ConfigureBot(BotConfig& Agent, + const std::string& BotId, + const std::string& Checksum, + const std::string& DataChecksum) { + + if (!Config->GetStringValue("BotDownloadPath").empty()) { + if (Checksum.empty()) { + PrintThread{} << "No bot checksum found. Skipping game" << std::endl; return false; } - if (!GetBot(Agent, Checksum, DataChecksum)) - { + + if (!GetBot(Agent, Checksum, DataChecksum)) { return false; } - const std::string BotLocation = Config->GetStringValue("BaseBotDirectory") + "/" + Agent.BotName; - AgentConfig->LoadAgents(BotLocation, BotLocation + "/ladderbots.json"); + + const std::string BotLocation = Config->GetStringValue( + "BaseBotDirectory" + ) + "/" + Agent.BotName; + + AgentConfig->LoadAgents(BotLocation, + BotLocation + "/ladderbots.json"); } + AgentConfig->FindBot(Agent.BotName, Agent); - if (Agent.Skeleton ) - { + + if (Agent.Skeleton) { PrintThread{} << "Unable to download bot " << Agent.BotName << std::endl; return false; } - if (BotId != "") - { + if (!BotId.empty()) { Agent.PlayerId = BotId; } + Agent.CheckSum = Checksum; AgentConfig->SaveBotConfig(Agent); + return true; } @@ -433,68 +397,94 @@ bool LadderManager::ConfgureBot(BotConfig& Agent, const std::string& BotId, cons void LadderManager::RunLadderManager() { AgentConfig = new AgentsConfig(Config); - MatchupList *Matchups = new MatchupList(Config->GetStringValue("MatchupListFile"), AgentConfig, Config->GetArrayValue("Maps"), getSC2Path(), Config->GetStringValue("MatchupGenerator"), Config->GetStringValue("ServerUsername"), Config->GetStringValue("ServerPassword")); + + auto *Matchups = new MatchupList( + Config->GetStringValue("MatchupListFile"), + AgentConfig, + Config->GetArrayValue("Maps"), + getSC2Path(), + Config->GetStringValue("MatchupGenerator"), + Config->GetStringValue("ServerUsername"), + Config->GetStringValue("ServerPassword") + ); + PrintThread{} << "Initialization finished." << std::endl << std::endl; Matchup NextMatch; - try - { - if (EnableServerLogin) - { + + try { + if (EnableServerLogin) { LoginToServer(); } - while (Matchups->GetNextMatchup(NextMatch)) - { + + while (Matchups->GetNextMatchup(NextMatch)) { GameResult result; - PrintThread{} << "Starting " << NextMatch.Agent1.BotName << " vs " << NextMatch.Agent2.BotName << " on " << NextMatch.Map << std::endl; - LadderGame CurrentLadderGame(CoordinatorArgc, CoordinatorArgv, Config); - if (!ConfgureBot(NextMatch.Agent1, NextMatch.Bot1Id, NextMatch.Bot1Checksum, NextMatch.Bot1DataChecksum)) - { - PrintThread{} << "Error configuring bot " << NextMatch.Agent1.BotName << " Skipping game" << std::endl; - continue; - } - if (!ConfgureBot(NextMatch.Agent2, NextMatch.Bot2Id, NextMatch.Bot2Checksum, NextMatch.Bot2DataChecksum)) - { - PrintThread{} << "Error configuring bot " << NextMatch.Agent1.BotName << " Skipping game" << std::endl; - continue; + PrintThread{} << "Starting " << NextMatch.GameName() + << " on " << NextMatch.Map << std::endl; + + LadderGame CurrentLadderGame(CoordinatorArgc, + CoordinatorArgv, + Config); + + for (int i = 0; i < NextMatch.Agents.size(); i++) { + std::string botId = NextMatch.BotIds.size() > i ? NextMatch.BotIds[i] : std::string(); + std::string botChecksum = NextMatch.BotChecksums.size() > i ? NextMatch.BotChecksums[i] : std::string(); + std::string botDataChecksum = NextMatch.BotDataChecksums.size() > i ? NextMatch.BotDataChecksums[i] : std::string(); + + if (!ConfigureBot(NextMatch.Agents[i], + botId, + botChecksum, + botDataChecksum)) { + PrintThread{} << "Error configuring bot " + << NextMatch.Agents[i].BotName + << " Skipping game" << std::endl; + break; + } } - result = CurrentLadderGame.StartGame(NextMatch.Agent1, NextMatch.Agent2, NextMatch.Map); - if (Config->GetStringValue("BotUploadPath") != "") - { - if(!UploadBot(NextMatch.Agent1, true)) - { - LogNetworkFailiure(NextMatch.Agent1.BotName, "Upload"); - } - if(!UploadBot(NextMatch.Agent2, true)) - { - LogNetworkFailiure(NextMatch.Agent2.BotName, "Upload"); + // todo replace with vector argument + result = CurrentLadderGame.StartGame(NextMatch.Agents, NextMatch.Map); + + if (!Config->GetStringValue("BotUploadPath").empty()) { + for (auto & Agent : NextMatch.Agents) { + if(!UploadBot(Agent, true)) { + LogNetworkFailure(Agent.BotName, "Upload"); + } } } - PrintThread{} << "Game finished with result: " << GetResultType(result.Result) << std::endl << std::endl; - if (EnableReplayUploads) - { - UploadCmdLine(result, NextMatch, Config->GetStringValue("UploadResultLocation")); + PrintThread{} << "Game finished with result: " + << GetResultType(result.Result) + << std::endl << std::endl; + + if (EnableReplayUploads) { + UploadCmdLine(result, + NextMatch, + Config->GetStringValue("UploadResultLocation")); } - if (ResultsLogFile.size() > 0) - { - SaveJsonResult(NextMatch.Agent1, NextMatch.Agent2, NextMatch.Map, result); + + if (!ResultsLogFile.empty()) { + SaveJsonResult( + NextMatch.Agents, + NextMatch.Map, + result); } + Matchups->SaveMatchList(); } } catch (const std::exception& e) { - PrintThread{} << "Exception in game " << NextMatch.Agent1.BotName << " vs " << NextMatch.Agent2.BotName << " : " << e.what() << std::endl; - SaveError(NextMatch.Agent1.BotName, NextMatch.Agent2.BotName, NextMatch.Map); + PrintThread{} << "Exception in game " << NextMatch.GameName() + << " : " << e.what() << std::endl; + SaveError(NextMatch.AgentNames(), NextMatch.Map); } } -void LadderManager::LogNetworkFailiure(const std::string &AgentName, const std::string &Action) +void LadderManager::LogNetworkFailure(const std::string &AgentName, const std::string &Action) { std::string ErrorListFile = Config->GetStringValue("ErrorListFile"); - if (ErrorListFile == "") + if (ErrorListFile.empty()) { return; } @@ -506,23 +496,33 @@ void LadderManager::LogNetworkFailiure(const std::string &AgentName, const std:: std::time_t t = std::time(nullptr); std::tm tm = *std::localtime(&t); - ofs << std::put_time(&tm, "%d-%m-%Y %H-%M-%S") << ": " << AgentName + " Failed to " << Action << std::endl; + ofs << std::put_time(&tm, "%d-%m-%Y %H-%M-%S") + << ": " << AgentName + " Failed to " << Action << std::endl; ofs.close(); } -void LadderManager::SaveError(const std::string &Agent1, const std::string &Agent2, const std::string &Map) +void LadderManager::SaveError(const std::vector &AgentNames, + const std::string &Map) { std::string ErrorListFile = Config->GetStringValue("ErrorListFile"); - if (ErrorListFile == "") - { + if (ErrorListFile.empty()) { return; } + std::ofstream ofs(ErrorListFile, std::ofstream::app); - if (!ofs) - { + if (!ofs) { return; } - ofs << "\"" + Agent1 + "\"vs\"" + Agent2 + "\" " + Map << std::endl; + + auto agents = std::accumulate( + std::begin(AgentNames), + std::end(AgentNames), + std::string(" "), + [](const std::string& a, const std::string& b) { + return a + "vs" + b; + }); + + ofs << "\"" + agents + "\" " + Map << std::endl; ofs.close(); } @@ -539,5 +539,6 @@ std::string LadderManager::getSC2Path() const { PrintThread{} << "Error: Could not detect StarCraft II executable at " << process_settings.process_path << std::endl; } + return process_settings.process_path; } diff --git a/src/sc2laddercore/LadderManager.h b/src/sc2laddercore/LadderManager.h index c92a6ce..43d4153 100644 --- a/src/sc2laddercore/LadderManager.h +++ b/src/sc2laddercore/LadderManager.h @@ -16,25 +16,23 @@ class LadderManager LadderManager(int InCoordinatorArgc, char** inCoordinatorArgv); LadderManager(int InCoordinatorArgc, char** inCoordinatorArgv, const char *InConfigFile); bool LoadSetup(); - void SaveJsonResult(const BotConfig & Bot1, const BotConfig & Bot2, const std::string & Map, GameResult Result); + void SaveJsonResult(const std::vector &Agents, const std::string &Map, const GameResult &Result); void RunLadderManager(); - void LogNetworkFailiure(const std::string &Agent1, const std::string &Action); + void LogNetworkFailure(const std::string &AgentName, const std::string &Action); private: - bool IsBotEnabled(std::string BotName); - bool IsInsideEloRange(std::string Bot1Name, std::string Bot2Name); bool DownloadBot(const std::string & BotName, const std::string & checksum, bool Data); - bool VerifyUploadRequest(const std::string & uploadResult); + static bool VerifyUploadRequest(const std::string & uploadResult); bool UploadBot(const BotConfig &bot, bool Data); bool GetBot(BotConfig& Agent, const std::string & BotChecksum, const std::string & DataChecksum); - bool ConfgureBot(BotConfig & Agent, const std::string & BotId, const std::string & Checksum, const std::string & DataChecksum); + bool ConfigureBot(BotConfig & Agent, const std::string & BotId, const std::string & Checksum, const std::string & DataChecksum); bool UploadCmdLine(GameResult result, const Matchup &ThisMatch, std::string UploadLocation); bool LoginToServer(); std::string ResultsLogFile; - void SaveError(const std::string &Agent1, const std::string &Agent2, const std::string &Map); + void SaveError(const std::vector &AgentNames, const std::string &Map); bool IsValidResult(GameResult Result); std::string getSC2Path() const; diff --git a/src/sc2laddercore/MatchupList.cpp b/src/sc2laddercore/MatchupList.cpp index 70df02e..f586e63 100644 --- a/src/sc2laddercore/MatchupList.cpp +++ b/src/sc2laddercore/MatchupList.cpp @@ -1,23 +1,16 @@ -#include "sc2lib/sc2_lib.h" -#include "sc2api/sc2_api.h" #include "sc2utils/sc2_manage_process.h" #include -#include -#include +#include #include #include #include #include -#include #define RAPIDJSON_HAS_STDSTRING 1 -#include "rapidjson.h" - #include "Types.h" -#include "LadderManager.h" #include "MatchupList.h" #include "Tools.h" #include "AgentsConfig.h" @@ -49,7 +42,7 @@ MatchupList::MatchupList(const std::string &inMatchupListFile, AgentsConfig *InA bool MatchupList::GenerateMatches(std::vector &&maps) { Matchups.clear(); - PrintThread{} << "Found agents: " << std::endl; + PrintThread{} << "Found Agents: " << std::endl; for (const auto &Agent : AgentConfig->BotConfigs) { PrintThread{} << "* " << Agent.second.BotName << std::endl; @@ -73,36 +66,18 @@ bool MatchupList::GenerateMatches(std::vector &&maps) PrintThread{} << "Please put the map files in '" << sc2::GetGameMapsDirectory(sc2Path) << "'." << std::endl; } maps.erase(firstInvalidMapIt,maps.end()); - if (MatchUpProcess == MatchupListType::URL) - { - return true; + + const std::vector &bots = AgentConfig->Bots(); + + for (const std::string &map : maps) { + Matchup NextMatchup(bots, map); + Matchups.push_back(NextMatchup); } - if (!LoadMatchupList()) - { - PrintThread{} << "Could not load MatchupList from file. Generating new one.." << std::endl; - for (const auto &Agent1 : AgentConfig->BotConfigs) - { - for (const auto &Agent2 : AgentConfig->BotConfigs) - { - if (Agent1.first == Agent2.first) - { - continue; - } - for (std::string map : maps) - { - Matchup NextMatchup(Agent1.second, Agent2.second, map); - Matchups.push_back(NextMatchup); - } - } - } - srand(time(0)); - std::random_shuffle(std::begin(Matchups), std::end(Matchups)); - return true; - } - else - { - PrintThread{} << "MatchupList loaded from file with " << Matchups.size() << " matches to go." << std::endl; - } + + std::random_device rd; + std::default_random_engine rng(rd()); + std::shuffle(std::begin(Matchups), std::end(Matchups), rng); + return true; } @@ -112,29 +87,19 @@ bool MatchupList::GetNextMatchup(Matchup &NextMatch) { case MatchupListType::File: { - if (MatchUpProcess == MatchupListType::File) - { - if (Matchups.empty()) - { - return false; - } - NextMatch = Matchups.back(); - Matchups.pop_back(); - return true; - } - break; - } - case MatchupListType::URL: - { - - return GetNextMatchFromURL(NextMatch); + if (Matchups.empty()) + { + return false; + } + NextMatch = Matchups.back(); + Matchups.pop_back(); + return true; } default: { return false; } } - return false; } bool MatchupList::SaveMatchList() @@ -146,153 +111,9 @@ bool MatchupList::SaveMatchList() } for (Matchup &NextMatch : Matchups) { - ofs << "\"" + NextMatch.Agent1.BotName + "\"vs\"" + NextMatch.Agent2.BotName + "\" " + NextMatch.Map << std::endl; + ofs << "\"" + NextMatch.GameName() + "\" " + NextMatch.Map << std::endl; } ofs.close(); return true; } - -bool MatchupList::LoadMatchupList() -{ - std::ifstream ifs(MatchupListFile); - if (!ifs.good()) - { - return false; - } - Matchups.clear(); - std::string line; - while (std::getline(ifs, line)) - { - size_t p = line.find_first_not_of("\""); - line.erase(0, p); - p = line.find_first_of("\""); - std::string FirstAgent = line.substr(0, p); - line.erase(0, p + 1); - p = line.find_first_of("\""); - line.erase(0, p + 1); - p = line.find_first_of("\""); - std::string SecondAgent = line.substr(0, p); - line.erase(0, p + 1); - p = line.find_first_not_of(" "); - line.erase(0, p); - p = line.find_last_not_of(" \t\r\n"); - std::string Map = line.substr(0, p + 1); - PrintThread{} << "Creating match: " + FirstAgent + " vs " + SecondAgent + " on " + Map << std::endl; - BotConfig Agent2, Agent1; - if (!AgentConfig->FindBot(FirstAgent, Agent1)) - { - PrintThread{} << "Unable to find agent: " + FirstAgent << std::endl; - continue; - } - if (!AgentConfig->FindBot(SecondAgent, Agent2)) - { - PrintThread{} << "Unable to find agent: " + SecondAgent << std::endl; - continue; - } - if (!isMapAvailable(Map,sc2Path)) - { - PrintThread{} << "Unable to find map: " + Map << std::endl; - continue; - } - Matchup NextMatchup(Agent1, Agent2, Map); - Matchups.push_back(NextMatchup); - - } - return true; -} - -bool MatchupList::GetNextMatchFromURL(Matchup &NextMatch) -{ - std::vector arguments; - std::string argument = " -F Username=" + ServerUsername; - arguments.push_back(argument); - argument = " -F Password=" + ServerPassword; - arguments.push_back(argument); - std::string ReturnString = PerformRestRequest(MatchupListFile, arguments); -// ReturnString = "{\"Bot1\":{\"name\":\"Lambdanaut\", \"race\" : \"Zerg\", \"elo\" : \"1270\", \"playerid\" : \"ioa874jd\", \"checksum\" : \"8f10769e137259b23a73e0f1aea2c503\"}, \"Bot2\" : {\"name\":\"VeTerran\", \"race\" : \"Terran\", \"elo\" : \"1120\", \"playerid\" : \"sd9836f\", \"checksum\" : \"0a748c62d21fa8d2d412489d651a63d1\"}, \"Map\" : \"ParaSiteLE.SC2Map\"}"; - - rapidjson::Document doc; - bool parsingFailed = doc.Parse(ReturnString.c_str()).HasParseError(); - if (parsingFailed) - { - std::cerr << "Unable to parse incoming bot config: " << ReturnString << std::endl; - return false; - } - if (doc.HasMember("Bot1") && doc["Bot1"].IsObject()) - { - const rapidjson::Value &Bot1Value = doc["Bot1"]; - if (Bot1Value.HasMember("name") && Bot1Value["name"].IsString()) - { - if(!AgentConfig->FindBot(Bot1Value["name"].GetString(), NextMatch.Agent1)) - { - BotConfig Agent1; - Agent1.BotName = Bot1Value["name"].GetString(); - Agent1.Skeleton = true; - NextMatch.Agent1 = Agent1; - if (Bot1Value.HasMember("playerid") && Bot1Value["playerid"].IsString()) - { - NextMatch.Bot1Id = Bot1Value["playerid"].GetString(); - Agent1.PlayerId = Bot1Value["playerid"].GetString(); - } - } - } - if (Bot1Value.HasMember("playerid") && Bot1Value["playerid"].IsString()) - { - NextMatch.Bot1Id = Bot1Value["playerid"].GetString(); - } - if (Bot1Value.HasMember("checksum") && Bot1Value["checksum"].IsString()) - { - NextMatch.Bot1Checksum = Bot1Value["checksum"].GetString(); - } - if (Bot1Value.HasMember("datachecksum") && Bot1Value["datachecksum"].IsString()) - { - NextMatch.Bot1DataChecksum = Bot1Value["datachecksum"].GetString(); - } - - } - else - { - return false; - } - if (doc.HasMember("Bot2") && doc["Bot2"].IsObject()) - { - const rapidjson::Value &Bot2Value = doc["Bot2"]; - if (Bot2Value.HasMember("name") && Bot2Value["name"].IsString()) - { - if (!AgentConfig->FindBot(Bot2Value["name"].GetString(), NextMatch.Agent2)) - { - BotConfig Agent2; - Agent2.BotName = Bot2Value["name"].GetString(); - Agent2.Skeleton = true; - NextMatch.Agent2 = Agent2; - } - } - if (Bot2Value.HasMember("playerid") && Bot2Value["playerid"].IsString()) - { - NextMatch.Bot2Id = Bot2Value["playerid"].GetString(); - } - if (Bot2Value.HasMember("checksum") && Bot2Value["checksum"].IsString()) - { - NextMatch.Bot2Checksum = Bot2Value["checksum"].GetString(); - } - if (Bot2Value.HasMember("datachecksum") && Bot2Value["datachecksum"].IsString()) - { - NextMatch.Bot2DataChecksum = Bot2Value["datachecksum"].GetString(); - } - } - else - { - return false; - } - if (doc.HasMember("Map") && doc["Map"].IsString()) - { - NextMatch.Map = doc["Map"].GetString(); - if (!isMapAvailable(NextMatch.Map,sc2Path)) - { - PrintThread{} << "Unable to find map: " + NextMatch.Map << std::endl; - return false; - } - } - return true; -} diff --git a/src/sc2laddercore/MatchupList.h b/src/sc2laddercore/MatchupList.h index 8c1c9c1..9d3bef2 100644 --- a/src/sc2laddercore/MatchupList.h +++ b/src/sc2laddercore/MatchupList.h @@ -16,8 +16,6 @@ class MatchupList const std::string MatchupListFile; std::vector Matchups; AgentsConfig *AgentConfig; - bool LoadMatchupList(); - bool GetNextMatchFromURL(Matchup &NextMatch); const std::string sc2Path{""}; MatchupListType MatchUpProcess; diff --git a/src/sc2laddercore/Proxy.cpp b/src/sc2laddercore/Proxy.cpp index 97a4c89..2f06da3 100644 --- a/src/sc2laddercore/Proxy.cpp +++ b/src/sc2laddercore/Proxy.cpp @@ -15,7 +15,7 @@ Proxy::Proxy(const uint32_t maxGameLoops, const uint32_t maxRealGameTime, const m_maxGameLoops(maxGameLoops) , m_maxRealGameTime(maxRealGameTime) , m_botConfig(botConfig) -{ +{ m_lastResponseSendTime = clock::now(); } @@ -98,7 +98,8 @@ bool Proxy::ConnectToSC2Instance(const sc2::ProcessSettings& processSettings, co } // Technically, we only need opponents race. But I think it looks clearer on the caller side with both races. -bool Proxy::setupGame(const sc2::ProcessSettings& processSettings, const std::string& map, const bool realTimeMode, const sc2::Race bot1Race, const sc2::Race bot2Race) +bool Proxy::setupGame(const sc2::ProcessSettings &processSettings, const std::string &map, const bool realTimeMode, + const std::vector &races) { m_realTimeMode = realTimeMode; // Only one client needs to / is allowed to send the create game request. @@ -111,17 +112,13 @@ bool Proxy::setupGame(const sc2::ProcessSettings& processSettings, const std::st SC2APIProtocol::RequestCreateGame* requestCreateGame = request->mutable_create_game(); - // Player 1 - SC2APIProtocol::PlayerSetup* playerSetup = requestCreateGame->add_player_setup(); - playerSetup->set_type(SC2APIProtocol::PlayerType::Participant); - playerSetup->set_race(SC2APIProtocol::Race(static_cast(bot1Race) + 1)); // Ugh - playerSetup->set_difficulty(SC2APIProtocol::Difficulty::VeryEasy); - - // Player 2 - playerSetup = requestCreateGame->add_player_setup(); - playerSetup->set_type(SC2APIProtocol::PlayerType::Participant); - playerSetup->set_race(SC2APIProtocol::Race(static_cast(bot2Race) + 1)); - playerSetup->set_difficulty(SC2APIProtocol::Difficulty::VeryEasy); + // setup players + for (const auto & race : races) { + SC2APIProtocol::PlayerSetup* playerSetup = requestCreateGame->add_player_setup(); + playerSetup->set_type(SC2APIProtocol::PlayerType::Participant); + playerSetup->set_race(SC2APIProtocol::Race(static_cast(race) + 1)); // Ugh + playerSetup->set_difficulty(SC2APIProtocol::Difficulty::VeryEasy); + } // Map // BattleNet map @@ -134,12 +131,9 @@ bool Proxy::setupGame(const sc2::ProcessSettings& processSettings, const std::st // Local map file SC2APIProtocol::LocalMap* const localMap = requestCreateGame->mutable_local_map(); // Absolute path - if (sc2::DoesFileExist(map)) - { + if (sc2::DoesFileExist(map)) { localMap->set_map_path(map); - } - else - { + } else { // Relative path - Game maps directory const std::string gameRelative = sc2::GetGameMapsDirectory(processSettings.process_path) + map; if (sc2::DoesFileExist(gameRelative)) @@ -280,12 +274,17 @@ bool Proxy::createGameHasErrors(const SC2APIProtocol::ResponseCreateGame& create std::string Proxy::getBotCommandLine(const int gamePort, const int startPort, const std::string& opponentID) const { // Add universal arguments - std::string ReturnCmd = m_botConfig.executeCommand + " --GamePort " + std::to_string(gamePort) + " --StartPort " + std::to_string(startPort) + " --LadderServer " + m_localHost + " --OpponentId " + opponentID; - if (m_realTimeMode) - { - ReturnCmd += " --RealTime"; - } - return ReturnCmd; + std::string ReturnCmd = m_botConfig.executeCommand + + " --GamePort " + std::to_string(gamePort) + + " --StartPort " + std::to_string(startPort) + + " --LadderServer " + m_localHost + + " --OpponentId " + opponentID + + " --ForceRace " + GetRaceString(m_botConfig.Race); + if (m_realTimeMode) + { + ReturnCmd += " --RealTime"; + } + return ReturnCmd; } diff --git a/src/sc2laddercore/Proxy.h b/src/sc2laddercore/Proxy.h index f74fb6f..17c05e5 100644 --- a/src/sc2laddercore/Proxy.h +++ b/src/sc2laddercore/Proxy.h @@ -76,7 +76,8 @@ class Proxy bool ConnectToSC2Instance(const sc2::ProcessSettings & processSettings, const int portServer, const int portClient); void startSC2Instance(const sc2::ProcessSettings& processSettings, const int portServer, const int portClient); - bool setupGame(const sc2::ProcessSettings& processSettings, const std::string& map, const bool realTimeMode, const sc2::Race bot1Race, const sc2::Race bot2Race); + bool setupGame(const sc2::ProcessSettings &processSettings, const std::string &map, const bool realTimeMode, + const std::vector &races); bool startBot(const int portServer, const int portStart, const std::string & opponentPlayerId); void startGame(); diff --git a/src/sc2laddercore/ToolsUnix.cpp b/src/sc2laddercore/ToolsUnix.cpp index 17778fc..d5b2ddc 100644 --- a/src/sc2laddercore/ToolsUnix.cpp +++ b/src/sc2laddercore/ToolsUnix.cpp @@ -66,12 +66,12 @@ void StartBotProcess(const BotConfig &Agent, const std::string &CommandLine, uns exit(errno); } - if (RedirectOutput(Agent, STDERR_FILENO, "data/stderr.log") < 0) + if (RedirectOutput(Agent, STDERR_FILENO, "data/stderr-" + Agent.PlayerId + ".log") < 0) exit(errno); if (Agent.Debug) { - if (RedirectOutput(Agent, STDOUT_FILENO, "data/stdout.log") < 0) + if (RedirectOutput(Agent, STDOUT_FILENO, "data/stdout" + Agent.PlayerId + ".log") < 0) exit(errno); } else diff --git a/src/sc2laddercore/ToolsWindows.cpp b/src/sc2laddercore/ToolsWindows.cpp index b0d9920..a2ac9f7 100644 --- a/src/sc2laddercore/ToolsWindows.cpp +++ b/src/sc2laddercore/ToolsWindows.cpp @@ -16,7 +16,7 @@ void StartBotProcess(const BotConfig &Agent, const std::string &CommandLine, uns securityAttributes.nLength = sizeof(securityAttributes); securityAttributes.lpSecurityDescriptor = NULL; securityAttributes.bInheritHandle = TRUE; - std::string stderrLogFile = Agent.RootPath + "/data/stderr.log"; + std::string stderrLogFile = Agent.RootPath + "/data/stderr-" + Agent.PlayerId + ".log"; HANDLE stderrfile = CreateFile(stderrLogFile.c_str(), FILE_APPEND_DATA, FILE_SHARE_WRITE | FILE_SHARE_READ, @@ -28,7 +28,7 @@ void StartBotProcess(const BotConfig &Agent, const std::string &CommandLine, uns HANDLE stdoutfile = NULL; if (Agent.Debug) { - std::string stdoutFile = Agent.RootPath + "/data/stdout.log"; + std::string stdoutFile = Agent.RootPath + "/data/stdout-" + Agent.PlayerId + ".log"; stdoutfile = CreateFile(stdoutFile.c_str(), FILE_APPEND_DATA, FILE_SHARE_WRITE | FILE_SHARE_READ, diff --git a/src/sc2laddercore/Types.h b/src/sc2laddercore/Types.h index fcf294a..deeb494 100644 --- a/src/sc2laddercore/Types.h +++ b/src/sc2laddercore/Types.h @@ -6,6 +6,7 @@ #include #include #include +#include class PrintThread : public std::ostringstream { @@ -41,12 +42,7 @@ enum class ResultType InitializationError, Timeout, ProcessingReplay, - Player1Win, - Player1Crash, - Player1TimeOut, - Player2Win, - Player2Crash, - Player2TimeOut, + Win, Tie, Error }; @@ -54,7 +50,6 @@ enum class ResultType enum MatchupListType { File, - URL, None }; @@ -86,18 +81,18 @@ struct GameState struct GameResult { ResultType Result; - float Bot1AvgFrame; - float Bot2AvgFrame; + std::vector AvgFrames; uint32_t GameLoop; std::string TimeStamp; + std::string Winner; + GameResult() : Result(ResultType::InitializationError) - , Bot1AvgFrame(0) - , Bot2AvgFrame(0) , GameLoop(0) , TimeStamp("") + , Winner("") + , AvgFrames{} {} - }; struct BotConfig @@ -148,25 +143,37 @@ struct BotConfig struct Matchup { - BotConfig Agent1; - BotConfig Agent2; - std::string Bot1Id; - std::string Bot1Checksum; - std::string Bot1DataChecksum; - std::string Bot2Id; - std::string Bot2Checksum; - std::string Bot2DataChecksum; + + std::vector Agents; // agents, ids and checksums size should be equal + std::vector BotIds; + std::vector BotChecksums; + std::vector BotDataChecksums; + std::string Map; + Matchup() {} - Matchup(const BotConfig &InAgent1, const BotConfig &InAgent2, const std::string &InMap) - : Agent1(InAgent1), - Agent2(InAgent2), - Map(InMap) - { - } + Matchup(const std::vector Agents, const std::string &InMap): + Map(InMap), + Agents(Agents) {} + +public: + std::string ReplayName() const { + return std::string(); + } + std::string GameName() const { + return std::string(); + } + + std::vector AgentNames() const { + std::vector agentNames = std::vector(); + for (const auto & bot : Agents) { + agentNames.push_back(bot.BotName); + } + return agentNames; + } }; static MatchupListType GetMatchupListTypeFromString(const std::string GeneratorType) @@ -174,11 +181,7 @@ static MatchupListType GetMatchupListTypeFromString(const std::string GeneratorT std::string type(GeneratorType); std::transform(type.begin(), type.end(), type.begin(), ::tolower); - if (type == "url") - { - return MatchupListType::URL; - } - else if (type == "file") + if (type == "file") { return MatchupListType::File; } @@ -400,27 +403,12 @@ static std::string GetResultType(ResultType InResultType) case ResultType::ProcessingReplay: return "ProcessingReplay"; - case ResultType::Player1Win: - return "Player1Win"; - - case ResultType::Player1Crash: - return "Player1Crash"; - - case ResultType::Player1TimeOut: - return "Player1TimeOut"; - - case ResultType::Player2Win: - return "Player2Win"; - - case ResultType::Player2Crash: - return "Player2Crash"; - - case ResultType::Player2TimeOut: - return "Player2TimeOut"; - case ResultType::Tie: return "Tie"; + case ResultType::Win: + return "Win"; + default: return "Error"; } @@ -436,42 +424,6 @@ static std::string RemoveMapExtension(const std::string& filename) return filename.substr(0, lastdot); } -static ResultType getEndResultFromProxyResults(const ExitCase resultBot1, const ExitCase resultBot2) -{ - if ((resultBot1 == ExitCase::BotCrashed && resultBot2 == ExitCase::BotCrashed) - || (resultBot1 == ExitCase::BotStepTimeout && resultBot2 == ExitCase::BotStepTimeout) - || (resultBot1 == ExitCase::Error && resultBot2 == ExitCase::Error)) - { - // If both bots crashed we assume the bots are not the problem. - return ResultType::Error; - } - if (resultBot1 == ExitCase::BotCrashed) - { - return ResultType::Player1Crash; - } - if (resultBot2 == ExitCase::BotCrashed) - { - return ResultType::Player2Crash; - } - if (resultBot1 == ExitCase::GameEndVictory && (resultBot2 == ExitCase::GameEndDefeat || resultBot2 == ExitCase::BotStepTimeout || resultBot2 == ExitCase::Error)) - { - return ResultType::Player1Win; - } - if (resultBot2 == ExitCase::GameEndVictory && (resultBot1 == ExitCase::GameEndDefeat || resultBot1 == ExitCase::BotStepTimeout || resultBot1 == ExitCase::Error)) - { - return ResultType::Player2Win; - } - if (resultBot1 == ExitCase::GameEndTie && resultBot2 == ExitCase::GameEndTie) - { - return ResultType::Tie; - } - if (resultBot1 == ExitCase::GameTimeOver && resultBot2 == ExitCase::GameTimeOver) - { - return ResultType::Timeout; - } - return ResultType::Error; -} - static std::string statusToString(SC2APIProtocol::Status status) { switch (status)