r5sdk/r5dev/game/server/ai_networkmanager.cpp

498 lines
17 KiB
C++
Raw Normal View History

//=============================================================================//
//
// Purpose:
//
//=============================================================================//
#include "core/stdafx.h"
#include "tier0/fasttimer.h"
#include "tier1/cvar.h"
#include "tier1/cmd.h"
#include "mathlib/crc32.h"
#include "public/edict.h"
#include "filesystem/filesystem.h"
#include "game/server/ai_node.h"
#include "game/server/ai_network.h"
#include "game/server/ai_networkmanager.h"
#include <public/worldsize.h>
2022-03-22 17:18:29 +01:00
constexpr int AINET_SCRIPT_VERSION_NUMBER = 21;
constexpr int AINET_VERSION_NUMBER = 57;
2022-08-31 12:49:48 +02:00
constexpr int AINET_MIN_FILE_SIZE = 82;
constexpr const char* AINETWORK_EXT = ".ain";
constexpr const char* AINETWORK_PATH = "maps/graphs/";
/*
==============================
CAI_NetworkBuilder::BuildFile
Build AI node graph file from
in-memory structures and write
to disk to be loaded
==============================
*/
void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
{
//int test = 0;
//while (pNetwork->m_iNumNodes < 8000)
//{
// Vector3D origin;
// origin.Init(
// RandomFloat(0.0f, MAX_COORD_FLOAT),
// RandomFloat(0.0f, MAX_COORD_FLOAT),
// RandomFloat(0.0f, MAX_COORD_FLOAT));
// /*CAI_Node* pNode = */pNetwork->AddPathNode(&origin, RandomFloat(-180, 180));
// if (test > 0)
// pNetwork->CreateNodeLink(test-1, test);
// CAI_NodeCluster* cluster = new CAI_NodeCluster();
// memset(cluster, '\0', sizeof(CAI_NodeCluster));
// origin.Init(
// RandomFloat(0.0f, MAX_COORD_FLOAT),
// RandomFloat(0.0f, MAX_COORD_FLOAT),
// RandomFloat(0.0f, MAX_COORD_FLOAT));
// cluster->m_nIndex = test;
// cluster->m_vOrigin = origin;
// g_pAINodeClusters->AddToTail(cluster);
// CAI_NodeClusterLink* clusterLink = new CAI_NodeClusterLink();
// memset(clusterLink, '\0', sizeof(CAI_NodeClusterLink));
// clusterLink->prevIndex_MAYBE = (short)test;
// clusterLink->nextIndex_MAYBE = (short)test + 1;
// clusterLink->flags = 4;
// g_pAINodeClusterLinks->AddToTail(clusterLink);
// ++test;
//}
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(szGraphPath, sizeof(szGraphPath), "%s%s%s", AINETWORK_PATH, g_ServerGlobalVariables->m_pszMapName, AINETWORK_EXT);
CFastTimer masterTimer;
CFastTimer timer;
2022-03-22 17:18:29 +01:00
// Build from memory.
Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
Msg(eDLL_T::SERVER, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> AI NETWORK GRAPH FILE CONSTRUCTION STARTED <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
masterTimer.Start();
timer.Start();
FileSystem()->CreateDirHierarchy(AINETWORK_PATH, "GAME");
FileHandle_t pAIGraph = FileSystem()->Open(szGraphPath, "wb", "GAME");
if (!pAIGraph)
{
Error(eDLL_T::SERVER, NO_ERROR, "%s - Unable to write to '%s' (read-only?)\n", __FUNCTION__, szGraphPath);
return;
}
2022-03-20 17:03:46 +01:00
DevMsg(eDLL_T::SERVER, "+- Writing header...\n");
DevMsg(eDLL_T::SERVER, " |-- AINet version: '%d'\n", AINET_VERSION_NUMBER);
FileSystem()->Write(&AINET_VERSION_NUMBER, sizeof(int), pAIGraph);
2022-03-20 17:03:46 +01:00
DevMsg(eDLL_T::SERVER, " |-- Map version: '%d'\n", g_ServerGlobalVariables->m_nMapVersion);
FileSystem()->Write(&g_ServerGlobalVariables->m_nMapVersion, sizeof(int), pAIGraph);
2022-03-20 17:03:46 +01:00
FileHandle_t pNavMesh = FileSystem()->Open(szMeshPath, "rb", "GAME");
uint32_t nNavMeshCRC = NULL;
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]);
}
else
{
const ssize_t nLen = FileSystem()->Size(pNavMesh);
std::unique_ptr<uint8_t[]> pBuf(new uint8_t[nLen]);
FileSystem()->Read(pBuf.get(), nLen, pNavMesh);
FileSystem()->Close(pNavMesh);
nNavMeshCRC = crc32::update(NULL, pBuf.get(), nLen);
}
2022-03-22 17:18:29 +01:00
// Large NavMesh CRC.
DevMsg(eDLL_T::SERVER, " |-- NavMesh CRC: '0x%lX'\n", nNavMeshCRC);
FileSystem()->Write(&nNavMeshCRC, sizeof(uint32_t), pAIGraph);
2022-03-20 17:03:46 +01:00
2022-03-22 17:18:29 +01:00
// Path nodes.
DevMsg(eDLL_T::SERVER, " |-- Node count: '%d'\n", pNetwork->m_iNumNodes);
FileSystem()->Write(&pNetwork->m_iNumNodes, sizeof(int), pAIGraph);
2022-03-20 17:03:46 +01:00
timer.End();
Msg(eDLL_T::SERVER, "...done writing header. %lf seconds\n", timer.GetDuration().GetSeconds());
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing node positions...\n");
2022-03-24 02:08:27 +01:00
if (pNetwork->m_pAInode)
2022-03-20 17:03:46 +01:00
{
int totalLinkCount = 0;
2022-03-24 02:08:27 +01:00
for (int i = 0; i < pNetwork->m_iNumNodes; i++)
2022-03-20 17:03:46 +01:00
{
const CAI_Node* aiNode = pNetwork->m_pAInode[i];
2022-03-24 02:08:27 +01:00
// Construct on-disk node struct.
CAI_NodeDisk diskNode;
diskNode.m_vOrigin = aiNode->m_vOrigin;
diskNode.m_flYaw = aiNode->m_flYaw;
diskNode.unk0 = static_cast<char>(aiNode->unk0);
diskNode.unk1 = aiNode->unk1;
2022-03-24 02:08:27 +01:00
for (int j = 0; j < MAX_HULLS; j++)
{
diskNode.unk2[j] = static_cast<short>(aiNode->unk2[j]);
diskNode.unk3[j] = aiNode->unk3[j];
diskNode.hulls[j] = aiNode->m_fHulls[j];
2022-03-24 02:08:27 +01:00
}
2022-03-20 17:03:46 +01:00
diskNode.unk4 = aiNode->unk6;
2022-03-24 02:08:27 +01:00
diskNode.unk5 = -1; // aiNetwork->nodes[i]->unk8; // This field is wrong, however it's always -1 in original navmeshes anyway.
memcpy(diskNode.unk6, aiNode->unk10, sizeof(diskNode.unk6));
2022-03-20 17:03:46 +01:00
DevMsg(eDLL_T::SERVER, " |-- Copying node '#%d' from '0x%p' to '0x%zX'\n", aiNode->m_nIndex, aiNode, FileSystem()->Tell(pAIGraph));
FileSystem()->Write(&diskNode, sizeof(CAI_NodeDisk), pAIGraph);
2022-03-20 17:03:46 +01:00
totalLinkCount += aiNode->m_nNumLinks;
2022-03-24 02:08:27 +01:00
}
pNetwork->m_iNumLinks = totalLinkCount;
}
else
{
// No links, this has to be initialized as the engine doesn't do it
// during build.
pNetwork->m_iNumLinks = 0;
2022-03-20 17:03:46 +01:00
}
timer.End();
Msg(eDLL_T::SERVER, "...done writing node positions. %lf seconds\n", timer.GetDuration().GetSeconds());
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing links...\n");
DevMsg(eDLL_T::SERVER, " |-- Cached link count: '%d'\n", pNetwork->m_iNumLinks);
2022-03-20 17:03:46 +01:00
int packedLinks = pNetwork->m_iNumLinks / 2;
FileSystem()->Write(&packedLinks, sizeof(int), pAIGraph);
2022-03-24 02:08:27 +01:00
if (pNetwork->m_pAInode)
2022-03-20 17:03:46 +01:00
{
2022-03-24 02:08:27 +01:00
for (int i = 0; i < pNetwork->m_iNumNodes; i++)
2022-03-20 17:03:46 +01:00
{
const CAI_Node* aiNode = pNetwork->m_pAInode[i];
for (int j = 0; j < aiNode->m_nNumLinks; j++)
{
const CAI_NodeLink* nodeLink = aiNode->links[j];
2022-03-24 02:08:27 +01:00
// Skip links that don't originate from current node.
if (nodeLink->m_iSrcID != aiNode->m_nIndex)
2022-03-24 02:08:27 +01:00
{
continue;
}
CAI_NodeLinkDisk diskLink;
diskLink.m_iSrcID = nodeLink->m_iSrcID;
diskLink.m_iDestID = nodeLink->m_iDestID;
diskLink.unk0 = nodeLink->unk1;
for (int k = 0; k < MAX_HULLS; k++)
{
diskLink.m_bHulls[k] = nodeLink->m_bHulls[k];
}
2022-03-24 02:08:27 +01:00
DevMsg(eDLL_T::SERVER, " |-- Writing link '%hd' => '%hd' to '0x%zX'\n", diskLink.m_iSrcID, diskLink.m_iDestID, FileSystem()->Tell(pAIGraph));
FileSystem()->Write(&diskLink, sizeof(CAI_NodeLinkDisk), pAIGraph);
}
2022-03-20 17:03:46 +01:00
}
}
timer.End();
Msg(eDLL_T::SERVER, "...done writing links. %lf seconds (%d links)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumLinks);
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing hull data...\n");
2022-03-22 17:18:29 +01:00
// Don't know what this is, it's likely a block from tf1 that got deprecated? should just be 1 int per node.
DevMsg(eDLL_T::SERVER, " |-- Writing '%d' bytes for node block at '0x%zX'\n", pNetwork->m_iNumNodes * sizeof(uint32_t), FileSystem()->Tell(pAIGraph));
2022-03-22 17:18:29 +01:00
if (pNetwork->m_iNumNodes > 0)
2022-03-22 17:18:29 +01:00
{
std::unique_ptr<uint32[]> unkNodeBlock(new uint32_t[pNetwork->m_iNumNodes * sizeof(uint32_t)]);
memset(unkNodeBlock.get(), '\0', pNetwork->m_iNumNodes * sizeof(uint32_t));
FileSystem()->Write(unkNodeBlock.get(), pNetwork->m_iNumNodes * sizeof(uint32_t), pAIGraph);
2022-03-22 17:18:29 +01:00
}
// TODO: This is traverse nodes i think? these aren't used in r2 ains so we can get away with just writing count=0 and skipping
// but ideally should actually dump these.
DevMsg(eDLL_T::SERVER, " |-- Writing '%d' traversal nodes at '0x%zX'\n", 0, FileSystem()->Tell(pAIGraph));
2022-03-22 17:18:29 +01:00
short traverseNodeCount = 0; // Only write count since count=0 means we don't have to actually do anything here.
FileSystem()->Write(&traverseNodeCount, sizeof(short), pAIGraph);
2022-03-20 17:03:46 +01:00
2022-03-22 17:18:29 +01:00
// TODO: Ideally these should be actually dumped, but they're always 0 in r2 from what i can tell.
DevMsg(eDLL_T::SERVER, " |-- Writing '%d' bytes for hull data block at '0x%zX'\n", (MAX_HULLS * 8), FileSystem()->Tell(pAIGraph));
for (int i = 0; i < (MAX_HULLS * 8); i++)
{
FileSystem()->Write("\0", sizeof(char), pAIGraph);
}
2022-03-20 17:03:46 +01:00
timer.End();
Msg(eDLL_T::SERVER, "...done writing hull data. %lf seconds\n", timer.GetDuration().GetSeconds());
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing clusters...\n");
const int numClusters = g_pAIPathClusters->Count();
FileSystem()->Write(&numClusters, sizeof(int), pAIGraph);
FOR_EACH_VEC(*g_pAIPathClusters, i)
2022-03-20 17:03:46 +01:00
{
DevMsg(eDLL_T::SERVER, " |-- Writing cluster '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph));
2022-03-20 17:03:46 +01:00
const CAI_Cluster* pathClusters = (*g_pAIPathClusters)[i];
FileSystem()->Write(&pathClusters->m_nIndex, sizeof(int), pAIGraph);
FileSystem()->Write(&pathClusters->unk1, sizeof(char), pAIGraph);
FileSystem()->Write(&pathClusters->m_vOrigin.x, sizeof(vec_t), pAIGraph);
FileSystem()->Write(&pathClusters->m_vOrigin.y, sizeof(vec_t), pAIGraph);
FileSystem()->Write(&pathClusters->m_vOrigin.z, sizeof(vec_t), pAIGraph);
2022-03-20 17:03:46 +01:00
const int unkVec0Size = pathClusters->unkVec0.Count();
FileSystem()->Write(&unkVec0Size, sizeof(int), pAIGraph);
2022-03-20 17:03:46 +01:00
FOR_EACH_VEC(pathClusters->unkVec0, j)
2022-03-20 17:03:46 +01:00
{
short unkShort = static_cast<short>(pathClusters->unkVec0[j]);
FileSystem()->Write(&unkShort, sizeof(short), pAIGraph);
2022-03-20 17:03:46 +01:00
}
const int unkVec1Size = pathClusters->unkVec1.Count();
FileSystem()->Write(&unkVec1Size, sizeof(int), pAIGraph);
FOR_EACH_VEC(pathClusters->unkVec1, j)
2022-03-20 17:03:46 +01:00
{
short unkShort = static_cast<short>(pathClusters->unkVec0[j]);
FileSystem()->Write(&unkShort, sizeof(short), pAIGraph);
2022-03-20 17:03:46 +01:00
}
FileSystem()->Write(&pathClusters->unk5, sizeof(char), pAIGraph);
2022-03-20 17:03:46 +01:00
}
timer.End();
Msg(eDLL_T::SERVER, "...done writing clusters. %lf seconds (%d clusters)\n", timer.GetDuration().GetSeconds(), numClusters);
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing cluster links...\n");
const int numClusterLinks = g_pAIClusterLinks->Count();
FileSystem()->Write(&numClusterLinks, sizeof(int), pAIGraph);
FOR_EACH_VEC(*g_pAIClusterLinks, i)
2022-03-20 17:03:46 +01:00
{
DevMsg(eDLL_T::SERVER, " |-- Writing cluster link '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph));
// Disk and memory structs are literally identical here so just directly write.
const CAI_ClusterLink* clusterLink = (*g_pAIClusterLinks)[i];
FileSystem()->Write(&clusterLink->prevIndex_MAYBE, sizeof(short), pAIGraph);
FileSystem()->Write(&clusterLink->nextIndex_MAYBE, sizeof(short), pAIGraph);
FileSystem()->Write(&clusterLink->unk2, sizeof(int), pAIGraph);
FileSystem()->Write(&clusterLink->flags, sizeof(char), pAIGraph);
FileSystem()->Write(&clusterLink->unk4, sizeof(char), pAIGraph);
FileSystem()->Write(&clusterLink->unk5, sizeof(char), pAIGraph);
2022-03-20 17:03:46 +01:00
}
timer.End();
Msg(eDLL_T::SERVER, "...done writing cluster links. %lf seconds (%d cluster links)\n", timer.GetDuration().GetSeconds(), numClusterLinks);
// This is always set to '-1'. Likely a field for maintaining compatibility.
FileSystem()->Write(&pNetwork->unk5, sizeof(pNetwork->unk5), pAIGraph);
2022-03-20 17:03:46 +01:00
2022-03-22 17:18:29 +01:00
// AIN v57 and above only (not present in r1, static array in r2, pointer to dynamic array in r5).
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing script nodes...\n");
FileSystem()->Write(&pNetwork->m_iNumScriptNodes, sizeof(pNetwork->m_iNumScriptNodes), pAIGraph);
for (int i = 0; i < pNetwork->m_iNumScriptNodes; i++)
{
2022-03-22 17:18:29 +01:00
// Disk and memory structs for script nodes are identical.
DevMsg(eDLL_T::SERVER, " |-- Writing script node '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph));
FileSystem()->Write(&pNetwork->m_ScriptNode[i], sizeof(CAI_ScriptNode), pAIGraph);
}
timer.End();
Msg(eDLL_T::SERVER, "...done writing script nodes. %lf seconds (%d nodes)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumScriptNodes);
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing hint data...\n");
FileSystem()->Write(&pNetwork->m_iNumHints, sizeof(pNetwork->m_iNumHints), pAIGraph);
for (int i = 0; i < pNetwork->m_iNumHints; i++)
2022-03-20 17:03:46 +01:00
{
DevMsg(eDLL_T::SERVER, " |-- Writing hint data '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph));
FileSystem()->Write(&pNetwork->m_Hints[i], sizeof(pNetwork->m_Hints[i]), pAIGraph);
2022-03-20 17:03:46 +01:00
}
timer.End();
Msg(eDLL_T::SERVER, "...done writing hint data. %lf seconds (%d hints)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumHints);
FileSystem()->Close(pAIGraph);
masterTimer.End();
Msg(eDLL_T::SERVER, "...done writing AI node graph. %lf seconds\n", masterTimer.GetDuration().GetSeconds());
Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
}
/*
==============================
CAI_NetworkManager::LoadNetworkGraph
Load network from the disk
and validate status
==============================
*/
void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile)
{
bool bNavMeshAvailable = true;
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(szGraphPath, sizeof(szGraphPath), "%s%s%s", AINETWORK_PATH, g_ServerGlobalVariables->m_pszMapName, AINETWORK_EXT);
int nAiNetVersion = NULL;
int nAiMapVersion = NULL;
uint32_t nAiGraphHash = NULL;
uint32_t nNavMeshHash = NULL;
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]);
bNavMeshAvailable = false;
}
else
{
const ssize_t nLen = FileSystem()->Size(pNavMesh);
std::unique_ptr<uint8_t[]> pBuf(new uint8_t[nLen]);
FileSystem()->Read(pBuf.get(), nLen, pNavMesh);
FileSystem()->Close(pNavMesh);
nNavMeshHash = crc32::update(NULL, pBuf.get(), nLen);
}
FileHandle_t pAIGraph = FileSystem()->Open(szGraphPath, "rb", "GAME");
if (!pAIGraph)
{
Error(eDLL_T::SERVER, NO_ERROR, "%s - Unable to open '%s' (insufficient rights?)\n", __FUNCTION__, szGraphPath);
LoadNetworkGraphEx(pAINetworkManager, pBuffer, szAIGraphFile);
return;
}
if (FileSystem()->Size(pAIGraph) >= AINET_MIN_FILE_SIZE)
{
FileSystem()->Read(&nAiNetVersion, sizeof(int), pAIGraph);
FileSystem()->Read(&nAiMapVersion, sizeof(int), pAIGraph);
FileSystem()->Read(&nAiGraphHash, sizeof(int), pAIGraph);
if (nAiNetVersion > AINET_VERSION_NUMBER)
{
Warning(eDLL_T::SERVER, "AI node graph '%s' is unsupported (net version: '%d' expected: '%d')\n",
szGraphPath, nAiNetVersion, AINET_VERSION_NUMBER);
}
else if (nAiMapVersion != g_ServerGlobalVariables->m_nMapVersion)
{
Warning(eDLL_T::SERVER, "AI node graph '%s' is out of date (map version: '%d' expected: '%d')\n",
szGraphPath, nAiMapVersion, g_ServerGlobalVariables->m_nMapVersion);
}
else if (bNavMeshAvailable && nAiGraphHash != nNavMeshHash)
{
Warning(eDLL_T::SERVER, "AI node graph '%s' is out of date (checksum: '%x' expected: '%x')\n",
szGraphPath, nAiGraphHash, nNavMeshHash);
}
}
else
{
Error(eDLL_T::SERVER, NO_ERROR, "%s - AI node graph '%s' is corrupt\n", __FUNCTION__, szGraphPath);
}
FileSystem()->Close(pAIGraph);
LoadNetworkGraphEx(pAINetworkManager, pBuffer, szAIGraphFile);
}
/*
==============================
CAI_NetworkManager::LoadNetworkGraphEx
Load network
(internal)
==============================
*/
void CAI_NetworkManager::LoadNetworkGraphEx(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile)
{
2022-03-07 11:36:45 +01:00
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
CAI_NetworkManager__LoadNetworkGraph(pAINetworkManager, pBuffer, szAIGraphFile, NULL);
2022-03-07 11:36:45 +01:00
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
CAI_NetworkManager__LoadNetworkGraph(pAINetworkManager, pBuffer, szAIGraphFile);
2022-03-07 11:36:45 +01:00
#endif
if (ai_ainDumpOnLoad->GetBool())
{
Msg(eDLL_T::SERVER, "Reparsing AI Network '%s'\n", szAIGraphFile);
CAI_NetworkBuilder::SaveNetworkGraph(*(CAI_Network**)(reinterpret_cast<char*>(pAINetworkManager) + AINETWORK_OFFSET));
}
}
/*
==============================
CAI_NetworkBuilder::Build
builds network in-memory
during level load
==============================
*/
void CAI_NetworkBuilder::Build(CAI_NetworkBuilder* pBuilder, CAI_Network* pAINetwork, void* a3, int a4)
{
CAI_NetworkBuilder__Build(pBuilder, pAINetwork, a3, a4);
CAI_NetworkBuilder::SaveNetworkGraph(pAINetwork);
}
void VAI_NetworkManager::Attach() const
{
DetourAttach((LPVOID*)&CAI_NetworkManager__LoadNetworkGraph, &CAI_NetworkManager::LoadNetworkGraph);
DetourAttach((LPVOID*)&CAI_NetworkBuilder__Build, &CAI_NetworkBuilder::Build);
}
void VAI_NetworkManager::Detach() const
{
DetourDetach((LPVOID*)&CAI_NetworkManager__LoadNetworkGraph, &CAI_NetworkManager::LoadNetworkGraph);
DetourDetach((LPVOID*)&CAI_NetworkBuilder__Build, &CAI_NetworkBuilder::Build);
}