From 568265a3306a404130550c0d07c0da10f343a3c0 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 5 Jul 2024 15:52:43 +0200 Subject: [PATCH] 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. --- src/game/CMakeLists.txt | 7 ++- src/game/server/ai_hull.h | 27 ----------- src/game/server/ai_network.h | 5 ++- src/game/server/ai_networkmanager.cpp | 15 ++++--- src/game/server/ai_node.h | 24 +++++----- src/game/server/ai_utility.cpp | 65 ++++++++++----------------- src/game/server/detour_impl.h | 26 ++--------- src/game/shared/ai_utility_shared.cpp | 8 ++-- 8 files changed, 61 insertions(+), 116 deletions(-) delete mode 100644 src/game/server/ai_hull.h diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index 74a8f912..30a62081 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -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" ) diff --git a/src/game/server/ai_hull.h b/src/game/server/ai_hull.h deleted file mode 100644 index e3964db7..00000000 --- a/src/game/server/ai_hull.h +++ /dev/null @@ -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 diff --git a/src/game/server/ai_network.h b/src/game/server/ai_network.h index 0d81cbfb..d722a292 100644 --- a/src/game/server/ai_network.h +++ b/src/game/server/ai_network.h @@ -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 diff --git a/src/game/server/ai_networkmanager.cpp b/src/game/server/ai_networkmanager.cpp index f8d8c015..2b736ab6 100644 --- a/src/game/server/ai_networkmanager.cpp +++ b/src/game/server/ai_networkmanager.cpp @@ -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 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 diff --git a/src/game/server/ai_node.h b/src/game/server/ai_node.h index b2a0041d..8f2d5d58 100644 --- a/src/game/server/ai_node.h +++ b/src/game/server/ai_node.h @@ -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 m_Links; short unk6; // Should match up to unk4 on disk diff --git a/src/game/server/ai_utility.cpp b/src/game/server/ai_utility.cpp index 7fb78eee..c7fdb9b5 100644 --- a/src/game/server/ai_utility.cpp +++ b/src/game/server/ai_utility.cpp @@ -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); } diff --git a/src/game/server/detour_impl.h b/src/game/server/detour_impl.h index 97d213fe..7685a3dc 100644 --- a/src/game/server/detour_impl.h +++ b/src/game/server/detour_impl.h @@ -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 diff --git a/src/game/shared/ai_utility_shared.cpp b/src/game/shared/ai_utility_shared.cpp index 55d36e7c..a418da9e 100644 --- a/src/game/shared/ai_utility_shared.cpp +++ b/src/game/shared/ai_utility_shared.cpp @@ -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.