Type 2 connections for navbot (vischeck)

This commit is contained in:
TotallyNotElite 2018-08-21 17:54:54 +02:00
parent d5c135df89
commit 126cd0028e
2 changed files with 269 additions and 178 deletions

View File

@ -24,32 +24,22 @@ bool Prepare();
void CreateMove();
void Draw();
struct inactivityTracker
size_t FindInVector(size_t id);
class inactivityTracker
{
// Map for storing inactivity per connection
std::map<std::pair<int, int>, unsigned int> inactives;
void reset()
std::map<std::pair<int, int>, std::pair<int, unsigned int>> inactives;
bool vischeckConnection(std::pair<int, int> &connection)
{
// inactives.clear();
Vector begin = areas.at(FindInVector(connection.first)).m_center;
Vector end = areas.at(FindInVector(connection.second)).m_center;
begin.z += 72;
end.z += 72;
bool result = IsVectorVisible(begin, end, false);
return result;
}
void addTime(std::pair<int, int> connection, Timer &timer,
bool &resetPather)
{
if (inactives.find(connection) == inactives.end())
{
inactives[connection] = 0;
}
inactives[connection] =
inactives[connection] +
(std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - timer.last)
.count());
if (inactives[connection] >= 5000)
resetPather = true;
}
void addTime(std::pair<Vector, Vector> connection, Timer &timer,
bool &resetPather)
std::pair<int, int> VectorToId(std::pair<Vector, Vector> &connection)
{
CNavArea *currnode = nullptr;
for (size_t i = 0; i < areas.size(); i++)
@ -61,7 +51,7 @@ struct inactivityTracker
}
}
if (!currnode)
return;
return { -1, -1 };
CNavArea *nextnode = nullptr;
for (size_t i = 0; i < areas.size(); i++)
@ -73,21 +63,197 @@ struct inactivityTracker
}
}
if (!nextnode)
return;
return { -1, -1 };
for (auto i : currnode->m_connections)
{
if (i.area->m_id == nextnode->m_id)
{
addTime(std::pair{ currnode->m_id, nextnode->m_id }, timer,
resetPather);
return;
return { currnode->m_id, nextnode->m_id };
}
}
}
unsigned int getTime(std::pair<int, int> connection)
public:
void reset()
{
for (auto i : inactives)
{
// What is this tf
i.second.second = 0;
}
}
bool IsIgnored(std::pair<int, int> connection)
{
if (inactives.find(connection) == inactives.end())
{
return false;
}
auto &pair = inactives.at(connection);
if (pair.second >= 5000)
{
pair.first = 1;
return true;
}
if (pair.first == 2 && !vischeckConnection(connection))
{
logging::Info(
"Ignored a connection due to type 2 connection type.");
return true;
}
return false;
}
void AddTime(std::pair<int, int> connection, Timer &timer,
bool &resetPather)
{
if (inactives.find(connection) == inactives.end())
{
inactives[connection] = { 0, 0 };
}
auto &pair = inactives.at(connection);
pair.second = pair.second +
(std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - timer.last)
.count());
if (pair.second >= 5000)
resetPather = true;
}
void AddTime(std::pair<Vector, Vector> connection, Timer &timer,
bool &resetPather)
{
auto pair = VectorToId(connection);
if (pair.first == -1 || pair.second == -1)
return;
AddTime(pair, timer, resetPather);
}
bool CheckType2(std::pair<int, int> connection)
{
// Fix calls vischeckConnection too often
if (inactives.find(connection) == inactives.end())
{
inactives[connection] = { 0, 0 };
}
auto pair = inactives.at(connection);
switch (pair.first)
{
case 0:
if (!vischeckConnection(connection))
{
inactives[connection].first = 2;
return true;
}
case 1:
case 2:
return false;
}
}
bool CheckType2(std::pair<Vector, Vector> connection)
{
auto pair = VectorToId(connection);
if (pair.first == -1 || pair.second == -1)
return false;
return CheckType2(pair);
}
}; // namespace nav
struct MAP : public micropather::Graph
{
std::unique_ptr<micropather::MicroPather> pather;
// Maps already utilize dynamic allocation and we don't need a custom
// constructor
inactivityTracker inactiveTracker;
Vector GetClosestCornerToArea(CNavArea *CornerOf, CNavArea *Target)
{
std::array<Vector, 4> corners;
corners.at(0) = CornerOf->m_nwCorner; // NW
corners.at(1) = CornerOf->m_seCorner; // SE
corners.at(2) = Vector{ CornerOf->m_seCorner.x, CornerOf->m_nwCorner.y,
CornerOf->m_nwCorner.z }; // NE
corners.at(3) = Vector{ CornerOf->m_nwCorner.x, CornerOf->m_seCorner.y,
CornerOf->m_seCorner.z }; // SW
Vector bestVec;
float bestDist = FLT_MAX;
for (size_t i = 0; i < corners.size(); i++)
{
float dist = corners.at(i).DistTo(Target->m_center);
if (dist < bestDist)
{
bestVec = corners.at(i);
bestDist = dist;
}
}
Vector bestVec2;
float bestDist2 = FLT_MAX;
for (size_t i = 0; i < corners.size(); i++)
{
if (corners.at(i) == bestVec2)
continue;
float dist = corners.at(i).DistTo(Target->m_center);
if (dist < bestDist2)
{
bestVec2 = corners.at(i);
bestDist2 = dist;
}
}
return (bestVec + bestVec2) / 2;
}
float GetZBetweenAreas(CNavArea *start, CNavArea *end)
{
float z1 = GetClosestCornerToArea(start, end).z;
float z2 = GetClosestCornerToArea(end, start).z;
return z2 - z1;
}
// Function required by MicroPather for getting an estimated cost
float LeastCostEstimate(void *stateStart, void *stateEnd)
{
CNavArea *start = static_cast<CNavArea *>(stateStart);
CNavArea *end = static_cast<CNavArea *>(stateEnd);
float dist = start->m_center.DistTo(end->m_center);
return dist;
}
// Function required by MicroPather to retrieve neighbours and their
// associated costs.
void AdjacentCost(void *state, MP_VECTOR<micropather::StateCost> *adjacent)
{
CNavArea *area = static_cast<CNavArea *>(state);
auto &neighbours = area->m_connections;
for (auto i : neighbours)
{
if (GetZBetweenAreas(area, i.area) > 42)
continue;
if (inactiveTracker.IsIgnored(
std::pair{ area->m_id, i.area->m_id }))
continue;
micropather::StateCost cost;
cost.state =
static_cast<void *>(&areas.at(FindInVector(i.area->m_id)));
cost.cost = area->m_center.DistTo(i.area->m_center);
adjacent->push_back(cost);
}
}
void PrintStateInfo(void *state)
{
CNavArea *area = static_cast<CNavArea *>(state);
logging::Info(format(area->m_center.x, " ", area->m_center.y, " ",
area->m_center.z)
.c_str());
}
MAP(size_t size)
{
pather =
std::make_unique<micropather::MicroPather>(this, size, 6, true);
}
~MAP()
{
return inactives[connection];
}
};

