From 73262ca616f0623a9715ceac90c17b0da4b320d7 Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:01:22 +0000 Subject: Port navmesh debug renderer from primedev (#626) Adds support for rendering navmeshes in-game using debug overlay Cherry-picked from primedev, originally written by F1F7Y Co-authored-by: F1F7Y Co-authored-by: Maya <11448698+RoyalBlue1@users.noreply.github.com> --- primedev/Northstar.cmake | 9 + primedev/client/cdll_client_int.h | 5 + primedev/client/debugoverlay.cpp | 69 ++--- primedev/client/debugoverlay.h | 28 +++ primedev/client/entity_client_tools.cpp | 13 + primedev/core/math/math_pfns.h | 7 + primedev/core/math/vector.h | 332 +++++++++++++++++++++++-- primedev/core/math/vplane.h | 106 ++++++++ primedev/core/tier1.cpp | 19 ++ primedev/core/tier1.h | 12 + primedev/scripts/scriptdatatables.cpp | 36 --- primedev/server/ai_helper.cpp | 159 ++++++++++++ primedev/server/ai_helper.h | 13 + primedev/server/ai_navmesh.cpp | 6 + primedev/server/ai_navmesh.h | 260 +++++++++++++++++++ primedev/shared/exploit_fixes/exploitfixes.cpp | 20 +- primedev/toolframework/itoolentity.h | 46 ++++ 17 files changed, 1026 insertions(+), 114 deletions(-) create mode 100644 primedev/client/cdll_client_int.h create mode 100644 primedev/client/debugoverlay.h create mode 100644 primedev/client/entity_client_tools.cpp create mode 100644 primedev/core/math/math_pfns.h create mode 100644 primedev/core/math/vplane.h create mode 100644 primedev/core/tier1.cpp create mode 100644 primedev/core/tier1.h create mode 100644 primedev/server/ai_helper.cpp create mode 100644 primedev/server/ai_helper.h create mode 100644 primedev/server/ai_navmesh.cpp create mode 100644 primedev/server/ai_navmesh.h create mode 100644 primedev/toolframework/itoolentity.h diff --git a/primedev/Northstar.cmake b/primedev/Northstar.cmake index 00a8dcaf..6b9c2380 100644 --- a/primedev/Northstar.cmake +++ b/primedev/Northstar.cmake @@ -16,6 +16,7 @@ add_library( "client/debugoverlay.cpp" "client/demofixes.cpp" "client/diskvmtfixes.cpp" + "client/entity_client_tools.cpp" "client/languagehooks.cpp" "client/latencyflex.cpp" "client/localchatwriter.cpp" @@ -41,7 +42,9 @@ add_library( "core/math/bits.h" "core/math/color.cpp" "core/math/color.h" + "core/math/math_pfns.h" "core/math/vector.h" + "core/math/vplane.h" "core/hooks.cpp" "core/hooks.h" "core/macros.h" @@ -53,6 +56,8 @@ add_library( "core/sourceinterface.h" "core/tier0.cpp" "core/tier0.h" + "core/tier1.cpp" + "core/tier1.h" "dedicated/dedicated.cpp" "dedicated/dedicated.h" "dedicated/dedicatedlogtoclient.cpp" @@ -116,6 +121,10 @@ add_library( "server/auth/serverauthentication.cpp" "server/auth/serverauthentication.h" "server/alltalk.cpp" + "server/ai_helper.cpp" + "server/ai_helper.h" + "server/ai_navmesh.cpp" + "server/ai_navmesh.h" "server/buildainfile.cpp" "server/r2server.cpp" "server/r2server.h" diff --git a/primedev/client/cdll_client_int.h b/primedev/client/cdll_client_int.h new file mode 100644 index 00000000..a32ccbeb --- /dev/null +++ b/primedev/client/cdll_client_int.h @@ -0,0 +1,5 @@ +#pragma once + +#include "toolframework/itoolentity.h" + +inline IClientTools* g_pClientTools = nullptr; diff --git a/primedev/client/debugoverlay.cpp b/primedev/client/debugoverlay.cpp index 8e860218..a67b8355 100644 --- a/primedev/client/debugoverlay.cpp +++ b/primedev/client/debugoverlay.cpp @@ -1,6 +1,9 @@ +#include "debugoverlay.h" + #include "dedicated/dedicated.h" #include "core/convar/cvar.h" #include "core/math/vector.h" +#include "server/ai_helper.h" AUTOHOOK_INIT() @@ -122,48 +125,13 @@ struct OverlaySphere_t : public OverlayBase_t bool m_bWireframe; }; -typedef bool (*OverlayBase_t__IsDeadType)(OverlayBase_t* a1); -static OverlayBase_t__IsDeadType OverlayBase_t__IsDead; -typedef void (*OverlayBase_t__DestroyOverlayType)(OverlayBase_t* a1); -static OverlayBase_t__DestroyOverlayType OverlayBase_t__DestroyOverlay; +static bool (*OverlayBase_t__IsDead)(OverlayBase_t* a1); +static void (*OverlayBase_t__DestroyOverlay)(OverlayBase_t* a1); static ConVar* Cvar_enable_debug_overlays; LPCRITICAL_SECTION s_OverlayMutex; -// Render Line -typedef void (*RenderLineType)(const Vector3& v1, const Vector3& v2, Color c, bool bZBuffer); -static RenderLineType RenderLine; - -// Render box -typedef void (*RenderBoxType)( - const Vector3& vOrigin, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer, bool bInsideOut); -static RenderBoxType RenderBox; - -// Render wireframe box -static RenderBoxType RenderWireframeBox; - -// Render swept box -typedef void (*RenderWireframeSweptBoxType)( - const Vector3& vStart, const Vector3& vEnd, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer); -RenderWireframeSweptBoxType RenderWireframeSweptBox; - -// Render Triangle -typedef void (*RenderTriangleType)(const Vector3& p1, const Vector3& p2, const Vector3& p3, Color c, bool bZBuffer); -static RenderTriangleType RenderTriangle; - -// Render Axis -typedef void (*RenderAxisType)(const Vector3& vOrigin, float flScale, bool bZBuffer); -static RenderAxisType RenderAxis; - -// I dont know -typedef void (*RenderUnknownType)(const Vector3& vUnk, float flUnk, bool bUnk); -static RenderUnknownType RenderUnknown; - -// Render Sphere -typedef void (*RenderSphereType)(const Vector3& vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer); -static RenderSphereType RenderSphere; - OverlayBase_t** s_pOverlays; int* g_nRenderTickCount; @@ -317,6 +285,11 @@ void, __fastcall, (bool bRender)) } } + if (bRender && Cvar_enable_debug_overlays->GetBool()) + { + g_pAIHelper->DrawNavmeshPolys(); + } + LeaveCriticalSection(s_OverlayMutex); } @@ -324,17 +297,17 @@ ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", DebugOverlay, ConVar, (CModule module) { AUTOHOOK_DISPATCH() - OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast(); - OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast(); - - RenderLine = module.Offset(0x192A70).RCast(); - RenderBox = module.Offset(0x192520).RCast(); - RenderWireframeBox = module.Offset(0x193DA0).RCast(); - RenderWireframeSweptBox = module.Offset(0x1945A0).RCast(); - RenderTriangle = module.Offset(0x193940).RCast(); - RenderAxis = module.Offset(0x1924D0).RCast(); - RenderSphere = module.Offset(0x194170).RCast(); - RenderUnknown = module.Offset(0x1924E0).RCast(); + OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast(); + OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast(); + + RenderLine = module.Offset(0x192A70).RCast(); + RenderBox = module.Offset(0x192520).RCast(); + RenderWireframeBox = module.Offset(0x193DA0).RCast(); + RenderWireframeSweptBox = module.Offset(0x1945A0).RCast(); + RenderTriangle = module.Offset(0x193940).RCast(); + RenderAxis = module.Offset(0x1924D0).RCast(); + RenderSphere = module.Offset(0x194170).RCast(); + RenderUnknown = module.Offset(0x1924E0).RCast(); s_OverlayMutex = module.Offset(0x10DB0A38).RCast(); diff --git a/primedev/client/debugoverlay.h b/primedev/client/debugoverlay.h new file mode 100644 index 00000000..498bfafd --- /dev/null +++ b/primedev/client/debugoverlay.h @@ -0,0 +1,28 @@ +#pragma once + +// Render Line +inline void (*RenderLine)(const Vector3& v1, const Vector3& v2, Color c, bool bZBuffer); + +// Render box +inline void (*RenderBox)( + const Vector3& vOrigin, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer, bool bInsideOut); + +// Render wireframe box +inline void (*RenderWireframeBox)( + const Vector3& vOrigin, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer, bool bInsideOut); + +// Render swept box +inline void (*RenderWireframeSweptBox)( + const Vector3& vStart, const Vector3& vEnd, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer); + +// Render Triangle +inline void (*RenderTriangle)(const Vector3& p1, const Vector3& p2, const Vector3& p3, Color c, bool bZBuffer); + +// Render Axis +inline void (*RenderAxis)(const Vector3& vOrigin, float flScale, bool bZBuffer); + +// I dont know +inline void (*RenderUnknown)(const Vector3& vUnk, float flUnk, bool bUnk); + +// Render Sphere +inline void (*RenderSphere)(const Vector3& vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer); diff --git a/primedev/client/entity_client_tools.cpp b/primedev/client/entity_client_tools.cpp new file mode 100644 index 00000000..d6f0be11 --- /dev/null +++ b/primedev/client/entity_client_tools.cpp @@ -0,0 +1,13 @@ +#include "toolframework/itoolentity.h" +#include "client/cdll_client_int.h" +#include "core/tier1.h" + +class CClientTools : public IClientTools +{ +public: +}; + +ON_DLL_LOAD("client.dll", ClientClientTools, (CModule module)) +{ + g_pClientTools = Sys_GetFactoryPtr("client.dll", "VCLIENTTOOLS001").RCast(); +} diff --git a/primedev/core/math/math_pfns.h b/primedev/core/math/math_pfns.h new file mode 100644 index 00000000..4dca9806 --- /dev/null +++ b/primedev/core/math/math_pfns.h @@ -0,0 +1,7 @@ +#pragma once + +inline float FastSqrt(float x) +{ + __m128 root = _mm_sqrt_ss(_mm_load_ss(&x)); + return *(reinterpret_cast(&root)); +} diff --git a/primedev/core/math/vector.h b/primedev/core/math/vector.h index 8684908f..e62f2c93 100644 --- a/primedev/core/math/vector.h +++ b/primedev/core/math/vector.h @@ -1,47 +1,331 @@ +#pragma once + +#include "bits.h" +#include "math_pfns.h" + #include -#pragma once +#define DEG2RAD(a) (a) * (3.14159265358979323846f / 180.0f) +#define RAD2DEG(a) (a) * (180.0f / 3.14159265358979323846f) -union Vector3 +class Vector3 { - struct +public: + float x, y, z; + + Vector3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + Vector3(float _f) : x(_f), y(_f), z(_f) {} + Vector3() : x(0.0f), y(0.0f), z(0.0f) {} + + inline bool IsValid() { - float x; - float y; - float z; - }; + return IsFinite(x) && IsFinite(y) && IsFinite(z); + } - float raw[3]; + inline void Init(float fX = 0.0f, float fY = 0.0f, float fZ = 0.0f) + { + x = fX; + y = fY; + z = fZ; + } - void MakeValid() + inline float Length() { - for (auto& fl : raw) - if (std::isnan(fl)) - fl = 0; + return FastSqrt(x * x + y * y + z * z); } - // todo: more operators maybe - bool operator==(const Vector3& other) + inline float DistTo(const Vector3& vOther) { - return x == other.x && y == other.y && z == other.z; + Vector3 vDelta; + vDelta.x = vOther.x - x; + vDelta.y = vOther.y - y; + vDelta.z = vOther.z - z; + + return vDelta.Length(); + } + + float Dot(const Vector3& vOther) const + { + return x * vOther.x + y * vOther.y + z * vOther.z; } + + // Cross product between two vectors. + Vector3 Cross(const Vector3& vOther) const; + + void Normalize(); + + bool operator==(const Vector3& v) const; + bool operator!=(const Vector3& v) const; + + FORCEINLINE Vector3& operator+=(const Vector3& v); + FORCEINLINE Vector3& operator-=(const Vector3& v); + FORCEINLINE Vector3& operator*=(const Vector3& v); + FORCEINLINE Vector3& operator*=(float s); + FORCEINLINE Vector3& operator/=(const Vector3& v); + FORCEINLINE Vector3& operator/=(float s); + FORCEINLINE Vector3& operator+=(float fl); ///< broadcast add + FORCEINLINE Vector3& operator-=(float fl); + + // arithmetic operations + Vector3 operator-(void) const; + + Vector3 operator+(const Vector3& v) const; + Vector3 operator-(const Vector3& v) const; + Vector3 operator*(const Vector3& v) const; + Vector3 operator/(const Vector3& v) const; + Vector3 operator*(float fl) const; + Vector3 operator/(float fl) const; }; -union QAngle +FORCEINLINE void VectorAdd(const Vector3& a, const Vector3& b, Vector3& result); +FORCEINLINE void VectorSubtract(const Vector3& a, const Vector3& b, Vector3& result); +FORCEINLINE void VectorMultiply(const Vector3& a, float b, Vector3& result); +FORCEINLINE void VectorMultiply(const Vector3& a, const Vector3& b, Vector3& result); +FORCEINLINE void VectorDivide(const Vector3& a, float b, Vector3& result); +FORCEINLINE void VectorDivide(const Vector3& a, const Vector3& b, Vector3& result); + +inline bool Vector3::operator==(const Vector3& src) const +{ + return (src.x == x) && (src.y == y) && (src.z == z); +} + +inline bool Vector3::operator!=(const Vector3& src) const +{ + return (src.x != x) || (src.y != y) || (src.z != z); +} + +FORCEINLINE Vector3& Vector3::operator+=(const Vector3& v) +{ + x += v.x; + y += v.y; + z += v.z; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator-=(const Vector3& v) +{ + x -= v.x; + y -= v.y; + z -= v.z; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator*=(float fl) +{ + x *= fl; + y *= fl; + z *= fl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator*=(const Vector3& v) +{ + x *= v.x; + y *= v.y; + z *= v.z; + return *this; +} + +// this ought to be an opcode. +FORCEINLINE Vector3& Vector3::operator+=(float fl) +{ + x += fl; + y += fl; + z += fl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator-=(float fl) +{ + x -= fl; + y -= fl; + z -= fl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator/=(float fl) +{ + float oofl = 1.0f / fl; + x *= oofl; + y *= oofl; + z *= oofl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator/=(const Vector3& v) +{ + x /= v.x; + y /= v.y; + z /= v.z; + return *this; +} + +inline Vector3 Vector3::operator-(void) const +{ + return Vector3(-x, -y, -z); +} + +inline Vector3 Vector3::operator+(const Vector3& v) const { - struct + Vector3 res; + VectorAdd(*this, v, res); + return res; +} + +inline Vector3 Vector3::operator-(const Vector3& v) const +{ + Vector3 res; + VectorSubtract(*this, v, res); + return res; +} + +inline Vector3 Vector3::operator*(float fl) const +{ + Vector3 res; + VectorMultiply(*this, fl, res); + return res; +} + +inline Vector3 Vector3::operator*(const Vector3& v) const +{ + Vector3 res; + VectorMultiply(*this, v, res); + return res; +} + +inline Vector3 Vector3::operator/(float fl) const +{ + Vector3 res; + VectorDivide(*this, fl, res); + return res; +} + +inline Vector3 Vector3::operator/(const Vector3& v) const +{ + Vector3 res; + VectorDivide(*this, v, res); + return res; +} + +inline Vector3 operator*(float fl, const Vector3& v) +{ + return v * fl; +} + +inline Vector3 Vector3::Cross(const Vector3& vOther) const +{ + return Vector3(y * vOther.z - z * vOther.y, z * vOther.x - x * vOther.z, x * vOther.y - y * vOther.x); +} + +inline void Vector3::Normalize() +{ + float fLen = Length(); + x /= fLen; + y /= fLen; + z /= fLen; +} + +inline Vector3 StringToVector(char* pString) +{ + Vector3 vRet; + + int length = 0; + while (pString[length]) { - float x; - float y; - float z; - float w; - }; + if ((pString[length] == '<') || (pString[length] == '>')) + pString[length] = '\0'; + length++; + } + + int startOfFloat = 1; + int currentIndex = 1; + + while (pString[currentIndex] && (pString[currentIndex] != ',')) + currentIndex++; + pString[currentIndex] = '\0'; + vRet.x = std::stof(&pString[startOfFloat]); + startOfFloat = ++currentIndex; - float raw[4]; + while (pString[currentIndex] && (pString[currentIndex] != ',')) + currentIndex++; + pString[currentIndex] = '\0'; + vRet.y = std::stof(&pString[startOfFloat]); + startOfFloat = ++currentIndex; + + while (pString[currentIndex] && (pString[currentIndex] != ',')) + currentIndex++; + pString[currentIndex] = '\0'; + vRet.z = std::stof(&pString[startOfFloat]); + startOfFloat = ++currentIndex; + + return vRet; +} + +FORCEINLINE void VectorAdd(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x + b.x; + c.y = a.y + b.y; + c.z = a.z + b.z; +} + +FORCEINLINE void VectorSubtract(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x - b.x; + c.y = a.y - b.y; + c.z = a.z - b.z; +} + +FORCEINLINE void VectorMultiply(const Vector3& a, float b, Vector3& c) +{ + c.x = a.x * b; + c.y = a.y * b; + c.z = a.z * b; +} + +FORCEINLINE void VectorMultiply(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x * b.x; + c.y = a.y * b.y; + c.z = a.z * b.z; +} + +FORCEINLINE void VectorDivide(const Vector3& a, float b, Vector3& c) +{ + float oob = 1.0f / b; + c.x = a.x * oob; + c.y = a.y * oob; + c.z = a.z * oob; +} + +FORCEINLINE void VectorDivide(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x / b.x; + c.y = a.y / b.y; + c.z = a.z / b.z; +} + +class QAngle +{ +public: + float x; + float y; + float z; + + QAngle(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + QAngle(float _f) : x(_f), y(_f), z(_f) {} + QAngle() : x(0.0f), y(0.0f), z(0.0f) {} + + Vector3 GetNormal() const; // todo: more operators maybe bool operator==(const QAngle& other) { - return x == other.x && y == other.y && z == other.z && w == other.w; + return x == other.x && y == other.y && z == other.z; } }; + +inline Vector3 QAngle::GetNormal() const +{ + Vector3 ret(cos(DEG2RAD(y)), sin(DEG2RAD(y)), -sin(DEG2RAD(x))); + return ret; +} diff --git a/primedev/core/math/vplane.h b/primedev/core/math/vplane.h new file mode 100644 index 00000000..8b8de423 --- /dev/null +++ b/primedev/core/math/vplane.h @@ -0,0 +1,106 @@ +#pragma once + +typedef int SideType; + +// Used to represent sides of things like planes. +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + +#define VP_EPSILON 0.01f + +class VPlane +{ +public: + VPlane(); + VPlane(const Vector3& vNormal, float dist); + VPlane(const Vector3& vPoint, const QAngle& ang); + + void Init(const Vector3& vNormal, float dist); + + // Return the distance from the point to the plane. + float DistTo(const Vector3& vVec) const; + + // Copy. + VPlane& operator=(const VPlane& thePlane); + + // Returns SIDE_ON, SIDE_FRONT, or SIDE_BACK. + // The epsilon for SIDE_ON can be passed in. + SideType GetPointSide(const Vector3& vPoint, float sideEpsilon = VP_EPSILON) const; + + // Returns SIDE_FRONT or SIDE_BACK. + SideType GetPointSideExact(const Vector3& vPoint) const; + + // Get a point on the plane (normal*dist). + Vector3 GetPointOnPlane() const; + + // Snap the specified point to the plane (along the plane's normal). + Vector3 SnapPointToPlane(const Vector3& vPoint) const; + +public: + Vector3 m_Normal; + float m_Dist; +}; + +//----------------------------------------------------------------------------- +// Inlines. +//----------------------------------------------------------------------------- +inline VPlane::VPlane() {} + +inline VPlane::VPlane(const Vector3& vNormal, float dist) +{ + m_Normal = vNormal; + m_Dist = dist; +} + +inline VPlane::VPlane(const Vector3& vPoint, const QAngle& ang) +{ + m_Normal = ang.GetNormal(); + m_Dist = vPoint.x * m_Normal.x + vPoint.y * m_Normal.y + vPoint.z * m_Normal.z; +} + +inline void VPlane::Init(const Vector3& vNormal, float dist) +{ + m_Normal = vNormal; + m_Dist = dist; +} + +inline float VPlane::DistTo(const Vector3& vVec) const +{ + return vVec.Dot(m_Normal) - m_Dist; +} + +inline VPlane& VPlane::operator=(const VPlane& thePlane) +{ + m_Normal = thePlane.m_Normal; + m_Dist = thePlane.m_Dist; + return *this; +} + +inline Vector3 VPlane::GetPointOnPlane() const +{ + return m_Normal * m_Dist; +} + +inline Vector3 VPlane::SnapPointToPlane(const Vector3& vPoint) const +{ + return vPoint - m_Normal * DistTo(vPoint); +} + +inline SideType VPlane::GetPointSide(const Vector3& vPoint, float sideEpsilon) const +{ + float fDist; + + fDist = DistTo(vPoint); + if (fDist >= sideEpsilon) + return SIDE_FRONT; + else if (fDist <= -sideEpsilon) + return SIDE_BACK; + else + return SIDE_ON; +} + +inline SideType VPlane::GetPointSideExact(const Vector3& vPoint) const +{ + return DistTo(vPoint) > 0.0f ? SIDE_FRONT : SIDE_BACK; +} diff --git a/primedev/core/tier1.cpp b/primedev/core/tier1.cpp new file mode 100644 index 00000000..a2995496 --- /dev/null +++ b/primedev/core/tier1.cpp @@ -0,0 +1,19 @@ +#include "tier1.h" + +// Note: this file is tier1/interface.cpp in primedev, but given that tier0 is yet to be split +// I am following the existing "pattern" and putting this here + +CMemoryAddress Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFactoryName) +{ + HMODULE hModule = GetModuleHandleA(svModuleName.c_str()); + + if (!hModule) + { + spdlog::error("Failed to get module handle of '{}'!", svModuleName.c_str()); + exit(EXIT_FAILURE); + } + + CreateInterfaceFn fnCreateInterface = reinterpret_cast(GetProcAddress(hModule, CREATEINTERFACE_PROCNAME)); + + return fnCreateInterface(svFactoryName.c_str(), NULL); +} diff --git a/primedev/core/tier1.h b/primedev/core/tier1.h new file mode 100644 index 00000000..5be58274 --- /dev/null +++ b/primedev/core/tier1.h @@ -0,0 +1,12 @@ +#pragma once + +// Note: this file is tier1/interface.h in primedev, but given that tier0 is yet to be split +// I am following the existing "pattern" and putting this here + +#include "memory.h" + +#define CREATEINTERFACE_PROCNAME "CreateInterface" + +typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode); + +CMemoryAddress Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFact); diff --git a/primedev/scripts/scriptdatatables.cpp b/primedev/scripts/scriptdatatables.cpp index 865b6243..47c3a0f1 100644 --- a/primedev/scripts/scriptdatatables.cpp +++ b/primedev/scripts/scriptdatatables.cpp @@ -59,42 +59,6 @@ struct CSVData std::unordered_map CSVCache; -Vector3 StringToVector(char* pString) -{ - Vector3 vRet; - - int length = 0; - while (pString[length]) - { - if ((pString[length] == '<') || (pString[length] == '>')) - pString[length] = '\0'; - length++; - } - - int startOfFloat = 1; - int currentIndex = 1; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.x = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.y = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.z = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - return vRet; -} - // var function GetDataTable( asset path ) REPLACE_SQFUNC(GetDataTable, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) { diff --git a/primedev/server/ai_helper.cpp b/primedev/server/ai_helper.cpp new file mode 100644 index 00000000..ebb56af2 --- /dev/null +++ b/primedev/server/ai_helper.cpp @@ -0,0 +1,159 @@ +#include "ai_helper.h" + +#include "client/debugoverlay.h" +#include "client/cdll_client_int.h" +#include "engine/hoststate.h" + +#include "core/math/vplane.h" + +#include + +const int AINET_VERSION_NUMBER = 57; +const int AINET_SCRIPT_VERSION_NUMBER = 21; +const int PLACEHOLDER_CRC = 0; + +static ConVar* Cvar_navmesh_debug_hull; +static ConVar* Cvar_navmesh_debug_camera_radius; +static ConVar* Cvar_navmesh_debug_lossy_optimization; + +//----------------------------------------------------------------------------- +// Purpose: Get navmesh pointer for hull +// Output : navmesh*, nullptr if out of range +//----------------------------------------------------------------------------- +dtNavMesh* GetNavMeshForHull(int nHull) +{ + if (nHull < 1 || nHull > 4) + return nullptr; + + return g_pNavMesh[nHull - 1]; +} + +//----------------------------------------------------------------------------- +// Purpose: Packs two vectors into a __m128i +// Input : &v1 - +// &v2 - +// Output : +//----------------------------------------------------------------------------- +__m128i PackVerticesSIMD16(const Vector3& v1, const Vector3& v2) +{ + short x1, x2, y1, y2, z1, z2; + x1 = static_cast(v1.x); + x2 = static_cast(v2.x); + y1 = static_cast(v1.y); + y2 = static_cast(v2.y); + z1 = static_cast(v1.z); + z2 = static_cast(v2.z); + + __m128i xRes = _mm_set_epi16(x1, x2, y1, y2, z1, z2, 0, 0); + + if (x1 < x2) + xRes = _mm_shufflehi_epi16(xRes, _MM_SHUFFLE(2, 3, 1, 0)); + + if (y1 < y2) + xRes = _mm_shufflehi_epi16(xRes, _MM_SHUFFLE(3, 2, 0, 1)); + + if (z1 < z2) + xRes = _mm_shufflelo_epi16(xRes, _MM_SHUFFLE(2, 3, 1, 0)); + + return xRes; +} + +//----------------------------------------------------------------------------- +// Purpose: Draw navmesh polys using debug overlay +// Input : *pNavMesh +//----------------------------------------------------------------------------- +void CAI_Helper::DrawNavmeshPolys(dtNavMesh* pNavMesh) +{ + if (!pNavMesh) + pNavMesh = GetNavMeshForHull(Cvar_navmesh_debug_hull->GetInt()); + if (!pNavMesh) + return; + + Vector3 vCamera; + QAngle aCamera; + float fFov; + g_pClientTools->GetLocalPlayerEyePosition(vCamera, aCamera, fFov); + + const VPlane CullPlane(vCamera - aCamera.GetNormal() * 256.0f, aCamera); + + const float fCamRadius = Cvar_navmesh_debug_camera_radius->GetFloat(); + const bool bOptimize = Cvar_navmesh_debug_lossy_optimization->GetBool(); + + // Used for lossy optimization ( z is ignored when checking for duplicates ) + // [Fifty]: On a release build i gained around 12 fps on a 1050 ti + std::unordered_set sOutlines; + + for (int i = 0; i < pNavMesh->m_maxTiles; ++i) + { + const dtMeshTile* pTile = &pNavMesh->m_tiles[i]; + + if (!pTile->header) + continue; + + for (int j = 0; j < pTile->header->polyCount; j++) + { + const dtPoly* pPoly = &pTile->polys[j]; + + if (vCamera.DistTo(pPoly->org) > fCamRadius) + continue; + + if (CullPlane.GetPointSide(pPoly->org) != SIDE_FRONT) + continue; + + const unsigned int ip = (unsigned int)(pPoly - pTile->polys); + + if (pPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + { + const dtOffMeshConnection* pCon = &pTile->offMeshConnections[ip - pTile->header->offMeshBase]; + RenderLine(pCon->origin, pCon->dest, Color(255, 250, 50, 255), true); + } + else + { + const dtPolyDetail* pDetail = &pTile->detailMeshes[ip]; + + Vector3 v[3]; + + for (int k = 0; k < pDetail->triCount; ++k) + { + const unsigned char* t = &pTile->detailTris[(pDetail->triBase + k) * 4]; + for (int l = 0; l < 3; ++l) + { + if (t[l] < pPoly->vertCount) + { + float* pfVerts = &pTile->verts[pPoly->verts[t[l]] * 3]; + v[l] = Vector3(pfVerts[0], pfVerts[1], pfVerts[2]); + } + else + { + float* pfVerts = &pTile->detailVerts[(pDetail->vertBase + t[l] - pPoly->vertCount) * 3]; + v[l] = Vector3(pfVerts[0], pfVerts[1], pfVerts[2]); + } + } + + RenderTriangle(v[0], v[1], v[2], Color(110, 200, 220, 160), true); + + auto r = sOutlines.insert(_mm_extract_epi64(PackVerticesSIMD16(v[0], v[1]), 1)); + if (r.second || !bOptimize) + RenderLine(v[0], v[1], Color(0, 0, 150), true); + + r = sOutlines.insert(_mm_extract_epi64(PackVerticesSIMD16(v[1], v[2]), 1)); + if (r.second || !bOptimize) + RenderLine(v[1], v[2], Color(0, 0, 150), true); + + r = sOutlines.insert(_mm_extract_epi64(PackVerticesSIMD16(v[2], v[0]), 1)); + if (r.second || !bOptimize) + RenderLine(v[2], v[0], Color(0, 0, 150), true); + } + } + } + } +} + +ON_DLL_LOAD("server.dll", ServerAIHelper, (CModule module)) +{ + Cvar_navmesh_debug_hull = new ConVar("navmesh_debug_hull", "0", FCVAR_RELEASE, "0 = NONE"); + Cvar_navmesh_debug_camera_radius = + new ConVar("navmesh_debug_camera_radius", "1000", FCVAR_RELEASE, "Radius in which to draw navmeshes"); + Cvar_navmesh_debug_lossy_optimization = + new ConVar("navmesh_debug_lossy_optimization", "1", FCVAR_RELEASE, "Whether to enable lossy navmesh debug draw optimizations"); +} diff --git a/primedev/server/ai_helper.h b/primedev/server/ai_helper.h new file mode 100644 index 00000000..0b3c3a53 --- /dev/null +++ b/primedev/server/ai_helper.h @@ -0,0 +1,13 @@ +#pragma once + +#include "server/ai_navmesh.h" + +dtNavMesh* GetNavMeshForHull(int nHull); + +class CAI_Helper +{ +public: + void DrawNavmeshPolys(dtNavMesh* pNavMesh = nullptr); +}; + +inline CAI_Helper* g_pAIHelper = nullptr; diff --git a/primedev/server/ai_navmesh.cpp b/primedev/server/ai_navmesh.cpp new file mode 100644 index 00000000..966726b2 --- /dev/null +++ b/primedev/server/ai_navmesh.cpp @@ -0,0 +1,6 @@ +#include "ai_navmesh.h" + +ON_DLL_LOAD("server.dll", ServerAiNavMesh, (CModule module)) +{ + g_pNavMesh = module.Offset(0x105F5D0).RCast(); +} diff --git a/primedev/server/ai_navmesh.h b/primedev/server/ai_navmesh.h new file mode 100644 index 00000000..65529f7a --- /dev/null +++ b/primedev/server/ai_navmesh.h @@ -0,0 +1,260 @@ +#pragma once + +// [Fifty]: Taken from https://github.com/ASpoonPlaysGames/r2recast + +#include "core/math/vector.h" + +struct dtMeshHeader; +struct dtMeshTile; +struct dtPoly; +struct dtBVNode; +struct dtLink; + +typedef unsigned int dtPolyRef; + +/// The maximum number of vertices per navigation polygon. +/// @ingroup detour +static const int DT_VERTS_PER_POLYGON = 6; + +/// Flags representing the type of a navigation mesh polygon. +enum dtPolyTypes +{ + /// The polygon is a standard convex polygon that is part of the surface of the mesh. + DT_POLYTYPE_GROUND = 0, + /// The polygon is an off-mesh connection consisting of two vertices. + DT_POLYTYPE_OFFMESH_CONNECTION = 1, +}; + +/// Configuration parameters used to define multi-tile navigation meshes. +/// The values are used to allocate space during the initialization of a navigation mesh. +/// @see dtNavMesh::init() +/// @ingroup detour +struct dtNavMeshParams +{ + float orig[3]; ///< The world space origin of the navigation mesh's tile space. [(x, y, z)] + float tileWidth; ///< The width of each tile. (Along the x-axis.) + float tileHeight; ///< The height of each tile. (Along the z-axis.) + int maxTiles; ///< The maximum number of tiles the navigation mesh can contain. This and maxPolys are used to calculate how many bits + ///< are needed to identify tiles and polygons uniquely. + int maxPolys; ///< The maximum number of polygons each tile can contain. This and maxTiles are used to calculate how many bits are + ///< needed to identify tiles and polygons uniquely. + // + //// i hate this + int disjointPolyGroupCount = 0; + int reachabilityTableSize = 0; + int reachabilityTableCount = 0; +}; + +/// Defines an navigation mesh off-mesh connection within a dtMeshTile object. +/// An off-mesh connection is a user defined traversable connection made up to two vertices. +struct dtOffMeshConnection +{ + /// The endpoints of the connection. + Vector3 origin; + Vector3 dest; + + /// The radius of the endpoints. [Limit: >= 0] + float rad; + + /// The polygon reference of the connection within the tile. + unsigned short poly; + + /// Link flags. + /// @note These are not the connection's user defined flags. Those are assigned via the + /// connection's dtPoly definition. These are link flags used for internal purposes. + unsigned char flags; + + /// End point side. + unsigned char side; + + /// The id of the offmesh connection. (User assigned when the navigation mesh is built.) + unsigned int userId; + + float unk[3]; + float another_unk; +}; + +/// A navigation mesh based on tiles of convex polygons. +/// @ingroup detour +class dtNavMesh +{ +public: + dtMeshTile** m_posLookup; ///< Tile hash lookup. + dtMeshTile* m_nextFree; ///< Freelist of tiles. + dtMeshTile* m_tiles; ///< List of tiles. + + void* disjointPolyGroup; + int** reachabilityTable; + + __int64 unk; + dtNavMeshParams m_params; ///< Current initialization params. TODO: do not store this info twice. + float m_orig[3]; ///< Origin of the tile (0,0) + float m_tileWidth, m_tileHight; ///< Dimensions of each tile. + int m_pad; + int m_maxTiles; ///< Max number of tiles. + + int m_tileLutSize; ///< Tile hash lookup size (must be pot). + int m_tileLutMask; ///< Tile hash lookup mask. + + int m_saltBits; ///< Number of salt bits in the tile ID. + int m_tileBits; ///< Number of tile bits in the tile ID. + int m_polyBits; ///< Number of poly bits in the tile ID. +}; + +/// Defines the location of detail sub-mesh data within a dtMeshTile. +struct dtPolyDetail +{ + unsigned int vertBase; ///< The offset of the vertices in the dtMeshTile::detailVerts array. + unsigned int triBase; ///< The offset of the triangles in the dtMeshTile::detailTris array. + unsigned char vertCount; ///< The number of vertices in the sub-mesh. + unsigned char triCount; ///< The number of triangles in the sub-mesh. +}; + +/// Defines a navigation mesh tile. +/// @ingroup detour +struct dtMeshTile +{ + int salt; ///< Counter describing modifications to the tile. + unsigned int linksFreeList; ///< Index to the next free link. + dtMeshHeader* header; ///< The tile header. + dtPoly* polys; ///< The tile polygons. [Size: dtMeshHeader::polyCount] + void* unkPolyThing; + float* verts; ///< The tile vertices. [Size: dtMeshHeader::vertCount] + dtLink* links; ///< The tile links. [Size: dtMeshHeader::maxLinkCount] + dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount] + + /// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount] + float* detailVerts; + + /// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount]. + /// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags. + unsigned char* detailTris; + + /// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount] + /// (Will be null if bounding volumes are disabled.) + dtBVNode* bvTree; + + dtOffMeshConnection* offMeshConnections; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount] + void* data; ///< The tile data. (Not directly accessed under normal situations.) + int dataSize; ///< Size of the tile data. + int flags; ///< Tile flags. (See: #dtTileFlags) + dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid. + __int64 unk; +}; + +/// Provides high level information related to a dtMeshTile object. +/// @ingroup detour +struct dtMeshHeader +{ + int magic; ///< Tile magic number. (Used to identify the data format.) + int version; ///< Tile data format version number. + int x; ///< The x-position of the tile within the dtNavMesh tile grid. (x, y, layer) + int y; ///< The y-position of the tile within the dtNavMesh tile grid. (x, y, layer) + int layer; ///< The layer of the tile within the dtNavMesh tile grid. (x, y, layer) + unsigned int userId; ///< The user defined id of the tile. + int polyCount; ///< The number of polygons in the tile. + int sth_per_poly; + int vertCount; ///< The number of vertices in the tile. + int maxLinkCount; ///< The number of allocated links. + + int detailMeshCount; + + /// The number of unique vertices in the detail mesh. (In addition to the polygon vertices.) + int detailVertCount; + + int detailTriCount; ///< The number of triangles in the detail mesh. + int bvNodeCount; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.) + int offMeshConCount; ///< The number of off-mesh connections. + // int unk1; + int offMeshBase; ///< The index of the first polygon which is an off-mesh connection. + + float walkableHeight; ///< The height of the agents using the tile. + float walkableRadius; ///< The radius of the agents using the tile. + float walkableClimb; ///< The maximum climb height of the agents using the tile. + float bmin[3]; ///< The minimum bounds of the tile's AABB. [(x, y, z)] + float bmax[3]; ///< The maximum bounds of the tile's AABB. [(x, y, z)] + + /// The bounding volume quantization factor. + float bvQuantFactor; +}; + +/// Defines a polygon within a dtMeshTile object. +/// @ingroup detour +struct dtPoly +{ + /// Index to first link in linked list. (Or #DT_NULL_LINK if there is no link.) + unsigned int firstLink; + + /// The indices of the polygon's vertices. + /// The actual vertices are located in dtMeshTile::verts. + unsigned short verts[DT_VERTS_PER_POLYGON]; + + /// Packed data representing neighbor polygons references and flags for each edge. + unsigned short neis[DT_VERTS_PER_POLYGON]; + + /// The user defined polygon flags. + unsigned short flags; + + /// The number of vertices in the polygon. + unsigned char vertCount; + + /// The bit packed area id and polygon type. + /// @note Use the structure's set and get methods to acess this value. + unsigned char areaAndtype; + + unsigned short disjointSetId; + unsigned short unk; // IDK but looks filled + Vector3 org; // + + /// Sets the user defined area id. [Limit: < #DT_MAX_AREAS] + inline void setArea(unsigned char a) + { + areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); + } + + /// Sets the polygon type. (See: #dtPolyTypes.) + inline void setType(unsigned char t) + { + areaAndtype = (areaAndtype & 0x3f) | (t << 6); + } + + /// Gets the user defined area id. + inline unsigned char getArea() const + { + return areaAndtype & 0x3f; + } + + /// Gets the polygon type. (See: #dtPolyTypes) + inline unsigned char getType() const + { + return areaAndtype >> 6; + } +}; + +/// Defines a link between polygons. +/// @note This structure is rarely if ever used by the end user. +/// @see dtMeshTile +struct dtLink +{ + dtPolyRef ref; ///< Neighbour reference. (The neighbor that is linked to.) + unsigned int next; ///< Index of the next link. + unsigned char edge; ///< Index of the polygon edge that owns this link. + unsigned char side; ///< If a boundary link, defines on which side the link is. + unsigned char bmin; ///< If a boundary link, defines the minimum sub-edge area. + unsigned char bmax; ///< If a boundary link, defines the maximum sub-edge area. + unsigned char jumpType; + unsigned char otherUnk; + unsigned short reverseLinkIndex; +}; + +/// Bounding volume node. +/// @note This structure is rarely if ever used by the end user. +/// @see dtMeshTile +struct dtBVNode +{ + unsigned short bmin[3]; ///< Minimum bounds of the node's AABB. [(x, y, z)] + unsigned short bmax[3]; ///< Maximum bounds of the node's AABB. [(x, y, z)] + int i; ///< The node's index. (Negative for escape sequence.) +}; + +inline dtNavMesh** g_pNavMesh = nullptr; diff --git a/primedev/shared/exploit_fixes/exploitfixes.cpp b/primedev/shared/exploit_fixes/exploitfixes.cpp index 44651b3c..cade4084 100644 --- a/primedev/shared/exploit_fixes/exploitfixes.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes.cpp @@ -222,16 +222,24 @@ void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) // 4C 89 44 24 "ReadUsercmd (command_number delta: " + std::to_string(cmd->command_number - fromCmd->command_number) + "): "; // fix invalid player angles - cmd->worldViewAngles.MakeValid(); - cmd->attackangles.MakeValid(); - cmd->localViewAngles.MakeValid(); + if (!cmd->worldViewAngles.IsValid()) + cmd->worldViewAngles.Init(); + + if (!cmd->attackangles.IsValid()) + cmd->attackangles.Init(); + + if (!cmd->localViewAngles.IsValid()) + cmd->localViewAngles.Init(); // Fix invalid camera angles - cmd->cameraPos.MakeValid(); - cmd->cameraAngles.MakeValid(); + if (!cmd->cameraPos.IsValid()) + cmd->cameraPos.Init(); + if (!cmd->cameraAngles.IsValid()) + cmd->cameraAngles.Init(); // Fix invaid movement vector - cmd->move.MakeValid(); + if (!cmd->move.IsValid()) + cmd->move.Init(); if (cmd->frameTime <= 0 || cmd->tick_count == 0 || cmd->command_time <= 0) { diff --git a/primedev/toolframework/itoolentity.h b/primedev/toolframework/itoolentity.h new file mode 100644 index 00000000..332004f2 --- /dev/null +++ b/primedev/toolframework/itoolentity.h @@ -0,0 +1,46 @@ +#pragma once + +#include "core/math/vector.h" + +class IClientTools // : public IBaseInterface +{ +public: + virtual void sub_1805E4960() = 0; + virtual void sub_1805E4B10() = 0; + virtual void sub_1805E4C50() = 0; + virtual void sub_1805E5670() = 0; + virtual void sub_1805E66C0() = 0; + virtual void sub_1805E5910() = 0; + virtual void sub_1805E59A0() = 0; + virtual void sub_1805E6B10() = 0; + virtual void sub_1805E8580() = 0; + virtual void sub_1805E59D0() = 0; + virtual void sub_1805E57B0() = 0; + virtual void sub_1805E5860() = 0; + virtual void sub_1805E55A0() = 0; + virtual void sub_1805E49C0() = 0; + virtual void sub_1805E7580() = 0; + virtual void sub_1805E86A0() = 0; + virtual void sub_1805E69B0() = 0; + virtual void sub_1805E4D70() = 0; // IClientTools::DrawSprite + virtual void* GetLocalPlayer() = 0; // return type unknown probably CBasePlayer* + virtual bool GetLocalPlayerEyePosition(Vector3& org, QAngle& ang, float& fov) = 0; + virtual void sub_1805E5960() = 0; + virtual void sub_1805E5650() = 0; + virtual void sub_1805E5920() = 0; + virtual void sub_1805E64E0() = 0; + virtual void sub_1805E63C0() = 0; + virtual void sub_1805E64C0() = 0; + virtual void sub_1805E6520() = 0; + virtual void sub_1805E6770() = 0; + virtual void sub_1805E67B0() = 0; + virtual void sub_1805E67F0() = 0; + virtual void sub_1805E66A0() = 0; + virtual void sub_1805E6500() = 0; + virtual void sub_1805E63B0() = 0; + virtual void sub_1805E54C0() = 0; + virtual void sub_1805E53E0() = 0; + virtual void sub_1805E6D70() = 0; + virtual void sub_1805E50D0() = 0; + virtual void sub_1805E65C0() = 0; +}; -- cgit v1.2.3