fix: use clamped health value to avoid overflows.

crlib: do not shutdown wsa, it's cleaned upon server exit anyway.
This commit is contained in:
ds 2020-09-26 18:41:53 +03:00
commit 1263a1a439
11 changed files with 109 additions and 126 deletions

View file

@ -22,6 +22,8 @@
#include <crlib/cr-logger.h> #include <crlib/cr-logger.h>
#include <crlib/cr-twin.h> #include <crlib/cr-twin.h>
#include <crlib/cr-platform.h> #include <crlib/cr-platform.h>
#include <crlib/cr-uniqueptr.h>
#include <crlib/cr-random.h>
#if defined (CR_LINUX) || defined (CR_OSX) #if defined (CR_LINUX) || defined (CR_OSX)
# include <netinet/in.h> # include <netinet/in.h>
@ -110,37 +112,72 @@ CR_DECLARE_SCOPED_ENUM (HttpClientResult,
SocketError = -1, SocketError = -1,
ConnectError = -2, ConnectError = -2,
HttpOnly = -3, HttpOnly = -3,
Undefined = -4, Undefined = -4,
NoLocalFile = -5, NoLocalFile = -5,
LocalFileExists = -6 LocalFileExists = -6
) )
CR_NAMESPACE_BEGIN CR_NAMESPACE_BEGIN
namespace detail {
// simple http uri omitting query-string and port
struct HttpUri {
String path, protocol, host;
public:
static HttpUri parse (StringRef uri) {
HttpUri result;
if (uri.empty ()) {
return result;
}
size_t protocol = uri.find ("://");
if (protocol != String::InvalidIndex) {
result.protocol = uri.substr (0, protocol);
size_t hostIndex = uri.find ("/", protocol + 3);
if (hostIndex != String::InvalidIndex) {
result.path = uri.substr (hostIndex + 1);
result.host = uri.substr (protocol + 3, hostIndex - protocol - 3);
return result;
}
}
return result;
}
};
struct SocketInit {
public:
static void start () {
#if defined(CR_WINDOWS)
WSADATA wsa;
if (WSAStartup (MAKEWORD (2, 2), &wsa) != 0) {
logger.error ("Unable to inialize sockets.");
}
#endif
}
};
}
class Socket final : public DenyCopying { class Socket final : public DenyCopying {
private: private:
int32 socket_; int32 socket_;
uint32 timeout_; uint32 timeout_;
public: public:
Socket () : socket_ (-1), timeout_ (2) { Socket () : socket_ (-1), timeout_ (2)
#if defined(CR_WINDOWS) { }
WSADATA wsa;
if (WSAStartup (MAKEWORD (2, 2), &wsa) != 0) {
logger.error ("Unable to inialize sockets.");
}
#endif
}
~Socket () { ~Socket () {
disconnect (); disconnect ();
#if defined (CR_WINDOWS)
WSACleanup ();
#endif
} }
public: public:
bool connect (StringRef hostname) { bool connect (StringRef hostname) {
addrinfo hints {}, *result = nullptr; addrinfo hints {}, *result = nullptr;
@ -162,7 +199,7 @@ public:
return false; return false;
} }
auto getTimeouts = [&] () -> Twin <char *, int32> { auto getTimeouts = [&]() -> Twin <char *, int32> {
#if defined (CR_WINDOWS) #if defined (CR_WINDOWS)
DWORD tv = timeout_ * 1000; DWORD tv = timeout_ * 1000;
#else #else
@ -240,63 +277,34 @@ public:
} }
}; };
namespace detail {
// simple http uri omitting query-string and port
struct HttpUri {
String path, protocol, host;
public:
static HttpUri parse (StringRef uri) {
HttpUri result;
if (uri.empty ()) {
return result;
}
size_t protocol = uri.find ("://");
if (protocol != String::InvalidIndex) {
result.protocol = uri.substr (0, protocol);
size_t hostIndex = uri.find ("/", protocol + 3);
if (hostIndex != String::InvalidIndex) {
result.path = uri.substr (hostIndex + 1);
result.host = uri.substr (protocol + 3, hostIndex - protocol - 3);
return result;
}
}
return result;
}
};
}
// simple http client for downloading/uploading files only // simple http client for downloading/uploading files only
class HttpClient final : public Singleton <HttpClient> { class HttpClient final : public Singleton <HttpClient> {
private: private:
enum : int32 { enum : int32 {
MaxReceiveErrors = 12 MaxReceiveErrors = 12,
DefaultSocketTimeout = 32
}; };
private: private:
Socket socket_;
String userAgent_ = "crlib"; String userAgent_ = "crlib";
HttpClientResult statusCode_ = HttpClientResult::Undefined; HttpClientResult statusCode_ = HttpClientResult::Undefined;
int32 chunkSize_ = 4096; int32 chunkSize_ = 4096;
public: public:
HttpClient () = default; HttpClient () {
detail::SocketInit::start ();
}
~HttpClient () = default; ~HttpClient () = default;
private: private:
HttpClientResult parseResponseHeader (uint8 *buffer) { HttpClientResult parseResponseHeader (Socket *socket, uint8 *buffer) {
bool isFinished = false; bool isFinished = false;
int32 pos = 0, symbols = 0, errors = 0; int32 pos = 0, symbols = 0, errors = 0;
// prase response header // prase response header
while (!isFinished && pos < chunkSize_) { while (!isFinished && pos < chunkSize_) {
if (socket_.recv (&buffer[pos], 1) < 1) { if (socket->recv (&buffer[pos], 1) < 1) {
if (++errors > MaxReceiveErrors) { if (++errors > MaxReceiveErrors) {
isFinished = true; isFinished = true;
} }
@ -335,24 +343,24 @@ private:
public: public:
// simple blocked download // simple blocked download
bool downloadFile (StringRef url, StringRef localPath) { bool downloadFile (StringRef url, StringRef localPath, int32 timeout = DefaultSocketTimeout) {
if (File::exists (localPath.chars ())) { if (File::exists (localPath)) {
statusCode_ = HttpClientResult::LocalFileExists; statusCode_ = HttpClientResult::LocalFileExists;
return false; return false;
} }
auto uri = detail::HttpUri::parse (url); auto uri = detail::HttpUri::parse (url);
auto socket = cr::makeUnique <Socket> ();
// no https... // no https...
if (uri.protocol == "https") { if (uri.protocol == "https") {
statusCode_ = HttpClientResult::HttpOnly; statusCode_ = HttpClientResult::HttpOnly;
return false; return false;
} }
socket->setTimeout (timeout);
// unable to connect... // unable to connect...
if (!socket_.connect (uri.host)) { if (!socket->connect (uri.host)) {
statusCode_ = HttpClientResult::ConnectError; statusCode_ = HttpClientResult::ConnectError;
socket_.disconnect ();
return false; return false;
} }
@ -364,17 +372,15 @@ public:
request.appendf ("User-Agent: %s\r\n", userAgent_); request.appendf ("User-Agent: %s\r\n", userAgent_);
request.appendf ("Host: %s\r\n\r\n", uri.host); request.appendf ("Host: %s\r\n\r\n", uri.host);
if (socket_.send (request.chars (), static_cast <int32> (request.length ())) < 1) { if (socket->send (request.chars (), static_cast <int32> (request.length ())) < 1) {
statusCode_ = HttpClientResult::SocketError; statusCode_ = HttpClientResult::SocketError;
socket_.disconnect ();
return false; return false;
} }
SmallArray <uint8> buffer (chunkSize_); SmallArray <uint8> buffer (chunkSize_);
statusCode_ = parseResponseHeader (buffer.data ()); statusCode_ = parseResponseHeader (socket.get (), buffer.data ());
if (statusCode_ != HttpClientResult::Ok) { if (statusCode_ != HttpClientResult::Ok) {
socket_.disconnect ();
return false; return false;
} }
@ -383,15 +389,13 @@ public:
if (!file) { if (!file) {
statusCode_ = HttpClientResult::Undefined; statusCode_ = HttpClientResult::Undefined;
socket_.disconnect ();
return false; return false;
} }
int32 length = 0; int32 length = 0;
int32 errors = 0; int32 errors = 0;
for (;;) { for (;;) {
length = socket_.recv (buffer.data (), chunkSize_); length = socket->recv (buffer.data (), chunkSize_);
if (length > 0) { if (length > 0) {
file.write (buffer.data (), length); file.write (buffer.data (), length);
@ -400,32 +404,29 @@ public:
break; break;
} }
} }
file.close ();
socket_.disconnect ();
statusCode_ = HttpClientResult::Ok; statusCode_ = HttpClientResult::Ok;
return true; return true;
} }
bool uploadFile (StringRef url, StringRef localPath) { bool uploadFile (StringRef url, StringRef localPath, const int32 timeout = DefaultSocketTimeout) {
if (!File::exists (localPath.chars ())) { if (!File::exists (localPath)) {
statusCode_ = HttpClientResult::NoLocalFile; statusCode_ = HttpClientResult::NoLocalFile;
return false; return false;
} }
auto uri = detail::HttpUri::parse (url); auto uri = detail::HttpUri::parse (url);
auto socket = cr::makeUnique <Socket> ();
// no https... // no https...
if (uri.protocol == "https") { if (uri.protocol == "https") {
statusCode_ = HttpClientResult::HttpOnly; statusCode_ = HttpClientResult::HttpOnly;
return false; return false;
} }
socket->setTimeout (timeout);
// unable to connect... // unable to connect...
if (!socket_.connect (uri.host)) { if (!socket->connect (uri.host)) {
statusCode_ = HttpClientResult::ConnectError; statusCode_ = HttpClientResult::ConnectError;
socket_.disconnect ();
return false; return false;
} }
@ -434,8 +435,6 @@ public:
if (!file) { if (!file) {
statusCode_ = HttpClientResult::Undefined; statusCode_ = HttpClientResult::Undefined;
socket_.disconnect ();
return false; return false;
} }
String boundaryName = localPath; String boundaryName = localPath;
@ -444,7 +443,7 @@ public:
if (boundarySlash != String::InvalidIndex) { if (boundarySlash != String::InvalidIndex) {
boundaryName = localPath.substr (boundarySlash + 1); boundaryName = localPath.substr (boundarySlash + 1);
} }
StringRef boundaryLine = "---crlib_upload_boundary_1337"; StringRef boundaryLine = strings.format ("---crlib_upload_boundary_%d%d%d%d", rg.int_ (0, 9), rg.int_ (0, 9), rg.int_ (0, 9), rg.int_ (0, 9));
String request, start, end; String request, start, end;
start.appendf ("--%s\r\n", boundaryLine); start.appendf ("--%s\r\n", boundaryLine);
@ -460,17 +459,14 @@ public:
request.appendf ("Content-Length: %d\r\n\r\n", file.length () + start.length () + end.length ()); request.appendf ("Content-Length: %d\r\n\r\n", file.length () + start.length () + end.length ());
// send the main request // send the main request
if (socket_.send (request.chars (), static_cast <int32> (request.length ())) < 1) { if (socket->send (request.chars (), static_cast <int32> (request.length ())) < 1) {
statusCode_ = HttpClientResult::SocketError; statusCode_ = HttpClientResult::SocketError;
socket_.disconnect ();
return false; return false;
} }
// send boundary start // send boundary start
if (socket_.send (start.chars (), static_cast <int32> (start.length ())) < 1) { if (socket->send (start.chars (), static_cast <int32> (start.length ())) < 1) {
statusCode_ = HttpClientResult::SocketError; statusCode_ = HttpClientResult::SocketError;
socket_.disconnect ();
return false; return false;
} }
@ -481,7 +477,7 @@ public:
length = static_cast <int32> (file.read (buffer.data (), 1, chunkSize_)); length = static_cast <int32> (file.read (buffer.data (), 1, chunkSize_));
if (length > 0) { if (length > 0) {
socket_.send (buffer.data (), length); socket->send (buffer.data (), length);
} }
else { else {
break; break;
@ -489,14 +485,11 @@ public:
} }
// send boundary end // send boundary end
if (socket_.send (end.chars (), static_cast <int32> (end.length ())) < 1) { if (socket->send (end.chars (), static_cast <int32> (end.length ())) < 1) {
statusCode_ = HttpClientResult::SocketError; statusCode_ = HttpClientResult::SocketError;
socket_.disconnect ();
return false; return false;
} }
statusCode_ = parseResponseHeader (buffer.data ()); statusCode_ = parseResponseHeader (socket.get (), buffer.data ());
socket_.disconnect ();
return statusCode_ == HttpClientResult::Ok; return statusCode_ == HttpClientResult::Ok;
} }
@ -513,10 +506,6 @@ public:
void setChunkSize (int32 chunkSize) { void setChunkSize (int32 chunkSize) {
chunkSize_ = chunkSize; chunkSize_ = chunkSize;
} }
void setTimeout (uint32 timeout) {
socket_.setTimeout (timeout);
}
}; };
// expose global http client // expose global http client

View file

@ -333,7 +333,7 @@ public:
template <typename ...Args> bool raiseLoadingError (bool isGraph, MemFile &file, const char *fmt, Args &&...args); template <typename ...Args> bool raiseLoadingError (bool isGraph, MemFile &file, const char *fmt, Args &&...args);
void saveOldFormat (); void saveOldFormat ();
void initGraph (); void reset ();
void frame (); void frame ();
void loadPractice (); void loadPractice ();
void loadVisibility (); void loadVisibility ();

View file

@ -976,6 +976,7 @@ public:
float m_timeLastFired; // time to last firing float m_timeLastFired; // time to last firing
float m_difficultyChange; // time when auto-difficulty was last applied to this bot float m_difficultyChange; // time when auto-difficulty was last applied to this bot
float m_kpdRatio; // kill per death ratio float m_kpdRatio; // kill per death ratio
float m_healthValue; // clamped bot health
int m_basePing; // base ping for bot int m_basePing; // base ping for bot
int m_numEnemiesLeft; // number of enemies alive left on map int m_numEnemiesLeft; // number of enemies alive left on map

View file

@ -628,7 +628,7 @@ void Bot::updatePickups () {
else if (m_isVIP || !rateGroundWeapon (ent)) { else if (m_isVIP || !rateGroundWeapon (ent)) {
allowPickup = false; allowPickup = false;
} }
else if (strcmp (model, "medkit.mdl") == 0 && pev->health >= 100.0f) { else if (strcmp (model, "medkit.mdl") == 0 && m_healthValue >= 100.0f) {
allowPickup = false; allowPickup = false;
} }
else if ((strcmp (model, "kevlar.mdl") == 0 || strcmp (model, "battery.mdl") == 0) && pev->armorvalue >= 100.0f) { else if ((strcmp (model, "kevlar.mdl") == 0 || strcmp (model, "battery.mdl") == 0) && pev->armorvalue >= 100.0f) {
@ -792,7 +792,7 @@ void Bot::updatePickups () {
m_itemIgnore = ent; m_itemIgnore = ent;
allowPickup = false; allowPickup = false;
if (!m_defendedBomb && m_difficulty >= Difficulty::Normal && rg.chance (75) && pev->health < 60) { if (!m_defendedBomb && m_difficulty >= Difficulty::Normal && rg.chance (75) && m_healthValue < 60) {
int index = findDefendNode (origin); int index = findDefendNode (origin);
startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, game.time () + rg.float_ (30.0f, 70.0f), true); // push camp task on to stack startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, game.time () + rg.float_ (30.0f, 70.0f), true); // push camp task on to stack
@ -1931,7 +1931,7 @@ void Bot::filterTasks () {
// calculate desires to seek cover or hunt // calculate desires to seek cover or hunt
if (util.isPlayer (m_lastEnemy) && !m_lastEnemyOrigin.empty () && !m_hasC4) { if (util.isPlayer (m_lastEnemy) && !m_lastEnemyOrigin.empty () && !m_hasC4) {
float retreatLevel = (100.0f - (pev->health > 50.0f ? 100.0f : pev->health)) * tempFear; // retreat level depends on bot health float retreatLevel = (100.0f - (m_healthValue > 50.0f ? 100.0f : m_healthValue)) * tempFear; // retreat level depends on bot health
if (m_numEnemiesLeft > m_numFriendsLeft / 2 && m_retreatTime < game.time () && m_seeEnemyTime - rg.float_ (2.0f, 4.0f) < game.time ()) { if (m_numEnemiesLeft > m_numFriendsLeft / 2 && m_retreatTime < game.time () && m_seeEnemyTime - rg.float_ (2.0f, 4.0f) < game.time ()) {
@ -2958,6 +2958,7 @@ void Bot::update () {
m_canChooseAimDirection = true; m_canChooseAimDirection = true;
m_notKilled = util.isAlive (ent ()); m_notKilled = util.isAlive (ent ());
m_team = game.getTeam (ent ()); m_team = game.getTeam (ent ());
m_healthValue = cr::clamp (pev->health, 0.0f, 100.0f);
if (game.mapIs (MapFlags::Assassination) && !m_isVIP) { if (game.mapIs (MapFlags::Assassination) && !m_isVIP) {
m_isVIP = util.isPlayerVIP (ent ()); m_isVIP = util.isPlayerVIP (ent ());
@ -5033,7 +5034,7 @@ void Bot::showDebugOverlay () {
auto weapon = util.weaponIdToAlias (m_currentWeapon); auto weapon = util.weaponIdToAlias (m_currentWeapon);
String debugData; String debugData;
debugData.assignf ("\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\nItem: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\nSP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\nEnemy=%s Pickup=%s Type=%s\n", pev->netname.chars (), pev->health, pev->armorvalue, taskID, tasks[taskID], getTask ()->desire, weapon, getAmmoInClip (), getAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags.trim (), m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - game.time (), pev->movetype, enemy, pickup, personalities[m_personality]); debugData.assignf ("\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\nItem: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\nSP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\nEnemy=%s Pickup=%s Type=%s\n", pev->netname.chars (), m_healthValue, pev->armorvalue, taskID, tasks[taskID], getTask ()->desire, weapon, getAmmoInClip (), getAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags.trim (), m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - game.time (), pev->movetype, enemy, pickup, personalities[m_personality]);
MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, game.getLocalEntity ()) MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, game.getLocalEntity ())
.writeByte (TE_TEXTMESSAGE) .writeByte (TE_TEXTMESSAGE)
@ -5117,7 +5118,7 @@ void Bot::takeDamage (edict_t *inflictor, int damage, int armor, int bits) {
} }
else { else {
// attacked by an enemy // attacked by an enemy
if (pev->health > 60.0f) { if (m_healthValue > 60.0f) {
m_agressionLevel += 0.1f; m_agressionLevel += 0.1f;
if (m_agressionLevel > 1.0f) { if (m_agressionLevel > 1.0f) {
@ -5186,7 +5187,7 @@ void Bot::takeBlind (int alpha) {
m_blindSidemoveSpeed = -pev->maxspeed; m_blindSidemoveSpeed = -pev->maxspeed;
} }
if (pev->health < 85.0f) { if (m_healthValue < 85.0f) {
m_blindMoveSpeed = -pev->maxspeed; m_blindMoveSpeed = -pev->maxspeed;
} }
else if (m_personality == Personality::Careful) { else if (m_personality == Personality::Careful) {
@ -5208,8 +5209,8 @@ void Bot::updatePracticeValue (int damage) {
// only rate goal waypoint if bot died because of the damage // only rate goal waypoint if bot died because of the damage
// FIXME: could be done a lot better, however this cares most about damage done by sniping or really deadly weapons // FIXME: could be done a lot better, however this cares most about damage done by sniping or really deadly weapons
if (pev->health - damage <= 0) { if (m_healthValue - damage <= 0) {
graph.setDangerValue (m_team, m_chosenGoalIndex, m_prevGoalIndex, cr::clamp (graph.getDangerValue (m_team, m_chosenGoalIndex, m_prevGoalIndex) - static_cast <int> (pev->health / 20), -kMaxPracticeGoalValue, kMaxPracticeGoalValue)); graph.setDangerValue (m_team, m_chosenGoalIndex, m_prevGoalIndex, cr::clamp (graph.getDangerValue (m_team, m_chosenGoalIndex, m_prevGoalIndex) - static_cast <int> (m_healthValue / 20), -kMaxPracticeGoalValue, kMaxPracticeGoalValue));
} }
} }
@ -5245,7 +5246,7 @@ void Bot::updatePracticeDamage (edict_t *attacker, int damage) {
victimIndex = findNearestNode (); victimIndex = findNearestNode ();
} }
if (pev->health > 20.0f) { if (m_healthValue > 20.0f) {
if (victimTeam == Team::Terrorist || victimTeam == Team::CT) { if (victimTeam == Team::Terrorist || victimTeam == Team::CT) {
graph.setDangerDamage (victimIndex, victimIndex, victimIndex, cr::clamp (graph.getDangerDamage (victimTeam, victimIndex, victimIndex), 0, kMaxPracticeDamageValue)); graph.setDangerDamage (victimIndex, victimIndex, victimIndex, cr::clamp (graph.getDangerDamage (victimTeam, victimIndex, victimIndex), 0, kMaxPracticeDamageValue));
} }

View file

@ -883,7 +883,7 @@ void Bot::fireWeapons () {
} }
// use knife if near and good difficulty (l33t dude!) // use knife if near and good difficulty (l33t dude!)
if (cv_stab_close_enemies.bool_ () && m_difficulty >= Difficulty::Hard && pev->health > 80.0f && !game.isNullEntity (enemy) && pev->health >= enemy->v.health && distance < 100.0f && !isOnLadder () && !isGroupOfEnemies (pev->origin)) { if (cv_stab_close_enemies.bool_ () && m_difficulty >= Difficulty::Hard && m_healthValue > 80.0f && !game.isNullEntity (enemy) && m_healthValue >= enemy->v.health && distance < 100.0f && !isOnLadder () && !isGroupOfEnemies (pev->origin)) {
selectWeapons (distance, selectIndex, selectId, choosenWeapon); selectWeapons (distance, selectIndex, selectId, choosenWeapon);
return; return;
} }
@ -1039,7 +1039,7 @@ void Bot::attackMovement () {
approach = 29; approach = 29;
} }
else { else {
approach = static_cast <int> (pev->health * m_agressionLevel); approach = static_cast <int> (m_healthValue * m_agressionLevel);
if (usesSniper () && approach > 49) { if (usesSniper () && approach > 49) {
approach = 49; approach = 49;

View file

@ -754,9 +754,6 @@ int BotControl::cmdNodeUpload () {
msg ("you may notice the game freezes a bit during upload and issue request creation. Please, be patient."); msg ("you may notice the game freezes a bit during upload and issue request creation. Please, be patient.");
msg ("\n"); msg ("\n");
// six seconds is enough?
http.setTimeout (6);
// try to upload the file // try to upload the file
if (http.uploadFile (strings.format ("http://%s/graph", product.download), strings.format ("%sgraph/%s.graph", graph.getDataDirectory (false), game.getMapName ()))) { if (http.uploadFile (strings.format ("http://%s/graph", product.download), strings.format ("%sgraph/%s.graph", graph.getDataDirectory (false), game.getMapName ()))) {
msg ("Graph file was successfully validated and uploaded to the YaPB Graph DB (%s).", product.download); msg ("Graph file was successfully validated and uploaded to the YaPB Graph DB (%s).", product.download);

View file

@ -448,7 +448,7 @@ void Game::prepareBotArgs (edict_t *ent, String str) {
} }
// helper to parse single (not multi) command // helper to parse single (not multi) command
auto parsePartArgs = [&] (String &args) { auto parsePartArgs = [& ] (String &args) {
args.trim ("\r\n\t\" "); // trim new lines args.trim ("\r\n\t\" "); // trim new lines
// we're have empty commands? // we're have empty commands?
@ -465,17 +465,17 @@ void Game::prepareBotArgs (edict_t *ent, String str) {
// check if we're got a quoted string // check if we're got a quoted string
if (quote < args.length () && args[quote] == '\"') { if (quote < args.length () && args[quote] == '\"') {
m_botArgs.push (cr::move (args.substr (0, space))); // add command m_botArgs.emplace (args.substr (0, space)); // add command
m_botArgs.push (cr::move (args.substr (quote, args.length () - 1).trim ("\""))); // add string with trimmed quotes m_botArgs.emplace (args.substr (quote, args.length () - 1).trim ("\"")); // add string with trimmed quotes
} }
else { else {
for (auto &&arg : args.split (" ")) { for (auto &&arg : args.split (" ")) {
m_botArgs.push (cr::move (arg)); m_botArgs.emplace (arg);
} }
} }
} }
else { else {
m_botArgs.push (cr::move (args)); // move all the part to args m_botArgs.emplace (args); // move all the part to args
} }
MDLL_ClientCommand (ent); MDLL_ClientCommand (ent);

View file

@ -18,7 +18,7 @@
ConVar cv_graph_fixcamp ("yb_graph_fixcamp", "1", "Specifies whether bot should not 'fix' camp directions of camp waypoints when loading old PWF format."); ConVar cv_graph_fixcamp ("yb_graph_fixcamp", "1", "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 ("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);
void BotGraph::initGraph () { void BotGraph::reset () {
// this function initialize the graph structures.. // this function initialize the graph structures..
m_loadAttempts = 0; m_loadAttempts = 0;
@ -1515,7 +1515,7 @@ bool BotGraph::convertOldFormat () {
if (header.pointNumber == 0 || header.pointNumber > kMaxNodes) { if (header.pointNumber == 0 || header.pointNumber > kMaxNodes) {
return false; return false;
} }
initGraph (); reset ();
m_paths.clear (); m_paths.clear ();
for (int i = 0; i < header.pointNumber; ++i) { for (int i = 0; i < header.pointNumber; ++i) {
@ -1578,8 +1578,6 @@ template <typename U> bool BotGraph::saveStorage (StringRef ext, StringRef name,
// no open no fun // no open no fun
if (!file) { if (!file) {
logger.error ("Unable to open %s file for writing (filename: '%s').", name, filename); logger.error ("Unable to open %s file for writing (filename: '%s').", name, filename);
file.close ();
return false; return false;
} }
int32 rawLength = data.template length <int32> () * sizeof (U); int32 rawLength = data.template length <int32> () * sizeof (U);
@ -1609,11 +1607,8 @@ template <typename U> bool BotGraph::saveStorage (StringRef ext, StringRef name,
} }
else { else {
logger.error ("Unable to compress %s data (filename: '%s').", name, filename); logger.error ("Unable to compress %s data (filename: '%s').", name, filename);
file.close ();
return false; return false;
} }
file.close ();
return true; return true;
} }
@ -1758,7 +1753,7 @@ template <typename U> bool BotGraph::loadStorage (StringRef ext, StringRef name,
if ((hdr.options & StorageOption::Exten) && exten != nullptr) { if ((hdr.options & StorageOption::Exten) && exten != nullptr) {
file.read (exten, sizeof (ExtenHeader)); file.read (exten, sizeof (ExtenHeader));
} }
game.print ("Successfully loaded Bots %s data v%d.0 (%d/%.2fMB).", name, hdr.version, m_paths.length (), static_cast <float> (data.capacity () * sizeof (U)) / 1024.0f / 1024.0f); game.print ("Successfully loaded Bots %s data v%d (%d/%.2fMB).", name, hdr.version, m_paths.length (), static_cast <float> (data.capacity () * sizeof (U)) / 1024.0f / 1024.0f);
file.close (); file.close ();
return true; return true;
@ -1779,7 +1774,7 @@ bool BotGraph::loadGraphData () {
bool dataLoaded = loadStorage <Path> ("graph", "Graph", StorageOption::Graph, StorageVersion::Graph, m_paths, &exten, &outOptions); bool dataLoaded = loadStorage <Path> ("graph", "Graph", StorageOption::Graph, StorageVersion::Graph, m_paths, &exten, &outOptions);
if (dataLoaded) { if (dataLoaded) {
initGraph (); reset ();
initBuckets (); initBuckets ();
// add data to buckets // add data to buckets
@ -2783,7 +2778,7 @@ void BotGraph::eraseFromDisk () {
logger.error ("Unable to open %s", item); logger.error ("Unable to open %s", item);
} }
} }
initGraph (); // reintialize points reset (); // reintialize points
m_paths.clear (); m_paths.clear ();
} }

View file

@ -62,7 +62,7 @@ namespace variadic {
vsnprintf (buffer, StringBuffer::StaticBufferSize, format, ap); vsnprintf (buffer, StringBuffer::StaticBufferSize, format, ap);
va_end (ap); va_end (ap);
if (ent && (ent->v.flags & (FL_FAKECLIENT | FL_DORMANT))) { if (util.isFakeClient (ent)) {
auto bot = bots[ent]; auto bot = bots[ent];
if (bot) { if (bot) {
@ -359,7 +359,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int) {
if (game.is (GameFlags::Xash3D)) { if (game.is (GameFlags::Xash3D)) {
bots.kickEveryone (true, false); bots.kickEveryone (true, false);
} }
graph.initGraph (); graph.reset ();
// clear all the bots // clear all the bots
bots.destroy (); bots.destroy ();

View file

@ -1035,6 +1035,7 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member) {
m_agressionLevel = m_baseAgressionLevel; m_agressionLevel = m_baseAgressionLevel;
m_fearLevel = m_baseFearLevel; m_fearLevel = m_baseFearLevel;
m_nextEmotionUpdate = game.time () + 0.5f; m_nextEmotionUpdate = game.time () + 0.5f;
m_healthValue = bot->v.health;
// just to be sure // just to be sure
m_msgQueue.clear (); m_msgQueue.clear ();

View file

@ -820,7 +820,7 @@ bool Bot::updateNavigation () {
// add goal values // add goal values
int goalValue = graph.getDangerValue (m_team, m_chosenGoalIndex, m_currentNodeIndex); int goalValue = graph.getDangerValue (m_team, m_chosenGoalIndex, m_currentNodeIndex);
int addedValue = static_cast <int> (pev->health * 0.5f + m_goalValue * 0.5f); int addedValue = static_cast <int> (m_healthValue * 0.5f + m_goalValue * 0.5f);
goalValue = cr::clamp (goalValue + addedValue, -kMaxPracticeGoalValue, kMaxPracticeGoalValue); goalValue = cr::clamp (goalValue + addedValue, -kMaxPracticeGoalValue, kMaxPracticeGoalValue);
@ -1416,8 +1416,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
if (m_routeQue.length () >= kMaxRouteLength - 1) { if (m_routeQue.length () >= kMaxRouteLength - 1) {
logger.error ("A* Search for bot \"%s\" has tried to build path with at least %d nodes. Seems to be graph is broken.", pev->netname.chars (), m_routeQue.length ()); logger.error ("A* Search for bot \"%s\" has tried to build path with at least %d nodes. Seems to be graph is broken.", pev->netname.chars (), m_routeQue.length ());
// bail out to shortest path kick (); // kick the bot off...
findShortestPath (srcIndex, destIndex);
return; return;
} }