View File

@ -8,12 +8,15 @@ std::vector<CNavArea> areas;
bool init = false;
static bool pathfinding = true;
bool ReadyForCommands = false;
inactivityTracker inactiveTracker;
static settings::Bool enabled{ "misc.pathing", "true" };
static settings::Bool draw{ "misc.pathing.draw", "false" };
static bool threadingFinished = false;
static std::atomic<bool> threadingFinished;
// Todo fix
static std::unique_ptr<MAP> TF2MAP;
// Function to get place in Vector by connection ID
// Todo: find an alternative for this, maybe a map for storing ptrs to the
// std::vector?
size_t FindInVector(size_t id)
{
for (size_t i = 0; i < areas.size(); i++)
@ -23,110 +26,6 @@ size_t FindInVector(size_t id)
}
}
struct MAP : public micropather::Graph
{
std::unique_ptr<micropather::MicroPather> pather;
// Function to check if a connection is ignored
bool IsIgnored(size_t currState, size_t connectionID)
{
if (inactiveTracker.getTime({ currState, connectionID }) >= 5000)
return true;
return false;
}
Vector GetClosestCornerToArea(CNavArea *CornerOf, CNavArea *Target)
{
std::array<Vector, 4> corners;
corners.at(0) = CornerOf->m_nwCorner; // NW
corners.at(1) = CornerOf->m_seCorner; // SE
corners.at(2) = Vector{ CornerOf->m_seCorner.x, CornerOf->m_nwCorner.y,
CornerOf->m_nwCorner.z }; // NE
corners.at(3) = Vector{ CornerOf->m_nwCorner.x, CornerOf->m_seCorner.y,
CornerOf->m_seCorner.z }; // SW
Vector bestVec;
float bestDist = FLT_MAX;
for (size_t i = 0; i < corners.size(); i++)
{
float dist = corners.at(i).DistTo(Target->m_center);
if (dist < bestDist)
{
bestVec = corners.at(i);
bestDist = dist;
}
}
Vector bestVec2;
float bestDist2 = FLT_MAX;
for (size_t i = 0; i < corners.size(); i++)
{
if (corners.at(i) == bestVec2)
continue;
float dist = corners.at(i).DistTo(Target->m_center);
if (dist < bestDist2)
{
bestVec2 = corners.at(i);
bestDist2 = dist;
}
}
return (bestVec + bestVec2) / 2;
}
float GetZBetweenAreas(CNavArea *start, CNavArea *end)
{
float z1 = GetClosestCornerToArea(start, end).z;
float z2 = GetClosestCornerToArea(end, start).z;
return z2 - z1;
}
// Function required by MicroPather for getting an estimated cost
float LeastCostEstimate(void *stateStart, void *stateEnd)
{
CNavArea *start = static_cast<CNavArea *>(stateStart);
CNavArea *end = static_cast<CNavArea *>(stateEnd);
float dist = start->m_center.DistTo(end->m_center);
return dist;
}
// Function required by MicroPather to retrieve neighbours and their
// associated costs.
void AdjacentCost(void *state, MP_VECTOR<micropather::StateCost> *adjacent)
{
CNavArea *area = static_cast<CNavArea *>(state);
auto &neighbours = area->m_connections;
for (auto i : neighbours)
{
if (GetZBetweenAreas(area, i.area) > 42)
continue;
if (IsIgnored(area->m_id, i.area->m_id))
continue;
micropather::StateCost cost;
cost.state =
static_cast<void *>(&areas.at(FindInVector(i.area->m_id)));
cost.cost = area->m_center.DistTo(i.area->m_center);
adjacent->push_back(cost);
}
}
void PrintStateInfo(void *state)
{
CNavArea *area = static_cast<CNavArea *>(state);
logging::Info(format(area->m_center.x, " ", area->m_center.y, " ",
area->m_center.z)
.c_str());
}
MAP(size_t size)
{
pather =
std::make_unique<micropather::MicroPather>(this, size, 6, true);
}
~MAP()
{
}
};
static std::unique_ptr<MAP> TF2MAP;
void Init()
{
// Get NavFile location
@ -161,10 +60,11 @@ void Init()
size = 7000;
// Initiate "Map", contains micropather object
TF2MAP = std::make_unique<MAP>(size);
TF2MAP->inactiveTracker.reset();
}
if (!areas.empty())
pathfinding = true;
threadingFinished = true;
threadingFinished.store(true);
}
static std::string lastmap;
@ -175,6 +75,7 @@ bool Prepare()
return false;
if (!init)
{
// Don't reinit if same map
if (lastmap == g_IEngine->GetLevelName())
{
init = true;
@ -184,12 +85,14 @@ bool Prepare()
lastmap = g_IEngine->GetLevelName();
pathfinding = false;
init = true;
threadingFinished = false;
threadingFinished.store(false);
// Parsing CNavFile takes time, run it in a seperate thread
std::thread initer(Init);
// We need to either detach or join to avoid std::terminate
initer.detach();
}
}
if (!pathfinding || !threadingFinished)
if (!pathfinding || !threadingFinished.load())
return false;
return true;
}
@ -211,7 +114,7 @@ int findClosestNavSquare(Vector vec)
// Make sure we're not stuck on the same area for too long
if (std::count(findClosestNavSquare_localAreas.begin(),
findClosestNavSquare_localAreas.end(), i) < 3)
overlapping.emplace_back( i, &areas.at(i) );
overlapping.emplace_back(i, &areas.at(i));
}
}
@ -290,8 +193,8 @@ static Timer lastJump{};
static std::vector<Vector> crumbs;
// Bot will keep trying to get to the target even if it fails a few times
static bool ensureArrival;
// Priority value for current instructions, only higher priority can overwrite
// itlocalAreas
// Priority value for current instructions, only higher or equal priorites can
// overwrite it
int priority = 0;
static Vector lastArea = { 0.0f, 0.0f, 0.0f };
@ -315,12 +218,13 @@ bool NavTo(Vector dest, bool navToLocalCenter, bool persistent,
if (!crumbs.empty())
{
bool reset = false;
inactiveTracker.addTime({ lastArea, crumbs.at(0) }, inactivity, reset);
TF2MAP->inactiveTracker.AddTime({ lastArea, crumbs.at(0) }, inactivity,
reset);
if (reset)
TF2MAP->pather->Reset();
}
crumbs.clear();
crumbs = std::move(path);
crumbs = std::move(path);
lastArea = crumbs.at(0);
if (!navToLocalCenter && crumbs.size() > 1)
crumbs.erase(crumbs.begin());
@ -337,21 +241,43 @@ void clearInstructions()
}
static Timer ignoreReset{};
static Timer patherReset{};
// Function for removing ignores
void clearIgnores()
{
if (ignoreReset.test_and_set(180000))
if (!TF2MAP || !TF2MAP->pather)
return;
if (ignoreReset.test_and_set(120000))
TF2MAP->inactiveTracker.reset();
if (patherReset.test_and_set(30000))
TF2MAP->pather->Reset();
}
void Repath()
{
if (ensureArrival)
{
inactiveTracker.reset();
if (TF2MAP && TF2MAP->pather)
TF2MAP->pather->Reset();
logging::Info("Pathing: NavBot inactive for too long. Ignoring "
"connection and finding another path...");
// Throwaway int
int i1, i2;
// Find a new path
crumbs = findPath(g_pLocalPlayer->v_Origin, crumbs.back(), i1, i2);
}
else
{
logging::Info(
"Pathing: NavBot inactive for too long. Canceling tasks and "
"ignoring connection...");
// Wait for new instructions
crumbs.clear();
}
}
// Main movement function, gets path from NavTo
void CreateMove()
{
if (!enabled || !threadingFinished)
if (!enabled || !threadingFinished.load())
return;
if (CE_BAD(LOCAL_E))
return;
@ -386,30 +312,26 @@ void CreateMove()
if (crumbs.at(0).z - g_pLocalPlayer->v_Origin.z > 18 &&
lastJump.test_and_set(200))
current_user_cmd->buttons |= IN_JUMP;
// Check if were dealing with a type 2 connection
if (inactivity.check(3000) &&
TF2MAP->inactiveTracker.CheckType2({ lastArea, crumbs.at(0) }))
{
logging::Info("Pathing: Type 2 connection detected!");
TF2MAP->pather->Reset();
Repath();
inactivity.update();
return;
}
// If inactive for too long
if (inactivity.check(5000))
{
// Ignore connection
bool i3 = false;
inactiveTracker.addTime({ lastArea, crumbs.at(0) }, inactivity, i3);
if (i3)
bool resetPather = false;
TF2MAP->inactiveTracker.AddTime({ lastArea, crumbs.at(0) }, inactivity,
resetPather);
if (resetPather)
TF2MAP->pather->Reset();
if (ensureArrival)
{
logging::Info("Pathing: NavBot inactive for too long. Ignoring "
"connection and finding another path...");
// Find a new path
int i1, i2;
crumbs = findPath(g_pLocalPlayer->v_Origin, crumbs.back(), i1, i2);
}
else
{
logging::Info(
"Pathing: NavBot inactive for too long. Canceling tasks and "
"ignoring connection...");
// Wait for new instructions
crumbs.clear();
}
Repath();
inactivity.update();
return;
}
@ -490,15 +412,18 @@ CatCommand navpath("nav_path", "Debug nav path", [](const CCommand &args) {
}
});
CatCommand navpathnolocal("nav_path_nolocal", "Debug nav path", [](const CCommand &args) {
if (NavTo(loc, false, true, 50 + priority))
{
logging::Info("Pathing: Success! Walking to path...");
}
else
{
logging::Info("Pathing: Failed!");
}
});
// Clang format pls
CatCommand navpathnolocal("nav_path_nolocal", "Debug nav path",
[](const CCommand &args) {
if (NavTo(loc, false, true, 50 + priority))
{
logging::Info(
"Pathing: Success! Walking to path...");
}
else
{
logging::Info("Pathing: Failed!");
}
});
} // namespace nav