From 9c73a070b79ab043af3153c90799fa271a726d4f Mon Sep 17 00:00:00 2001 From: jeefo Date: Thu, 13 Apr 2023 03:05:35 +0300 Subject: [PATCH] aim: do not reduce prediction for grenades refactor: standardize access to bot files graph: allow to specify graph upload url via cvar graph. do not spam if extensions files are not found --- cfg/addons/yapb/conf/lang/de_lang.cfg | 2 +- cfg/addons/yapb/conf/lang/ru_lang.cfg | 2 +- inc/graph.h | 13 ++-- inc/support.h | 16 +++++ inc/yapb.h | 1 + src/botlib.cpp | 8 +-- src/combat.cpp | 20 +++--- src/control.cpp | 7 +- src/engine.cpp | 2 +- src/graph.cpp | 98 +++++++++++---------------- src/support.cpp | 77 +++++++++++++++++++++ 11 files changed, 160 insertions(+), 86 deletions(-) diff --git a/cfg/addons/yapb/conf/lang/de_lang.cfg b/cfg/addons/yapb/conf/lang/de_lang.cfg index 1ba1196..9bd170a 100644 --- a/cfg/addons/yapb/conf/lang/de_lang.cfg +++ b/cfg/addons/yapb/conf/lang/de_lang.cfg @@ -446,7 +446,7 @@ Successfully saved Bots %s data. Bot %s Daten wurden erfolgreich gespeichert. [ORIGINAL] -Successfully loaded Bots %s data v%d (%d/%.2fMB). +Loaded Bots %s data v%d (%d/%.2fMB). [TRANSLATED] Bot %s Daten v%d wurden erfolgreich geladen (%d/%.2fMB). diff --git a/cfg/addons/yapb/conf/lang/ru_lang.cfg b/cfg/addons/yapb/conf/lang/ru_lang.cfg index 64ee069..e388d9f 100644 --- a/cfg/addons/yapb/conf/lang/ru_lang.cfg +++ b/cfg/addons/yapb/conf/lang/ru_lang.cfg @@ -544,7 +544,7 @@ Successfully saved Bots %s data. Успешно сохранены данные %s ботов. [ORIGINAL] -Successfully loaded Bots %s data v%d (%d/%.2fMB). +Loaded Bots %s data v%d (%d/%.2fMB). [TRANSLATED] Успешно загружены данные %s ботов v%d (%d/%.2fМБ). diff --git a/inc/graph.h b/inc/graph.h index 4ba137c..caa6b11 100644 --- a/inc/graph.h +++ b/inc/graph.h @@ -343,8 +343,8 @@ public: bool loadGraphData (); bool canDownload (); - template bool saveStorage (StringRef ext, StringRef name, StorageOption options, StorageVersion version, const SmallArray &data, ExtenHeader *exten); - template bool loadStorage (StringRef ext, StringRef name, StorageOption options, StorageVersion version, SmallArray &data, ExtenHeader *exten, int32_t *outOptions); + template bool saveStorage (StringRef name, StorageOption options, StorageVersion version, const SmallArray &data, ExtenHeader *exten); + template bool loadStorage (StringRef name, StorageOption options, StorageVersion version, SmallArray &data, ExtenHeader *exten, int32_t *outOptions); template bool raiseLoadingError (bool isGraph, MemFile &file, const char *fmt, Args &&...args); void saveOldFormat (); @@ -389,9 +389,6 @@ public: void showStats (); void showFileInfo (); - const char *getDataDirectory (bool isMemoryFile = false); - const char *getOldFormatGraphName (bool isMemoryFile = false); - IntArray getNarestInRadius (float radius, const Vector &origin, int maxCount = -1); const IntArray &getNodesInBucket (const Vector &pos); @@ -490,7 +487,11 @@ public: // helper for reporting load errors template bool BotGraph::raiseLoadingError (bool isGraph, MemFile &file, const char *fmt, Args &&...args) { auto result = strings.format (fmt, cr::forward (args)...); - logger.error (result); + + // display error only for graph file + if (isGraph || cv_debug.bool_ ()) { + logger.error (result); + } // if graph reset paths if (isGraph) { diff --git a/inc/support.h b/inc/support.h index e6ede2f..4c0d825 100644 --- a/inc/support.h +++ b/inc/support.h @@ -7,6 +7,16 @@ #pragma once +CR_DECLARE_SCOPED_ENUM_TYPE (BotFile, uint32_t, + Vistable = 0, + LogFile = 1, + Practice = 2, + Graph = 3, + Pathmatrix = 4, + PodbotPWF = 5, + EbotEWP = 6 +) + class BotSupport final : public Singleton { private: bool m_needToSendWelcome {}; @@ -96,6 +106,12 @@ public: // get the current date and time as string String getCurrentDateTime (); + // builds the filename to requested filename + String buildPath (int32_t type, bool isMemoryLoad = false); + + // converts storage option to stroage filename + int32_t storageToBotFile (StorageOption options); + public: // re-show welcome after changelevel ? diff --git a/inc/yapb.h b/inc/yapb.h index 26e2167..fe1d74e 100644 --- a/inc/yapb.h +++ b/inc/yapb.h @@ -1211,6 +1211,7 @@ extern ConVar cv_debug_goal; extern ConVar cv_save_bots_names; extern ConVar cv_random_knife_attacks; extern ConVar cv_rotate_bots; +extern ConVar cv_graph_url_upload; extern ConVar mp_freezetime; extern ConVar mp_roundtime; diff --git a/src/botlib.cpp b/src/botlib.cpp index e22d258..2ebe55d 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -3992,7 +3992,7 @@ void Bot::throwExplosive_ () { m_moveToGoal = false; } else if (!(m_states & Sense::SuspectEnemy) && !game.isNullEntity (m_enemy)) { - dest = m_enemy->v.origin + m_enemy->v.velocity.get2d () * 0.6f; + dest = m_enemy->v.origin + m_enemy->v.velocity.get2d (); } m_isUsingGrenade = true; m_checkTerrain = false; @@ -4058,7 +4058,7 @@ void Bot::throwFlashbang_ () { m_moveToGoal = false; } else if (!(m_states & Sense::SuspectEnemy) && !game.isNullEntity (m_enemy)) { - dest = m_enemy->v.origin + m_enemy->v.velocity.get2d () * 0.6f; + dest = m_enemy->v.origin + m_enemy->v.velocity.get2d (); } m_isUsingGrenade = true; @@ -4129,9 +4129,9 @@ void Bot::throwSmoke_ () { Vector src = m_lastEnemyOrigin - pev->velocity; - // predict where the enemy is in 0.5 secs + // predict where the enemy is in secs if (!game.isNullEntity (m_enemy)) { - src = src + m_enemy->v.velocity * 0.6f; + src = src + m_enemy->v.velocity; } m_grenade = (src - getEyesPos ()).normalize (); diff --git a/src/combat.cpp b/src/combat.cpp index 3d6f693..ad08d55 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -252,7 +252,6 @@ bool Bot::lookupEnemies () { newEnemy = player; } } - auto distanceScale = usesKnife () ? 1.0f : 0.75f; // the old enemy is no longer visible or if (game.isNullEntity (newEnemy)) { @@ -279,7 +278,7 @@ bool Bot::lookupEnemies () { float scaleFactor = (1.0f / calculateScaleFactor (intresting)); float distance = intresting->v.origin.distanceSq (pev->origin) * scaleFactor; - if (distance * distanceScale < nearestDistance) { + if (distance < nearestDistance) { nearestDistance = distance; newEnemy = intresting; } @@ -312,7 +311,7 @@ bool Bot::lookupEnemies () { } float distance = player->v.origin.distanceSq (pev->origin); - if (distance * distanceScale < nearestDistance) { + if (distance < nearestDistance) { nearestDistance = distance; newEnemy = player; @@ -602,6 +601,8 @@ bool Bot::isFriendInLineOfFire (float distance) { } } + // doesn't seems to be have any effect +#if 0 // search the world for players for (const auto &client : util.getClients ()) { if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.team != m_team || client.ent == ent ()) { @@ -613,6 +614,7 @@ bool Bot::isFriendInLineOfFire (float distance) { return true; } } +#endif return false; } @@ -1934,12 +1936,9 @@ void Bot::checkGrenadesThrow () { allowThrowing = false; } else { - float radius = m_lastEnemy->v.velocity.length2d (); - const Vector &pos = (m_lastEnemy->v.velocity * 0.5f).get2d () + m_lastEnemy->v.origin; + float radius = cr::max (192.0f, m_lastEnemy->v.velocity.length2d ()); + const Vector &pos = m_lastEnemy->v.velocity.get2d () + m_lastEnemy->v.origin; - if (radius < 164.0f) { - radius = 164.0f; - } auto predicted = graph.getNarestInRadius (radius, pos, 12); if (predicted.empty ()) { @@ -1980,9 +1979,8 @@ void Bot::checkGrenadesThrow () { } break; - case Weapon::Flashbang: - { - int nearest = graph.getNearest ((m_lastEnemy->v.velocity * 0.5f).get2d () + m_lastEnemy->v.origin); + case Weapon::Flashbang: { + int nearest = graph.getNearest (m_lastEnemy->v.velocity.get2d () + m_lastEnemy->v.origin); if (nearest != kInvalidNodeIndex) { m_throw = graph[nearest].origin; diff --git a/src/control.cpp b/src/control.cpp index 2b3ecba..43b53f4 100644 --- a/src/control.cpp +++ b/src/control.cpp @@ -759,11 +759,12 @@ int BotControl::cmdNodeUpload () { msg ("you may notice the game freezes a bit during upload and issue request creation. Please, be patient."); msg ("\n"); - // upload everytime in lowercase - String mapName = game.getMapName (); + String uploadUrl = cv_graph_url_upload.str (); + + game.print ("%s", uploadUrl); // try to upload the file - if (http.uploadFile ("http://yapb.jeefo.net/upload", strings.format ("%sgraph/%s.graph", graph.getDataDirectory (false), mapName.lowercase ()))) { + if (http.uploadFile (uploadUrl, util.buildPath (BotFile::Graph))) { msg ("Graph file was successfully validated and uploaded to the YaPB Graph DB (%s).", product.download); msg ("It will be available for download for all YaPB users in a few minutes."); msg ("\n"); diff --git a/src/engine.cpp b/src/engine.cpp index f1617d2..db439db 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -834,7 +834,7 @@ bool Game::loadCSBinary () { bool Game::postload () { // register logger - logger.initialize (strings.format ("%slogs/%s.log", graph.getDataDirectory (false), product.folder), [] (const char *msg) { + logger.initialize (util.buildPath (BotFile::LogFile), [] (const char *msg) { game.print (msg); }); diff --git a/src/graph.cpp b/src/graph.cpp index 6ab5777..b3044b4 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -9,6 +9,7 @@ ConVar cv_graph_fixcamp ("yb_graph_fixcamp", "0", "Specifies whether bot should not 'fix' camp directions of camp waypoints when loading old PWF format."); ConVar cv_graph_url ("yb_graph_url", product.download.chars (), "Specifies the URL from bots will be able to download graph in case of missing local one. Set to empty, if no downloads needed.", false, 0.0f, 0.0f); +ConVar cv_graph_url_upload ("yb_graph_url_upload", "http://yapb.jeefo.net/upload", "Specifies the URL to bots will try to upload te graph file to database.", false, 0.0f, 0.0f); ConVar cv_graph_auto_save_count ("yb_graph_auto_save_count", "15", "Every N graph nodes placed on map, the graph will be saved automatically (without checks).", true, 0.0f, kMaxNodes); ConVar cv_graph_draw_distance ("yb_graph_draw_distance", "400", "Maximum distance to draw graph nodes from editor viewport.", true, 64.0f, 3072.0f); @@ -1270,7 +1271,7 @@ void BotGraph::loadPractice () { m_highestDamage[team] = 1; } - bool dataLoaded = loadStorage ("prc", "Practice", StorageOption::Practice, StorageVersion::Practice, m_practice, nullptr, nullptr); + bool dataLoaded = loadStorage ("Practice", StorageOption::Practice, StorageVersion::Practice, m_practice, nullptr, nullptr); int count = length (); // set's the highest damage if loaded ok @@ -1308,7 +1309,7 @@ void BotGraph::savePractice () { if (m_paths.empty () || m_hasChanged) { return; } - saveStorage ("prc", "Practice", StorageOption::Practice, StorageVersion::Practice, m_practice, nullptr); + saveStorage ("Practice", StorageOption::Practice, StorageVersion::Practice, m_practice, nullptr); } void BotGraph::loadVisibility () { @@ -1317,7 +1318,7 @@ void BotGraph::loadVisibility () { if (m_paths.empty ()) { return; } - bool dataLoaded = loadStorage ("vis", "Visibility", StorageOption::Vistable, StorageVersion::Vistable, m_vistable, nullptr, nullptr); + bool dataLoaded = loadStorage ("Visibility", StorageOption::Vistable, StorageVersion::Vistable, m_vistable, nullptr, nullptr); // if loaded, do not recalculate visibility if (dataLoaded) { @@ -1329,14 +1330,14 @@ void BotGraph::saveVisibility () { if (m_paths.empty () || m_hasChanged || m_needsVisRebuild) { return; } - saveStorage ("vis", "Visibility", StorageOption::Vistable, StorageVersion::Vistable, m_vistable, nullptr); + saveStorage ("Visibility", StorageOption::Vistable, StorageVersion::Vistable, m_vistable, nullptr); } bool BotGraph::loadPathMatrix () { if (m_paths.empty ()) { return false; } - bool dataLoaded = loadStorage ("pmx", "Pathmatrix", StorageOption::Matrix, StorageVersion::Matrix, m_matrix, nullptr, nullptr); + bool dataLoaded = loadStorage ("Pathmatrix", StorageOption::Matrix, StorageVersion::Matrix, m_matrix, nullptr, nullptr); // do not rebuild if loaded if (dataLoaded) { @@ -1387,7 +1388,7 @@ void BotGraph::savePathMatrix () { if (m_paths.empty ()) { return; } - saveStorage ("pmx", "Pathmatrix", StorageOption::Matrix, StorageVersion::Matrix, m_matrix, nullptr); + saveStorage ("Pathmatrix", StorageOption::Matrix, StorageVersion::Matrix, m_matrix, nullptr); } void BotGraph::initLightLevels () { @@ -1533,7 +1534,13 @@ void BotGraph::initNodesTypes () { } bool BotGraph::convertOldFormat () { - MemFile fp (getOldFormatGraphName (true)); + MemFile fp (util.buildPath (BotFile::PodbotPWF, true)); + + if (!fp) { + if (!fp.open (util.buildPath (BotFile::EbotEWP, true))) { + return false; + } + } PODGraphHeader header {}; plat.bzero (&header, sizeof (header)); @@ -1606,7 +1613,7 @@ bool BotGraph::convertOldFormat () { return false; } -template bool BotGraph::saveStorage (StringRef ext, StringRef name, StorageOption options, StorageVersion version, const SmallArray &data, ExtenHeader *exten) { +template bool BotGraph::saveStorage (StringRef name, StorageOption options, StorageVersion version, const SmallArray &data, ExtenHeader *exten) { bool isGraph = !!(options & StorageOption::Graph); // do not allow to save graph with less than 8 nodes @@ -1614,9 +1621,7 @@ template bool BotGraph::saveStorage (StringRef ext, StringRef name, ctrl.msg ("Can't save graph data with less than %d nodes. Please add some more before saving.", kMaxNodeLinks); return false; } - - String filename; - filename.assignf ("%s.%s", game.getMapName (), ext).lowercase (); + String filename = util.buildPath (util.storageToBotFile (options)); if (data.empty ()) { logger.error ("Unable to save %s file. Empty data. (filename: '%s').", name, filename); @@ -1630,7 +1635,7 @@ template bool BotGraph::saveStorage (StringRef ext, StringRef name, } // open the file - File file (strings.format ("%s%s/%s", getDataDirectory (false), isGraph ? "graph" : "train", filename), "wb"); + File file (filename, "wb"); // no open no fun if (!file) { @@ -1660,7 +1665,11 @@ template bool BotGraph::saveStorage (StringRef ext, StringRef name, if ((options & StorageOption::Exten) && exten != nullptr) { file.write (exten, sizeof (ExtenHeader)); } - ctrl.msg ("Successfully saved Bots %s data.", name); + + // notify only about graph + if (isGraph || cv_debug.bool_ ()) { + ctrl.msg ("Successfully saved Bots %s data.", name); + } } else { logger.error ("Unable to compress %s data (filename: '%s').", name, filename); @@ -1669,13 +1678,12 @@ template bool BotGraph::saveStorage (StringRef ext, StringRef name, return true; } -template bool BotGraph::loadStorage (StringRef ext, StringRef name, StorageOption options, StorageVersion version, SmallArray &data, ExtenHeader *exten, int32_t *outOptions) { - String filename; - filename.assignf ("%s.%s", game.getMapName (), ext).lowercase (); +template bool BotGraph::loadStorage (StringRef name, StorageOption options, StorageVersion version, SmallArray &data, ExtenHeader *exten, int32_t *outOptions) { + String filename = util.buildPath (util.storageToBotFile (options), true); // graphs can be downloaded... bool isGraph = !!(options & StorageOption::Graph); - MemFile file (strings.format ("%s%s/%s", getDataDirectory (true), isGraph ? "graph" : "train", filename)); // open the file + MemFile file (filename); // open the file data.clear (); data.shrink (); @@ -1707,8 +1715,8 @@ template bool BotGraph::loadStorage (StringRef ext, StringRef name, } auto downloadAddress = cv_graph_url.str (); - auto toDownload = strings.format ("%sgraph/%s", getDataDirectory (false), filename); - auto fromDownload = strings.format ("http://%s/graph/%s", downloadAddress, filename); + auto toDownload = util.buildPath (util.storageToBotFile (options), false); + auto fromDownload = strings.format ("http://%s/graph/%s.graph", downloadAddress, game.getMapName ()); // try to download if (http.downloadFile (fromDownload, toDownload)) { @@ -1730,11 +1738,11 @@ template bool BotGraph::loadStorage (StringRef ext, StringRef name, } if (download ()) { - return loadStorage (ext, name, options, version, data, exten, outOptions); + return loadStorage (name, options, version, data, exten, outOptions); } if (convertOldFormat ()) { - return loadStorage (ext, name, options, version, data, exten, outOptions); + return loadStorage (name, options, version, data, exten, outOptions); } return false; }; @@ -1780,13 +1788,12 @@ template bool BotGraph::loadStorage (StringRef ext, StringRef name, return raiseLoadingError (isGraph, file, "Damaged %s (filename: '%s'). Version number differs (got: '%d', need: '%d').", name, filename, hdr.version, version); } - // temporary solution to kill version 1 vistables, which has a bugs if ((options & StorageOption::Vistable) && hdr.version == 1) { - auto vistablePath = strings.format ("%strain/%s.vis", getDataDirectory (), game.getMapName ()); + auto vistablePath = util.buildPath (BotFile::Vistable); if (File::exists (vistablePath)) { - plat.removeFile (vistablePath); + plat.removeFile (vistablePath.chars ()); } return raiseLoadingError (isGraph, file, "Bugged vistable %s (filename: '%s'). Version 1 has a bugs, vistable will be recreated.", name, filename); } @@ -1843,7 +1850,7 @@ template bool BotGraph::loadStorage (StringRef ext, StringRef name, m_extenHeader.mapSize = exten->mapSize; } } - ctrl.msg ("Successfully loaded Bots %s data v%d (%d/%.2fMB).", name, hdr.version, m_paths.length (), static_cast (data.capacity () * sizeof (U)) / 1024.0f / 1024.0f); + ctrl.msg ("Loaded Bots %s data v%d (%d/%.2fMB).", name, hdr.version, m_paths.length (), static_cast (data.capacity () * sizeof (U)) / 1024.0f / 1024.0f); file.close (); return true; @@ -1863,7 +1870,7 @@ bool BotGraph::loadGraphData () { m_extenHeader = {}; // check if loaded - bool dataLoaded = loadStorage ("graph", "Graph", StorageOption::Graph, StorageVersion::Graph, m_paths, &exten, &outOptions); + bool dataLoaded = loadStorage ("Graph", StorageOption::Graph, StorageVersion::Graph, m_paths, &exten, &outOptions); if (dataLoaded) { reset (); @@ -1951,7 +1958,7 @@ bool BotGraph::saveGraphData () { m_narrowChecked = false; initNarrowPlaces (); - return saveStorage ("graph", "Graph", static_cast (options), StorageVersion::Graph, m_paths, &exten); + return saveStorage ("Graph", static_cast (options), StorageVersion::Graph, m_paths, &exten); } void BotGraph::saveOldFormat () { @@ -1968,7 +1975,7 @@ void BotGraph::saveOldFormat () { File fp; // file was opened - if (fp.open (getOldFormatGraphName (), "wb")) { + if (fp.open (util.buildPath (BotFile::PodbotPWF), "wb")) { // write the node header to the file... fp.write (&header, sizeof (header)); @@ -1986,16 +1993,6 @@ void BotGraph::saveOldFormat () { } } -const char *BotGraph::getOldFormatGraphName (bool isMemoryFile) { - static String buffer; - buffer.assignf ("%s/pwf/%s.pwf", getDataDirectory (isMemoryFile), game.getMapName ()); - - if (File::exists (buffer)) { - return buffer.chars (); - } - return strings.format ("%s/pwf/%s.pwf", getDataDirectory (isMemoryFile), game.getMapName ()); -} - float BotGraph::calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin) { // this function returns 2D traveltime to a position @@ -2898,15 +2895,11 @@ void BotGraph::eraseFromDisk () { StringArray forErase; bots.kickEveryone (true); - auto map = game.getMapName (); - auto data = getDataDirectory (); - // if we're delete graph, delete all corresponding to it files - forErase.push (strings.format ("%spwf/%s.pwf", data, map)); // graph itself - forErase.push (strings.format ("%strain/%s.prc", data, map)); // corresponding to practice - forErase.push (strings.format ("%strain/%s.vis", data, map)); // corresponding to vistable - forErase.push (strings.format ("%strain/%s.pmx", data, map)); // corresponding to matrix - forErase.push (strings.format ("%sgraph/%s.graph", data, map)); // new format graph + forErase.emplace (util.buildPath (BotFile::Graph)); // graph itself + forErase.emplace (util.buildPath (BotFile::Practice)); // corresponding to practice + forErase.emplace (util.buildPath (BotFile::Vistable)); // corresponding to vistable + forErase.emplace (util.buildPath (BotFile::Pathmatrix)); // corresponding to matrix for (const auto &item : forErase) { if (File::exists (item)) { @@ -2921,19 +2914,6 @@ void BotGraph::eraseFromDisk () { m_paths.clear (); } -const char *BotGraph::getDataDirectory (bool isMemoryFile) { - static String buffer; - buffer.clear (); - - if (isMemoryFile) { - buffer.assignf ("addons/%s/data/", product.folder); - } - else { - buffer.assignf ("%s/addons/%s/data/", game.getRunningModName (), product.folder); - } - return buffer.chars (); -} - void BotGraph::setBombOrigin (bool reset, const Vector &pos) { // this function stores the bomb position as a vector diff --git a/src/support.cpp b/src/support.cpp index afdfdee..338581c 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -538,6 +538,83 @@ String BotSupport::getCurrentDateTime () { return String (timebuf); } +String BotSupport::buildPath (int32_t file, bool isMemoryLoad) { + static HashMap directories; + static HashMap extensions; + + // fill out directories paths, it's permanenet + if (directories.empty ()) { + directories[BotFile::Vistable] = { "data", "train" }; + directories[BotFile::Practice] = { "data", "train" }; + directories[BotFile::Pathmatrix] = { "data", "train" }; + directories[BotFile::LogFile] = { "data", "logs" }; + directories[BotFile::Graph] = { "data", "graph" }; + directories[BotFile::PodbotPWF] = { "data", "pwf" }; + directories[BotFile::EbotEWP] = { "data", "ewp" }; + + // fill out extensions fo needed types + extensions[BotFile::Vistable] = "vis"; + extensions[BotFile::Practice] = "prc"; + extensions[BotFile::Pathmatrix] = "pmx"; + extensions[BotFile::LogFile] = "txt"; + extensions[BotFile::Graph] = "graph"; + extensions[BotFile::PodbotPWF] = "pwf"; + extensions[BotFile::EbotEWP] = "ewp"; + } + + static StringArray path; + path.clear (); + + // if not memory file we're don't need game dir + if (!isMemoryLoad) { + path.emplace (game.getRunningModName ()); + } + + // allways append addons/product + path.emplace ("addons"); + path.emplace (product.folder); + + // append real filepath + path.extend (directories[file]); + + // if file is logfile use correct logfile name with date + if (file == BotFile::LogFile) { + time_t ticks = time (&ticks); + tm timeinfo {}; + + plat.loctime (&timeinfo, &ticks); + auto timebuf = strings.chars (); + + strftime (timebuf, StringBuffer::StaticBufferSize, "L%d%m%Y", &timeinfo); + path.emplace (strings.format ("%s_%s.%s", product.folder, timebuf, extensions[file])); + } + else { + String mapName = game.getMapName (); + path.emplace (strings.format ("%s.%s", mapName.lowercase (), extensions[file])); + } + + // finally use correct path separarators for us + return String::join (path, plat.win ? "\\" : "/"); +} + +// converts storage option to stroage filename + +int32_t BotSupport::storageToBotFile (StorageOption options) { + if (options & StorageOption::Graph) { + return BotFile::Graph; + } + else if (options & StorageOption::Matrix) { + return BotFile::Pathmatrix; + } + else if (options & StorageOption::Vistable) { + return BotFile::Vistable; + } + else if (options & StorageOption::Practice) { + return BotFile::Practice; + } + return BotFile::Graph; +} + int32_t BotSupport::sendTo (int socket, const void *message, size_t length, int flags, const sockaddr *dest, int destLength) { const auto send = [&] (const Twin &msg) -> int32_t { return Socket::sendto (socket, msg.first, msg.second, flags, dest, destLength);