fix conflicts

This commit is contained in:
ASpoonPlaysGames 2024-02-14 19:13:42 +00:00
commit 5cb3d3c523
42 changed files with 1771 additions and 872 deletions

View File

@ -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"
@ -35,7 +36,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"
@ -47,6 +50,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"
@ -78,11 +83,16 @@ add_library(
"mods/modmanager.h"
"mods/modsavefiles.cpp"
"mods/modsavefiles.h"
"plugins/plugin_abi.h"
"plugins/pluginbackend.cpp"
"plugins/pluginbackend.h"
"plugins/interfaces/interface.h"
"plugins/interfaces/interface.cpp"
"plugins/interfaces/sys/ISys.h"
"plugins/interfaces/sys/ISys.cpp"
"plugins/interfaces/IPluginId.h"
"plugins/interfaces/IPluginCallbacks.h"
"plugins/plugins.cpp"
"plugins/plugins.h"
"plugins/pluginmanager.h"
"plugins/pluginmanager.cpp"
"scripts/client/clientchathooks.cpp"
"scripts/client/cursorposition.cpp"
"scripts/client/scriptbrowserhooks.cpp"
@ -105,6 +115,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"

View File

@ -0,0 +1,5 @@
#pragma once
#include "toolframework/itoolentity.h"
inline IClientTools* g_pClientTools = nullptr;

View File

@ -1,6 +1,9 @@
#include "debugoverlay.h"
#include "dedicated/dedicated.h"
#include "tier1/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__IsDeadType>();
OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast<OverlayBase_t__DestroyOverlayType>();
OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast<decltype(OverlayBase_t__IsDead)>();
OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast<decltype(OverlayBase_t__DestroyOverlay)>();
RenderLine = module.Offset(0x192A70).RCast<RenderLineType>();
RenderBox = module.Offset(0x192520).RCast<RenderBoxType>();
RenderWireframeBox = module.Offset(0x193DA0).RCast<RenderBoxType>();
RenderWireframeSweptBox = module.Offset(0x1945A0).RCast<RenderWireframeSweptBoxType>();
RenderTriangle = module.Offset(0x193940).RCast<RenderTriangleType>();
RenderAxis = module.Offset(0x1924D0).RCast<RenderAxisType>();
RenderSphere = module.Offset(0x194170).RCast<RenderSphereType>();
RenderUnknown = module.Offset(0x1924E0).RCast<RenderUnknownType>();
RenderLine = module.Offset(0x192A70).RCast<decltype(RenderLine)>();
RenderBox = module.Offset(0x192520).RCast<decltype(RenderBox)>();
RenderWireframeBox = module.Offset(0x193DA0).RCast<decltype(RenderWireframeBox)>();
RenderWireframeSweptBox = module.Offset(0x1945A0).RCast<decltype(RenderWireframeSweptBox)>();
RenderTriangle = module.Offset(0x193940).RCast<decltype(RenderTriangle)>();
RenderAxis = module.Offset(0x1924D0).RCast<decltype(RenderAxis)>();
RenderSphere = module.Offset(0x194170).RCast<decltype(RenderSphere)>();
RenderUnknown = module.Offset(0x1924E0).RCast<decltype(RenderUnknown)>();
s_OverlayMutex = module.Offset(0x10DB0A38).RCast<LPCRITICAL_SECTION>();

View File

@ -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);

View File

@ -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<IClientTools*>();
}

View File

@ -1,5 +1,5 @@
#include "dedicated/dedicated.h"
#include "plugins/pluginbackend.h"
#include "plugins/pluginmanager.h"
#include <iostream>
#include <wchar.h>
@ -417,7 +417,7 @@ HMODULE, WINAPI, (LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags))
if (moduleAddress)
{
CallLoadLibraryACallbacks(lpLibFileName, moduleAddress);
InformPluginsDLLLoad(fs::path(lpLibFileName), moduleAddress);
g_pPluginManager->InformDllLoad(moduleAddress, fs::path(lpLibFileName));
}
return moduleAddress;
@ -459,7 +459,7 @@ HMODULE, WINAPI, (LPCWSTR lpLibFileName))
if (moduleAddress)
{
CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress);
InformPluginsDLLLoad(fs::path(lpLibFileName), moduleAddress);
g_pPluginManager->InformDllLoad(moduleAddress, fs::path(lpLibFileName));
}
return moduleAddress;

View File

@ -0,0 +1,7 @@
#pragma once
inline float FastSqrt(float x)
{
__m128 root = _mm_sqrt_ss(_mm_load_ss(&x));
return *(reinterpret_cast<float*>(&root));
}

View File

@ -1,47 +1,331 @@
#include <cmath>
#pragma once
union Vector3
#include "bits.h"
#include "math_pfns.h"
#include <cmath>
#define DEG2RAD(a) (a) * (3.14159265358979323846f / 180.0f)
#define RAD2DEG(a) (a) * (180.0f / 3.14159265358979323846f)
class Vector3
{
struct
{
float x;
float y;
float z;
};
public:
float x, y, z;
float raw[3];
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) {}
void MakeValid()
inline bool IsValid()
{
for (auto& fl : raw)
if (std::isnan(fl))
fl = 0;
return IsFinite(x) && IsFinite(y) && IsFinite(z);
}
inline void Init(float fX = 0.0f, float fY = 0.0f, float fZ = 0.0f)
{
x = fX;
y = fY;
z = fZ;
}
inline float Length()
{
return FastSqrt(x * x + y * y + z * z);
}
inline float DistTo(const Vector3& vOther)
{
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;
};
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
{
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])
{
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;
}
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 Vector3& other)
bool operator==(const QAngle& other)
{
return x == other.x && y == other.y && z == other.z;
}
};
union QAngle
inline Vector3 QAngle::GetNormal() const
{
struct
{
float x;
float y;
float z;
float w;
};
float raw[4];
// todo: more operators maybe
bool operator==(const QAngle& other)
{
return x == other.x && y == other.y && z == other.z && w == other.w;
}
};
Vector3 ret(cos(DEG2RAD(y)), sin(DEG2RAD(y)), -sin(DEG2RAD(x)));
return ret;
}

106
primedev/core/math/vplane.h Normal file
View File

@ -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;
}

View File

@ -1,6 +1,13 @@
#pragma once
#include <string>
// interface return status
enum class InterfaceStatus : int
{
IFACE_OK = 0,
IFACE_FAILED,
};
// literally just copied from ttf2sdk definition
typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode);

19
primedev/core/tier1.cpp Normal file
View File

@ -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<CreateInterfaceFn>(GetProcAddress(hModule, CREATEINTERFACE_PROCNAME));
return fnCreateInterface(svFactoryName.c_str(), NULL);
}

12
primedev/core/tier1.h Normal file
View File

@ -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);

View File

@ -3,10 +3,10 @@
#include "core/memalloc.h"
#include "core/vanilla.h"
#include "config/profile.h"
#include "plugins/plugin_abi.h"
#include "plugins/plugins.h"
#include "plugins/pluginbackend.h"
#include "plugins/pluginmanager.h"
#include "util/version.h"
#include "util/wininfo.h"
#include "squirrel/squirrel.h"
#include "server/serverpresence.h"
@ -23,6 +23,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_NorthstarModule = hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
@ -65,7 +67,6 @@ bool InitialiseNorthstar()
g_pServerPresence = new ServerPresenceManager();
g_pPluginManager = new PluginManager();
g_pPluginCommunicationhandler = new PluginCommunicationHandler();
g_pPluginManager->LoadPlugins();
InitialiseSquirrelManagers();

View File

@ -7,8 +7,7 @@
#include "engine/r2engine.h"
#include "shared/exploit_fixes/ns_limits.h"
#include "squirrel/squirrel.h"
#include "plugins/plugins.h"
#include "plugins/pluginbackend.h"
#include "plugins/pluginmanager.h"
AUTOHOOK_INIT()

