Server: refactor AI detour implementation

The current implementation was mixing several types with each other, making it hard to determine what a hull and what a navmesh type is. Code has been refactored to use the new types and names introduced in commit e638cc9323c7b74279f0274591264efc53c29d4d.
This commit is contained in:
Kawe Mazidjatari 2024-07-05 15:52:43 +02:00
parent d2cf0ebac6
commit 568265a330
8 changed files with 61 additions and 116 deletions

View File

@ -71,7 +71,6 @@ add_sources( SOURCE_GROUP "AI"
"server/ai_networkmanager.cpp"
"server/ai_networkmanager.h"
"server/ai_node.h"
"server/ai_hull.h"
"server/ai_utility.cpp"
"server/ai_utility.h"
"server/detour_impl.h"
@ -137,6 +136,12 @@ add_sources( SOURCE_GROUP "Public"
"${ENGINE_SOURCE_DIR}/public/iserverunknown.h"
)
add_sources( SOURCE_GROUP "Public/AI"
"${ENGINE_SOURCE_DIR}/public/game/server/ai_agent.h"
"${ENGINE_SOURCE_DIR}/public/game/server/ai_hull.h"
"${ENGINE_SOURCE_DIR}/public/game/server/ai_navmesh.h"
)
endif()
if( ${PROJECT_NAME} STREQUAL "client_static" )

View File

@ -1,27 +0,0 @@
//=============================================================================//
//
// Purpose:
//
//=============================================================================//
#ifndef AI_HULL_H
#define AI_HULL_H
//=========================================================
// Link Properties. These hulls must correspond to the hulls
// in AI_Hull.cpp!
//=========================================================
enum Hull_t
{
HULL_HUMAN,
HULL_MEDIUM,
HULL_FLYING_VEHICLE,
HULL_SMALL,
HULL_TITAN,
HULL_GOLIATH,
HULL_PROWLER,
//--------------------------------------------
NUM_HULLS, // Confirmed, see [r5apex_ds + CB2765]
HULL_NONE = -1 // No Hull (appears after num hulls as we don't want to count it)
};
#endif // AI_HULL_H

View File

@ -1,5 +1,6 @@
#pragma once
#include "game/server/ai_node.h"
#include "game/server/ai_navmesh.h"
//-----------------------------------------------------------------------------
// CAI_Network
@ -31,8 +32,8 @@ public:
int m_iNumLinks; // +0x0008
int m_nUnk0;
CAI_HullData m_HullData[MAX_HULLS];
int m_iNumZones[MAX_HULLS]; // +0x0088
CAI_HullData m_HullData[NAVMESH_COUNT];
int m_iNumZones[NAVMESH_COUNT];// +0x0088
// unk8 on disk
int unk5; // +0x009C

View File

@ -14,6 +14,7 @@
#include "game/server/ai_node.h"
#include "game/server/ai_network.h"
#include "game/server/ai_networkmanager.h"
#include "game/server/ai_navmesh.h"
#include <public/worldsize.h>
constexpr int AINET_SCRIPT_VERSION_NUMBER = 21;
@ -39,7 +40,7 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
char szMeshPath[MAX_PATH];
char szGraphPath[MAX_PATH];
V_snprintf(szMeshPath, sizeof(szMeshPath), "%s%s_%s%s", NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName, S_HULL_TYPE[E_HULL_TYPE::LARGE], NAVMESH_EXT);
V_snprintf(szMeshPath, sizeof(szMeshPath), "%s%s_%s%s", NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName, NavMesh_GetNameForType(NAVMESH_LARGE), NAVMESH_EXT);
V_snprintf(szGraphPath, sizeof(szGraphPath), "%s%s%s", AINETWORK_PATH, g_ServerGlobalVariables->m_pszMapName, AINETWORK_EXT);
CFastTimer masterTimer;
@ -104,7 +105,7 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
buf.PutChar((char)aiNode->GetType());
buf.PutInt(aiNode->GetInfo());
for (int j = 0; j < MAX_HULLS; j++)
for (int j = 0; j < NAVMESH_COUNT; j++)
{
buf.PutShort((short)aiNode->unk2[j]);
}
@ -225,7 +226,7 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
// Dump the hull data blocks
// -------------------------------
for (int i = 0; i < MAX_HULLS; i++)
for (int i = 0; i < NAVMESH_COUNT; i++)
{
const CAI_HullData& hullData = pNetwork->m_HullData[i];
const int numHullZones = pNetwork->m_iNumZones[i];
@ -240,7 +241,7 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
}
timer.End();
Msg(eDLL_T::SERVER, "...done writing hull data blocks. %lf seconds (%d blocks)\n", timer.GetDuration().GetSeconds(), MAX_HULLS);
Msg(eDLL_T::SERVER, "...done writing hull data blocks. %lf seconds (%d blocks)\n", timer.GetDuration().GetSeconds(), NAVMESH_COUNT);
timer.Start();
@ -371,7 +372,7 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
if (!pNavMesh)
{
Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network\n",
__FUNCTION__, S_HULL_TYPE[E_HULL_TYPE::LARGE]);
__FUNCTION__, NavMesh_GetNameForType(NAVMESH_LARGE));
}
else
{
@ -418,7 +419,7 @@ void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pManager, CUtlBuff
char szMeshPath[MAX_PATH];
char szGraphPath[MAX_PATH];
V_snprintf(szMeshPath, sizeof(szMeshPath), "%s%s_%s%s", NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName, S_HULL_TYPE[E_HULL_TYPE::LARGE], NAVMESH_EXT);
V_snprintf(szMeshPath, sizeof(szMeshPath), "%s%s_%s%s", NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName, NavMesh_GetNameForType(NAVMESH_LARGE), NAVMESH_EXT);
V_snprintf(szGraphPath, sizeof(szGraphPath), "%s%s%s", AINETWORK_PATH, g_ServerGlobalVariables->m_pszMapName, AINETWORK_EXT);
int nAiNetVersion = NULL;
@ -432,7 +433,7 @@ void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pManager, CUtlBuff
FileHandle_t pNavMesh = FileSystem()->Open(szMeshPath, "rb", "GAME");
if (!pNavMesh)
{
Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network\n", __FUNCTION__, S_HULL_TYPE[E_HULL_TYPE::LARGE]);
Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network\n", __FUNCTION__, NavMesh_GetNameForType(NAVMESH_LARGE));
bNavMeshAvailable = false;
}
else

View File

@ -6,7 +6,7 @@
#pragma once
#include "mathlib/vector.h"
#include "mathlib/bitvec.h"
constexpr int MAX_HULLS = 5;
#include "game/server/ai_navmesh.h"
constexpr int NOT_CACHED = -2; // Returned if data not in cache
constexpr int NO_NODE = -1; // Returned when no node meets the qualification
@ -31,7 +31,7 @@ struct CAI_NodeLink
{
short m_iSrcID;
short m_iDestID;
byte m_iAcceptedMoveTypes[MAX_HULLS];
byte m_iAcceptedMoveTypes[NAVMESH_COUNT];
byte m_LinkInfo;
char unk1; // maps => unk0 on disk
char unk2[5];
@ -58,20 +58,20 @@ public:
int SetInfo(int info) { return m_eNodeInfo = info; }
int GetInfo() const { return m_eNodeInfo; }
int m_iID; // ID for this node
Vector3D m_vOrigin; // location of this node in space
float m_flVOffset[MAX_HULLS]; // vertical offset for each hull type, assuming ground node, 0 otherwise
float m_flYaw; // NPC on this node should face this yaw to face the hint, or climb a ladder
int m_iID; // ID for this node
Vector3D m_vOrigin; // location of this node in space
float m_flVOffset[NAVMESH_COUNT]; // vertical offset for each hull type, assuming ground node, 0 otherwise
float m_flYaw; // NPC on this node should face this yaw to face the hint, or climb a ladder
NodeType_e m_eNodeType; // The type of node; always 2 in buildainfile.
int m_eNodeInfo; // bits that tell us more about this nodes
NodeType_e m_eNodeType; // The type of node; always 2 in buildainfile.
int m_eNodeInfo; // bits that tell us more about this nodes
int unk2[MAX_HULLS]; // Maps directly to unk2 in disk struct, despite being ints rather than shorts
int unk2[NAVMESH_COUNT]; // Maps directly to unk2 in disk struct, despite being ints rather than shorts
// View server.dll+393672 for context
char unk3[MAX_HULLS]; // Should map to unk3 on disk
char pad[3]; // Aligns next bytes
float unk4[MAX_HULLS]; // I have no clue, calculated using some kind float function magic
char unk3[NAVMESH_COUNT]; // Should map to unk3 on disk
char pad[3]; // Aligns next bytes
float unk4[NAVMESH_COUNT]; // I have no clue, calculated using some kind float function magic
CUtlVector<CAI_NodeLink*> m_Links;
short unk6; // Should match up to unk4 on disk

View File

@ -16,69 +16,51 @@
static ConVar navmesh_always_reachable("navmesh_always_reachable", "0", FCVAR_DEVELOPMENTONLY, "Marks goal poly from agent poly as reachable regardless of table data ( !slower! )");
inline uint32_t g_HullMasks[10] = // Hull mask table [r5apex_ds.exe + 131a2f8].
{
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xfffffffb, 0xfffffffa, 0xfffffff9, 0xfffffff8, 0x00040200
};
//-----------------------------------------------------------------------------
// Purpose: gets the navmesh by hull from global array [small, med_short, medium, large, extra_large]
// input : hull -
// Purpose: gets the navmesh by type from global array [small, med_short, medium, large, extra_large]
// input : navMeshType -
// Output : pointer to navmesh
//-----------------------------------------------------------------------------
dtNavMesh* GetNavMeshForHull(int hullSize)
dtNavMesh* Detour_GetNavMeshByType(const NavMeshType_e navMeshType)
{
Assert(hullSize >= NULL && hullSize < MAX_HULLS); // Programmer error.
return g_pNavMesh[hullSize];
Assert(navMeshType >= NULL && navMeshType < NAVMESH_COUNT); // Programmer error.
return g_pNavMesh[navMeshType];
}
//-----------------------------------------------------------------------------
// Purpose: gets the navmesh by hull from global array [small, med_short, medium, large, extra_large]
// input : hull -
// Output : pointer to navmesh
// Purpose: free's the navmesh by type from global array [small, med_short, medium, large, extra_large]
// input : navMeshType -
//-----------------------------------------------------------------------------
void ClearNavMeshForHull(int hullSize)
void Detour_FreeNavMeshByType(const NavMeshType_e navMeshType)
{
Assert(hullSize >= NULL && hullSize < MAX_HULLS); // Programmer error.
dtNavMesh* nav = g_pNavMesh[hullSize];
Assert(navMeshType >= NULL && navMeshType < NAVMESH_COUNT); // Programmer error.
dtNavMesh* const nav = g_pNavMesh[navMeshType];
if (nav) // Only free if NavMesh for hull is loaded.
if (nav) // Only free if NavMesh for type is loaded.
{
// Frees tiles, polys, tris, anything dynamically
// allocated for this navmesh, and the navmesh itself.
v_Detour_FreeNavMesh(nav);
free(nav);
g_pNavMesh[hullSize] = nullptr;
g_pNavMesh[navMeshType] = nullptr;
}
}
//-----------------------------------------------------------------------------
// Purpose: gets hull mask by id
// input : hullId -
// Output : hull mask
//-----------------------------------------------------------------------------
uint32_t GetHullMaskById(int hullId)
{
Assert(hullId >= NULL && hullId < SDK_ARRAYSIZE(g_HullMasks)); // Programmer error.
return (hullId + g_HullMasks[hullId]);
}
//-----------------------------------------------------------------------------
// Purpose: determines whether goal poly is reachable from agent poly
// input : *nav -
// fromRef -
// goalRef -
// hull_type -
// animType -
// Output : value if reachable, false otherwise
//-----------------------------------------------------------------------------
uint8_t IsGoalPolyReachable(dtNavMesh* nav, dtPolyRef fromRef, dtPolyRef goalRef, int hullId)
uint8_t Detour_IsGoalPolyReachable(dtNavMesh* nav, dtPolyRef fromRef, dtPolyRef goalRef, TraverseAnimType_e animType)
{
if (navmesh_always_reachable.GetBool())
return true;
return dtNavMesh__isPolyReachable(nav, fromRef, goalRef, hullId);
return dtNavMesh__isPolyReachable(nav, fromRef, goalRef, animType);
}
//-----------------------------------------------------------------------------
@ -95,9 +77,9 @@ void Detour_LevelInit()
//-----------------------------------------------------------------------------
void Detour_LevelShutdown()
{
for (int i = 0; i < MAX_HULLS; i++)
for (int i = 0; i < NAVMESH_COUNT; i++)
{
ClearNavMeshForHull(i);
Detour_FreeNavMeshByType(NavMeshType_e(i));
}
}
@ -108,20 +90,21 @@ void Detour_LevelShutdown()
bool Detour_IsLoaded()
{
int ret = 0;
for (int i = 0; i < MAX_HULLS; i++)
for (int i = 0; i < NAVMESH_COUNT; i++)
{
const dtNavMesh* nav = GetNavMeshForHull(i);
const dtNavMesh* nav = Detour_GetNavMeshByType(NavMeshType_e(i));
if (!nav) // Failed to load...
{
Warning(eDLL_T::SERVER, "NavMesh '%s%s_%s%s' not loaded\n",
NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName, S_HULL_TYPE[i], NAVMESH_EXT);
NAVMESH_PATH, g_ServerGlobalVariables->m_pszMapName,
NavMesh_GetNameForType(NavMeshType_e(i)), NAVMESH_EXT);
ret++;
}
}
Assert(ret <= MAX_HULLS);
return (ret != MAX_HULLS);
Assert(ret <= NAVMESH_COUNT);
return (ret != NAVMESH_COUNT);
}
//-----------------------------------------------------------------------------
@ -177,6 +160,6 @@ static ConCommand navmesh_hotswap("navmesh_hotswap", Detour_HotSwap_f, "Hot swap
///////////////////////////////////////////////////////////////////////////////
void VRecast::Detour(const bool bAttach) const
{
DetourSetup(&dtNavMesh__isPolyReachable, &IsGoalPolyReachable, bAttach);
DetourSetup(&dtNavMesh__isPolyReachable, &Detour_IsGoalPolyReachable, bAttach);
DetourSetup(&v_Detour_LevelInit, &Detour_LevelInit, bAttach);
}

View File

@ -2,6 +2,7 @@
#include "thirdparty/recast/Detour/Include/DetourStatus.h"
#include "thirdparty/recast/Detour/Include/DetourNavMesh.h"
#include "thirdparty/recast/Detour/Include/DetourNavMeshQuery.h"
#include "game/server/ai_navmesh.h"
//-------------------------------------------------------------------------
// RUNTIME: DETOUR
@ -10,35 +11,16 @@ inline void(*v_Detour_LevelInit)(void);
inline void(*v_Detour_FreeNavMesh)(dtNavMesh* mesh);
inline dtStatus(*dtNavMesh__Init)(dtNavMesh* thisptr, unsigned char* data, int flags);
inline dtStatus(*dtNavMesh__addTile)(dtNavMesh* thisptr, unsigned char* data, dtMeshHeader* header, int dataSize, int flags, dtTileRef lastRef);
inline uint8_t(*dtNavMesh__isPolyReachable)(dtNavMesh* thisptr, dtPolyRef poly_1, dtPolyRef poly_2, int hull_type);
inline uint8_t(*dtNavMesh__isPolyReachable)(dtNavMesh* thisptr, dtPolyRef poly_1, dtPolyRef poly_2, TraverseAnimType_e animType);
constexpr const char* NAVMESH_PATH = "maps/navmesh/";
constexpr const char* NAVMESH_EXT = ".nm";
static const char* S_HULL_TYPE[5] =
{
"small",
"med_short",
"medium",
"large",
"extra_large"
};
enum E_HULL_TYPE
{
SMALL = 0,
MED_SHORT,
MEDIUM,
LARGE,
EXTRA_LARGE
};
inline dtNavMesh** g_pNavMesh = nullptr;
inline dtNavMeshQuery* g_pNavMeshQuery = nullptr;
dtNavMesh* GetNavMeshForHull(int hullSize);
uint32_t GetHullMaskById(int hullId);
dtNavMesh* Detour_GetNavMeshByType(const NavMeshType_e navMeshType);
void Detour_LevelInit();
void Detour_LevelShutdown();
@ -54,7 +36,7 @@ class VRecast : public IDetour
LogFunAdr("dtNavMesh::Init", dtNavMesh__Init);
LogFunAdr("dtNavMesh::addTile", dtNavMesh__addTile);
LogFunAdr("dtNavMesh::isPolyReachable", dtNavMesh__isPolyReachable);
LogVarAdr("g_pNavMesh[ MAX_HULLS ]", g_pNavMesh);
LogVarAdr("g_pNavMesh[ NavMeshType_e::NAVMESH_COUNT ]", g_pNavMesh);
LogVarAdr("g_pNavMeshQuery", g_pNavMeshQuery);
}
virtual void GetFun(void) const

View File

@ -187,7 +187,7 @@ void CAI_Utility::DrawNavMeshBVTree(
const bool bDepthBuffer) const
{
if (!pMesh)
pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt());
pMesh = Detour_GetNavMeshByType(NavMeshType_e(navmesh_debug_type.GetInt()));
if (!pMesh)
return; // NavMesh for hull not loaded.
@ -248,7 +248,7 @@ void CAI_Utility::DrawNavMeshPortals(const dtNavMesh* pMesh,
const bool bDepthBuffer) const
{
if (!pMesh)
pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt());
pMesh = Detour_GetNavMeshByType(NavMeshType_e(navmesh_debug_type.GetInt()));
if (!pMesh)
return; // NavMesh for hull not loaded.
@ -368,7 +368,7 @@ void CAI_Utility::DrawNavMeshPolys(const dtNavMesh* pMesh,
const bool bDepthBuffer) const
{
if (!pMesh)
pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt());
pMesh = Detour_GetNavMeshByType(NavMeshType_e(navmesh_debug_type.GetInt()));
if (!pMesh)
return; // NavMesh for hull not loaded.
@ -455,7 +455,7 @@ void CAI_Utility::DrawNavMeshPolyBoundaries(const dtNavMesh* pMesh,
Color col{ 20, 140, 255, 255 };
if (!pMesh)
pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt());
pMesh = Detour_GetNavMeshByType(NavMeshType_e(navmesh_debug_type.GetInt()));
if (!pMesh)
return; // NavMesh for hull not loaded.