CAI related stuff (see description)

* Partially rebuild CGlobalVarsBase and CGlobalVars struct.
* Partially implement BuildAINFile (disk builder) written by BobTheBob9. All that needs to be found are the addresses/offsets for 'pUnkStruct0Count' and 'pUnkLinkStruct1Count' (see ai_node.h). I didn't have time for it yet.
* Patch call which restarts the engine once AIN file has been build (so it doesn't get cleared from memory).
* Temporarily patch write operation in 'Detours_LevelInit()' which writes out of scope since the navmesh structure is misaligned somewhere still with a few bytes.

NOTE: 'CAI_NetworkBuilder::BuildFile()' is for now commented out until the 2 other structs are found in the game dll. Once found then we could start fixing the struct offsets and hope it works as-is.
This commit is contained in:
Amos 2022-02-27 03:15:00 +01:00
parent 92c0c61450
commit 62604e8fe9
18 changed files with 650 additions and 5 deletions

View File

@ -13,9 +13,10 @@
#include "engine/cl_main.h"
#include "engine/sv_main.h"
#include "engine/sys_getmodes.h"
#include "game/server/ai_networkmanager.h"
#include "game/server/fairfight_impl.h"
#include "rtech/rtech_game.h"
#include "client/cdll_engine_int.h"
#include "game/server/fairfight_impl.h"
#include "materialsystem/materialsystem.h"
#include "studiorender/studiorendercontext.h"
#include "squirrel/sqvm.h"
@ -300,6 +301,11 @@ void RuntimePtc_Init() /* .TEXT */
#else
p_SQVM_CompileError.Offset(0xE0).FindPatternSelf("E8", ADDRESS::Direction::DOWN, 200).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | For dedicated we should not perform post-error events such as telemetry / showing 'COM_ExplainDisconnection' UI etc.
#endif // !DEDICATED
#if defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
p_CAI_NetworkManager__ShouldRebuild.Offset(0xA0).FindPatternSelf("FF ?? ?? ?? 00 00", ADDRESS::Direction::DOWN, 200).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | Virtual call to restart when building AIN (which clears the AIN memory). Remove this once writing to file works.
Detour_LevelInit.Offset(0x100).FindPatternSelf("74", ADDRESS::Direction::DOWN, 600).Patch({ 0xEB }); // JE --> JMP | Do while loop setting fields to -1 in navmesh is writing out of bounds (!TODO).
#endif
}
void RuntimePtc_Toggle() /* .TEXT */

View File

@ -96,6 +96,13 @@ namespace
ADDRESS _Host_RunFrame = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x48\x89\x58\x18\x48\x89\x70\x20\xF3\x0F\x11\x48\x00", "xxxxxxxxxxxxxxx?"); // _Host_RunFrame() with inlined CFrameTimer::MarkFrame()?
// 0x140231C00 // 48 8B C4 48 89 58 18 48 89 70 20 F3 0F 11 48 ? //
//-------------------------------------------------------------------------
// RUNTIME: DETOUR_LEVELINIT
//-------------------------------------------------------------------------
ADDRESS Detour_LevelInit = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x74\x24\x00\x48\x89\x7C\x24\x00\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\xAC\x24\x00\x00\x00\x00\x48\x81\xEC\x00\x00\x00\x00\x45\x33\xE4", "xxxx?xxxx?xxxx?xxxxxxxxxxxxx????xxx????xxx");
// 0x140EF9100 // 48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 45 33 E4 //
//-------------------------------------------------------------------------
// .RDATA
//-------------------------------------------------------------------------

View File

@ -62,8 +62,12 @@
#include "engine/gl_screen.h"
#include "engine/debugoverlay.h"
#endif // !DEDICATED
#include "game/server/ai_node.h"
#include "game/server/ai_network.h"
#include "game/server/ai_networkmanager.h"
#include "game/server/fairfight_impl.h"
#include "game/server/gameinterface.h"
#include "public/include/edict.h"
#ifndef DEDICATED
#include "inputsystem/inputsystem.h"
#include "windows/id3dx.h"

View File