View File

@ -4,6 +4,7 @@
#include "util/version.h"
#include "mods/modmanager.h"
#include "plugins/plugins.h"
#include "plugins/pluginmanager.h"
#include <minidumpapiset.h>
@ -524,9 +525,9 @@ void CCrashHandler::FormatLoadedPlugins()
if (g_pPluginManager)
{
spdlog::error("Loaded Plugins:");
for (const Plugin& plugin : g_pPluginManager->m_vLoadedPlugins)
for (const Plugin& plugin : g_pPluginManager->GetLoadedPlugins())
{
spdlog::error("\t{}", plugin.name);
spdlog::error("\t{}", plugin.GetName());
}
}
}

View File

@ -4,7 +4,7 @@ private:
const char* VERIFICATION_FLAG = "-disablemodverification";
const char* CUSTOM_MODS_URL_FLAG = "-customverifiedurl=";
const char* STORE_URL = "https://gcdn.thunderstore.io/live/repository/packages/";
const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/master/verified-mods.json";
const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json";
char* modsListUrl;
struct VerifiedModVersion
@ -79,7 +79,7 @@ public:
* The Northstar auto-downloading feature does NOT allow automatically installing
* all mods for various (notably security) reasons; mods that are candidate to
* auto-downloading are rather listed on a GitHub repository
* (https://raw.githubusercontent.com/R2Northstar/VerifiedMods/master/verified-mods.json),
* (https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json),
* which this method gets via a HTTP call to load into local state.
*
* If list fetching fails, local mods list will be initialized as empty, thus

View File

@ -0,0 +1,36 @@
#ifndef IPLUGIN_CALLBACKS_H
#define IPLUGIN_CALLBACKS_H
#include <windows.h>
#include <stdint.h>
#include "squirrel/squirrel.h"
// can't use bitwise ops on enum classes but I don't want these in the global namespace (user defined operators suck)
namespace PluginContext
{
enum : uint64_t
{
DEDICATED = 0x1,
CLIENT = 0x2,
};
}
struct PluginNorthstarData
{
HMODULE pluginHandle;
};
class IPluginCallbacks
{
public:
virtual void
Init(HMODULE northstarModule, const PluginNorthstarData* initData, bool reloaded) = 0; // runs after the plugin is loaded and validated
virtual void Finalize() = 0; // runs after all plugins have been loaded
virtual bool Unload() = 0; // runs just before the library is freed
virtual void OnSqvmCreated(CSquirrelVM* sqvm) = 0;
virtual void OnSqvmDestroying(CSquirrelVM* sqvm) = 0;
virtual void OnLibraryLoaded(HMODULE module, const char* name) = 0;
virtual void RunFrame() = 0;
};
#endif

View File

@ -0,0 +1,31 @@
#ifndef IPLUGIN_ID_H
#define IPLUGIN_ID_H
#include <stdint.h>
#include "squirrel/squirrelclasstypes.h"
#define PLUGIN_ID_VERSION "PluginId001"
// an identifier for the type of string data requested from the plugin
enum class PluginString : int
{
NAME = 0,
LOG_NAME = 1,
DEPENDENCY_NAME = 2,
};
// an identifier for the type of bitflag requested from the plugin
enum class PluginField : int
{
CONTEXT = 0,
};
// an interface that is required from every plugin to query data about it
class IPluginId
{
public:
virtual const char* GetString(PluginString prop) = 0;
virtual int64_t GetField(PluginField prop) = 0;
};
#endif

View File

@ -0,0 +1,36 @@
#include <string.h>
#include "interface.h"
InterfaceReg* s_pInterfaceRegs;
InterfaceReg::InterfaceReg(InstantiateInterfaceFn fn, const char* pName) : m_pName(pName)
{
m_CreateFn = fn;
m_pNext = s_pInterfaceRegs;
s_pInterfaceRegs = this;
}
void* CreateInterface(const char* pName, InterfaceStatus* pReturnCode)
{
for (InterfaceReg* pCur = s_pInterfaceRegs; pCur; pCur = pCur->m_pNext)
{
if (strcmp(pCur->m_pName, pName) == 0)
{
if (pReturnCode)
{
*pReturnCode = InterfaceStatus::IFACE_OK;
}
NS::log::PLUGINSYS->info("creating interface {}", pName);
return pCur->m_CreateFn();
}
}
if (pReturnCode)
{
*pReturnCode = InterfaceStatus::IFACE_FAILED;
}
NS::log::PLUGINSYS->error("could not find interface {}", pName);
return NULL;
}

View File

@ -0,0 +1,39 @@
#ifndef INTERFACE_H
#define INTERFACE_H
typedef void* (*InstantiateInterfaceFn)();
// Used internally to register classes.
class InterfaceReg
{
public:
InterfaceReg(InstantiateInterfaceFn fn, const char* pName);
InstantiateInterfaceFn m_CreateFn;
const char* m_pName;
InterfaceReg* m_pNext;
};
// Use this to expose an interface that can have multiple instances.
#define EXPOSE_INTERFACE(className, interfaceName, versionName) \
static void* __Create##className##_interface() \
{ \
return static_cast<interfaceName*>(new className); \
} \
static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName);
#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \
static void* __Create##className##interfaceName##_interface() \
{ \
return static_cast<interfaceName*>(&globalVarName); \
} \
static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName);
// Use this to expose a singleton interface. This creates the global variable for you automatically.
#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \
static className __g_##className##_singleton; \
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton)
EXPORT void* CreateInterface(const char* pName, InterfaceStatus* pReturnCode);
#endif

View File

@ -0,0 +1,66 @@
#include "plugins/interfaces/interface.h"
#include "ISys.h"
#include "plugins/plugins.h"
#include "plugins/pluginmanager.h"
class CSys : public ISys
{
public:
void Log(HMODULE handle, LogLevel level, char* msg)
{
spdlog::level::level_enum spdLevel;
switch (level)
{
case LogLevel::WARN:
spdLevel = spdlog::level::level_enum::warn;
break;
case LogLevel::ERR:
spdLevel = spdlog::level::level_enum::err;
break;
default:
NS::log::PLUGINSYS->warn("Attempted to log with invalid level {}. Defaulting to info", (int)level);
case LogLevel::INFO:
spdLevel = spdlog::level::level_enum::info;
break;
}
std::optional<Plugin> plugin = g_pPluginManager->GetPlugin(handle);
if (plugin)
{
plugin->Log(spdLevel, msg);
}
else
{
NS::log::PLUGINSYS->warn("Attempted to log message '{}' with invalid plugin handle {}", msg, static_cast<void*>(handle));
}
}
void Unload(HMODULE handle)
{
std::optional<Plugin> plugin = g_pPluginManager->GetPlugin(handle);
if (plugin)
{
plugin->Unload();
}
else
{
NS::log::PLUGINSYS->warn("Attempted to unload plugin with invalid handle {}", static_cast<void*>(handle));
}
}
void Reload(HMODULE handle)
{
std::optional<Plugin> plugin = g_pPluginManager->GetPlugin(handle);
if (plugin)
{
plugin->Reload();
}
else
{
NS::log::PLUGINSYS->warn("Attempted to reload plugin with invalid handle {}", static_cast<void*>(handle));
}
}
};
EXPOSE_SINGLE_INTERFACE(CSys, ISys, SYS_VERSION);

View File

@ -0,0 +1,21 @@
#ifndef ILOGGING_H
#define ILOGGING_H
#define SYS_VERSION "NSSys001"
enum class LogLevel : int
{
INFO = 0,
WARN,
ERR,
};
class ISys
{
public:
virtual void Log(HMODULE handle, LogLevel level, char* msg) = 0;
virtual void Unload(HMODULE handle) = 0;
virtual void Reload(HMODULE handle) = 0;
};
#endif

