Use internal FileSystem for loading and handling

AIN files are now written to GAME dir, and the engine attempts to load the AI files from any GAME dir (search includes the VPK's).
This commit is contained in:
Kawe Mazidjatari 2022-08-23 21:32:12 +02:00
parent 260f862a69
commit 6af5c7cdcf
2 changed files with 124 additions and 66 deletions

View File

@ -10,8 +10,8 @@
#include "tier1/cmd.h"
#include "mathlib/crc32.h"
#include "public/edict.h"
#include "public/utility/binstream.h"
#include "public/utility/utility.h"
#include "filesystem/filesystem.h"
#include "engine/host_state.h"
#include "game/server/ai_node.h"
#include "game/server/ai_network.h"
@ -19,6 +19,7 @@
constexpr int AINET_SCRIPT_VERSION_NUMBER = 21;
constexpr int AINET_VERSION_NUMBER = 57;
constexpr int AINETWORK_MIN_SIZE = 82;
/*
==============================
@ -31,8 +32,8 @@ CAI_NetworkBuilder::BuildFile
*/
void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
{
const string svMeshDir = "maps\\navmesh\\";
const string svGraphDir = "maps\\graphs\\";
const string svMeshDir = "maps/navmesh/";
const string svGraphDir = "maps/graphs/";
fs::path fsMeshPath(svMeshDir + g_pHostState->m_levelName + "_" + SHULL_SIZE[EHULL_SIZE::LARGE] + ".nm");
fs::path fsGraphPath(svGraphDir + g_pHostState->m_levelName + ".ain");
@ -40,45 +41,54 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
CFastTimer masterTimer;
CFastTimer timer;
CIOStream writer(fsGraphPath, CIOStream::Mode_t::WRITE);
int nCalculatedLinkcount = 0;
// Build from memory.
DevMsg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
DevMsg(eDLL_T::SERVER, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> AI NETWORK GRAPH FILE CONSTRUCTION STARTED <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
DevMsg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
DevMsg(eDLL_T::SERVER, "+- Writing header...\n");
masterTimer.Start();
timer.Start();
CreateDirectories(svGraphDir);
FileHandle_t pAIGraph = FileSystem()->Open(fsGraphPath.relative_path().u8string().c_str(), "wb", "GAME");
if (!pAIGraph)
{
Error(eDLL_T::SERVER, false, "%s - Unable to write to '%s' (read-only?)\n", __FUNCTION__, fsGraphPath.relative_path().u8string().c_str());
return;
}
DevMsg(eDLL_T::SERVER, "+- Writing header...\n");
DevMsg(eDLL_T::SERVER, " |-- AINet version: '%d'\n", AINET_VERSION_NUMBER);
writer.Write(&AINET_VERSION_NUMBER, sizeof(int));
FileSystem()->Write(&AINET_VERSION_NUMBER, sizeof(int), pAIGraph);
DevMsg(eDLL_T::SERVER, " |-- Map version: '%d'\n", g_ServerGlobalVariables->m_nMapVersion);
writer.Write(&g_ServerGlobalVariables->m_nMapVersion, sizeof(int));
FileSystem()->Write(&g_ServerGlobalVariables->m_nMapVersion, sizeof(int), pAIGraph);
CIOStream reader(fsMeshPath, CIOStream::Mode_t::READ);
FileHandle_t pNavMesh = FileSystem()->Open(fsMeshPath.relative_path().u8string().c_str(), "rb", "GAME");
uint32_t nNavMeshHash = NULL;
if (reader.IsReadable())
if (!pNavMesh)
{
nNavMeshHash = crc32::update(NULL, reader.GetData(), reader.GetSize());
Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network.\n", __FUNCTION__, SHULL_SIZE[EHULL_SIZE::LARGE].c_str());
}
else
{
Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network.\n", __FUNCTION__, SHULL_SIZE[EHULL_SIZE::LARGE].c_str());
uint32_t nLen = FileSystem()->Size(pNavMesh);
uint8_t* pBuf = MemAllocSingleton()->Alloc<uint8_t>(nLen);
FileSystem()->Read(pBuf, nLen, pNavMesh);
FileSystem()->Close(pNavMesh);
nNavMeshHash = crc32::update(NULL, pBuf, nLen);
MemAllocSingleton()->Free(pBuf);
}
// Large NavMesh CRC.
DevMsg(eDLL_T::SERVER, " |-- NavMesh CRC: '%lx'\n", nNavMeshHash);
writer.Write(&nNavMeshHash, sizeof(int));
FileSystem()->Write(&nNavMeshHash, sizeof(uint32_t), pAIGraph);
// Path nodes.
DevMsg(eDLL_T::SERVER, " |-- Nodecount: '%d'\n", pNetwork->m_iNumNodes);
writer.Write(&pNetwork->m_iNumNodes, sizeof(int));
FileSystem()->Write(&pNetwork->m_iNumNodes, sizeof(int), pAIGraph);
timer.End();
DevMsg(eDLL_T::SERVER, "...done writing header. %lf seconds\n", timer.GetDuration().GetSeconds());
@ -86,6 +96,8 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing node positions...\n");
int nCalculatedLinkcount = 0;
if (pNetwork->m_pAInode)
{
for (int i = 0; i < pNetwork->m_iNumNodes; i++)
@ -111,8 +123,8 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
memcpy(diskNode.unk6, pNetwork->m_pAInode[i]->unk10, sizeof(diskNode.unk6));
DevMsg(eDLL_T::SERVER, " |-- Copying node '#%d' from '0x%p' to '0x%zX'\n", pNetwork->m_pAInode[i]->m_nIndex, reinterpret_cast<void*>(pNetwork->m_pAInode[i]), writer.GetPosition());
writer.Write(&diskNode, sizeof(CAI_NodeDisk));
DevMsg(eDLL_T::SERVER, " |-- Copying node '#%d' from '0x%p' to '0x%zX'\n", pNetwork->m_pAInode[i]->m_nIndex, reinterpret_cast<void*>(pNetwork->m_pAInode[i]), FileSystem()->Tell(pAIGraph));
FileSystem()->Write(&diskNode, sizeof(CAI_NodeDisk), pAIGraph);
nCalculatedLinkcount += pNetwork->m_pAInode[i]->m_nNumLinks;
}
@ -135,7 +147,7 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
}
}
writer.Write(&nCalculatedLinkcount, sizeof(int));
FileSystem()->Write(&nCalculatedLinkcount, sizeof(int), pAIGraph);
if (pNetwork->m_pAInode)
{
@ -155,8 +167,8 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
diskLink.unk0 = pNetwork->m_pAInode[i]->links[j]->unk1;
memcpy(diskLink.m_bHulls, pNetwork->m_pAInode[i]->links[j]->m_bHulls, sizeof(diskLink.m_bHulls));
DevMsg(eDLL_T::SERVER, " |-- Writing link '%h' => '%h' to '0x%zX'\n", diskLink.m_iSrcID, diskLink.m_iDestID, writer.GetPosition());
writer.Write(&diskLink, sizeof(CAI_NodeLinkDisk));
DevMsg(eDLL_T::SERVER, " |-- Writing link '%h' => '%h' to '0x%zX'\n", diskLink.m_iSrcID, diskLink.m_iDestID, FileSystem()->Tell(pAIGraph));
FileSystem()->Write(&diskLink, sizeof(CAI_NodeLinkDisk), pAIGraph);
}
}
}
@ -167,27 +179,28 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
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), writer.GetPosition());
DevMsg(eDLL_T::SERVER, " |-- Writing '%d' bytes for node block at '0x%zX'\n", pNetwork->m_iNumNodes * sizeof(uint32_t), FileSystem()->Tell(pAIGraph));
if (pNetwork->m_iNumNodes > 0)
{
uint32_t* unkNodeBlock = new uint32_t[pNetwork->m_iNumNodes];
uint32_t* unkNodeBlock = MemAllocSingleton()->Alloc<uint32_t>(pNetwork->m_iNumNodes * sizeof(uint32_t));
memset(&unkNodeBlock, '\0', pNetwork->m_iNumNodes * sizeof(uint32_t));
writer.Write(&*unkNodeBlock, pNetwork->m_iNumNodes * sizeof(uint32_t));
delete[] unkNodeBlock;
FileSystem()->Write(&*unkNodeBlock, pNetwork->m_iNumNodes * sizeof(uint32_t), pAIGraph);
MemAllocSingleton()->Free(unkNodeBlock);
}
// 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, writer.GetPosition());
DevMsg(eDLL_T::SERVER, " |-- Writing '%d' traversal nodes at '0x%zX'\n", 0, FileSystem()->Tell(pAIGraph));
short traverseNodeCount = 0; // Only write count since count=0 means we don't have to actually do anything here.
writer.Write(&traverseNodeCount, sizeof(short));
FileSystem()->Write(&traverseNodeCount, sizeof(short), pAIGraph);
// 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, writer.GetPosition());
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++)
{
writer.Write<uint8_t>('\0');
FileSystem()->Write("\0", sizeof(char), pAIGraph);
}
timer.End();
@ -196,34 +209,34 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing clusters...\n");
writer.Write(&*g_nAiNodeClusters, sizeof(*g_nAiNodeClusters));
FileSystem()->Write(&*g_nAiNodeClusters, sizeof(*g_nAiNodeClusters), pAIGraph);
for (int i = 0; i < *g_nAiNodeClusters; i++)
{
DevMsg(eDLL_T::SERVER, " |-- Writing cluster '#%d' at '0x%zX'\n", i, writer.GetPosition());
DevMsg(eDLL_T::SERVER, " |-- Writing cluster '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph));
AINodeClusters* nodeClusters = (*g_pppAiNodeClusters)[i];
writer.Write(&nodeClusters->m_nIndex, sizeof(nodeClusters->m_nIndex));
writer.Write(&nodeClusters->unk1, sizeof(nodeClusters->unk1));
FileSystem()->Write(&nodeClusters->m_nIndex, sizeof(nodeClusters->m_nIndex), pAIGraph);
FileSystem()->Write(&nodeClusters->unk1, sizeof(nodeClusters->unk1), pAIGraph);
writer.Write(&nodeClusters->m_vOrigin.x, sizeof(nodeClusters->m_vOrigin.x));
writer.Write(&nodeClusters->m_vOrigin.y, sizeof(nodeClusters->m_vOrigin.y));
writer.Write(&nodeClusters->m_vOrigin.z, sizeof(nodeClusters->m_vOrigin.z));
FileSystem()->Write(&nodeClusters->m_vOrigin.x, sizeof(nodeClusters->m_vOrigin.x), pAIGraph);
FileSystem()->Write(&nodeClusters->m_vOrigin.y, sizeof(nodeClusters->m_vOrigin.y), pAIGraph);
FileSystem()->Write(&nodeClusters->m_vOrigin.z, sizeof(nodeClusters->m_vOrigin.z), pAIGraph);
writer.Write(&nodeClusters->unkcount0, sizeof(nodeClusters->unkcount0));
FileSystem()->Write(&nodeClusters->unkcount0, sizeof(nodeClusters->unkcount0), pAIGraph);
for (int j = 0; j < nodeClusters->unkcount0; j++)
{
short unk2Short = static_cast<short>(nodeClusters->unk2[j]);
writer.Write(&unk2Short, sizeof(unk2Short));
FileSystem()->Write(&unk2Short, sizeof(unk2Short), pAIGraph);
}
writer.Write(&nodeClusters->unkcount1, sizeof(nodeClusters->unkcount1));
FileSystem()->Write(&nodeClusters->unkcount1, sizeof(nodeClusters->unkcount1), pAIGraph);
for (int j = 0; j < nodeClusters->unkcount1; j++)
{
short unk3Short = static_cast<short>(nodeClusters->unk3[j]);
writer.Write(&unk3Short, sizeof(unk3Short));
FileSystem()->Write(&unk3Short, sizeof(unk3Short), pAIGraph);
}
writer.Write(&nodeClusters->unk5, sizeof(nodeClusters->unk5));
FileSystem()->Write(&nodeClusters->unk5, sizeof(nodeClusters->unk5), pAIGraph);
}
timer.End();
@ -232,30 +245,30 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing cluster links...\n");
writer.Write(&*g_nAiNodeClusterLinks, sizeof(*g_nAiNodeClusterLinks));
FileSystem()->Write(&*g_nAiNodeClusterLinks, sizeof(*g_nAiNodeClusterLinks), pAIGraph);
for (int i = 0; i < *g_nAiNodeClusterLinks; i++)
{
// Disk and memory structs are literally identical here so just directly write.
DevMsg(eDLL_T::SERVER, " |-- Writing cluster link '#%d' at '0x%zX'\n", i, writer.GetPosition());
writer.Write(&*g_pppAiNodeClusterLinks[i], sizeof(*(*g_pppAiNodeClusterLinks)[i]));
DevMsg(eDLL_T::SERVER, " |-- Writing cluster link '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph));
FileSystem()->Write(&*g_pppAiNodeClusterLinks[i], sizeof(*(*g_pppAiNodeClusterLinks)[i]), pAIGraph);
}
timer.End();
DevMsg(eDLL_T::SERVER, "...done writing cluster links. %lf seconds (%d cluster links)\n", timer.GetDuration().GetSeconds(), *g_nAiNodeClusterLinks);
// This is always set to '-1'. Likely a field for maintaining compatibility.
writer.Write(&pNetwork->unk5, sizeof(pNetwork->unk5));
FileSystem()->Write(&pNetwork->unk5, sizeof(pNetwork->unk5), pAIGraph);
// 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");
writer.Write(&pNetwork->m_iNumScriptNodes, sizeof(pNetwork->m_iNumScriptNodes));
FileSystem()->Write(&pNetwork->m_iNumScriptNodes, sizeof(pNetwork->m_iNumScriptNodes), pAIGraph);
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, writer.GetPosition());
writer.Write(&pNetwork->m_ScriptNode[i], sizeof(CAI_ScriptNode));
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();
@ -264,16 +277,18 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork)
timer.Start();
DevMsg(eDLL_T::SERVER, "+- Writing hint data...\n");
writer.Write(&pNetwork->m_iNumHints, sizeof(pNetwork->m_iNumHints));
FileSystem()->Write(&pNetwork->m_iNumHints, sizeof(pNetwork->m_iNumHints), pAIGraph);
for (int i = 0; i < pNetwork->m_iNumHints; i++)
{
DevMsg(eDLL_T::SERVER, " |-- Writing hint data '#%d' at '0x%zX'\n", i, writer.GetPosition());
writer.Write(&pNetwork->m_Hints[i], sizeof(pNetwork->m_Hints[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);
}
timer.End();
DevMsg(eDLL_T::SERVER, "...done writing hint data. %lf seconds (%d hints)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumHints);
FileSystem()->Close(pAIGraph);
masterTimer.End();
DevMsg(eDLL_T::SERVER, "...done writing AI node graph. %lf seconds\n", masterTimer.GetDuration().GetSeconds());
DevMsg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
@ -290,43 +305,66 @@ CAI_NetworkManager::LoadNetworkGraph
*/
void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile)
{
string svMeshDir = "maps\\navmesh\\";
string svGraphDir = "maps\\graphs\\";
string svMeshDir = "maps/navmesh/";
string svGraphDir = "maps/graphs/";
fs::path fsMeshPath(svMeshDir + g_pHostState->m_levelName + "_" + SHULL_SIZE[EHULL_SIZE::LARGE] + ".nm");
fs::path fsGraphPath(svGraphDir + g_pHostState->m_levelName + ".ain");
int nAiNetVersion = NULL;
int nAiMapVersion = NULL;
bool bNavMeshAvailable = true;
uint32_t nAiGraphHash = NULL;
uint32_t nNavMeshHash = NULL;
CIOStream iAIGraph(fsGraphPath, CIOStream::Mode_t::READ);
if (iAIGraph.IsReadable())
FileHandle_t pNavMesh = FileSystem()->Open(fsMeshPath.relative_path().u8string().c_str(), "rb", "GAME");
if (!pNavMesh)
{
iAIGraph.Read(nAiNetVersion);
iAIGraph.Read(nAiMapVersion);
iAIGraph.Read(nAiGraphHash);
Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network.\n", __FUNCTION__, SHULL_SIZE[EHULL_SIZE::LARGE].c_str());
bNavMeshAvailable = false;
}
else
{
uint32_t nLen = FileSystem()->Size(pNavMesh);
uint8_t* pBuf = MemAllocSingleton()->Alloc<uint8_t>(nLen);
FileSystem()->Read(pBuf, nLen, pNavMesh);
FileSystem()->Close(pNavMesh);
nNavMeshHash = crc32::update(NULL, pBuf, nLen);
MemAllocSingleton()->Free(pBuf);
}
FileHandle_t pAIGraph = FileSystem()->Open(fsGraphPath.relative_path().u8string().c_str(), "rb", "GAME");
if (!pAIGraph)
{
Error(eDLL_T::SERVER, false, "%s - Unable to open '%s' (insufficient rights?)\n", __FUNCTION__,
fsGraphPath.relative_path().u8string().c_str());
LoadNetworkGraphEx(pAINetworkManager, pBuffer, szAIGraphFile);
return;
}
if (FileSystem()->Size(pAIGraph) >= AINETWORK_MIN_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' deviates expectations (net version: '%d' expected: '%d')\n",
Warning(eDLL_T::SERVER, "AI node graph '%s' deviates expectations (net version: '%d' expected: '%d')\n",
fsGraphPath.relative_path().u8string().c_str(), 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",
Warning(eDLL_T::SERVER, "AI node graph '%s' is out of date (map version: '%d' expected: '%d')\n",
fsGraphPath.relative_path().u8string().c_str(), nAiMapVersion, g_ServerGlobalVariables->m_nMapVersion);
}
else
{
CIOStream iNavMesh(fsMeshPath, CIOStream::Mode_t::READ);
if (iNavMesh.IsReadable())
if (bNavMeshAvailable)
{
nNavMeshHash = crc32::update(NULL, iNavMesh.GetData(), iNavMesh.GetSize());
if (nNavMeshHash != nAiGraphHash)
{
Warning(eDLL_T::SERVER, "AI node graph '%s' is out of date (checksum: '0x%X' expected: '0x%X')\n",
@ -335,7 +373,26 @@ void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager,
}
}
}
else
{
Error(eDLL_T::SERVER, false, "%s - AI node graph '%s' is corrupt (LEN_BYTES < AINETWORK_MIN_SIZE)\n", __FUNCTION__,
fsGraphPath.relative_path().u8string().c_str());
}
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)
{
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
CAI_NetworkManager__LoadNetworkGraph(pAINetworkManager, pBuffer, szAIGraphFile, NULL);
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)

View File

@ -60,6 +60,7 @@ class CAI_NetworkManager
{
public:
static void LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile);
static void LoadNetworkGraphEx(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile);
};
///////////////////////////////////////////////////////////////////////////////