@ -5,7 +5,7 @@ namespace
{
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
ADDRESS SCR_BeginLoadingPlaque = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x74\x24\x00\x57\x48\x83\xEC\x30\x0F\x29\x74\x24\x00\x48\x8B\xF9", "xxxx?xxxx?xxxxxxxxx?xxx");
// 0x14022A4A0 // 48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 30 0F 29 74 24 ? 48 8B F9 //
// 0x14022A4A0 // 48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 30 0F 29 74 24 ? 48 8B F9 //
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
ADDRESS SCR_BeginLoadingPlaque = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x38\x0F\x29\x74\x24\x00\x48\x89\x5C\x24\x00", "xxxxxxxx?xxxx?");
// 0x14022A4A0 // 48 83 EC 38 0F 29 74 24 ? 48 89 5C 24 ? //

View File

@ -5,12 +5,15 @@
namespace
{
/* ==== SV_MAIN ======================================================================================================================================================= */
ADDRESS p_SV_CreateBaseline = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x48\x8B\x0D\x00\x00\x00\x00\x48\x85\xC9\x75\x07", "xxxxxxx????xxxxx");
bool (*SV_CreateBaseline)() = (bool(*)())p_SV_CreateBaseline.GetPtr(); /*48 83 EC 28 48 8B 0D ? ? ? ? 48 85 C9 75 07*/
ADDRESS p_SV_InitGameDLL = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x81\xEC\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x80\x3D\x00\x00\x00\x00\x00\x0F\x85\x00\x00\x00\x00", "xxx????x????xx?????xx????");
void (*SV_InitGameDLL)(float a1) = (void(*)(float))p_SV_InitGameDLL.GetPtr(); /*48 81 EC ? ? ? ? E8 ? ? ? ? 80 3D ? ? ? ? ? 0F 85 ? ? ? ?*/
ADDRESS p_SV_ShutdownGameDLL = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x80\x3D\x00\x00\x00\x00\x00\x0F\x84\x00\x00\x00\x00\x48\x8B\x0D\x00\x00\x00\x00\x48\x89\x5C\x24\x00", "xxxxxx?????xx????xxx????xxxx?");
void (*SV_ShutdownGameDLL)() = (void(*)())p_SV_ShutdownGameDLL.GetPtr(); /*48 83 EC 28 80 3D ? ? ? ? ? 0F 84 ? ? ? ? 48 8B 0D ? ? ? ? 48 89 5C 24 ?*/
ADDRESS p_SV_CreateBaseline = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x48\x8B\x0D\x00\x00\x00\x00\x48\x85\xC9\x75\x07", "xxxxxxx????xxxxx");
bool (*SV_CreateBaseline)() = (bool(*)())p_SV_CreateBaseline.GetPtr(); /*48 83 EC 28 48 8B 0D ? ? ? ? 48 85 C9 75 07*/
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
ADDRESS CGameServer__SpawnServer = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x55\x56\x57\x41\x55\x41\x56\x41\x57\x48\x81\xEC\x00\x00\x00\x00", "xxxxxxxxxxxxxx????");
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
@ -28,8 +31,9 @@ class HSV_Main : public IDetour
{
virtual void debugp()
{
std::cout << "| FUN: SV_CreateBaseline : 0x" << std::hex << std::uppercase << p_SV_CreateBaseline.GetPtr() << std::setw(npad) << " |" << std::endl;
std::cout << "| FUN: SV_InitGameDLL : 0x" << std::hex << std::uppercase << p_SV_ShutdownGameDLL.GetPtr() << std::setw(npad) << " |" << std::endl;
std::cout << "| FUN: SV_ShutdownGameDLL : 0x" << std::hex << std::uppercase << p_SV_ShutdownGameDLL.GetPtr() << std::setw(npad) << " |" << std::endl;
std::cout << "| FUN: SV_CreateBaseline : 0x" << std::hex << std::uppercase << p_SV_CreateBaseline.GetPtr() << std::setw(npad) << " |" << std::endl;
std::cout << "| FUN: CGameServer::SpawnServer : 0x" << std::hex << std::uppercase << CGameServer__SpawnServer.GetPtr() << std::setw(npad) << " |" << std::endl;
std::cout << "+----------------------------------------------------------------+" << std::endl;
}

View File

@ -0,0 +1,34 @@
#pragma once
#include "ai_node.h"
//-----------------------------------------------------------------------------
// CAI_Network
//
// Purpose: Stores a node graph through which an AI may pathfind
//-----------------------------------------------------------------------------
class CAI_Network
{
public:
char unk0[8]; // +0
// this is uninitialised and never set on ain build, fun!
int linkcount; // +8
char unk1[124]; // +12
int zonecount; // +136
char unk2[16]; // +140
// unk8 on disk
int unk5; // +156
char unk6[4]; // +160
int hintcount; // +164
// these probably aren't actually hints, but there's 1 of them per hint so idk
short hints[2000]; // +168
int scriptnodecount; // +4168
CAI_ScriptNode scriptnodes[4000]; // +4172
int nodecount; // +84172
CAI_Node** nodes; // +84176
public:
static void BuildAINFile(CAI_Network* aiNetwork);
};

View File

@ -0,0 +1,241 @@
//=============================================================================//
//
// Purpose:
//
//=============================================================================//
#include "core/stdafx.h"
#include "public/include/edict.h"
#include "tier0/cvar.h"
#include "tier0/cmd.h"
#include "engine/host_state.h"
#include "engine/sys_utils.h"
#include "game/server/ai_node.h"
#include "game/server/ai_network.h"
#include "game/server/ai_networkmanager.h"
const unsigned int PLACEHOLDER_CRC = 0;
const int AINET_SCRIPT_VERSION_NUMBER = 21;
const int AINET_VERSION_NUMBER = 57;
const int MAP_VERSION_TEMP = 30;
/*
==============================
CAI_NetworkBuilder::BuildFile
Build AI node graph file from
in-memory structures and write
to disk to be loaded
==============================
*/
void CAI_NetworkBuilder::BuildFile(CAI_Network* pNetwork)
{
//std::filesystem::path fsWritePath("platform/maps/graphs");
//fsWritePath /= g_pHostState->m_levelName;
//fsWritePath += ".ain";
//// Dump from memory.
//DevMsg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
//DevMsg(eDLL_T::SERVER, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> AI NODE GRAPH FILE CONSTRUCTION STARTED <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
//DevMsg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n");
//DevMsg(eDLL_T::SERVER, "Output file: '%s'\n", fsWritePath.string());
//std::ofstream writeStream(fsWritePath, std::ofstream::binary);
//DevMsg(eDLL_T::SERVER, "Writing AINet version: %d\n", AINET_VERSION_NUMBER);
//writeStream.write((char*)&AINET_VERSION_NUMBER, sizeof(int));
//// Could probably be cleaner but whatever
////int nMapVersion = *(int*)(*pUnkServerMapversionGlobal + 104); // TODO: Find in apex
////DevMsg(eDLL_T::SERVER, "Writing map version: %d\n", nMapVersion); // temp
////writeStream.write((char*)&nMapVersion, sizeof(int));
//DevMsg(eDLL_T::SERVER, "Writing placeholder crc: %d\n", PLACEHOLDER_CRC);
//writeStream.write((char*)&PLACEHOLDER_CRC, sizeof(int));
//int nCalculatedLinkcount = 0;
//// Path nodes
//DevMsg(eDLL_T::SERVER, "Writing nodecount: %d\n", pNetwork->nodecount);
//writeStream.write((char*)&pNetwork->nodecount, sizeof(int));
//for (int i = 0; i < pNetwork->nodecount; i++)
//{
// // construct on-disk node struct
// CAI_NodeDisk diskNode{};
// diskNode.x = pNetwork->nodes[i]->x;
// diskNode.y = pNetwork->nodes[i]->y;
// diskNode.z = pNetwork->nodes[i]->z;
// diskNode.yaw = pNetwork->nodes[i]->yaw;
// memcpy(diskNode.hulls, pNetwork->nodes[i]->hulls, sizeof(diskNode.hulls));
// diskNode.unk0 = (char)pNetwork->nodes[i]->unk0;
// diskNode.unk1 = pNetwork->nodes[i]->unk1;
// for (int j = 0; j < MAX_HULLS; j++)
// {
// diskNode.unk2[j] = (short)pNetwork->nodes[i]->unk2[j];
// spdlog::info((short)pNetwork->nodes[i]->unk2[j]);
// }
// memcpy(diskNode.unk3, pNetwork->nodes[i]->unk3, sizeof(diskNode.unk3));
// diskNode.unk4 = pNetwork->nodes[i]->unk6;
// diskNode.unk5 =
// -1; // aiNetwork->nodes[i]->unk8; // This field is wrong, however it's always -1 in original navmeshes anyway.
// memcpy(diskNode.unk6, pNetwork->nodes[i]->unk10, sizeof(diskNode.unk6));
// DevMsg(eDLL_T::SERVER, "Writing node %d from %d to %x\n", pNetwork->nodes[i]->index, (void*)pNetwork->nodes[i], writeStream.tellp());
// writeStream.write((char*)&diskNode, sizeof(CAI_NodeDisk));
// nCalculatedLinkcount += pNetwork->nodes[i]->linkcount;
//}
//// links
//DevMsg(eDLL_T::SERVER, "Linkcount: %d\n", pNetwork->linkcount);
//DevMsg(eDLL_T::SERVER, "Calculated total linkcount: %d\n", nCalculatedLinkcount);
//nCalculatedLinkcount /= 2;
//if (ai_dumpAINfileFromLoad->GetBool())
//{
// if (pNetwork->linkcount == nCalculatedLinkcount)
// DevMsg(eDLL_T::SERVER, "Caculated linkcount is normal!");
// else
// DevMsg(eDLL_T::SERVER, "Calculated linkcount has weird value! this is expected on build!");
//}
//spdlog::info("Writing linkcount: %d\n", nCalculatedLinkcount);
//writeStream.write((char*)&nCalculatedLinkcount, sizeof(int));
//for (int i = 0; i < pNetwork->nodecount; i++)
//{
// for (int j = 0; j < pNetwork->nodes[i]->linkcount; j++)
// {
// // skip links that don't originate from current node
// if (pNetwork->nodes[i]->links[j]->srcId != pNetwork->nodes[i]->index)
// continue;
// CAI_NodeLinkDisk diskLink{};
// diskLink.srcId = pNetwork->nodes[i]->links[j]->srcId;
// diskLink.destId = pNetwork->nodes[i]->links[j]->destId;
// diskLink.unk0 = pNetwork->nodes[i]->links[j]->unk1;
// memcpy(diskLink.hulls, pNetwork->nodes[i]->links[j]->hulls, sizeof(diskLink.hulls));
// DevMsg(eDLL_T::SERVER, "Writing link %d => %d to %x\n", diskLink.srcId, diskLink.destId, writeStream.tellp());
// writeStream.write((char*)&diskLink, sizeof(CAI_NodeLinkDisk));
// }
//}
//// 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 %x bytes for unknown block at %x\n", pNetwork->nodecount * sizeof(uint32_t), writeStream.tellp());
//uint32_t* unkNodeBlock = new uint32_t[pNetwork->nodecount];
//memset(unkNodeBlock, 0, pNetwork->nodecount * sizeof(uint32_t));
//writeStream.write((char*)unkNodeBlock, pNetwork->nodecount * sizeof(uint32_t));
//delete[] unkNodeBlock;
//// TODO: this is traverse nodes i think? these aren't used in tf2 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 %x\n", 0, writeStream.tellp());
//short traverseNodeCount = 0;
//writeStream.write((char*)&traverseNodeCount, sizeof(short));
//// Only write count since count=0 means we don't have to actually do anything here
//// TODO: ideally these should be actually dumped, but they're always 0 in tf2 from what i can tell
//DevMsg(eDLL_T::SERVER, "Writing %d bytes for unknown hull block at %x\n", MAX_HULLS * 8, writeStream.tellp());
//char* unkHullBlock = new char[MAX_HULLS * 8];
//memset(unkHullBlock, 0, MAX_HULLS * 8);
//writeStream.write(unkHullBlock, MAX_HULLS * 8);
//delete[] unkHullBlock;
//// Snknown struct that's seemingly node-related
//DevMsg(eDLL_T::SERVER, "Writing %d unknown node structs at %x\n", *pUnkStruct0Count, writeStream.tellp());
//writeStream.write((char*)pUnkStruct0Count, sizeof(*pUnkStruct0Count));
//for (int i = 0; i < *pUnkStruct0Count; i++)
//{
// DevMsg(eDLL_T::SERVER, "Writing unknown node struct %d at %x\n", i, writeStream.tellp());
// UnkNodeStruct0* nodeStruct = (*pppUnkNodeStruct0s)[i];
// writeStream.write((char*)&nodeStruct->index, sizeof(nodeStruct->index));
// writeStream.write((char*)&nodeStruct->unk1, sizeof(nodeStruct->unk1));
// writeStream.write((char*)&nodeStruct->x, sizeof(nodeStruct->x));
// writeStream.write((char*)&nodeStruct->y, sizeof(nodeStruct->y));
// writeStream.write((char*)&nodeStruct->z, sizeof(nodeStruct->z));
// writeStream.write((char*)&nodeStruct->unkcount0, sizeof(nodeStruct->unkcount0));
// for (int j = 0; j < nodeStruct->unkcount0; j++)
// {
// short unk2Short = (short)nodeStruct->unk2[j];
// writeStream.write((char*)&unk2Short, sizeof(unk2Short));
// }
// writeStream.write((char*)&nodeStruct->unkcount1, sizeof(nodeStruct->unkcount1));
// for (int j = 0; j < nodeStruct->unkcount1; j++)
// {
// short unk3Short = (short)nodeStruct->unk3[j];
// writeStream.write((char*)&unk3Short, sizeof(unk3Short));
// }
// writeStream.write((char*)&nodeStruct->unk5, sizeof(nodeStruct->unk5));
//}
//// Unknown struct that's seemingly link-related
//DevMsg(eDLL_T::SERVER, "Writing %d unknown link structs at %x\n", *pUnkLinkStruct1Count, writeStream.tellp());
//writeStream.write((char*)pUnkLinkStruct1Count, sizeof(*pUnkLinkStruct1Count));
//for (int i = 0; i < *pUnkLinkStruct1Count; i++)
//{
// // disk and memory structs are literally identical here so just directly write
// DevMsg(eDLL_T::SERVER, "Writing unknown link struct %d at %x\n", i, writeStream.tellp());
// writeStream.write((char*)(*pppUnkStruct1s)[i], sizeof(*(*pppUnkStruct1s)[i]));
//}
//// Some weird int idk what this is used for
//writeStream.write((char*)&pNetwork->unk5, sizeof(pNetwork->unk5));
//// Tf2-exclusive stuff past this point, i.e. ain v57 only
//DevMsg(eDLL_T::SERVER, "Writing %d script nodes at %x\n", pNetwork->scriptnodecount, writeStream.tellp());
//writeStream.write((char*)&pNetwork->scriptnodecount, sizeof(pNetwork->scriptnodecount));
//for (int i = 0; i < pNetwork->scriptnodecount; i++)
//{
// // disk and memory structs are literally identical here so just directly write
// DevMsg(eDLL_T::SERVER, "Writing script node %d at %x\n", i, writeStream.tellp());
// writeStream.write((char*)&pNetwork->scriptnodes[i], sizeof(pNetwork->scriptnodes[i]));
//}
//DevMsg(eDLL_T::SERVER, "Writing %d hints at %x\n", pNetwork->hintcount, writeStream.tellp());
//writeStream.write((char*)&pNetwork->hintcount, sizeof(pNetwork->hintcount));
//for (int i = 0; i < pNetwork->hintcount; i++)
//{
// DevMsg(eDLL_T::SERVER, "Writing hint data %d at %x\n", i, writeStream.tellp());
// writeStream.write((char*)&pNetwork->hints[i], sizeof(pNetwork->hints[i]));
//}
//writeStream.close();
}
void HCAI_NetworkManager__LoadNetworkGraph(void* aimanager, void* buf, const char* filename)
{
CAI_NetworkManager__LoadNetworkGraph(aimanager, buf, filename);
if (ai_dumpAINfileFromLoad->GetBool())
{
DevMsg(eDLL_T::SERVER, "Running BuildAINFile for loaded file %s\n", filename);
CAI_NetworkBuilder::BuildFile(*(CAI_Network**)((char*)aimanager + 2536)); // TODO: Verify in r5apex.exe.
}
}
void HCAI_NetworkBuilder__Build(void* builder, CAI_Network* aiNetwork, void* a3, int a4)
{
CAI_NetworkBuilder__Build(builder, aiNetwork, a3, a4);
CAI_NetworkBuilder::BuildFile(aiNetwork);
}
void CAI_NetworkManager_Attach()
{
DetourAttach((LPVOID*)&CAI_NetworkManager__LoadNetworkGraph, &HCAI_NetworkManager__LoadNetworkGraph);
DetourAttach((LPVOID*)&CAI_NetworkBuilder__Build, &HCAI_NetworkBuilder__Build);
}
void CAI_NetworkManager_Detach()
{
DetourDetach((LPVOID*)&CAI_NetworkManager__LoadNetworkGraph, &HCAI_NetworkManager__LoadNetworkGraph);
DetourDetach((LPVOID*)&CAI_NetworkBuilder__Build, &HCAI_NetworkBuilder__Build);
}

View File

@ -0,0 +1,57 @@
//=============================================================================//
//
// Purpose:
//
//=============================================================================//
#pragma once
#include "game/server/ai_network.h"
namespace
{
/* ==== CAI_NETWORKMANAGER ============================================================================================================================================== */
ADDRESS p_CAI_NetworkManager__ShouldRebuild = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8B\xD9\x48\x8B\x0D\x00\x00\x00\x00\x8B\x41\x6C", "xxxxxxxxxxxx????xxx");
void* (*CAI_NetworkManager__ShouldRebuild)(void* thisptr, CAI_Network* pNetWork, void* a3, int a4) = (void* (*)(void*, CAI_Network*, void*, int))p_CAI_NetworkManager__ShouldRebuild.GetPtr(); /*40 53 48 83 EC 20 48 8B D9 48 8B 0D ? ? ? ? 8B 41 6C*/
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
ADDRESS p_CAI_NetworkManager__LoadNetworkGraph = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x89\x44\x24\x00\x48\x89\x4C\x24\x00\x55\x53\x57\x41\x54\x41\x55\x41\x56", "xxxx?xxxx?xxxxxxxxx");
void* (*CAI_NetworkManager__LoadNetworkGraph)(void* thisptr, void* pBuffer, const char* pszFileName, int a4) = (void* (*)(void*, void*, const char*, int))p_CAI_NetworkManager__LoadNetworkGraph.GetPtr(); /*4C 89 44 24 ? 48 89 4C 24 ? 55 53 57 41 54 41 55 41 56*/
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
ADDRESS p_CAI_NetworkManager__LoadNetworkGraph = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x89\x44\x24\x00\x48\x89\x4C\x24\x00\x55\x53\x56\x57\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C\x24\x00\x48\x81\xEC\x00\x00\x00\x00\x48\x8B\xFA", "xxxx?xxxx?xxxxxxxxxxxxxxxx?xxx????xxx");
void* (*CAI_NetworkManager__LoadNetworkGraph)(void* thisptr, void* pBuffer, const char* pszFileName) = (void* (*)(void*, void*, const char*))p_CAI_NetworkManager__LoadNetworkGraph.GetPtr(); /*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*/
#endif
/* ==== CAI_NETWORKBUILDER ============================================================================================================================================== */
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
ADDRESS p_CAI_NetworkBuilder__Build = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x6C\x24\x00\x48\x89\x74\x24\x00\x48\x89\x4C\x24\x00\x57\x41\x54\x41\x55\x41\x56\x41\x57\x48\x83\xEC\x30\x48\x63\xBA\x00\x00\x00\x00", "xxxx?xxxx?xxxx?xxxx?xxxxxxxxxxxxxxxx????");
void* (*CAI_NetworkBuilder__Build)(void* thisptr, CAI_Network* pNetWork, void* a3, int a4) = (void* (*)(void*, CAI_Network*, void*, int))p_CAI_NetworkBuilder__Build.GetPtr(); /*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 ? ? ? ?*/
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
ADDRESS p_CAI_NetworkBuilder__Build = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8B\xD9\x48\x8B\x0D\x00\x00\x00\x00\x8B\x41\x6C", "xxxxxxxxxxxx????xxx");
void* (*CAI_NetworkBuilder__Build)(void* thisptr, CAI_Network* pNetWork, void* a3, int a4) = (void* (*)(void*, CAI_Network*, void*, int))p_CAI_NetworkBuilder__Build.GetPtr(); /*48 89 54 24 ? 48 89 4C 24 ? 53 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 38 8B B2 ? ? ? ?*/
#endif
}
//-----------------------------------------------------------------------------
// CAI_NetworkBuilder
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
class CAI_NetworkBuilder
{
public:
static void BuildFile(CAI_Network* pNetwork);
};
///////////////////////////////////////////////////////////////////////////////
class HCAI_NetworkManager : public IDetour
{
virtual void debugp()
{
std::cout << "| FUN: CAI_NetworkManager::LoadNetworkGraph : 0x" << std::hex << std::uppercase << p_CAI_NetworkManager__ShouldRebuild.GetPtr() << std::setw(npad) << " |" << std::endl;
std::cout << "| FUN: CAI_NetworkManager::ShouldRebuild : 0x" << std::hex << std::uppercase << p_CAI_NetworkManager__ShouldRebuild.GetPtr() << std::setw(npad) << " |" << std::endl;
std::cout << "| FUN: CAI_NetworkBuilder::Build : 0x" << std::hex << std::uppercase << p_CAI_NetworkBuilder__Build.GetPtr() << std::setw(npad) << " |" << std::endl;
std::cout << "+----------------------------------------------------------------+" << std::endl;
}
};
///////////////////////////////////////////////////////////////////////////////
REGISTER(HCAI_NetworkManager);

140
r5dev/game/server/ai_node.h Normal file
View File

@ -0,0 +1,140 @@
//=============================================================================//
//
// Purpose:
//
//=============================================================================//
#pragma once
const int MAX_HULLS = 5;
//=============================================================================
// >> CAI_NodeLink
//=============================================================================
struct CAI_NodeLink
{
short srcId;
short destId;
bool hulls[MAX_HULLS];
char unk0;
char unk1; // maps => unk0 on disk
char unk2[5];
int64_t flags;
};
//=============================================================================
// >> CAI_NodeLinkDisk
//=============================================================================
#pragma pack(push, 1)
struct CAI_NodeLinkDisk
{
short srcId;
short destId;
char unk0;
bool hulls[MAX_HULLS];
};
#pragma pack(pop)
//=============================================================================
// >> CAI_Node
//=============================================================================
struct CAI_Node
{
int index; // Not present on disk
float x;
float y;
float z;
float hulls[MAX_HULLS];
float yaw;
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
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
CAI_NodeLink** links;
char unk5[16];
int linkcount;
int unk11; // Bad name lmao
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_NodeDisk
//=============================================================================
#pragma pack(push, 1)
struct CAI_NodeDisk // The way CAI_Nodes are represented in on-disk ain files
{
float x;
float y;
float z;
float yaw;
float hulls[MAX_HULLS];
char unk0;
int unk1;
short unk2[MAX_HULLS];
char unk3[MAX_HULLS];
short unk4;
short unk5;
char unk6[8];
}; // Total size of 68 bytes
#pragma pack(pop)
//=============================================================================
// >> CAI_ScriptNode
//=============================================================================
struct CAI_ScriptNode
{
float x;
float y;
float z;
uint64_t scriptdata;
};
struct UnkNodeStruct0
{
int index;
char unk0;
char unk1; // Maps to unk1 on disk
char pad0[2]; // Padding to +8
float x;
float y;
float z;
char pad5[4];
int* unk2; // Maps to unk5 on disk;
char pad1[16]; // Pad to +48
int unkcount0; // Maps to unkcount0 on disk
char pad2[4]; // Pad to +56
int* unk3;
char pad3[16]; // Pad to +80
int unkcount1;
char pad4[132];
char unk5;
};
//int* pUnkStruct0Count;
//UnkNodeStruct0*** pppUnkNodeStruct0s;
struct UnkLinkStruct1
{
short unk0;
short unk1;
int unk2;
char unk3;
char unk4;
char unk5;
};
//int* pUnkLinkStruct1Count;
//UnkLinkStruct1*** pppUnkStruct1s;

View File

@ -0,0 +1,61 @@
#pragma once
#ifndef DEDICATED
#include "launcher/IApplication.h"
#endif // !DEDICATED
#include "public/include/globalvars_base.h"
#include "engine/sv_main.h"
//-----------------------------------------------------------------------------
// Purpose: Defines the ways that a map can be loaded.
// Note : Only seems to get set and checked on 0 and 2: [r5apex_ds.exe + d55990d] for more details.
//-----------------------------------------------------------------------------
enum MapLoadType_t
{
MapLoad_NewGame = 0,
MapLoad_Background = 2
};
//-----------------------------------------------------------------------------
// Purpose: Global variables shared between the engine and the game .dll
//-----------------------------------------------------------------------------
class CGlobalVars : public CGlobalVarsBase
{
public:
// Current map
const char* m_pszMapName;
int m_nMapVersion;
char m_pad0[4];
const char* m_pszStartSpot; // Seems empty at all times.
MapLoadType_t m_eLoadType; // How the current map was loaded.
bool m_bMapLoadFailed; // Map has failed to load, we need to kick back to the main menu (unused?).
void* m_pUnk0; // r5apex_ds.exe 'CBaseServer::Clear() + 7E'
void* m_pUnk1; // r5apex_ds.exe 'CBaseServer::Clear() + 93'
void* m_pUnk2; // r5apex_ds.exe 'CServer::FrameJob() + 20'
void* m_pUnk3;
}; // Size 0x0098
namespace
{
CGlobalVars* g_ServerGlobalVariables = p_SV_InitGameDLL.Offset(0x0).FindPatternSelf("48 8D ?? ?? ?? ?? 01", ADDRESS::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast<CGlobalVars*>();
#ifndef DEDICATED
CGlobalVarsBase* g_ClientGlobalVariables = p_CModAppSystemGroup_Create.Offset(0x0).FindPatternSelf("4C 8D ?? ?? ?? ?? 01", ADDRESS::Direction::DOWN, 8000).ResolveRelativeAddressSelf(0x3, 0x7).RCast<CGlobalVarsBase*>();
#endif // !DEDICATED
}
///////////////////////////////////////////////////////////////////////////////
class HEdict : public IDetour
{
virtual void debugp()
{
std::cout << "| VAR: g_ServerGlobalVariables : 0x" << std::hex << std::uppercase << g_ServerGlobalVariables << std::setw(0) << " |" << std::endl;
#ifndef DEDICATED
std::cout << "| VAR: g_ClientGlobalVariables : 0x" << std::hex << std::uppercase << g_ClientGlobalVariables << std::setw(0) << " |" << std::endl;
#endif // !DEDICATED
std::cout << "+----------------------------------------------------------------+" << std::endl;
}
};
///////////////////////////////////////////////////////////////////////////////
REGISTER(HEdict);

View File

@ -0,0 +1,39 @@
#pragma once
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
enum GameMode_t
{
NO_MODE = 0,
MP_MODE,
PVE_MODE,
SP_MODE,
};
//-----------------------------------------------------------------------------
// Purpose: Global variables used by shared code
//-----------------------------------------------------------------------------
class CGlobalVarsBase
{
public:
float m_nUnkTime;
float m_rRealTime; // Absolute time (per frame still - Use Plat_FloatTime() for a high precision real time.
int m_nFrameCount; // Absolute frame counter - continues to increase even if game is paused - never resets.
float m_fAbsoluteFrameTime; // Non-paused frametime
float m_fAbsoluteFrameStartTimeStdDev;
float m_fFrameTime; // Time spent on last server or client frame (has nothing to do with think intervals) (Also empty on dedicated)
float m_fCurTime;
char m_nPad0[24]; // All unknown.
int m_nMaxClients; // Current maxplayers setting
int m_nMaxMilesAudioQueues_Maybe; // Only used on the server.
GameMode_t m_nGameMode;; // 1 (MP) 2 (PVE) 3 (SP)
int m_nTickCount; // Simulation ticks - does not increase when game is paused - resets on restart.
int m_nUnk0;
int m_nUnk1;
// The following seem to be mainly used in c:\depot\r5launch\src\engine\client\clientstate.cpp.
int m_nUnk2;
int m_nUnk3; // Seems to be used on client only.
int m_nUnk4; // Seems to be used on client only.
};

View File

@ -55,6 +55,8 @@ void ConVar::Init(void) const
rcon_password = new ConVar("rcon_password", "" , FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE, "Remote server access password (rcon is disabled if empty).", false, 0.f, false, 0.f, nullptr, nullptr);
//-------------------------------------------------------------------------
// SERVER |
ai_dumpAINfileFromLoad = new ConVar("ai_dumpAINfileFromLoad" , "0", FCVAR_DEVELOPMENTONLY, "Dumps AIN data from node graphs loaded from the disk on load.", false, 0.f, false, 0.f, nullptr, nullptr);
sv_showconnecting = new ConVar("sv_showconnecting" , "1", FCVAR_RELEASE, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr);
sv_pylonvisibility = new ConVar("sv_pylonvisibility", "0", FCVAR_RELEASE, "Determines the visiblity to the Pylon Master Server, 0 = Not visible, 1 = Visible, 2 = Hidden !TODO: not implemented yet.", false, 0.f, false, 0.f, nullptr, nullptr);

View File

@ -14,6 +14,7 @@ ConVar* rcon_address = nullptr;
ConVar* rcon_password = nullptr;
//-----------------------------------------------------------------------------
// SERVER |
ConVar* ai_dumpAINfileFromLoad = nullptr;
ConVar* sv_showconnecting = nullptr;
ConVar* sv_pylonvisibility = nullptr;

View File

@ -26,6 +26,7 @@ extern ConVar* rcon_address;
extern ConVar* rcon_password;
//-------------------------------------------------------------------------
// SERVER |
extern ConVar* ai_dumpAINfileFromLoad;
extern ConVar* sv_showconnecting;
extern ConVar* sv_pylonvisibility;
#ifdef DEDICATED

View File

@ -215,6 +215,9 @@
<ClInclude Include="..\engine\sys_engine.h" />
<ClInclude Include="..\engine\sys_getmodes.h" />
<ClInclude Include="..\engine\sys_utils.h" />
<ClInclude Include="..\game\server\ai_network.h" />
<ClInclude Include="..\game\server\ai_networkmanager.h" />
<ClInclude Include="..\game\server\ai_node.h" />
<ClInclude Include="..\game\server\fairfight_impl.h" />
<ClInclude Include="..\game\server\gameinterface.h" />
<ClInclude Include="..\launcher\IApplication.h" />
@ -234,6 +237,8 @@
<ClInclude Include="..\networksystem\sm_protocol.h" />
<ClInclude Include="..\protoc\cl_rcon.pb.h" />
<ClInclude Include="..\protoc\sv_rcon.pb.h" />
<ClInclude Include="..\public\include\edict.h" />
<ClInclude Include="..\public\include\globalvars_base.h" />
<ClInclude Include="..\public\include\memaddr.h" />
<ClInclude Include="..\public\include\bansystem.h" />
<ClInclude Include="..\public\include\binstream.h" />
@ -420,6 +425,7 @@
<ClCompile Include="..\engine\sys_dll2.cpp" />
<ClCompile Include="..\engine\sys_engine.cpp" />
<ClCompile Include="..\engine\sys_utils.cpp" />
<ClCompile Include="..\game\server\ai_networkmanager.cpp" />
<ClCompile Include="..\game\server\gameinterface.cpp" />
<ClCompile Include="..\launcher\IApplication.cpp" />
<ClCompile Include="..\launcher\prx.cpp" />

View File

@ -768,6 +768,21 @@
<ClInclude Include="..\game\server\gameinterface.h">
<Filter>sdk\game\server</Filter>
</ClInclude>
<ClInclude Include="..\game\server\ai_networkmanager.h">
<Filter>sdk\game\server</Filter>
</ClInclude>
<ClInclude Include="..\game\server\ai_node.h">
<Filter>sdk\game\server</Filter>
</ClInclude>
<ClInclude Include="..\game\server\ai_network.h">
<Filter>sdk\game\server</Filter>
</ClInclude>
<ClInclude Include="..\public\include\edict.h">
<Filter>sdk\public\include</Filter>
</ClInclude>
<ClInclude Include="..\public\include\globalvars_base.h">
<Filter>sdk\public\include</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\client\IVEngineClient.cpp">
@ -950,6 +965,9 @@
<ClCompile Include="..\game\server\gameinterface.cpp">
<Filter>sdk\game\server</Filter>
</ClCompile>
<ClCompile Include="..\game\server\ai_networkmanager.cpp">
<Filter>sdk\game\server</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\Dedicated.def" />

View File

@ -49,6 +49,7 @@
<ClCompile Include="..\engine\sys_utils.cpp" />
<ClCompile Include="..\gameui\IConsole.cpp" />
<ClCompile Include="..\gameui\IBrowser.cpp" />
<ClCompile Include="..\game\server\ai_networkmanager.cpp" />
<ClCompile Include="..\game\server\gameinterface.cpp" />
<ClCompile Include="..\inputsystem\inputsystem.cpp" />
<ClCompile Include="..\launcher\IApplication.cpp" />
@ -141,6 +142,9 @@
<ClInclude Include="..\engine\sys_utils.h" />
<ClInclude Include="..\gameui\IConsole.h" />
<ClInclude Include="..\gameui\IBrowser.h" />
<ClInclude Include="..\game\server\ai_network.h" />
<ClInclude Include="..\game\server\ai_networkmanager.h" />
<ClInclude Include="..\game\server\ai_node.h" />
<ClInclude Include="..\game\server\fairfight_impl.h" />
<ClInclude Include="..\game\server\gameinterface.h" />
<ClInclude Include="..\inputsystem\ButtonCode.h" />
@ -163,6 +167,8 @@
<ClInclude Include="..\networksystem\sm_protocol.h" />
<ClInclude Include="..\protoc\cl_rcon.pb.h" />
<ClInclude Include="..\protoc\sv_rcon.pb.h" />
<ClInclude Include="..\public\include\edict.h" />
<ClInclude Include="..\public\include\globalvars_base.h" />
<ClInclude Include="..\public\include\memaddr.h" />
<ClInclude Include="..\public\include\bansystem.h" />
<ClInclude Include="..\public\include\binstream.h" />

View File

@ -378,6 +378,9 @@
<ClCompile Include="..\game\server\gameinterface.cpp">
<Filter>sdk\game\server</Filter>
</ClCompile>
<ClCompile Include="..\game\server\ai_networkmanager.cpp">
<Filter>sdk\game\server</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\client\cdll_engine_int.h">
@ -1097,6 +1100,21 @@
<ClInclude Include="..\game\server\gameinterface.h">
<Filter>sdk\game\server</Filter>
</ClInclude>
<ClInclude Include="..\game\server\ai_networkmanager.h">
<Filter>sdk\game\server</Filter>
</ClInclude>
<ClInclude Include="..\game\server\ai_node.h">
<Filter>sdk\game\server</Filter>
</ClInclude>
<ClInclude Include="..\game\server\ai_network.h">
<Filter>sdk\game\server</Filter>
</ClInclude>
<ClInclude Include="..\public\include\edict.h">
<Filter>sdk\public\include</Filter>
</ClInclude>
<ClInclude Include="..\public\include\globalvars_base.h">
<Filter>sdk\public\include</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="..\shared\resource\lockedserver.png">