View File

@ -1,151 +0,0 @@
#pragma once
#include "squirrel/squirrelclasstypes.h"
#define ABI_VERSION 3
enum PluginLoadDLL
{
ENGINE = 0,
CLIENT,
SERVER
};
enum ObjectType
{
CONCOMMANDS = 0,
CONVAR = 1,
};
struct SquirrelFunctions
{
RegisterSquirrelFuncType RegisterSquirrelFunc;
sq_defconstType __sq_defconst;
sq_compilebufferType __sq_compilebuffer;
sq_callType __sq_call;
sq_raiseerrorType __sq_raiseerror;
sq_compilefileType __sq_compilefile;
sq_newarrayType __sq_newarray;
sq_arrayappendType __sq_arrayappend;
sq_newtableType __sq_newtable;
sq_newslotType __sq_newslot;
sq_pushroottableType __sq_pushroottable;
sq_pushstringType __sq_pushstring;
sq_pushintegerType __sq_pushinteger;
sq_pushfloatType __sq_pushfloat;
sq_pushboolType __sq_pushbool;
sq_pushassetType __sq_pushasset;
sq_pushvectorType __sq_pushvector;
sq_pushobjectType __sq_pushobject;
sq_getstringType __sq_getstring;
sq_getintegerType __sq_getinteger;
sq_getfloatType __sq_getfloat;
sq_getboolType __sq_getbool;
sq_getType __sq_get;
sq_getassetType __sq_getasset;
sq_getuserdataType __sq_getuserdata;
sq_getvectorType __sq_getvector;
sq_getthisentityType __sq_getthisentity;
sq_getobjectType __sq_getobject;
sq_stackinfosType __sq_stackinfos;
sq_createuserdataType __sq_createuserdata;
sq_setuserdatatypeidType __sq_setuserdatatypeid;
sq_getfunctionType __sq_getfunction;
sq_schedule_call_externalType __sq_schedule_call_external;
sq_getentityfrominstanceType __sq_getentityfrominstance;
sq_GetEntityConstantType __sq_GetEntityConstant_CBaseEntity;
sq_pushnewstructinstanceType __sq_pushnewstructinstance;
sq_sealstructslotType __sq_sealstructslot;
};
struct MessageSource
{
const char* file;
const char* func;
int line;
};
// This is a modified version of spdlog::details::log_msg
// This is so that we can make it cross DLL boundaries
struct LogMsg
{
int level;
uint64_t timestamp;
const char* msg;
MessageSource source;
int pluginHandle;
};
extern "C"
{
typedef void (*loggerfunc_t)(LogMsg* msg);
typedef void (*PLUGIN_RELAY_INVITE_TYPE)(const char* invite);
typedef void* (*CreateObjectFunc)(ObjectType type);
typedef void (*PluginFnCommandCallback_t)(void* command);
typedef void (*PluginConCommandConstructorType)(
void* newCommand, const char* name, PluginFnCommandCallback_t callback, const char* helpString, int flags, void* parent);
typedef void (*PluginConVarRegisterType)(
void* pConVar,
const char* pszName,
const char* pszDefaultValue,
int nFlags,
const char* pszHelpString,
bool bMin,
float fMin,
bool bMax,
float fMax,
void* pCallback);
typedef void (*PluginConVarMallocType)(void* pConVarMaloc, int a2, int a3);
}
struct PluginNorthstarData
{
const char* version;
HMODULE northstarModule;
int pluginHandle;
};
struct PluginInitFuncs
{
loggerfunc_t logger;
PLUGIN_RELAY_INVITE_TYPE relayInviteFunc;
CreateObjectFunc createObject;
};
struct PluginEngineData
{
PluginConCommandConstructorType ConCommandConstructor;
PluginConVarMallocType conVarMalloc;
PluginConVarRegisterType conVarRegister;
void* ConVar_Vtable;
void* IConVar_Vtable;
void* g_pCVar;
};
/// <summary> Async communication within the plugin system
/// Due to the asynchronous nature of plugins, combined with the limitations of multi-compiler support
/// and the custom memory allocator used by r2, is it difficult to safely get data across DLL boundaries
/// from Northstar to plugin unless Northstar can own that memory.
/// This means that plugins should manage their own memory and can only receive data from northstar using one of the functions below.
/// These should be exports of the plugin DLL. If they are not exported, they will not be called.
/// Note that it is not required to have these exports if you do not use them.
/// </summary>
// Northstar -> Plugin
typedef void (*PLUGIN_INIT_TYPE)(PluginInitFuncs* funcs, PluginNorthstarData* data);
typedef void (*PLUGIN_INIT_SQVM_TYPE)(SquirrelFunctions* funcs);
typedef void (*PLUGIN_INFORM_SQVM_CREATED_TYPE)(ScriptContext context, CSquirrelVM* sqvm);
typedef void (*PLUGIN_INFORM_SQVM_DESTROYED_TYPE)(ScriptContext context);
typedef void (*PLUGIN_INFORM_DLL_LOAD_TYPE)(const char* dll, PluginEngineData* data, void* dllPtr);
typedef void (*PLUGIN_RUNFRAME)();

View File

@ -1,50 +0,0 @@
#include "pluginbackend.h"
#include "plugin_abi.h"
#include "server/serverpresence.h"
#include "masterserver/masterserver.h"
#include "squirrel/squirrel.h"
#include "plugins.h"
#include "tier1/cmd.h"
#include <filesystem>
#define EXPORT extern "C" __declspec(dllexport)
AUTOHOOK_INIT()
PluginCommunicationHandler* g_pPluginCommunicationhandler;
static PluginDataRequest storedRequest {PluginDataRequestType::END, (PluginRespondDataCallable) nullptr};
void PluginCommunicationHandler::RunFrame()
{
std::lock_guard<std::mutex> lock(requestMutex);
if (!requestQueue.empty())
{
storedRequest = requestQueue.front();
switch (storedRequest.type)
{
default:
spdlog::error("{} was called with invalid request type '{}'", __FUNCTION__, static_cast<int>(storedRequest.type));
}
requestQueue.pop();
}
}
void PluginCommunicationHandler::PushRequest(PluginDataRequestType type, PluginRespondDataCallable func)
{
std::lock_guard<std::mutex> lock(requestMutex);
requestQueue.push(PluginDataRequest {type, func});
}
void InformPluginsDLLLoad(fs::path dllPath, void* address)
{
std::string dllName = dllPath.filename().string();
void* data = NULL;
if (strncmp(dllName.c_str(), "engine.dll", 10) == 0)
data = &g_pPluginCommunicationhandler->m_sEngineData;
g_pPluginManager->InformDLLLoad(dllName.c_str(), data, address);
}

View File

@ -1,40 +0,0 @@
#pragma once
#include "plugin_abi.h"
#include <queue>
#include <mutex>
enum PluginDataRequestType
{
END = 0,
};
union PluginRespondDataCallable
{
// Empty for now
void* UNUSED;
};
class PluginDataRequest
{
public:
PluginDataRequestType type;
PluginRespondDataCallable func;
PluginDataRequest(PluginDataRequestType type, PluginRespondDataCallable func) : type(type), func(func) {}
};
class PluginCommunicationHandler
{
public:
void RunFrame();
void PushRequest(PluginDataRequestType type, PluginRespondDataCallable func);
public:
std::queue<PluginDataRequest> requestQueue = {};
std::mutex requestMutex;
PluginEngineData m_sEngineData {};
};
void InformPluginsDLLLoad(fs::path dllPath, void* address);
extern PluginCommunicationHandler* g_pPluginCommunicationhandler;

View File

