graph: allow graphs to be auto-collected (controlled via yb_graph_auto_collect_db)
By default it's off, but it's allow bot to scan graph directory, do a diff with a graph db server and upload every single graph file that do not exist in central database. This is done in a separate thread and do not block server process, and only once server/game is started, not on change level. Also, it's not working on currently started map.
This commit is contained in:
parent
d82124e595
commit
46ebbeea57
8 changed files with 200 additions and 11 deletions
|
|
@ -38,7 +38,7 @@ yb_graph_analyze_max_jump_height "44"
|
||||||
//
|
//
|
||||||
// The FPS at which analyzer process is running. This keeps game from freezing during analyzing.
|
// The FPS at which analyzer process is running. This keeps game from freezing during analyzing.
|
||||||
// ---
|
// ---
|
||||||
// Default: "30.0"
|
// Default: "30.0", Min: "25.0", Max: "99.0"
|
||||||
//
|
//
|
||||||
yb_graph_analyze_fps "30.0"
|
yb_graph_analyze_fps "30.0"
|
||||||
|
|
||||||
|
|
@ -345,6 +345,13 @@ yb_ignore_map_prefix_game_mode "0"
|
||||||
//
|
//
|
||||||
yb_threadpool_workers "-1"
|
yb_threadpool_workers "-1"
|
||||||
|
|
||||||
|
//
|
||||||
|
// If enabled, bots will not apply throwing condition on grenades.
|
||||||
|
// ---
|
||||||
|
// Default: "0", Min: "0", Max: "1"
|
||||||
|
//
|
||||||
|
yb_grenadier_mode "0"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Specifies whether bot should not 'fix' camp directions of camp waypoints when loading old PWF format.
|
// Specifies whether bot should not 'fix' camp directions of camp waypoints when loading old PWF format.
|
||||||
// ---
|
// ---
|
||||||
|
|
@ -380,6 +387,13 @@ yb_graph_auto_save_count "15"
|
||||||
//
|
//
|
||||||
yb_graph_draw_distance "400"
|
yb_graph_draw_distance "400"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Allows bot's to exchange your graph files with graph database automatically.
|
||||||
|
// ---
|
||||||
|
// Default: "0", Min: "0", Max: "1"
|
||||||
|
//
|
||||||
|
yb_graph_auto_collect_db "0"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Kick bots to automatically make room for human players.
|
// Kick bots to automatically make room for human players.
|
||||||
// ---
|
// ---
|
||||||
|
|
@ -388,7 +402,7 @@ yb_graph_draw_distance "400"
|
||||||
yb_autovacate "1"
|
yb_autovacate "1"
|
||||||
|
|
||||||
//
|
//
|
||||||
// How many slots autovacate feature should keep for human players
|
// How many slots autovacate feature should keep for human players.
|
||||||
// ---
|
// ---
|
||||||
// Default: "1", Min: "1", Max: "8"
|
// Default: "1", Min: "1", Max: "8"
|
||||||
//
|
//
|
||||||
|
|
@ -541,6 +555,15 @@ yb_botskin_t "0"
|
||||||
//
|
//
|
||||||
yb_botskin_ct "0"
|
yb_botskin_ct "0"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sets the default personality when creating bots with quota management.
|
||||||
|
// Allowed values: 'none', 'normal', 'careful', 'rusher'.
|
||||||
|
// If 'none' is specified personality chosen randomly.
|
||||||
|
// ---
|
||||||
|
// Default: "none"
|
||||||
|
//
|
||||||
|
yb_preferred_personality "none"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lower bound for base bot ping shown in scoreboard upon creation.
|
// Lower bound for base bot ping shown in scoreboard upon creation.
|
||||||
// ---
|
// ---
|
||||||
|
|
@ -597,12 +620,19 @@ yb_rotate_stay_min "360.0"
|
||||||
//
|
//
|
||||||
yb_rotate_stay_max "3600.0"
|
yb_rotate_stay_max "3600.0"
|
||||||
|
|
||||||
|
//
|
||||||
|
// When enabled, bots will not try to avoid teammates on their way. Assuming some of the semiclip plugins are in use.
|
||||||
|
// ---
|
||||||
|
// Default: "0", Min: "0", Max: "1"
|
||||||
|
//
|
||||||
|
yb_has_team_semiclip "0"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Selects the heuristic function mode. For debug purposes only.
|
// Selects the heuristic function mode. For debug purposes only.
|
||||||
// ---
|
// ---
|
||||||
// Default: "3", Min: "0", Max: "4"
|
// Default: "0", Min: "0", Max: "4"
|
||||||
//
|
//
|
||||||
yb_path_heuristic_mode "3"
|
yb_path_heuristic_mode "0"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Limit maximum floyd-warshall memory (megabytes). Use Dijkstra if memory exceeds.
|
// Limit maximum floyd-warshall memory (megabytes). Use Dijkstra if memory exceeds.
|
||||||
|
|
@ -625,6 +655,13 @@ yb_path_dijkstra_simple_distance "1"
|
||||||
//
|
//
|
||||||
yb_path_astar_post_smooth "0"
|
yb_path_astar_post_smooth "0"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Randomize pathfinding on each round start.
|
||||||
|
// ---
|
||||||
|
// Default: "1", Min: "0", Max: "1"
|
||||||
|
//
|
||||||
|
yb_path_randomize_on_round_start "1"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Enables or disables showing welcome message to host entity on game start.
|
// Enables or disables showing welcome message to host entity on game start.
|
||||||
// ---
|
// ---
|
||||||
|
|
@ -646,6 +683,20 @@ yb_enable_query_hook "0"
|
||||||
//
|
//
|
||||||
yb_breakable_health_limit "500.0"
|
yb_breakable_health_limit "500.0"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Allows or disallows bots to return fake steam id.
|
||||||
|
// ---
|
||||||
|
// Default: "0", Min: "0", Max: "1"
|
||||||
|
//
|
||||||
|
yb_enable_fake_steamids "0"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Count player pings when calculating average ping for bots. If no, some random ping chosen for bots.
|
||||||
|
// ---
|
||||||
|
// Default: "1", Min: "0", Max: "1"
|
||||||
|
//
|
||||||
|
yb_count_players_for_fakeping "1"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Specifies whether bots able to use 'shift' if they thinks that enemy is near.
|
// Specifies whether bots able to use 'shift' if they thinks that enemy is near.
|
||||||
// ---
|
// ---
|
||||||
|
|
@ -688,3 +739,10 @@ yb_random_knife_attacks "1"
|
||||||
//
|
//
|
||||||
yb_max_nodes_for_predict "25"
|
yb_max_nodes_for_predict "25"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Enables or disables extra hard difficulty for bots.
|
||||||
|
// ---
|
||||||
|
// Default: "0", Min: "0", Max: "1"
|
||||||
|
//
|
||||||
|
yb_whose_your_daddy "0"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit aa8e1e0eaed2e4b75e6b2a0fff5a68756cddc972
|
Subproject commit 5225f857c44ced8e61e65b25892a0c8ab015250c
|
||||||
|
|
@ -168,6 +168,7 @@ private:
|
||||||
bool m_narrowChecked {};
|
bool m_narrowChecked {};
|
||||||
bool m_silenceMessages {};
|
bool m_silenceMessages {};
|
||||||
bool m_lightChecked {};
|
bool m_lightChecked {};
|
||||||
|
bool m_isOnlineCollected {};
|
||||||
|
|
||||||
Vector m_learnVelocity {};
|
Vector m_learnVelocity {};
|
||||||
Vector m_learnPosition {};
|
Vector m_learnPosition {};
|
||||||
|
|
@ -259,6 +260,8 @@ public:
|
||||||
void showStats ();
|
void showStats ();
|
||||||
void showFileInfo ();
|
void showFileInfo ();
|
||||||
void emitNotify (int32_t sound);
|
void emitNotify (int32_t sound);
|
||||||
|
void syncCollectOnline ();
|
||||||
|
void collectOnline ();
|
||||||
|
|
||||||
IntArray getNearestInRadius (float radius, const Vector &origin, int maxCount = -1);
|
IntArray getNearestInRadius (float radius, const Vector &origin, int maxCount = -1);
|
||||||
const IntArray &getNodesInBucket (const Vector &pos);
|
const IntArray &getNodesInBucket (const Vector &pos);
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ public:
|
||||||
static constexpr StringRef url { "https://yapb.jeefo.net/" };
|
static constexpr StringRef url { "https://yapb.jeefo.net/" };
|
||||||
static constexpr StringRef download { "yapb.jeefo.net" };
|
static constexpr StringRef download { "yapb.jeefo.net" };
|
||||||
static constexpr StringRef upload { "yapb.jeefo.net/upload" };
|
static constexpr StringRef upload { "yapb.jeefo.net/upload" };
|
||||||
|
static constexpr StringRef httpScheme { "http" };
|
||||||
static constexpr StringRef logtag { "YB" };
|
static constexpr StringRef logtag { "YB" };
|
||||||
static constexpr StringRef dtime { __DATE__ " " __TIME__ };
|
static constexpr StringRef dtime { __DATE__ " " __TIME__ };
|
||||||
static constexpr StringRef date { __DATE__ };
|
static constexpr StringRef date { __DATE__ };
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ public:
|
||||||
template <typename ...Args> bool error (bool isGraph, bool isDebug, MemFile &file, const char *fmt, Args &&...args);
|
template <typename ...Args> bool error (bool isGraph, bool isDebug, MemFile &file, const char *fmt, Args &&...args);
|
||||||
|
|
||||||
// builds the filename to requested filename
|
// builds the filename to requested filename
|
||||||
String buildPath (int32_t type, bool isMemoryLoad = false);
|
String buildPath (int32_t type, bool isMemoryLoad = false, bool withoutMapName = false);
|
||||||
|
|
||||||
// get's relative path against bot library (bot library should reside in bin dir)
|
// get's relative path against bot library (bot library should reside in bin dir)
|
||||||
StringRef getRunningPath ();
|
StringRef getRunningPath ();
|
||||||
|
|
|
||||||
|
|
@ -843,11 +843,11 @@ int BotControl::cmdNodeUpload () {
|
||||||
String uploadUrlAddress = cv_graph_url_upload.str ();
|
String uploadUrlAddress = cv_graph_url_upload.str ();
|
||||||
|
|
||||||
// only allow to upload to non-https endpoint
|
// only allow to upload to non-https endpoint
|
||||||
if (uploadUrlAddress.startsWith ("http")) {
|
if (uploadUrlAddress.startsWith ("https")) {
|
||||||
msg ("Value of \"%s\" cvar should not contain URL scheme, only the host name and path.", cv_graph_url_upload.name ());
|
msg ("Value of \"%s\" cvar should not contain URL scheme, only the host name and path.", cv_graph_url_upload.name ());
|
||||||
return BotCommandResult::Handled;
|
return BotCommandResult::Handled;
|
||||||
}
|
}
|
||||||
String uploadUrl = strings.format ("http://%s", uploadUrlAddress);
|
String uploadUrl = strings.format ("%s://%s", product.httpScheme, uploadUrlAddress);
|
||||||
|
|
||||||
msg ("\n");
|
msg ("\n");
|
||||||
msg ("WARNING!");
|
msg ("WARNING!");
|
||||||
|
|
|
||||||
127
src/graph.cpp
127
src/graph.cpp
|
|
@ -12,6 +12,7 @@ ConVar cv_graph_url ("graph_url", product.download.chars (), "Specifies the URL
|
||||||
ConVar cv_graph_url_upload ("graph_url_upload", product.upload.chars (), "Specifies the URL to which bots will try to upload the graph file to database.", false, 0.0f, 0.0f);
|
ConVar cv_graph_url_upload ("graph_url_upload", product.upload.chars (), "Specifies the URL to which bots will try to upload the graph file to database.", false, 0.0f, 0.0f);
|
||||||
ConVar cv_graph_auto_save_count ("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_auto_save_count ("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 ("graph_draw_distance", "400", "Maximum distance to draw graph nodes from editor viewport.", true, 64.0f, 3072.0f);
|
ConVar cv_graph_draw_distance ("graph_draw_distance", "400", "Maximum distance to draw graph nodes from editor viewport.", true, 64.0f, 3072.0f);
|
||||||
|
ConVar cv_graph_auto_collect_db ("graph_auto_collect_db", "0", "Allows bot's to exchange your graph files with graph database automatically.");
|
||||||
|
|
||||||
void BotGraph::reset () {
|
void BotGraph::reset () {
|
||||||
// this function initialize the graph structures..
|
// this function initialize the graph structures..
|
||||||
|
|
@ -1277,6 +1278,129 @@ void BotGraph::emitNotify (int32_t sound) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BotGraph::syncCollectOnline () {
|
||||||
|
m_isOnlineCollected = true; // once per server start
|
||||||
|
|
||||||
|
// path to graph files
|
||||||
|
auto graphFilesPath = bstor.buildPath (BotFile::Graph, false, true);
|
||||||
|
|
||||||
|
// enumerate graph files
|
||||||
|
FileEnumerator enumerator { strings.joinPath (graphFilesPath, "*.graph") };
|
||||||
|
|
||||||
|
// listing of graphs locally available
|
||||||
|
StringArray localGraphs {};
|
||||||
|
|
||||||
|
// collect all the files
|
||||||
|
while (enumerator) {
|
||||||
|
auto match = enumerator.getMatch ();
|
||||||
|
|
||||||
|
match = match.substr (match.findLastOf (kPathSeparator) + 1);
|
||||||
|
match = match.substr (0, match.findFirstOf ("."));
|
||||||
|
|
||||||
|
localGraphs.emplace (match);
|
||||||
|
enumerator.next ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// no graphs ? unbelievable
|
||||||
|
if (localGraphs.empty ()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String uploadUrlAddress = cv_graph_url_upload.str ();
|
||||||
|
|
||||||
|
// only allow to upload to non-https endpoint
|
||||||
|
if (uploadUrlAddress.startsWith ("https")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String localFile = plat.tmpfname ();
|
||||||
|
|
||||||
|
// don't forget remove temporary file
|
||||||
|
auto unlinkTemporary = [&] () {
|
||||||
|
if (plat.fileExists (localFile.chars ())) {
|
||||||
|
plat.removeFile (localFile.chars ());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// write out our list of files into temporary
|
||||||
|
if (File lc { localFile, "wt" }) {
|
||||||
|
auto graphList = String::join (localGraphs, ",");
|
||||||
|
auto collectUrl = strings.format ("%s://%s/collect/%u", product.httpScheme, uploadUrlAddress, graphList.hash ());
|
||||||
|
|
||||||
|
lc.puts (graphList.chars ());
|
||||||
|
lc.close ();
|
||||||
|
|
||||||
|
// upload to collection diff
|
||||||
|
if (!http.uploadFile (collectUrl, localFile)) {
|
||||||
|
unlinkTemporary ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unlinkTemporary ();
|
||||||
|
|
||||||
|
// download collection diff
|
||||||
|
if (!http.downloadFile (collectUrl, localFile)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StringArray wanted {};
|
||||||
|
|
||||||
|
// decode answer
|
||||||
|
if (lc.open (localFile, "rt")) {
|
||||||
|
String lines;
|
||||||
|
|
||||||
|
if (lc.getLine (lines)) {
|
||||||
|
wanted = lines.split (",");
|
||||||
|
}
|
||||||
|
lc.close ();
|
||||||
|
}
|
||||||
|
unlinkTemporary ();
|
||||||
|
|
||||||
|
// if 'we're have something in diff, bailout
|
||||||
|
if (wanted.empty ()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localGraphs.clear ();
|
||||||
|
|
||||||
|
// convert graphs names into full paths
|
||||||
|
for (const auto &wn : wanted) {
|
||||||
|
if (wn == game.getMapName ()) {
|
||||||
|
continue; // skip current map always
|
||||||
|
}
|
||||||
|
localGraphs.emplace (strings.joinPath (graphFilesPath, wn) + ".graph");
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to upload everything database wants
|
||||||
|
for (const auto &lg : localGraphs) {
|
||||||
|
if (!plat.fileExists (lg.chars ())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
StorageHeader hdr {};
|
||||||
|
|
||||||
|
// read storage header and check if file NOT analyzed
|
||||||
|
if (File gp { lg, "rb" }) {
|
||||||
|
gp.read (&hdr, sizeof (StorageHeader));
|
||||||
|
|
||||||
|
// check the magic, graph is NOT analyzed and have some viable nodes number
|
||||||
|
if (hdr.magic == kStorageMagic && !(hdr.options & StorageOption::Analyzed) && hdr.length > 48) {
|
||||||
|
String uploadUrl = strings.format ("%s://%s", product.httpScheme, uploadUrlAddress);
|
||||||
|
|
||||||
|
// try to upload to database (no need check if it's succeed)
|
||||||
|
http.uploadFile (uploadUrl, lg);
|
||||||
|
}
|
||||||
|
gp.close ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unlinkTemporary ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BotGraph::collectOnline () {
|
||||||
|
if (m_isOnlineCollected || !cv_graph_auto_collect_db.bool_ ()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.enqueue ([this] () {
|
||||||
|
syncCollectOnline ();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void BotGraph::calculatePathRadius (int index) {
|
void BotGraph::calculatePathRadius (int index) {
|
||||||
// calculate "wayzones" for the nearest node (meaning a dynamic distance area to vary node origin)
|
// calculate "wayzones" for the nearest node (meaning a dynamic distance area to vary node origin)
|
||||||
|
|
||||||
|
|
@ -1658,6 +1782,9 @@ bool BotGraph::loadGraphData () {
|
||||||
}
|
}
|
||||||
cv_debug_goal.set (kInvalidNodeIndex);
|
cv_debug_goal.set (kInvalidNodeIndex);
|
||||||
|
|
||||||
|
// try to do graph collection, and push them to graph database automatically
|
||||||
|
collectOnline ();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ template <typename U> bool BotStorage::load (SmallArray <U> &data, ExtenHeader *
|
||||||
auto downloadAddress = cv_graph_url.str ();
|
auto downloadAddress = cv_graph_url.str ();
|
||||||
|
|
||||||
auto toDownload = buildPath (storageToBotFile (type.option), false);
|
auto toDownload = buildPath (storageToBotFile (type.option), false);
|
||||||
auto fromDownload = strings.format ("http://%s/graph/%s.graph", downloadAddress, lowercaseMapName);
|
auto fromDownload = strings.format ("%s://%s/graph/%s.graph", product.httpScheme, downloadAddress, lowercaseMapName);
|
||||||
|
|
||||||
// try to download
|
// try to download
|
||||||
if (http.downloadFile (fromDownload, toDownload)) {
|
if (http.downloadFile (fromDownload, toDownload)) {
|
||||||
|
|
@ -301,7 +301,7 @@ template <typename U> BotStorage::SaveLoadData BotStorage::guessType () {
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
String BotStorage::buildPath (int32_t file, bool isMemoryLoad) {
|
String BotStorage::buildPath (int32_t file, bool isMemoryLoad, bool withoutMapName) {
|
||||||
using FilePath = Twin <String, String>;
|
using FilePath = Twin <String, String>;
|
||||||
|
|
||||||
static HashMap <int32_t, FilePath> paths = {
|
static HashMap <int32_t, FilePath> paths = {
|
||||||
|
|
@ -342,7 +342,7 @@ String BotStorage::buildPath (int32_t file, bool isMemoryLoad) {
|
||||||
strftime (timebuf, StringBuffer::StaticBufferSize, "L%d%m%Y", &timeinfo);
|
strftime (timebuf, StringBuffer::StaticBufferSize, "L%d%m%Y", &timeinfo);
|
||||||
path.emplace (strings.format ("%s_%s.%s", product.nameLower, timebuf, paths[file].second));
|
path.emplace (strings.format ("%s_%s.%s", product.nameLower, timebuf, paths[file].second));
|
||||||
}
|
}
|
||||||
else {
|
else if (!withoutMapName) {
|
||||||
String mapName = game.getMapName ();
|
String mapName = game.getMapName ();
|
||||||
path.emplace (strings.format ("%s.%s", mapName.lowercase (), paths[file].second));
|
path.emplace (strings.format ("%s.%s", mapName.lowercase (), paths[file].second));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue