CAI_NetworkBuilder::SaveNetworkGraph() rewrite

- Use CUtlBuffer instead to construct the data.
- Dump traverse ex nodes as well (CAI_TraverseNode).
This commit is contained in:
Kawe Mazidjatari 2023-08-28 20:01:45 +02:00
parent db6ccff8c2
commit 25e5ea0c83
6 changed files with 263 additions and 164 deletions

View File

@ -54,7 +54,7 @@ void* CAI_Network::GetVTable(void) const
// Purpose: gets the number of node links
// Output : int
//-----------------------------------------------------------------------------
int CAI_Network::GetNumLinks(void) const
int CAI_Network::NumLinks(void) const
{
return m_iNumLinks;
}
@ -63,7 +63,7 @@ int CAI_Network::GetNumLinks(void) const
// Purpose: gets the number of zones
// Output : int
//-----------------------------------------------------------------------------
int CAI_Network::GetNumZones(void) const
int CAI_Network::NumZones(void) const
{
return m_iNumZones;
}
@ -72,7 +72,7 @@ int CAI_Network::GetNumZones(void) const
// Purpose: gets the number of hints
// Output : int
//-----------------------------------------------------------------------------
int CAI_Network::GetNumHints(void) const
int CAI_Network::NumHints(void) const
{
return m_iNumHints;
}
@ -81,16 +81,16 @@ int CAI_Network::GetNumHints(void) const
// Purpose: gets the number of script nodes
// Output : int
//-----------------------------------------------------------------------------
int CAI_Network::GetNumScriptNodes(void) const
int CAI_Network::NumScriptNodes(void) const
{
return m_iNumScriptNodes;
}
//-----------------------------------------------------------------------------
// Purpose: gets the path nodes
// Output : int64_t
// Output : int
//-----------------------------------------------------------------------------
int64_t CAI_Network::GetNumPathNodes(void) const
int CAI_Network::NumPathNodes(void) const
{
return m_iNumNodes;
}
@ -115,12 +115,20 @@ CAI_ScriptNode* CAI_Network::GetScriptNodes(void) const
}
//-----------------------------------------------------------------------------
// Purpose: gets the pointer to path nodes
// Purpose: gets the pointer to path node
// Input : id -
// Output : CAI_Node**
//-----------------------------------------------------------------------------
CAI_Node** CAI_Network::GetPathNodes(void) const
CAI_Node* CAI_Network::GetPathNode(int id) const
{
return m_pAInode;
if (id >= 0 &&
id < m_iNumNodes)
{
return m_pAInode[id];
}
Assert(0);
return NULL;
}
//-----------------------------------------------------------------------------

View File

@ -11,17 +11,17 @@ class CAI_Network
public:
static void DebugConnectMsg(int node1, int node2, const char* pszFormat, ...);
void* GetVTable(void) const;
int GetNumLinks(void) const;
int GetNumZones(void) const;
int GetNumHints(void) const;
int GetNumScriptNodes(void) const;
int64_t GetNumPathNodes(void) const;
int NumLinks(void) const;
int NumZones(void) const;
int NumHints(void) const;
int NumScriptNodes(void) const;
int NumPathNodes(void) const;
short GetHint(int nIndex) const;
CAI_ScriptNode* GetScriptNodes(void) const;
CAI_Node* AddPathNode(const Vector3D* origin, const float jaw);
CAI_Node** GetPathNodes(void) const;
CAI_Node* GetPathNode(int id) const;
CAI_NodeLink* CreateNodeLink(int srcID, int destID);

View File

@ -59,11 +59,6 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
}
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);
DevMsg(eDLL_T::SERVER, " |-- Map version: '%d'\n", g_ServerGlobalVariables->m_nMapVersion);
FileSystem()->Write(&g_ServerGlobalVariables->m_nMapVersion, sizeof(int), pAIGraph);
FileHandle_t pNavMesh = FileSystem()->Open(szMeshPath, "rb", "GAME");
uint32_t nNavMeshCRC = NULL;
@ -85,12 +80,18 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
}
// Large NavMesh CRC.
DevMsg(eDLL_T::SERVER, " |-- AINet version: '%d'\n", AINET_VERSION_NUMBER);
DevMsg(eDLL_T::SERVER, " |-- Map version: '%d'\n", g_ServerGlobalVariables->m_nMapVersion);
DevMsg(eDLL_T::SERVER, " |-- NavMesh CRC: '0x%lX'\n", nNavMeshCRC);
FileSystem()->Write(&nNavMeshCRC, sizeof(uint32_t), pAIGraph);
// Path nodes.
DevMsg(eDLL_T::SERVER, " |-- Node count: '%d'\n", pNetwork->m_iNumNodes);
FileSystem()->Write(&pNetwork->m_iNumNodes, sizeof(int), pAIGraph);
CUtlBuffer buf;
// ---------------------------
// Save the version numbers
// ---------------------------
buf.PutInt(AINET_VERSION_NUMBER);
buf.PutInt(g_ServerGlobalVariables->m_nMapVersion);
buf.PutInt(nNavMeshCRC);
timer.End();
Msg(eDLL_T::SERVER, "...done writing header. %lf seconds\n", timer.GetDuration().GetSeconds());
@ -98,49 +99,42 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing path nodes...\n");
if (pNetwork->m_pAInode)
// -------------------------------
// Dump all the nodes to the file
// -------------------------------
buf.PutInt(pNetwork->NumPathNodes());
int totalNumLinks = 0;
for (int node = 0; node < pNetwork->NumPathNodes(); node++)
{
int totalLinkCount = 0;
const CAI_Node* aiNode = pNetwork->GetPathNode(node);
for (int i = 0; i < pNetwork->m_iNumNodes; i++)
DevMsg(eDLL_T::SERVER, " |-- Copying node '#%d' from '0x%p' to '0x%zX'\n", aiNode->m_iID, aiNode, buf.TellPut());
buf.PutFloat(aiNode->GetOrigin().x);
buf.PutFloat(aiNode->GetOrigin().y);
buf.PutFloat(aiNode->GetOrigin().z);
buf.PutFloat(aiNode->GetYaw());
buf.Put(aiNode->m_flVOffset, sizeof(aiNode->m_flVOffset));
buf.PutChar((char)aiNode->GetType());
buf.PutInt(aiNode->GetInfo());
for (int j = 0; j < MAX_HULLS; j++)
{
const CAI_Node* aiNode = pNetwork->m_pAInode[i];
DevMsg(eDLL_T::SERVER, " |-- Copying node '#%d' from '0x%p' to '0x%zX'\n", aiNode->m_nIndex, aiNode, FileSystem()->Tell(pAIGraph));
FileSystem()->Write(&aiNode->m_vOrigin, sizeof(Vector3D), pAIGraph);
FileSystem()->Write(&aiNode->m_flYaw, sizeof(float), pAIGraph);
FileSystem()->Write(&aiNode->m_fHulls, sizeof(aiNode->m_fHulls), pAIGraph);
FileSystem()->Write(&aiNode->unk0, sizeof(char), pAIGraph);
FileSystem()->Write(&aiNode->unk1, sizeof(int), pAIGraph);
for (int j = 0; j < MAX_HULLS; j++)
{
FileSystem()->Write(&aiNode->unk2[j], sizeof(short), pAIGraph);
}
FileSystem()->Write(&aiNode->unk3, sizeof(aiNode->unk3), pAIGraph);
FileSystem()->Write(&aiNode->unk6, sizeof(short), pAIGraph);
// aiNetwork->nodes[i]->unk8; // This field is wrong, however it's always -1 in original navmeshes anyway.
short unk8 = -1;
FileSystem()->Write(&unk8/*aiNode->unk8*/, sizeof(short), pAIGraph);
FileSystem()->Write(&aiNode->unk10, sizeof(aiNode->unk10), pAIGraph);
totalLinkCount += aiNode->m_nNumLinks;
buf.PutShort((short)aiNode->unk2[j]);
}
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;
buf.Put(aiNode->unk3, sizeof(aiNode->unk3));
buf.PutShort(aiNode->unk6);
buf.PutShort(aiNode->unk9); // Always -1;
buf.Put(aiNode->unk11, sizeof(aiNode->unk11));
totalNumLinks += aiNode->NumLinks();
}
pNetwork->m_iNumLinks = totalNumLinks;
timer.End();
Msg(eDLL_T::SERVER, "...done writing path nodes. %lf seconds (%d nodes)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumNodes);
@ -148,31 +142,30 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
DevMsg(eDLL_T::SERVER, "+- Writing node links...\n");
DevMsg(eDLL_T::SERVER, " |-- Cached node link count: '%d'\n", pNetwork->m_iNumLinks);
// -------------------------------
// Dump all the links to the file
// -------------------------------
int packedLinks = pNetwork->m_iNumLinks / 2;
FileSystem()->Write(&packedLinks, sizeof(int), pAIGraph);
buf.PutInt(packedLinks);
if (pNetwork->m_pAInode)
for (int node = 0; node < pNetwork->NumPathNodes(); node++)
{
for (int i = 0; i < pNetwork->m_iNumNodes; i++)
const CAI_Node* aiNode = pNetwork->GetPathNode(node);
for (int link = 0; link < aiNode->NumLinks(); link++)
{
const CAI_Node* aiNode = pNetwork->m_pAInode[i];
const CAI_NodeLink* nodeLink = aiNode->GetLinkByIndex(link);
for (int j = 0; j < aiNode->m_nNumLinks; j++)
// Skip links that don't originate from current node.
if (nodeLink->m_iSrcID == aiNode->m_iID)
{
const CAI_NodeLink* nodeLink = aiNode->links[j];
DevMsg(eDLL_T::SERVER, " |-- Writing link (%hd <--> %hd) to '0x%zX'\n", nodeLink->m_iSrcID, nodeLink->m_iDestID, buf.TellPut());
// Skip links that don't originate from current node.
if (nodeLink->m_iSrcID != aiNode->m_nIndex)
{
continue;
}
buf.PutShort(nodeLink->m_iSrcID);
buf.PutShort(nodeLink->m_iDestID);
DevMsg(eDLL_T::SERVER, " |-- Writing link '%hd' => '%hd' to '0x%zX'\n", nodeLink->m_iSrcID, nodeLink->m_iDestID, FileSystem()->Tell(pAIGraph));
FileSystem()->Write(&nodeLink->m_iSrcID, sizeof(short), pAIGraph);
FileSystem()->Write(&nodeLink->m_iDestID, sizeof(short), pAIGraph);
FileSystem()->Write(&nodeLink->unk1, sizeof(char), pAIGraph);
FileSystem()->Write(&nodeLink->m_bHulls, sizeof(nodeLink->m_bHulls), pAIGraph);
buf.PutChar(nodeLink->unk1);
buf.Put(nodeLink->m_iAcceptedMoveTypes, sizeof(nodeLink->m_iAcceptedMoveTypes));
}
}
}
@ -181,30 +174,68 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
Msg(eDLL_T::SERVER, "...done writing node links. %lf seconds (%d links)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumLinks);
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing hull data...\n");
// 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));
DevMsg(eDLL_T::SERVER, "+- Writing WC lookup table...\n");
if (pNetwork->m_iNumNodes > 0)
// -------------------------------
// Dump WC lookup table
// -------------------------------
CUtlMap<int, int> wcIDs;
SetDefLessFunc(wcIDs);
bool bCheckForProblems = false;
const CAI_NetworkEditTools* const pEditOps = (*g_ppAINetworkManager)->GetEditOps();
for (int node = 0; node < pNetwork->m_iNumNodes; node++)
{
std::unique_ptr<uint32[]> unkNodeBlock(new uint32_t[pNetwork->m_iNumNodes * sizeof(uint32_t)]);
memset(unkNodeBlock.get(), '\0', pNetwork->m_iNumNodes * sizeof(uint32_t));
const int nIndex = pEditOps->m_pNodeIndexTable[node];
const int iPreviousNodeBinding = wcIDs.Find(nIndex);
FileSystem()->Write(unkNodeBlock.get(), pNetwork->m_iNumNodes * sizeof(uint32_t), pAIGraph);
if (iPreviousNodeBinding != wcIDs.InvalidIndex())
{
if (!bCheckForProblems)
{
DevWarning(eDLL_T::SERVER, "******* MAP CONTAINS DUPLICATE HAMMER NODE IDS! CHECK FOR PROBLEMS IN HAMMER TO CORRECT *******\n");
bCheckForProblems = true;
}
DevWarning(eDLL_T::SERVER, " AI node %d is associated with Hammer node %d, but %d is already bound to node %d\n",
node, nIndex, nIndex, wcIDs[(unsigned short)nIndex]);
}
else
{
wcIDs.Insert(nIndex, node);
}
DevMsg(eDLL_T::SERVER, " |-- Writing Hammer node (%d <--> %d) at '0x%zX'\n", nIndex, wcIDs.Element((unsigned short)nIndex), buf.TellPut());
buf.PutInt(nIndex);
}
// 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' traverse ex nodes at '0x%zX'\n", 0, FileSystem()->Tell(pAIGraph));
timer.End();
Msg(eDLL_T::SERVER, "...done writing hammer nodes. %lf seconds (%d indices)\n", timer.GetDuration().GetSeconds(), wcIDs.Count());
short traverseExNodeCount = 0; // Only write count since count=0 means we don't have to actually do anything here.
FileSystem()->Write(&traverseExNodeCount, sizeof(short), pAIGraph);
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing traverse ex nodes...\n");
const int traverseExNodeCount = g_pAITraverseNodes->Count();
buf.PutShort((short)traverseExNodeCount);
FOR_EACH_VEC(*g_pAITraverseNodes, i)
{
DevMsg(eDLL_T::SERVER, " |-- Writing traverse ex node '%d' at '0x%zX'\n", i, buf.TellPut());
const CAI_TraverseNode& traverseExNode = (*g_pAITraverseNodes)[i];
buf.Put(&traverseExNode.m_Quat, sizeof(Quaternion));
buf.PutInt(traverseExNode.m_Index_MAYBE);
}
timer.End();
Msg(eDLL_T::SERVER, "...done writing traverse ex nodes. %lf seconds (%d nodes)\n", timer.GetDuration().GetSeconds(), traverseExNodeCount);
// 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));
timer.Start();
DevMsg(eDLL_T::SERVER, " |-- Writing '%d' bytes for hull data block at '0x%zX'\n", (MAX_HULLS * 8), buf.TellPut());
for (int i = 0; i < (MAX_HULLS * 8); i++)
{
FileSystem()->Write("\0", sizeof(char), pAIGraph);
buf.PutChar('\0');
}
timer.End();
@ -214,40 +245,40 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
DevMsg(eDLL_T::SERVER, "+- Writing path clusters...\n");
const int numClusters = g_pAIPathClusters->Count();
FileSystem()->Write(&numClusters, sizeof(int), pAIGraph);
buf.PutInt(numClusters);
FOR_EACH_VEC(*g_pAIPathClusters, i)
{
DevMsg(eDLL_T::SERVER, " |-- Writing cluster '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph));
DevMsg(eDLL_T::SERVER, " |-- Writing cluster '#%d' at '0x%zX'\n", i, buf.TellPut());
const CAI_Cluster* pathClusters = (*g_pAIPathClusters)[i];
FileSystem()->Write(&pathClusters->m_nIndex, sizeof(int), pAIGraph);
FileSystem()->Write(&pathClusters->unk1, sizeof(char), pAIGraph);
buf.PutInt(pathClusters->m_nIndex);
buf.PutChar(pathClusters->unk1);
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);
buf.PutFloat(pathClusters->GetOrigin().x);
buf.PutFloat(pathClusters->GetOrigin().y);
buf.PutFloat(pathClusters->GetOrigin().z);
const int unkVec0Size = pathClusters->unkVec0.Count();
FileSystem()->Write(&unkVec0Size, sizeof(int), pAIGraph);
buf.PutInt(unkVec0Size);
FOR_EACH_VEC(pathClusters->unkVec0, j)
{
short unkShort = static_cast<short>(pathClusters->unkVec0[j]);
FileSystem()->Write(&unkShort, sizeof(short), pAIGraph);
buf.PutShort(unkShort);
}
const int unkVec1Size = pathClusters->unkVec1.Count();
FileSystem()->Write(&unkVec1Size, sizeof(int), pAIGraph);
buf.PutInt(unkVec1Size);
FOR_EACH_VEC(pathClusters->unkVec1, j)
{
short unkShort = static_cast<short>(pathClusters->unkVec1[j]);
FileSystem()->Write(&unkShort, sizeof(short), pAIGraph);
buf.PutShort(unkShort);
}
FileSystem()->Write(&pathClusters->unk5, sizeof(char), pAIGraph);
buf.PutChar(pathClusters->unk5);
}
timer.End();
@ -257,41 +288,41 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
DevMsg(eDLL_T::SERVER, "+- Writing cluster links...\n");
const int numClusterLinks = g_pAIClusterLinks->Count();
FileSystem()->Write(&numClusterLinks, sizeof(int), pAIGraph);
buf.PutInt(numClusterLinks);
FOR_EACH_VEC(*g_pAIClusterLinks, i)
{
DevMsg(eDLL_T::SERVER, " |-- Writing 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);
DevMsg(eDLL_T::SERVER, " |-- Writing link (%hd <--> %hd) to '0x%zX'\n", clusterLink->m_iSrcID, clusterLink->m_iDestID, buf.TellPut());
FileSystem()->Write(&clusterLink->unk2, sizeof(int), pAIGraph);
FileSystem()->Write(&clusterLink->flags, sizeof(char), pAIGraph);
buf.PutShort(clusterLink->m_iSrcID);
buf.PutShort(clusterLink->m_iDestID);
FileSystem()->Write(&clusterLink->unk4, sizeof(char), pAIGraph);
FileSystem()->Write(&clusterLink->unk5, sizeof(char), pAIGraph);
buf.PutInt(clusterLink->unk2);
buf.PutChar(clusterLink->flags);
buf.PutChar(clusterLink->unkFlags4);
buf.PutChar(clusterLink->unkFlags5);
}
timer.End();
Msg(eDLL_T::SERVER, "...done writing cluster links. %lf seconds (%d 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);
buf.PutInt(pNetwork->unk5);
// 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);
buf.PutInt(pNetwork->m_iNumScriptNodes);
for (int i = 0; i < pNetwork->m_iNumScriptNodes; i++)
{
// 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);
DevMsg(eDLL_T::SERVER, " |-- Writing script node '#%d' at '0x%zX'\n", i, buf.TellPut());
buf.Put(&pNetwork->m_ScriptNode[i], sizeof(CAI_ScriptNode));
}
timer.End();
@ -300,16 +331,17 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing hint data...\n");
FileSystem()->Write(&pNetwork->m_iNumHints, sizeof(pNetwork->m_iNumHints), pAIGraph);
buf.PutInt(pNetwork->m_iNumHints);
for (int i = 0; i < pNetwork->m_iNumHints; i++)
{
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);
DevMsg(eDLL_T::SERVER, " |-- Writing hint data '#%d' at '0x%zX'\n", i, buf.TellPut());
buf.PutShort(pNetwork->m_Hints[i]);
}
timer.End();
Msg(eDLL_T::SERVER, "...done writing hint data. %lf seconds (%d hints)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumHints);
FileSystem()->Write(buf.Base(), buf.TellPut(), pAIGraph);
FileSystem()->Close(pAIGraph);
masterTimer.End();
@ -326,7 +358,7 @@ CAI_NetworkManager::LoadNetworkGraph
and validate status
==============================
*/
void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pManager, void* pBuffer, const char* szAIGraphFile)
void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pManager, CUtlBuffer* pBuffer, const char* szAIGraphFile)
{
bool bNavMeshAvailable = true;
@ -407,7 +439,7 @@ CAI_NetworkManager::LoadNetworkGraphEx
(internal)
==============================
*/
void CAI_NetworkManager::LoadNetworkGraphEx(CAI_NetworkManager* pManager, void* pBuffer, const char* szAIGraphFile)
void CAI_NetworkManager::LoadNetworkGraphEx(CAI_NetworkManager* pManager, CUtlBuffer* pBuffer, const char* szAIGraphFile)
{
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
CAI_NetworkManager__LoadNetworkGraph(pManager, pBuffer, szAIGraphFile);

View File

@ -15,21 +15,27 @@ class CAI_NetworkBuilder;
class CAI_NetworkManager;
/* ==== CAI_NETWORKMANAGER ============================================================================================================================================== */
inline CMemory p_CAI_NetworkManager__InitializeAINetworks = nullptr;
inline void (*CAI_NetworkManager__InitializeAINetworks)(void); // Static
inline CMemory p_CAI_NetworkManager__DelayedInit = nullptr;
inline void (*CAI_NetworkManager__DelayedInit)(CAI_NetworkManager* thisptr, CAI_Network* pNetwork);
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
inline CMemory p_CAI_NetworkManager__LoadNetworkGraph = nullptr;
inline void (*CAI_NetworkManager__LoadNetworkGraph)(CAI_NetworkManager* thisptr, void* pBuffer, const char* pszFileName);
inline void (*CAI_NetworkManager__LoadNetworkGraph)(CAI_NetworkManager* thisptr, CUtlBuffer* pBuffer, const char* pszFileName);
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
inline CMemory p_CAI_NetworkManager__LoadNetworkGraph = nullptr;
inline void (*CAI_NetworkManager__LoadNetworkGraph)(CAI_NetworkManager* thisptr, void* pBuffer, const char* pszFileName);
inline void (*CAI_NetworkManager__LoadNetworkGraph)(CAI_NetworkManager* thisptr, CUtlBuffer* pBuffer, const char* pszFileName);
#endif
/* ==== CAI_NETWORKBUILDER ============================================================================================================================================== */
inline CMemory p_CAI_NetworkBuilder__Build;
inline void (*CAI_NetworkBuilder__Build)(CAI_NetworkBuilder* thisptr, CAI_Network* pNetwork);
inline CAI_NetworkManager** g_ppAINetworkManager = nullptr;
inline CUtlVector<CAI_Cluster*>* g_pAIPathClusters = nullptr;
inline CUtlVector<CAI_ClusterLink*>* g_pAIClusterLinks = nullptr;
inline CUtlVector<CAI_TraverseNode>* g_pAITraverseNodes = nullptr;
//-----------------------------------------------------------------------------
// CAI_NetworkEditTools
@ -53,7 +59,7 @@ public:
// Debugging Tools
//-----------------
int m_debugNetOverlays;
CAI_Node** m_pNodes; // either nodes or node links.
int* m_pNodeIndexTable;
//-----------------
// Network pointers
@ -92,8 +98,11 @@ public:
class CAI_NetworkManager : public CBaseEntity
{
public:
static void LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile);
static void LoadNetworkGraphEx(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile);
static void LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager, CUtlBuffer* pBuffer, const char* szAIGraphFile);
static void LoadNetworkGraphEx(CAI_NetworkManager* pAINetworkManager, CUtlBuffer* pBuffer, const char* szAIGraphFile);
CAI_NetworkEditTools* GetEditOps() { return m_pEditOps; }
CAI_Network* GetNetwork() { /*Assert(!m_ThreadedBuild.pBuildingNetwork);*/ return m_pNetwork; }
private:
// !TODO[ AMOS ]: If found, change to ptr and hook up to engine!
@ -118,21 +127,27 @@ class VAI_NetworkManager : public IDetour
{
virtual void GetAdr(void) const
{
LogFunAdr("CAI_NetworkManager::InitializeAINetworks", p_CAI_NetworkManager__InitializeAINetworks.GetPtr());
LogFunAdr("CAI_NetworkManager::LoadNetworkGraph", p_CAI_NetworkManager__LoadNetworkGraph.GetPtr());
LogFunAdr("CAI_NetworkManager::DelayedInit", p_CAI_NetworkManager__DelayedInit.GetPtr());
LogFunAdr("CAI_NetworkBuilder::Build", p_CAI_NetworkBuilder__Build.GetPtr());
LogVarAdr("g_pAINetworkManager", reinterpret_cast<uintptr_t>(g_ppAINetworkManager));
LogVarAdr("g_AIPathClusters< CAI_Cluster* >", reinterpret_cast<uintptr_t>(g_pAIPathClusters));
LogVarAdr("g_AIClusterLinks< CAI_ClusterLink* >", reinterpret_cast<uintptr_t>(g_pAIClusterLinks));
LogVarAdr("g_AITraverseNodes< CAI_TraverseNode >", reinterpret_cast<uintptr_t>(g_pAITraverseNodes));
}
virtual void GetFun(void) const
{
p_CAI_NetworkManager__InitializeAINetworks = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 4C 89 74 24 ?? 55 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? E8 ?? ?? ?? ??");
CAI_NetworkManager__InitializeAINetworks = p_CAI_NetworkManager__InitializeAINetworks.RCast<void (*)(void)>();
p_CAI_NetworkManager__DelayedInit = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 48 8B 0D ?? ?? ?? ?? 8B 41 6C");
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
p_CAI_NetworkManager__LoadNetworkGraph = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 4C 24 ?? 55 53 57 41 54 41 55 41 56");
CAI_NetworkManager__LoadNetworkGraph = p_CAI_NetworkManager__LoadNetworkGraph.RCast<void (*)(CAI_NetworkManager*, void*, const char*)>();
CAI_NetworkManager__LoadNetworkGraph = p_CAI_NetworkManager__LoadNetworkGraph.RCast<void (*)(CAI_NetworkManager*, CUtlBuffer*, const char*)>();
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
p_CAI_NetworkManager__LoadNetworkGraph = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 4C 24 ?? 55 53 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 48 8B FA");
CAI_NetworkManager__LoadNetworkGraph = p_CAI_NetworkManager__LoadNetworkGraph.RCast<void (*)(CAI_NetworkManager*, void*, const char*)>();
CAI_NetworkManager__LoadNetworkGraph = p_CAI_NetworkManager__LoadNetworkGraph.RCast<void (*)(CAI_NetworkManager*, CUtlBuffer*, const char*)>();
#endif
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
p_CAI_NetworkBuilder__Build = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 4C 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 30 48 63 BA ?? ?? ?? ??");
@ -144,10 +159,14 @@ class VAI_NetworkManager : public IDetour
}
virtual void GetVar(void) const
{
g_ppAINetworkManager = p_CAI_NetworkManager__InitializeAINetworks.FindPattern("48 89 05", CMemory::Direction::DOWN)
.ResolveRelativeAddressSelf(0x3, 0x7).RCast<CAI_NetworkManager**>();
g_pAIPathClusters = g_GameDll.FindPatternSIMD("F3 0F 10 52 ?? 4C 8B CA")
.FindPatternSelf("48 8B 35", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast<CUtlVector<CAI_Cluster*>*>();
g_pAIClusterLinks = g_GameDll.FindPatternSIMD("F3 0F 10 52 ?? 4C 8B CA")
.FindPatternSelf("4C 8B 1D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast<CUtlVector<CAI_ClusterLink*>*>();
g_pAITraverseNodes = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 54 41 56 48 8B EC 48 81 EC ?? ?? ?? ??").OffsetSelf(0x2EF)
.FindPatternSelf("48 8B 05", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast<CUtlVector<CAI_TraverseNode>*>();
}
virtual void GetCon(void) const { }
virtual void Attach(void) const;

View File

@ -10,6 +10,19 @@ constexpr int MAX_HULLS = 5;
constexpr int NOT_CACHED = -2; // Returned if data not in cache
constexpr int NO_NODE = -1; // Returned when no node meets the qualification
//=========================================================
// >> The type of node
//=========================================================
enum NodeType_e // !TODO: unconfirmed for r1/r2/r5.
{
NODE_ANY, // Used to specify any type of node (for search)
NODE_DELETED, // Used in wc_edit mode to remove nodes during runtime
NODE_GROUND,
NODE_AIR,
NODE_CLIMB,
NODE_WATER
};
//=============================================================================
// >> CAI_NodeLink
//=============================================================================
@ -17,7 +30,7 @@ struct CAI_NodeLink
{
short m_iSrcID;
short m_iDestID;
bool m_bHulls[MAX_HULLS];
byte m_iAcceptedMoveTypes[MAX_HULLS];
byte m_LinkInfo;
char unk1; // maps => unk0 on disk
char unk2[5];
@ -27,15 +40,31 @@ struct CAI_NodeLink
//=============================================================================
// >> CAI_Node
//=============================================================================
struct CAI_Node
class CAI_Node
{
int m_nIndex; // Not present on disk
Vector3D m_vOrigin;
float m_fHulls[MAX_HULLS];
float m_flYaw;
public:
const Vector3D& GetOrigin() const { return m_vOrigin; }
Vector3D& AccessOrigin() { return m_vOrigin; }
float GetYaw() const { return m_flYaw; }
int NumLinks() const { return m_Links.Count(); }
void ClearLinks() { m_Links.Purge(); }
CAI_NodeLink* GetLinkByIndex(int i) const { return m_Links[i]; }
NodeType_e SetType(NodeType_e type) { return (m_eNodeType = type); }
NodeType_e GetType() const { return m_eNodeType; }
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
NodeType_e m_eNodeType; // The type of node; always 2 in buildainfile.
int m_eNodeInfo; // bits that tell us more about this nodes
int unk0; // Always 2 in buildainfile, maps directly to unk0 in disk struct
int unk1; // Maps directly to unk1 in disk struct
int unk2[MAX_HULLS]; // Maps directly to unk2 in disk struct, despite being ints rather than shorts
// View server.dll+393672 for context
@ -43,36 +72,24 @@ struct CAI_Node
char pad[3]; // Aligns next bytes
float unk4[MAX_HULLS]; // I have no clue, calculated using some kind float function magic
CAI_NodeLink** links;
void* unkBuf0;
void* unkBuf1;
int m_nNumLinks;
int unk11; // Bad name lmao
CUtlVector<CAI_NodeLink*> m_Links;
short unk6; // Should match up to unk4 on disk
char unk7[16]; // Padding until next bit
short unk8; // Should match up to unk5 on disk
char unk9[8]; // Padding until next bit
char unk10[8]; // Should match up to unk6 on disk
};
//=============================================================================
// >> CAI_ScriptNode
//=============================================================================
struct CAI_ScriptNode
{
Vector3D m_vOrigin;
// Might be wrong; seems to be used for clamping.
// See [r5apex_ds + 0xF28A6E]
int m_nMin;
int m_nMax;
short unk8;
short unk9; // Should match up to unk5 on disk
char unk10[6]; // Padding until next bit
char unk11[8]; // Should match up to unk6 on disk
};
//=============================================================================
// >> CAI_Cluster
//=============================================================================
struct CAI_Cluster
class CAI_Cluster
{
public:
const Vector3D& GetOrigin() const { return m_vOrigin; }
Vector3D& AccessOrigin() { return m_vOrigin; }
int m_nIndex;
char unk0;
char unk1; // Maps to unk1 on disk
@ -103,11 +120,34 @@ static_assert(sizeof(CAI_Cluster) == 608);
//=============================================================================
struct CAI_ClusterLink
{
short prevIndex_MAYBE;
short nextIndex_MAYBE;
short m_iSrcID;
short m_iDestID;
int unk2;
char flags;
char unk4;
char unk5;
char unkFlags4;
char unkFlags5;
};
static_assert(sizeof(CAI_ClusterLink) == 12);
//=============================================================================
// >> CAI_ScriptNode
//=============================================================================
struct CAI_TraverseNode
{
Quaternion m_Quat;
int m_Index_MAYBE;
};
static_assert(sizeof(CAI_TraverseNode) == 20);
//=============================================================================
// >> CAI_ScriptNode
//=============================================================================
struct CAI_ScriptNode
{
Vector3D m_vOrigin;
// Might be wrong; seems to be used for clamping.
// See [r5apex_ds + 0xF28A6E]
int m_nMin;
int m_nMax;
};
static_assert(sizeof(CAI_ClusterLink) == 12);

View File

@ -55,7 +55,7 @@ void CAI_Utility::DrawAIScriptNetwork(const CAI_Network* pNetwork) const
OverlayBox_t::Transforms vTransforms;
std::unordered_set<int64_t> uLinkSet;
for (int i = ai_script_nodes_draw->GetInt(), ns = pNetwork->GetNumScriptNodes(); i < ns; i++)
for (int i = ai_script_nodes_draw->GetInt(), ns = pNetwork->NumScriptNodes(); i < ns; i++)
{
if (nNodeRange && i > nNodeRange)
break;