@ -0,0 +1,184 @@
#include "pluginmanager.h"
#include <regex>
#include "plugins.h"
#include "config/profile.h"
#include "tier1/cmd.h"
PluginManager* g_pPluginManager;
const std::vector<Plugin>& PluginManager::GetLoadedPlugins() const
{
return this->plugins;
}
const std::optional<Plugin> PluginManager::GetPlugin(HMODULE handle) const
{
for (const Plugin& plugin : GetLoadedPlugins())
if (plugin.m_handle == handle)
return plugin;
return std::nullopt;
}
void PluginManager::LoadPlugin(fs::path path, bool reloaded)
{
Plugin plugin = Plugin(path.string());
if (!plugin.IsValid())
{
NS::log::PLUGINSYS->warn("Unloading invalid plugin '{}'", path.string());
plugin.Unload();
return;
}
plugins.push_back(plugin);
plugin.Init(reloaded);
}
inline void FindPlugins(fs::path pluginPath, std::vector<fs::path>& paths)
{
// ensure dirs exist
if (!fs::exists(pluginPath) || !fs::is_directory(pluginPath))
{
return;
}
for (const fs::directory_entry& entry : fs::directory_iterator(pluginPath))
{
if (fs::is_regular_file(entry) && entry.path().extension() == ".dll")
paths.emplace_back(entry.path());
}
}
bool PluginManager::LoadPlugins(bool reloaded)
{
if (strstr(GetCommandLineA(), "-noplugins") != NULL)
{
NS::log::PLUGINSYS->warn("-noplugins detected; skipping loading plugins");
return false;
}
fs::create_directories(GetThunderstoreModFolderPath());
std::vector<fs::path> paths;
pluginPath = GetNorthstarPrefix() + "\\plugins";
fs::path libPath = fs::absolute(pluginPath + "\\lib");
if (fs::exists(libPath) && fs::is_directory(libPath))
AddDllDirectory(libPath.wstring().c_str());
FindPlugins(pluginPath, paths);
// Special case for Thunderstore mods dir
std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath());
// Set up regex for `AUTHOR-MOD-VERSION` pattern
std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))");
for (fs::directory_entry dir : thunderstoreModsDir)
{
fs::path pluginsDir = dir.path() / "plugins";
// Use regex to match `AUTHOR-MOD-VERSION` pattern
if (!std::regex_match(dir.path().string(), pattern))
{
spdlog::warn("The following directory did not match 'AUTHOR-MOD-VERSION': {}", dir.path().string());
continue; // skip loading package that doesn't match
}
fs::path libDir = fs::absolute(pluginsDir / "lib");
if (fs::exists(libDir) && fs::is_directory(libDir))
AddDllDirectory(libDir.wstring().c_str());
FindPlugins(pluginsDir, paths);
}
if (paths.empty())
{
NS::log::PLUGINSYS->warn("Could not find any plugins. Skipped loading plugins");
return false;
}
for (fs::path path : paths)
{
LoadPlugin(path, reloaded);
}
InformAllPluginsInitialized();
return true;
}
void PluginManager::ReloadPlugins()
{
for (const Plugin& plugin : this->GetLoadedPlugins())
{
plugin.Unload();
}
this->plugins.clear();
this->LoadPlugins(true);
}
void PluginManager::RemovePlugin(HMODULE handle)
{
for (size_t i = 0; i < plugins.size(); i++)
{
Plugin* plugin = &plugins[i];
if (plugin->m_handle == handle)
{
plugins.erase(plugins.begin() + i);
return;
}
}
}
void PluginManager::InformAllPluginsInitialized() const
{
for (const Plugin& plugin : GetLoadedPlugins())
{
plugin.Finalize();
}
}
void PluginManager::InformSqvmCreated(CSquirrelVM* sqvm) const
{
for (const Plugin& plugin : GetLoadedPlugins())
{
plugin.OnSqvmCreated(sqvm);
}
}
void PluginManager::InformSqvmDestroying(CSquirrelVM* sqvm) const
{
for (const Plugin& plugin : GetLoadedPlugins())
{
plugin.OnSqvmDestroying(sqvm);
}
}
void PluginManager::InformDllLoad(HMODULE module, fs::path path) const
{
std::string fn = path.filename().string(); // without this the string gets freed immediately lmao
const char* filename = fn.c_str();
for (const Plugin& plugin : GetLoadedPlugins())
{
plugin.OnLibraryLoaded(module, filename);
}
}
void PluginManager::RunFrame() const
{
for (const Plugin& plugin : GetLoadedPlugins())
{
plugin.RunFrame();
}
}
void ConCommand_reload_plugins(const CCommand& args)
{
g_pPluginManager->ReloadPlugins();
}
ON_DLL_LOAD_RELIESON("engine.dll", PluginManager, ConCommand, (CModule module))
{
RegisterConCommand("reload_plugins", ConCommand_reload_plugins, "reloads plugins", FCVAR_NONE);
}

View File

@ -0,0 +1,33 @@
#ifndef PLUGIN_MANAGER_H
#define PLUGIN_MANAGER_H
#include <windows.h>
class Plugin;
class PluginManager
{
public:
const std::vector<Plugin>& GetLoadedPlugins() const;
const std::optional<Plugin> GetPlugin(HMODULE handle) const;
bool LoadPlugins(bool reloaded = false);
void LoadPlugin(fs::path path, bool reloaded = false);
void ReloadPlugins();
void RemovePlugin(HMODULE handle);
// callback triggers
void InformSqvmCreated(CSquirrelVM* sqvm) const;
void InformSqvmDestroying(CSquirrelVM* sqvm) const;
void InformDllLoad(HMODULE module, fs::path path) const;
void RunFrame() const;
private:
void InformAllPluginsInitialized() const;
std::vector<Plugin> plugins;
std::string pluginPath;
};
extern PluginManager* g_pPluginManager;
#endif

View File

@ -1,340 +1,233 @@
#include "plugins.h"
#include "config/profile.h"
#include "pluginmanager.h"
#include "squirrel/squirrel.h"
#include "plugins.h"
#include "masterserver/masterserver.h"
#include "tier1/convar.h"
#include "server/serverpresence.h"
#include <optional>
#include <regex>
#include "util/version.h"
#include "pluginbackend.h"
#include "util/wininfo.h"
#include "core/sourceinterface.h"
#include "logging/logging.h"
#include "dedicated/dedicated.h"
PluginManager* g_pPluginManager;
void freeLibrary(HMODULE hLib)
bool isValidSquirrelIdentifier(std::string s)
{
if (!FreeLibrary(hLib))
if (!s.size())
return false; // identifiers can't be empty
if (s[0] <= 57)
return false; // identifier can't start with a number
for (char& c : s)
{
spdlog::error("There was an error while trying to free library");
}
}
EXPORT void PLUGIN_LOG(LogMsg* msg)
{
spdlog::source_loc src {};
src.filename = msg->source.file;
src.funcname = msg->source.func;
src.line = msg->source.line;
auto&& logger = g_pPluginManager->m_vLoadedPlugins[msg->pluginHandle].logger;
logger->log(src, (spdlog::level::level_enum)msg->level, msg->msg);
}
EXPORT void* CreateObject(ObjectType type)
{
switch (type)
{
case ObjectType::CONVAR:
return (void*)new ConVar;
case ObjectType::CONCOMMANDS:
return (void*)new ConCommand;
default:
return NULL;
}
}
std::optional<Plugin> PluginManager::LoadPlugin(fs::path path, PluginInitFuncs* funcs, PluginNorthstarData* data)
{
Plugin plugin {};
std::string pathstring = path.string();
std::wstring wpath = path.wstring();
LPCWSTR wpptr = wpath.c_str();
HMODULE datafile = LoadLibraryExW(wpptr, 0, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); // Load the DLL as a data file
if (datafile == NULL)
{
NS::log::PLUGINSYS->info("Failed to load library '{}': ", std::system_category().message(GetLastError()));
return std::nullopt;
}
HRSRC manifestResource = FindResourceW(datafile, MAKEINTRESOURCEW(IDR_RCDATA1), RT_RCDATA);
if (manifestResource == NULL)
{
NS::log::PLUGINSYS->info("Could not find manifest for library '{}'", pathstring);
freeLibrary(datafile);
return std::nullopt;
}
HGLOBAL myResourceData = LoadResource(datafile, manifestResource);
if (myResourceData == NULL)
{
NS::log::PLUGINSYS->error("Failed to load manifest from library '{}'", pathstring);
freeLibrary(datafile);
return std::nullopt;
}
int manifestSize = SizeofResource(datafile, manifestResource);
std::string manifest = std::string((const char*)LockResource(myResourceData), 0, manifestSize);
freeLibrary(datafile);
rapidjson_document manifestJSON;
manifestJSON.Parse(manifest.c_str());
if (manifestJSON.HasParseError())
{
NS::log::PLUGINSYS->error("Manifest for '{}' was invalid", pathstring);
return std::nullopt;
}
if (!manifestJSON.HasMember("name"))
{
NS::log::PLUGINSYS->error("'{}' is missing a name in its manifest", pathstring);
return std::nullopt;
}
if (!manifestJSON.HasMember("displayname"))
{
NS::log::PLUGINSYS->error("'{}' is missing a displayname in its manifest", pathstring);
return std::nullopt;
}
if (!manifestJSON.HasMember("description"))
{
NS::log::PLUGINSYS->error("'{}' is missing a description in its manifest", pathstring);
return std::nullopt;
}
if (!manifestJSON.HasMember("api_version"))
{
NS::log::PLUGINSYS->error("'{}' is missing a api_version in its manifest", pathstring);
return std::nullopt;
}
if (!manifestJSON.HasMember("version"))
{
NS::log::PLUGINSYS->error("'{}' is missing a version in its manifest", pathstring);
return std::nullopt;
}
if (!manifestJSON.HasMember("run_on_server"))
{
NS::log::PLUGINSYS->error("'{}' is missing 'run_on_server' in its manifest", pathstring);
return std::nullopt;
}
if (!manifestJSON.HasMember("run_on_client"))
{
NS::log::PLUGINSYS->error("'{}' is missing 'run_on_client' in its manifest", pathstring);
return std::nullopt;
}
auto test = manifestJSON["api_version"].GetString();
if (strcmp(manifestJSON["api_version"].GetString(), std::to_string(ABI_VERSION).c_str()))
{
NS::log::PLUGINSYS->error(
"'{}' has an incompatible API version number in its manifest. Current ABI version is '{}'", pathstring, ABI_VERSION);
return std::nullopt;
}
// Passed all checks, going to actually load it now
HMODULE pluginLib =
LoadLibraryExW(wpptr, 0, LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); // Load the DLL with lib folders
if (pluginLib == NULL)
{
NS::log::PLUGINSYS->info("Failed to load library '{}': ", std::system_category().message(GetLastError()));
return std::nullopt;
}
plugin.init = (PLUGIN_INIT_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT");
if (plugin.init == NULL)
{
NS::log::PLUGINSYS->info("Library '{}' has no function 'PLUGIN_INIT'", pathstring);
return std::nullopt;
}
NS::log::PLUGINSYS->info("Succesfully loaded {}", pathstring);
plugin.name = manifestJSON["name"].GetString();
plugin.displayName = manifestJSON["displayname"].GetString();
plugin.description = manifestJSON["description"].GetString();
plugin.api_version = manifestJSON["api_version"].GetString();
plugin.version = manifestJSON["version"].GetString();
plugin.run_on_client = manifestJSON["run_on_client"].GetBool();
plugin.run_on_server = manifestJSON["run_on_server"].GetBool();
if (!plugin.run_on_server && IsDedicatedServer())
return std::nullopt;
if (manifestJSON.HasMember("dependencyName"))
{
plugin.dependencyName = manifestJSON["dependencyName"].GetString();
}
else
{
plugin.dependencyName = plugin.name;
}
if (std::find_if(
plugin.dependencyName.begin(),
plugin.dependencyName.end(),
[&](char c) -> bool { return !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'); }) !=
plugin.dependencyName.end())
{
NS::log::PLUGINSYS->warn("Dependency string \"{}\" in {} is not valid a squirrel constant!", plugin.dependencyName, plugin.name);
}
plugin.init_sqvm_client = (PLUGIN_INIT_SQVM_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT_SQVM_CLIENT");
plugin.init_sqvm_server = (PLUGIN_INIT_SQVM_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT_SQVM_SERVER");
plugin.inform_sqvm_created = (PLUGIN_INFORM_SQVM_CREATED_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_SQVM_CREATED");
plugin.inform_sqvm_destroyed = (PLUGIN_INFORM_SQVM_DESTROYED_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_SQVM_DESTROYED");
plugin.inform_dll_load = (PLUGIN_INFORM_DLL_LOAD_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_DLL_LOAD");
plugin.run_frame = (PLUGIN_RUNFRAME)GetProcAddress(pluginLib, "PLUGIN_RUNFRAME");
plugin.handle = (int)m_vLoadedPlugins.size();
plugin.logger = std::make_shared<ColoredLogger>(plugin.displayName.c_str(), NS::Colors::PLUGIN);
RegisterLogger(plugin.logger);
NS::log::PLUGINSYS->info("Loading plugin {} version {}", plugin.displayName, plugin.version);
m_vLoadedPlugins.push_back(plugin);
plugin.init(funcs, data);
return plugin;
}
inline void FindPlugins(fs::path pluginPath, std::vector<fs::path>& paths)
{
// ensure dirs exist
if (!fs::exists(pluginPath) || !fs::is_directory(pluginPath))
{
return;
}
for (const fs::directory_entry& entry : fs::directory_iterator(pluginPath))
{
if (fs::is_regular_file(entry) && entry.path().extension() == ".dll")
paths.emplace_back(entry.path());
}
}
bool PluginManager::LoadPlugins()
{
if (strstr(GetCommandLineA(), "-noplugins") != NULL)
{
NS::log::PLUGINSYS->warn("-noplugins detected; skipping loading plugins");
// only allow underscores, 0-9, A-Z and a-z
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
continue;
return false;
}
fs::create_directories(GetThunderstoreModFolderPath());
std::vector<fs::path> paths;
pluginPath = GetNorthstarPrefix() + "\\plugins";
PluginNorthstarData data {};
std::string ns_version {version};
PluginInitFuncs funcs {};
funcs.logger = PLUGIN_LOG;
funcs.relayInviteFunc = nullptr;
funcs.createObject = CreateObject;
data.version = ns_version.c_str();
data.northstarModule = g_NorthstarModule;
fs::path libPath = fs::absolute(pluginPath + "\\lib");
if (fs::exists(libPath) && fs::is_directory(libPath))
AddDllDirectory(libPath.wstring().c_str());
FindPlugins(pluginPath, paths);
// Special case for Thunderstore mods dir
std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath());
// Set up regex for `AUTHOR-MOD-VERSION` pattern
std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))");
for (fs::directory_entry dir : thunderstoreModsDir)
{
fs::path pluginsDir = dir.path() / "plugins";
// Use regex to match `AUTHOR-MOD-VERSION` pattern
if (!std::regex_match(dir.path().string(), pattern))
{
spdlog::warn("The following directory did not match 'AUTHOR-MOD-VERSION': {}", dir.path().string());
continue; // skip loading package that doesn't match
}
fs::path libDir = fs::absolute(pluginsDir / "lib");
if (fs::exists(libDir) && fs::is_directory(libDir))
AddDllDirectory(libDir.wstring().c_str());
FindPlugins(pluginsDir, paths);
}
if (paths.empty())
{
NS::log::PLUGINSYS->warn("Could not find any plugins. Skipped loading plugins");
return false;
}
for (fs::path path : paths)
{
if (LoadPlugin(path, &funcs, &data))
data.pluginHandle += 1;
}
return true;
}
void PluginManager::InformSQVMLoad(ScriptContext context, SquirrelFunctions* s)
Plugin::Plugin(std::string path) : m_location(path)
{
for (auto plugin : m_vLoadedPlugins)
HMODULE pluginModule = GetModuleHandleA(path.c_str());
if (pluginModule)
{
if (context == ScriptContext::CLIENT && plugin.init_sqvm_client != NULL)
{
plugin.init_sqvm_client(s);
}
else if (context == ScriptContext::SERVER && plugin.init_sqvm_server != NULL)
{
plugin.init_sqvm_server(s);
}
// plugins may refuse to get unloaded for any reason so we need to prevent them getting loaded twice when reloading plugins
NS::log::PLUGINSYS->warn("Plugin has already been loaded");
return;
}
m_handle = LoadLibraryExA(path.c_str(), 0, LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
NS::log::PLUGINSYS->info("loaded plugin handle {}", static_cast<void*>(m_handle));
if (!m_handle)
{
NS::log::PLUGINSYS->error("Failed to load plugin '{}' (Error: {})", path, GetLastError());
return;
}
m_initData = {.pluginHandle = m_handle};
CreateInterfaceFn CreatePluginInterface = (CreateInterfaceFn)GetProcAddress(m_handle, "CreateInterface");
if (!CreatePluginInterface)
{
NS::log::PLUGINSYS->error("Plugin at '{}' does not expose CreateInterface()", path);
return;
}
m_pluginId = (IPluginId*)CreatePluginInterface(PLUGIN_ID_VERSION, 0);
if (!m_pluginId)
{
NS::log::PLUGINSYS->error("Could not load IPluginId interface of plugin at '{}'", path);
return;
}
const char* name = m_pluginId->GetString(PluginString::NAME);
const char* logName = m_pluginId->GetString(PluginString::LOG_NAME);
const char* dependencyName = m_pluginId->GetString(PluginString::DEPENDENCY_NAME);
int64_t context = m_pluginId->GetField(PluginField::CONTEXT);
m_runOnServer = context & PluginContext::DEDICATED;
m_runOnClient = context & PluginContext::CLIENT;
m_name = std::string(name);
m_logName = std::string(logName);
m_dependencyName = std::string(dependencyName);
if (!name)
{
NS::log::PLUGINSYS->error("Could not load name of plugin at '{}'", path);
return;
}
if (!logName)
{
NS::log::PLUGINSYS->error("Could not load logName of plugin {}", name);
return;
}
if (!dependencyName)
{
NS::log::PLUGINSYS->error("Could not load dependencyName of plugin {}", name);
return;
}
if (!isValidSquirrelIdentifier(m_dependencyName))
{
NS::log::PLUGINSYS->error("Dependency name \"{}\" of plugin {} is not valid", dependencyName, name);
return;
}
m_callbacks = (IPluginCallbacks*)CreatePluginInterface("PluginCallbacks001", 0);
if (!m_callbacks)
{
NS::log::PLUGINSYS->error("Could not create callback interface of plugin {}", name);
return;
}
m_logger = std::make_shared<ColoredLogger>(m_logName, NS::Colors::PLUGIN);
RegisterLogger(m_logger);
if (IsDedicatedServer() && !m_runOnServer)
{
NS::log::PLUGINSYS->info("Unloading {} because it's not supposed to run on dedicated servers", m_name);
return;
}
if (!IsDedicatedServer() && !m_runOnClient)
{
NS::log::PLUGINSYS->info("Unloading {} because it's only supposed to run on dedicated servers", m_name);
return;
}
m_valid = true;
}
void PluginManager::InformSQVMCreated(ScriptContext context, CSquirrelVM* sqvm)
bool Plugin::Unload() const
{
for (auto plugin : m_vLoadedPlugins)
if (!m_handle)
return true;
if (IsValid())
{
if (plugin.inform_sqvm_created != NULL)
{
plugin.inform_sqvm_created(context, sqvm);
}
bool unloaded = m_callbacks->Unload();
if (!unloaded)
return false;
}
if (!FreeLibrary(m_handle))
{
NS::log::PLUGINSYS->error("Failed to unload plugin at '{}'", m_location);
return false;
}
g_pPluginManager->RemovePlugin(m_handle);
return true;
}
void PluginManager::InformSQVMDestroyed(ScriptContext context)
void Plugin::Reload() const
{
for (auto plugin : m_vLoadedPlugins)
{
if (plugin.inform_sqvm_destroyed != NULL)
{
plugin.inform_sqvm_destroyed(context);
}
}
bool unloaded = Unload();
if (!unloaded)
return;
g_pPluginManager->LoadPlugin(fs::path(m_location), true);
}
void PluginManager::InformDLLLoad(const char* dll, void* data, void* dllPtr)
void Plugin::Log(spdlog::level::level_enum level, char* msg) const
{
for (auto plugin : m_vLoadedPlugins)
{
if (plugin.inform_dll_load != NULL)
{
plugin.inform_dll_load(dll, (PluginEngineData*)data, dllPtr);
}
}
m_logger->log(level, msg);
}
void PluginManager::RunFrame()
bool Plugin::IsValid() const
{
for (auto plugin : m_vLoadedPlugins)
{
if (plugin.run_frame != NULL)
{
plugin.run_frame();
}
}
return m_valid && m_pCreateInterface && m_pluginId && m_callbacks && m_handle;
}
const std::string& Plugin::GetName() const
{
return m_name;
}
const std::string& Plugin::GetLogName() const
{
return m_logName;
}
const std::string& Plugin::GetDependencyName() const
{
return m_dependencyName;
}
const std::string& Plugin::GetLocation() const
{
return m_location;
}
bool Plugin::ShouldRunOnServer() const
{
return m_runOnServer;
}
bool Plugin::ShouldRunOnClient() const
{
return m_runOnClient;
}
void* Plugin::CreateInterface(const char* name, int* status) const
{
return m_pCreateInterface(name, status);
}
void Plugin::Init(bool reloaded) const
{
m_callbacks->Init(g_NorthstarModule, &m_initData, reloaded);
}
void Plugin::Finalize() const
{
m_callbacks->Finalize();
}
void Plugin::OnSqvmCreated(CSquirrelVM* sqvm) const
{
m_callbacks->OnSqvmCreated(sqvm);
}
void Plugin::OnSqvmDestroying(CSquirrelVM* sqvm) const
{
NS::log::PLUGINSYS->info("destroying sqvm {}", sqvm->vmContext);
m_callbacks->OnSqvmDestroying(sqvm);
}
void Plugin::OnLibraryLoaded(HMODULE module, const char* name) const
{
m_callbacks->OnLibraryLoaded(module, name);
}
void Plugin::RunFrame() const
{
m_callbacks->RunFrame();
}

View File

@ -1,59 +1,50 @@
#pragma once
#include "plugin_abi.h"
const int IDR_RCDATA1 = 101;
#include "core/sourceinterface.h"
#include "plugins/interfaces/interface.h"
#include "plugins/interfaces/IPluginId.h"
#include "plugins/interfaces/IPluginCallbacks.h"
class Plugin
{
public:
std::string name;
std::string displayName;
std::string dependencyName;
std::string description;
std::string api_version;
std::string version;
// For now this is just implemented as the index into the plugins array
// Maybe a bit shit but it works
int handle;
std::shared_ptr<ColoredLogger> logger;
bool run_on_client = false;
bool run_on_server = false;
public:
PLUGIN_INIT_TYPE init;
PLUGIN_INIT_SQVM_TYPE init_sqvm_client;
PLUGIN_INIT_SQVM_TYPE init_sqvm_server;
PLUGIN_INFORM_SQVM_CREATED_TYPE inform_sqvm_created;
PLUGIN_INFORM_SQVM_DESTROYED_TYPE inform_sqvm_destroyed;
PLUGIN_INFORM_DLL_LOAD_TYPE inform_dll_load;
PLUGIN_RUNFRAME run_frame;
};
class PluginManager
{
public:
std::vector<Plugin> m_vLoadedPlugins;
public:
bool LoadPlugins();
std::optional<Plugin> LoadPlugin(fs::path path, PluginInitFuncs* funcs, PluginNorthstarData* data);
void InformSQVMLoad(ScriptContext context, SquirrelFunctions* s);
void InformSQVMCreated(ScriptContext context, CSquirrelVM* sqvm);
void InformSQVMDestroyed(ScriptContext context);
void InformDLLLoad(const char* dll, void* data, void* dllPtr);
void RunFrame();
private:
std::string pluginPath;
};
CreateInterfaceFn m_pCreateInterface;
IPluginId* m_pluginId = 0;
IPluginCallbacks* m_callbacks = 0;
extern PluginManager* g_pPluginManager;
std::shared_ptr<ColoredLogger> m_logger;
bool m_valid = false;
std::string m_name;
std::string m_logName;
std::string m_dependencyName;
std::string m_location; // path of the dll
bool m_runOnServer;
bool m_runOnClient;
public:
HMODULE m_handle;
PluginNorthstarData m_initData;
Plugin(std::string path);
bool Unload() const;
void Reload() const;
// sys
void Log(spdlog::level::level_enum level, char* msg) const;
// callbacks
bool IsValid() const;
const std::string& GetName() const;
const std::string& GetLogName() const;
const std::string& GetDependencyName() const;
const std::string& GetLocation() const;
bool ShouldRunOnServer() const;
bool ShouldRunOnClient() const;
void* CreateInterface(const char* pName, int* pStatus) const;
void Init(bool reloaded) const;
void Finalize() const;
void OnSqvmCreated(CSquirrelVM* sqvm) const;
void OnSqvmDestroying(CSquirrelVM* sqvm) const;
void OnLibraryLoaded(HMODULE module, const char* name) const;
void RunFrame() const;
};

View File

@ -59,42 +59,6 @@ struct CSVData
std::unordered_map<std::string, CSVData> 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))
{

View File

@ -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 <fstream>
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<short>(v1.x);
x2 = static_cast<short>(v2.x);
y1 = static_cast<short>(v1.y);
y2 = static_cast<short>(v2.y);
z1 = static_cast<short>(v1.z);
z2 = static_cast<short>(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<int64_t> 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");
}

View File

@ -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;

View File

@ -0,0 +1,6 @@
#include "ai_navmesh.h"
ON_DLL_LOAD("server.dll", ServerAiNavMesh, (CModule module))
{
g_pNavMesh = module.Offset(0x105F5D0).RCast<dtNavMesh**>();
}

View File

@ -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;

View File

@ -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)
{

View File

@ -6,8 +6,8 @@
#include "dedicated/dedicated.h"
#include "engine/r2engine.h"
#include "core/tier0.h"
#include "plugins/plugin_abi.h"
#include "plugins/plugins.h"
#include "plugins/pluginmanager.h"
#include "ns_version.h"
#include "core/vanilla.h"
@ -157,79 +157,6 @@ const char* SQTypeNameFromID(int type)
return "";
}
template <ScriptContext context> void SquirrelManager<context>::GenerateSquirrelFunctionsStruct(SquirrelFunctions* s)
{
s->RegisterSquirrelFunc = RegisterSquirrelFunc;
s->__sq_defconst = __sq_defconst;
s->__sq_compilebuffer = __sq_compilebuffer;
s->__sq_call = __sq_call;
s->__sq_raiseerror = __sq_raiseerror;
s->__sq_compilefile = __sq_compilefile;
s->__sq_newarray = __sq_newarray;
s->__sq_arrayappend = __sq_arrayappend;
s->__sq_newtable = __sq_newtable;
s->__sq_newslot = __sq_newslot;
s->__sq_pushroottable = __sq_pushroottable;
s->__sq_pushstring = __sq_pushstring;
s->__sq_pushinteger = __sq_pushinteger;
s->__sq_pushfloat = __sq_pushfloat;
s->__sq_pushbool = __sq_pushbool;
s->__sq_pushasset = __sq_pushasset;
s->__sq_pushvector = __sq_pushvector;
s->__sq_pushobject = __sq_pushobject;
s->__sq_getstring = __sq_getstring;
s->__sq_getinteger = __sq_getinteger;
s->__sq_getfloat = __sq_getfloat;
s->__sq_getbool = __sq_getbool;
s->__sq_get = __sq_get;
s->__sq_getasset = __sq_getasset;
s->__sq_getuserdata = __sq_getuserdata;
s->__sq_getvector = __sq_getvector;
s->__sq_getthisentity = __sq_getthisentity;
s->__sq_getobject = __sq_getobject;
s->__sq_stackinfos = __sq_stackinfos;
s->__sq_createuserdata = __sq_createuserdata;
s->__sq_setuserdatatypeid = __sq_setuserdatatypeid;
s->__sq_getfunction = __sq_getfunction;
s->__sq_schedule_call_external = AsyncCall_External;
s->__sq_getentityfrominstance = __sq_getentityfrominstance;
s->__sq_GetEntityConstant_CBaseEntity = __sq_GetEntityConstant_CBaseEntity;
s->__sq_pushnewstructinstance = __sq_pushnewstructinstance;
s->__sq_sealstructslot = __sq_sealstructslot;
}
// Allows for generating squirrelmessages from plugins.
void AsyncCall_External(ScriptContext context, const char* func_name, SquirrelMessage_External_Pop function, void* userdata)
{
SquirrelMessage message {};
message.functionName = func_name;
message.isExternal = true;
message.externalFunc = function;
message.userdata = userdata;
switch (context)
{
case ScriptContext::CLIENT:
g_pSquirrel<ScriptContext::CLIENT>->messageBuffer->push(message);
break;
case ScriptContext::SERVER:
g_pSquirrel<ScriptContext::SERVER>->messageBuffer->push(message);
break;
case ScriptContext::UI:
g_pSquirrel<ScriptContext::UI>->messageBuffer->push(message);
break;
}
}
// needed to define implementations for squirrelmanager outside of squirrel.h without compiler errors
template class SquirrelManager<ScriptContext::SERVER>;
template class SquirrelManager<ScriptContext::CLIENT>;
@ -264,11 +191,11 @@ template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquir
defconst(m_pSQVM, pair.first.c_str(), bWasFound);
}
auto loadedPlugins = &g_pPluginManager->m_vLoadedPlugins;
std::vector<Plugin> loadedPlugins = g_pPluginManager->GetLoadedPlugins();
for (const auto& pluginName : g_pModManager->m_PluginDependencyConstants)
{
auto f = [&](Plugin plugin) -> bool { return plugin.dependencyName == pluginName; };
defconst(m_pSQVM, pluginName.c_str(), std::find_if(loadedPlugins->begin(), loadedPlugins->end(), f) != loadedPlugins->end());
auto f = [&](Plugin plugin) -> bool { return plugin.GetDependencyName() == pluginName; };
defconst(m_pSQVM, pluginName.c_str(), std::find_if(loadedPlugins.begin(), loadedPlugins.end(), f) != loadedPlugins.end());
}
defconst(m_pSQVM, "MAX_FOLDER_SIZE", GetMaxSaveFolderSize() / 1024);
@ -284,7 +211,7 @@ template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquir
defconst(m_pSQVM, "VANILLA", g_pVanillaCompatibility->GetVanillaCompatibility());
g_pSquirrel<context>->messageBuffer = new SquirrelMessageBuffer();
g_pPluginManager->InformSQVMCreated(context, newSqvm);
g_pPluginManager->InformSqvmCreated(newSqvm);
}
template <ScriptContext context> void SquirrelManager<context>::VMDestroyed()
@ -312,7 +239,7 @@ template <ScriptContext context> void SquirrelManager<context>::VMDestroyed()
}
}
g_pPluginManager->InformSQVMDestroyed(context);
g_pPluginManager->InformSqvmDestroying(m_pSQVM);
// Discard the previous vm and delete the message buffer.
m_pSQVM = nullptr;
@ -661,17 +588,10 @@ template <ScriptContext context> void SquirrelManager<context>::ProcessMessageBu
size_t argsAmount = message.args.size();
if (message.isExternal && message.externalFunc != NULL)
for (auto& v : message.args)
{
argsAmount = message.externalFunc(m_pSQVM->sqvm, message.userdata);
}
else
{
for (auto& v : message.args)
{
// Execute lambda to push arg to stack
v();
}
// Execute lambda to push arg to stack
v();
}
_call(m_pSQVM->sqvm, (SQInteger)argsAmount);
@ -839,10 +759,6 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction = module.Offset(0x6CB0).RCast<sq_getfunctionType>();
g_pSquirrel<ScriptContext::UI>->__sq_getfunction = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction;
SquirrelFunctions s = {};
g_pSquirrel<ScriptContext::CLIENT>->GenerateSquirrelFunctionsStruct(&s);
g_pPluginManager->InformSQVMLoad(ScriptContext::CLIENT, &s);
}
ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
@ -921,10 +837,6 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
FCVAR_GAMEDLL | FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS | FCVAR_CHEAT);
StubUnsafeSQFuncs<ScriptContext::SERVER>();
SquirrelFunctions s = {};
g_pSquirrel<ScriptContext::SERVER>->GenerateSquirrelFunctionsStruct(&s);
g_pPluginManager->InformSQVMLoad(ScriptContext::SERVER, &s);
}
void InitialiseSquirrelManagers()

View File

@ -3,7 +3,6 @@
#include "squirrelclasstypes.h"
#include "squirrelautobind.h"
#include "core/math/vector.h"
#include "plugins/plugin_abi.h"
#include "mods/modmanager.h"
/*
@ -51,8 +50,6 @@ const char* GetContextName_Short(ScriptContext context);
eSQReturnType SQReturnTypeFromString(const char* pReturnType);
const char* SQTypeNameFromID(const int iTypeId);
void AsyncCall_External(ScriptContext context, const char* func_name, SquirrelMessage_External_Pop function, void* userdata);
ScriptContext ScriptContextFromString(std::string string);
namespace NS::log
@ -417,7 +414,6 @@ public:
SQRESULT setupfunc(const SQChar* funcname);
void AddFuncOverride(std::string name, SQFunction func);
void ProcessMessageBuffer();
void GenerateSquirrelFunctionsStruct(SquirrelFunctions* s);
};
template <ScriptContext context> SquirrelManager<context>* g_pSquirrel;
@ -483,7 +479,7 @@ requires is_iterable<T>
inline VoidFunction SQMessageBufferPushArg(T& arg) {
FunctionVector localv = {};
localv.push_back([]{g_pSquirrel<context>->newarray(g_pSquirrel<context>->m_pSQVM->sqvm, 0);});
for (const auto& item : arg) {
localv.push_back(SQMessageBufferPushArg<context>(item));
localv.push_back([]{g_pSquirrel<context>->arrayappend(g_pSquirrel<context>->m_pSQVM->sqvm, -2);});
@ -497,7 +493,7 @@ requires is_map<T>
inline VoidFunction SQMessageBufferPushArg(T& map) {
FunctionVector localv = {};
localv.push_back([]{g_pSquirrel<context>->newtable(g_pSquirrel<context>->m_pSQVM->sqvm);});
for (const auto& item : map) {
localv.push_back(SQMessageBufferPushArg<context>(item.first));
localv.push_back(SQMessageBufferPushArg<context>(item.second));

View File

@ -116,18 +116,11 @@ concept is_iterable = requires(std::ranges::range_value_t<T> x)
// clang-format on
typedef int (*SquirrelMessage_External_Pop)(HSquirrelVM* sqvm, void* userdata);
typedef void (*sq_schedule_call_externalType)(
ScriptContext context, const char* funcname, SquirrelMessage_External_Pop function, void* userdata);
class SquirrelMessage
{
public:
std::string functionName;
FunctionVector args;
bool isExternal = false;
void* userdata = NULL;
SquirrelMessage_External_Pop externalFunc = NULL;
};
class SquirrelMessageBuffer
@ -243,6 +236,3 @@ typedef SQRESULT (*sq_pushnewstructinstanceType)(HSquirrelVM* sqvm, int fieldCou
typedef SQRESULT (*sq_sealstructslotType)(HSquirrelVM* sqvm, int slotIndex);
#pragma endregion
// These "external" versions of the types are for plugins
typedef int64_t (*RegisterSquirrelFuncType_External)(ScriptContext context, SQFuncRegistration* funcReg, char unknown);

View File

@ -2,9 +2,6 @@
#include "shared/misccommands.h"
#include "engine/r2engine.h"
#include "plugins/pluginbackend.h"
#include "plugins/plugin_abi.h"
#include <iostream>
//-----------------------------------------------------------------------------
@ -152,7 +149,4 @@ ON_DLL_LOAD("engine.dll", ConCommand, (CModule module))
{
ConCommandConstructor = module.Offset(0x415F60).RCast<ConCommandConstructorType>();
AddMiscConCommands();
g_pPluginCommunicationhandler->m_sEngineData.ConCommandConstructor =
reinterpret_cast<PluginConCommandConstructorType>(ConCommandConstructor);
}

View File

@ -3,9 +3,6 @@
#include "convar.h"
#include "core/sourceinterface.h"
#include "plugins/pluginbackend.h"
#include "plugins/plugin_abi.h"
#include <float.h>
typedef void (*ConVarRegisterType)(
@ -40,12 +37,6 @@ ON_DLL_LOAD("engine.dll", ConVar, (CModule module))
g_pCVarInterface = new SourceInterface<CCvar>("vstdlib.dll", "VEngineCvar007");
g_pCVar = *g_pCVarInterface;
g_pPluginCommunicationhandler->m_sEngineData.conVarMalloc = reinterpret_cast<PluginConVarMallocType>(conVarMalloc);
g_pPluginCommunicationhandler->m_sEngineData.conVarRegister = reinterpret_cast<PluginConVarRegisterType>(conVarRegister);
g_pPluginCommunicationhandler->m_sEngineData.ConVar_Vtable = reinterpret_cast<void*>(g_pConVar_Vtable);
g_pPluginCommunicationhandler->m_sEngineData.IConVar_Vtable = reinterpret_cast<void*>(g_pIConVar_Vtable);
g_pPluginCommunicationhandler->m_sEngineData.g_pCVar = reinterpret_cast<void*>(g_pCVar);
}
//-----------------------------------------------------------------------------

View File

@ -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;
};

View File

@ -2,22 +2,24 @@
void RemoveAsciiControlSequences(char* str, bool allow_color_codes);
class ScopeGuard
template <typename T> class ScopeGuard
{
public:
auto operator=(ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&) = delete;
ScopeGuard(std::function<void()> callback) : m_callback(callback) {}
ScopeGuard(T callback) : m_callback(callback) {}
~ScopeGuard()
{
m_callback();
if (!m_dismissed)
m_callback();
}
void Dismiss()
{
m_callback = [] {};
m_dismissed = true;
}
private:
std::function<void()> m_callback;
bool m_dismissed = false;
T m_callback;
};