bot: implemented asynchronous pathfinding
nav: floyd-warshall matrices and practice updates are done asynchronously by now add: yb_threadpool_workers cvar, that controls number of worker threads bot will use add: cv_autovacate_keep_slots, the amount of slots to keep by auto vacate aim: enemy prediction is now done asynchronously by now bot: minor fixes and refactoring, including analyze suspend mistake (ref #441) note: the master builds are now NOT production ready, please test before installing on real servers!
This commit is contained in:
parent
e7712a551a
commit
a616f25b1a
30 changed files with 743 additions and 421 deletions
|
|
@ -146,6 +146,9 @@ public:
|
|||
// initialize levels
|
||||
void levelInitialize (edict_t *entities, int max);
|
||||
|
||||
// shutdown levels
|
||||
void levelShutdown ();
|
||||
|
||||
// display world line
|
||||
void drawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, const Color &color, int brightness, int speed, int life, DrawLine type = DrawLine::Simple);
|
||||
|
||||
|
|
@ -688,8 +691,7 @@ public:
|
|||
int close (DLSYM_HANDLE module) {
|
||||
if (m_self.handle () == module) {
|
||||
disable ();
|
||||
|
||||
return m_dlclose (module);
|
||||
return m_dlclose (module);
|
||||
}
|
||||
return m_dlclose (module);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,6 @@ private:
|
|||
int m_lastJumpNode {};
|
||||
int m_findWPIndex {};
|
||||
int m_facingAtIndex {};
|
||||
int m_highestDamage[kGameTeamNum] {};
|
||||
int m_autoSaveCount {};
|
||||
|
||||
float m_timeJumpStarted {};
|
||||
|
|
@ -265,14 +264,6 @@ public:
|
|||
return m_paths.length () / 2;
|
||||
}
|
||||
|
||||
int getHighestDamageForTeam (int team) const {
|
||||
return cr::max (1, m_highestDamage[team]);
|
||||
}
|
||||
|
||||
void setHighestDamageForTeam (int team, int value) {
|
||||
m_highestDamage[team] = value;
|
||||
}
|
||||
|
||||
StringRef getAuthor () const {
|
||||
return m_graphAuthor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ private:
|
|||
float m_maintainTime {}; // time to maintain bot creation
|
||||
float m_quotaMaintainTime {}; // time to maintain bot quota
|
||||
float m_grenadeUpdateTime {}; // time to update active grenades
|
||||
float m_entityUpdateTime {}; // time to update intresting entities
|
||||
float m_entityUpdateTime {}; // time to update interesting entities
|
||||
float m_plantSearchUpdateTime {}; // time to update for searching planted bomb
|
||||
float m_lastChatTime {}; // global chat time timestamp
|
||||
float m_timeBombPlanted {}; // time the bomb were planted
|
||||
|
|
@ -81,7 +81,7 @@ private:
|
|||
bool m_roundOver {};
|
||||
|
||||
Array <edict_t *> m_activeGrenades {}; // holds currently active grenades on the map
|
||||
Array <edict_t *> m_intrestingEntities {}; // holds currently intresting entities on the map
|
||||
Array <edict_t *> m_interestingEntities {}; // holds currently interesting entities on the map
|
||||
|
||||
Deque <String> m_saveBotNames {}; // bots names that persist upon changelevel
|
||||
Deque <BotRequest> m_addRequests {}; // bot creation tab
|
||||
|
|
@ -143,7 +143,7 @@ public:
|
|||
void initFilters ();
|
||||
void resetFilters ();
|
||||
void updateActiveGrenade ();
|
||||
void updateIntrestingEntities ();
|
||||
void updateInterestingEntities ();
|
||||
void captureChatRadio (const char *cmd, const char *arg, edict_t *ent);
|
||||
void notifyBombDefuse ();
|
||||
void execGameEntity (edict_t *ent);
|
||||
|
|
@ -160,16 +160,16 @@ public:
|
|||
return m_activeGrenades;
|
||||
}
|
||||
|
||||
const Array <edict_t *> &getIntrestingEntities () {
|
||||
return m_intrestingEntities;
|
||||
const Array <edict_t *> &getInterestingEntities () {
|
||||
return m_interestingEntities;
|
||||
}
|
||||
|
||||
bool hasActiveGrenades () const {
|
||||
return !m_activeGrenades.empty ();
|
||||
}
|
||||
|
||||
bool hasIntrestingEntities () const {
|
||||
return !m_intrestingEntities.empty ();
|
||||
bool hasInterestingEntities () const {
|
||||
return !m_interestingEntities.empty ();
|
||||
}
|
||||
|
||||
bool checkTeamEco (int team) const {
|
||||
|
|
@ -302,5 +302,31 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// bot async worker wrapper
|
||||
class BotThreadWorker final : public Singleton <BotThreadWorker> {
|
||||
private:
|
||||
ThreadPool m_botWorker;
|
||||
|
||||
public:
|
||||
BotThreadWorker () = default;
|
||||
~BotThreadWorker () = default;
|
||||
|
||||
public:
|
||||
void shutdown ();
|
||||
void startup (int workers);
|
||||
|
||||
public:
|
||||
template <typename F> void enqueue (F &&fn) {
|
||||
if (m_botWorker.threadCount () == 0) {
|
||||
fn (); // no threads, no fun, just run task in current thread
|
||||
return;
|
||||
}
|
||||
m_botWorker.enqueue (cr::forward <F> (fn));
|
||||
}
|
||||
};
|
||||
|
||||
// explose global
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (BotManager, bots);
|
||||
|
||||
// expose async worker
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (BotThreadWorker, worker);
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ public:
|
|||
};
|
||||
|
||||
// A* algorithm for bots
|
||||
class AStarAlgo final {
|
||||
class AStarAlgo final : public DenyCopying {
|
||||
public:
|
||||
using HeuristicFn = Heuristic::Func;
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ private:
|
|||
Array <int> m_smoothedPath;
|
||||
|
||||
private:
|
||||
// cleares the currently built route
|
||||
// clears the currently built route
|
||||
void clearRoute ();
|
||||
|
||||
// can the node can be skipped?
|
||||
|
|
@ -118,6 +118,10 @@ private:
|
|||
void postSmooth (NodeAdderFn onAddedNode);
|
||||
|
||||
public:
|
||||
explicit AStarAlgo (const int length) {
|
||||
init (length);
|
||||
}
|
||||
|
||||
AStarAlgo () = default;
|
||||
~AStarAlgo () = default;
|
||||
|
||||
|
|
@ -180,6 +184,9 @@ public:
|
|||
|
||||
private:
|
||||
// create floyd matrics
|
||||
void syncRebuild ();
|
||||
|
||||
// async rebuild
|
||||
void rebuild ();
|
||||
|
||||
public:
|
||||
|
|
@ -201,6 +208,9 @@ public:
|
|||
|
||||
// dijkstra shortest path algorithm
|
||||
class DijkstraAlgo final {
|
||||
private:
|
||||
mutable Mutex m_cs {};
|
||||
|
||||
private:
|
||||
using Route = Twin <int, int>;
|
||||
|
||||
|
|
@ -215,11 +225,6 @@ public:
|
|||
DijkstraAlgo () = default;
|
||||
~DijkstraAlgo () = default;
|
||||
|
||||
|
||||
private:
|
||||
// reset pathfinder state to defaults
|
||||
void resetState ();
|
||||
|
||||
public:
|
||||
// initialize dijkstra with valid path length
|
||||
void init (const int length);
|
||||
|
|
@ -236,7 +241,6 @@ class PathPlanner : public Singleton <PathPlanner> {
|
|||
private:
|
||||
UniquePtr <DijkstraAlgo> m_dijkstra;
|
||||
UniquePtr <FloydWarshallAlgo> m_floyd;
|
||||
UniquePtr <AStarAlgo > m_astar;
|
||||
bool m_memoryLimitHit {};
|
||||
|
||||
public:
|
||||
|
|
@ -261,17 +265,15 @@ public:
|
|||
return m_floyd.get ();
|
||||
}
|
||||
|
||||
// get the floyd algo
|
||||
decltype (auto) getAStar () {
|
||||
return m_astar.get ();
|
||||
}
|
||||
|
||||
public:
|
||||
// do the pathfinding
|
||||
bool find (int srcIndex, int destIndex, NodeAdderFn onAddedNode, int *pathDistance = nullptr);
|
||||
|
||||
// distance between two nodes with pathfinder
|
||||
int dist (int srcIndex, int destIndex);
|
||||
float dist (int srcIndex, int destIndex);
|
||||
|
||||
// get the precise distanace regardless of cvar
|
||||
float preciseDistance (int srcIndex, int destIndex);
|
||||
};
|
||||
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (PathPlanner, planner);
|
||||
|
|
|
|||
|
|
@ -80,17 +80,22 @@ public:
|
|||
DangerSaveRestore (const DangerStorage &ds, const PracticeData &pd) : danger (ds), data (pd) {}
|
||||
};
|
||||
|
||||
private:
|
||||
HashMap <DangerStorage, PracticeData> m_data {};
|
||||
int32_t m_teamHighestDamage[kGameTeamNum] {};
|
||||
|
||||
// avoid concurrent access to practice
|
||||
mutable Mutex m_damageUpdateLock {};
|
||||
|
||||
public:
|
||||
BotPractice () = default;
|
||||
~BotPractice () = default;
|
||||
|
||||
private:
|
||||
inline bool exists (int32_t team, int32_t start, int32_t goal) const {
|
||||
bool exists (int32_t team, int32_t start, int32_t goal) const {
|
||||
return m_data.exists ({ start, goal, team });
|
||||
}
|
||||
void syncUpdate ();
|
||||
|
||||
public:
|
||||
int32_t getIndex (int32_t team, int32_t start, int32_t goal);
|
||||
|
|
@ -102,17 +107,21 @@ public:
|
|||
int32_t getDamage (int32_t team, int32_t start, int32_t goal);
|
||||
void setDamage (int32_t team, int32_t start, int32_t goal, int32_t value);
|
||||
|
||||
// interlocked get damage
|
||||
float plannerGetDamage (int32_t team, int32_t start, int32_t goal, bool addTeamHighestDamage);
|
||||
|
||||
public:
|
||||
void update ();
|
||||
void load ();
|
||||
void save ();
|
||||
|
||||
public:
|
||||
int32_t getHighestDamageForTeam (int32_t team) const {
|
||||
return cr::max (1, m_teamHighestDamage[team]);
|
||||
template <typename U = int32_t> U getHighestDamageForTeam (int32_t team) const {
|
||||
return static_cast <U> (cr::max (1, m_teamHighestDamage[team]));
|
||||
}
|
||||
|
||||
void setHighestDamageForTeam (int32_t team, int32_t value) {
|
||||
MutexScopedLock lock (m_damageUpdateLock);
|
||||
m_teamHighestDamage[team] = value;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
#pragma once
|
||||
|
||||
class BotSupport final : public Singleton <BotSupport> {
|
||||
private:
|
||||
mutable Mutex m_cs {};
|
||||
|
||||
private:
|
||||
bool m_needToSendWelcome {};
|
||||
float m_welcomeReceiveTime {};
|
||||
|
|
@ -78,6 +81,9 @@ public:
|
|||
// generates ping bitmask for SVC_PINGS message
|
||||
int getPingBitmask (edict_t *ent, int loss, int ping);
|
||||
|
||||
// calculate our own pings for all the players
|
||||
void syncCalculatePings ();
|
||||
|
||||
// calculate our own pings for all the players
|
||||
void calculatePings ();
|
||||
|
||||
|
|
|
|||
28
inc/yapb.h
28
inc/yapb.h
|
|
@ -663,6 +663,10 @@ class Bot final {
|
|||
public:
|
||||
friend class BotManager;
|
||||
|
||||
private:
|
||||
mutable Mutex m_pathFindLock {};
|
||||
mutable Mutex m_predictLock {};
|
||||
|
||||
private:
|
||||
uint32_t m_states {}; // sensing bitstates
|
||||
uint32_t m_collideMoves[kMaxCollideMoves] {}; // sorted array of movements
|
||||
|
|
@ -689,8 +693,10 @@ private:
|
|||
int m_tryOpenDoor {}; // attempt's to open the door
|
||||
int m_liftState {}; // state of lift handling
|
||||
int m_radioSelect {}; // radio entry
|
||||
int m_lastPredictIndex {}; // last predicted index
|
||||
|
||||
|
||||
int m_lastPredictIndex { kInvalidNodeIndex }; // last predicted path index
|
||||
int m_lastPredictLength {}; // last predicted path length
|
||||
|
||||
float m_headedTime {};
|
||||
float m_prevTime {}; // time previously checked movement speed
|
||||
float m_heavyTimestamp {}; // is it time to execute heavy-weight functions
|
||||
|
|
@ -747,6 +753,7 @@ private:
|
|||
|
||||
bool m_moveToGoal {}; // bot currently moving to goal??
|
||||
bool m_isStuck {}; // bot is stuck
|
||||
bool m_isStale {}; // bot is leaving server
|
||||
bool m_isReloading {}; // bot is reloading a gun
|
||||
bool m_forceRadio {}; // should bot use radio anyway?
|
||||
bool m_defendedBomb {}; // defend action issued
|
||||
|
|
@ -773,6 +780,7 @@ private:
|
|||
FindPath m_pathType {}; // which pathfinder to use
|
||||
uint8_t m_enemyParts {}; // visibility flags
|
||||
TraceResult m_lastTrace[TraceChannel::Num] {}; // last trace result
|
||||
UniquePtr <class AStarAlgo> m_planner;
|
||||
|
||||
edict_t *m_pickupItem {}; // pointer to entity of item to use/pickup
|
||||
edict_t *m_itemIgnore {}; // pointer to entity to ignore for pickup
|
||||
|
|
@ -888,7 +896,6 @@ private:
|
|||
|
||||
void doPlayerAvoidance (const Vector &normal);
|
||||
void selectCampButtons (int index);
|
||||
void markStale ();
|
||||
void instantChatter (int type);
|
||||
void update ();
|
||||
void runMovement ();
|
||||
|
|
@ -920,6 +927,7 @@ private:
|
|||
void findShortestPath (int srcIndex, int destIndex);
|
||||
void calculateFrustum ();
|
||||
void findPath (int srcIndex, int destIndex, FindPath pathType = FindPath::Fast);
|
||||
void syncFindPath (int srcIndex, int destIndex, FindPath pathType);
|
||||
void debugMsgInternal (const char *str);
|
||||
void frame ();
|
||||
void resetCollision ();
|
||||
|
|
@ -933,6 +941,7 @@ private:
|
|||
void decideFollowUser ();
|
||||
void attackMovement ();
|
||||
void findValidNode ();
|
||||
void setPathOrigin ();
|
||||
void fireWeapons ();
|
||||
void selectWeapons (float distance, int index, int id, int choosen);
|
||||
void focusEnemy ();
|
||||
|
|
@ -940,6 +949,9 @@ private:
|
|||
void selectSecondary ();
|
||||
void selectWeaponById (int id);
|
||||
void selectWeaponByIndex (int index);
|
||||
void refreshEnemyPredict ();
|
||||
void syncUpdatePredictedIndex ();
|
||||
void updatePredictedIndex ();
|
||||
|
||||
void completeTask ();
|
||||
void executeTasks ();
|
||||
|
|
@ -1094,11 +1106,13 @@ public:
|
|||
bool m_hasNVG {}; // does bot has nightvision goggles
|
||||
bool m_usesNVG {}; // does nightvision goggles turned on
|
||||
bool m_hasC4 {}; // does bot has c4 bomb
|
||||
bool m_hasHostage {}; // does bot owns some hostages
|
||||
bool m_hasProgressBar {}; // has progress bar on a HUD
|
||||
bool m_jumpReady {}; // is double jump ready
|
||||
bool m_canChooseAimDirection {}; // can choose aiming direction
|
||||
bool m_isEnemyReachable {}; // direct line to enemy
|
||||
bool m_kickedByRotation {}; // is bot kicked due to rotation ?
|
||||
bool m_kickMeFromServer {}; // kick the bot off the server?
|
||||
|
||||
edict_t *m_doubleJumpEntity {}; // pointer to entity that request double jump
|
||||
edict_t *m_radioEntity {}; // pointer to entity issuing a radio command
|
||||
|
|
@ -1121,7 +1135,11 @@ public:
|
|||
|
||||
public:
|
||||
Bot (edict_t *bot, int difficulty, int personality, int team, int skin);
|
||||
~Bot () = default;
|
||||
|
||||
// need to wait until all threads will finish it's work before terminating bot object
|
||||
~Bot () {
|
||||
MutexScopedLock lock (m_pathFindLock);
|
||||
}
|
||||
|
||||
public:
|
||||
void logic (); /// the things that can be executed while skipping frames
|
||||
|
|
@ -1154,7 +1172,7 @@ public:
|
|||
void resetDoubleJump ();
|
||||
void startDoubleJump (edict_t *ent);
|
||||
void sendBotToOrigin (const Vector &origin);
|
||||
|
||||
void markStale ();
|
||||
bool hasHostage ();
|
||||
bool usesRifle ();
|
||||
bool usesPistol ();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue