aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKittenPopo <Pokeberry123@gmail.com>2022-03-26 16:20:09 -0700
committerGitHub <noreply@github.com>2022-03-26 23:20:09 +0000
commit379cbc8bc251307777a14b901e5617e834398485 (patch)
tree4722db4a3c301462ca009f5bb2976508b652677e
parent2c74033440212308eb0fe0d8b3135ffc5cd0422e (diff)
downloadNorthstarLauncher-379cbc8bc251307777a14b901e5617e834398485.tar.gz
NorthstarLauncher-379cbc8bc251307777a14b901e5617e834398485.zip
Major exploit fixes and some minor bug fixes/improvements (#117)v1.6.0-rc3
* Added main exploit fixes * Fixed typo in sigscanning.cpp * Fully implemented * Added proper includes for new files * Update README.md * typo * spare me my sanity (fixed ridiculous code) * Added rest of KittenMemUtils * Rename KittenMemUtils * Removed all messy memory edits, implemented NSMem instead * Update NorthstarDedicatedTest.vcxproj * [1] Move everything from securitypatches to ExploitFixes * [2] Move everything from securitypatches to ExploitFixes * Fixed module offsets in stack trace * Fixed UTF8 Parsing (Multiplayer Crash) * Implemented UT8 fix * Update NorthstarDedicatedTest.vcxproj * Update hookutils.cpp * Small fixes * all my homies hate clang-format * Temporarily restore README.md * Added string hash macro * Added convenient vtfunc macro * Made lil ConCommand creation macro * Fixed multiple NET_SetConVar exploits * Quick fixerino * Fix convar struct (and other things) * Revive clang-format (but good, i think) * Update .clang-format * Reformatted code to meet .clang-format requirements * Minor formatting fixes * Fixed Northstar "crashing" when console is closed * Update .clang-format
-rw-r--r--NorthstarDedicatedTest/ExploitFixes.cpp177
-rw-r--r--NorthstarDedicatedTest/ExploitFixes.h5
-rw-r--r--NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h102
-rw-r--r--NorthstarDedicatedTest/NSMem.h95
-rw-r--r--NorthstarDedicatedTest/concommand.h5
-rw-r--r--NorthstarDedicatedTest/convar.cpp13
-rw-r--r--NorthstarDedicatedTest/convar.h5
-rw-r--r--NorthstarDedicatedTest/cvar.cpp40
-rw-r--r--NorthstarDedicatedTest/cvar.h13
-rw-r--r--NorthstarDedicatedTest/dllmain.cpp2
-rw-r--r--NorthstarDedicatedTest/hooks.cpp2
-rw-r--r--NorthstarDedicatedTest/keyvalues.cpp2
-rw-r--r--NorthstarDedicatedTest/languagehooks.cpp4
-rw-r--r--NorthstarDedicatedTest/latencyflex.cpp4
-rw-r--r--NorthstarDedicatedTest/logging.cpp66
-rw-r--r--NorthstarDedicatedTest/misccommands.cpp73
-rw-r--r--NorthstarDedicatedTest/miscserverfixes.cpp5
-rw-r--r--NorthstarDedicatedTest/modmanager.cpp15
-rw-r--r--NorthstarDedicatedTest/pch.h14
-rw-r--r--loader_launcher_proxy/Memory.cpp104
20 files changed, 436 insertions, 310 deletions
diff --git a/NorthstarDedicatedTest/ExploitFixes.cpp b/NorthstarDedicatedTest/ExploitFixes.cpp
index d36b4175..36bd36f4 100644
--- a/NorthstarDedicatedTest/ExploitFixes.cpp
+++ b/NorthstarDedicatedTest/ExploitFixes.cpp
@@ -3,39 +3,36 @@
#include "ExploitFixes.h"
#include "ExploitFixes_UTF8Parser.h"
#include "NSMem.h"
+#include "cvar.h"
// Make sure 3 or less floats are valid
-bool ValidateFloats(float a, float b = 0, float c = 0) {
- return !isnan(a) && !isnan(b) && !isnan(c);
-}
+bool ValidateFloats(float a, float b = 0, float c = 0) { return !isnan(a) && !isnan(b) && !isnan(c); }
-struct Vector {
+struct Vector
+{
float x, y, z;
Vector(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {}
- bool IsValid() {
- return ValidateFloats(x, y, z);
- }
+ bool IsValid() { return ValidateFloats(x, y, z); }
};
-struct Angle {
+struct Angle
+{
float pitch, yaw, roll;
Angle(float pitch = 0, float yaw = 0, float roll = 0) : pitch(pitch), yaw(yaw), roll(roll) {}
- bool IsInvalid() {
+ bool IsInvalid()
+ {
if (!ValidateFloats(pitch, yaw, roll))
return false;
- return
- (pitch > 90 || pitch < -90)
- || (yaw > 180 || yaw < -180)
- || (roll > 180 || roll < -180);
+ return (pitch > 90 || pitch < -90) || (yaw > 180 || yaw < -180) || (roll > 180 || roll < -180);
}
};
-#define BLOCK_NETMSG_FUNC(name, pattern) \
+#define BLOCK_NETMSG_FUNC(name, pattern) \
KHOOK(name, ("engine.dll", pattern), bool, __fastcall, (void* thisptr, void* buffer)) { return false; }
// Servers can literally request a screenshot from any client, yeah no
@@ -45,9 +42,83 @@ BLOCK_NETMSG_FUNC(CLC_Screenshot_ReadFromBuffer, "48 89 5C 24 ? 48 89 6C 24 ? 48
// This is unused ingame and a big exploit vector
BLOCK_NETMSG_FUNC(Base_CmdKeyValues_ReadFromBuffer, "40 55 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 5D 70");
+KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10"), bool, __fastcall, (void* pMsg))
+{
+
+ constexpr int ENTRY_STR_LEN = 260;
+ struct SetConVarEntry
+ {
+ char name[ENTRY_STR_LEN];
+ char val[ENTRY_STR_LEN];
+ };
+
+ struct NET_SetConVar
+ {
+ void* vtable;
+ void* unk1;
+ void* unk2;
+ void* m_pMessageHandler;
+ SetConVarEntry* m_ConVars; // convar entry array
+ void* unk5; // these 2 unks are just vector capacity or whatever
+ void* unk6;
+ int m_ConVars_count; // amount of cvar entries in array (this will not be out of bounds)
+ };
+
+ auto msg = (NET_SetConVar*)pMsg;
+
+ constexpr int SETCONVAR_SANITY_AMOUNT_LIMIT = 20;
+ if (msg->m_ConVars_count < 1 || msg->m_ConVars_count > SETCONVAR_SANITY_AMOUNT_LIMIT)
+ return false; // Nope
+
+ for (int i = 0; i < msg->m_ConVars_count; i++)
+ {
+ auto entry = msg->m_ConVars + i;
+
+ // Safety check for memory access
+ if (NSMem::IsMemoryReadable(entry, sizeof(*entry)))
+ {
+
+ // Find null terminators
+ bool nameValid = false, valValid = false;
+ for (int i = 0; i < ENTRY_STR_LEN; i++)
+ {
+ if (!entry->name[i])
+ nameValid = true;
+ if (!entry->val[i])
+ valValid = true;
+ }
+
+ if (!nameValid || !valValid)
+ return false; // Missing null terminators
+
+ auto realVar = g_pCVar->FindVar(entry->name);
+
+ if (!realVar)
+ // Not an actual cvar, no thanks
+ return false;
+
+ // Force name to match case
+ memcpy(entry->name, realVar->m_ConCommandBase.m_pszName, strlen(realVar->m_ConCommandBase.m_pszName) + 1);
+
+ if (!ConVar::IsFlagSet(realVar, FCVAR_USERINFO) || ConVar::IsFlagSet(realVar, FCVAR_CHEAT))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false; // Not risking that one, they all gotta be readable
+ }
+ }
+
+ return oCClient_ProcessSetConVar(msg);
+}
+
// Purpose: prevent invalid user CMDs
-KHOOK(CClient_ProcessUsercmds, ("engine.dll", "40 55 56 48 83 EC 58"), bool, __fastcall, (void* thisptr, void* pMsg)) {
- struct __declspec(align(8)) CLC_Move {
+KHOOK(CClient_ProcessUsercmds, ("engine.dll", "40 55 56 48 83 EC 58"), bool, __fastcall, (void* thisptr, void* pMsg))
+{
+ struct CLC_Move
+ {
BYTE gap0[24];
void* m_pMessageHandler;
int m_nBackupCommands;
@@ -72,7 +143,8 @@ KHOOK(CClient_ProcessUsercmds, ("engine.dll", "40 55 56 48 83 EC 58"), bool, __f
return oCClient_ProcessUsercmds(thisptr, pMsg);
}
-KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) {
+KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from))
+{
// Let normal usercmd read happen first, it's safe
oReadUsercmd(buf, pCmd_move, pCmd_from);
@@ -105,20 +177,18 @@ KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall
};
auto cmd = (SV_CUserCmd*)pCmd_move;
- if (
- cmd->worldViewAngles.IsInvalid() ||
- cmd->localViewAngles.IsInvalid() ||
- cmd->attackangles.IsInvalid() ||
- cmd->cameraAngles.IsInvalid()) {
+ if (cmd->worldViewAngles.IsInvalid() || cmd->localViewAngles.IsInvalid() || cmd->attackangles.IsInvalid() ||
+ cmd->cameraAngles.IsInvalid())
+ {
goto INVALID_CMD;
}
if (cmd->frameTime <= 0 || cmd->tick_count == 0 || cmd->command_time <= 0)
goto INVALID_CMD; // No simulation of bogus-timed cmds
- if (!cmd->move.IsValid() || // Prevent player freeze (and even server crash) exploit
+ if (!cmd->move.IsValid() || // Prevent player freeze (and even server crash) exploit
!cmd->cameraPos.IsValid()) // IIRC this can crash spectating clients or anyone watching replays
- goto INVALID_CMD;
+ goto INVALID_CMD;
if (!ValidateFloats(cmd->cameraPos.x, cmd->cameraPos.y, cmd->cameraPos.z))
goto INVALID_CMD; // IIRC this can crash spectating clients or anyone watching replays
@@ -128,7 +198,7 @@ INVALID_CMD:
// Fix any gameplay-affecting cmd properties
// NOTE: Currently tickcount/frametime is set to 0, this ~shouldn't~ cause any problems
- cmd->worldViewAngles = cmd->localViewAngles = cmd->attackangles = cmd->cameraAngles = Angle(0,0,0);
+ cmd->worldViewAngles = cmd->localViewAngles = cmd->attackangles = cmd->cameraAngles = Angle(0, 0, 0);
cmd->tick_count = cmd->frameTime = 0;
cmd->move = cmd->cameraPos = Vector(0, 0, 0);
cmd->buttons = 0;
@@ -139,19 +209,24 @@ INVALID_CMD:
// this is HORRIBLE for security, because it means servers can run arbitrary concommands on clients
// especially since we have script commands this could theoretically be awful
#include "gameutils.h"
-KHOOK(IsValveMod, ("engine.dll", "48 83 EC 28 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? E8 ? ? ? ? 85 C0 74 63"), bool, __fastcall, ()) {
+KHOOK(IsValveMod, ("engine.dll", "48 83 EC 28 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? E8 ? ? ? ? 85 C0 74 63"), bool, __fastcall, ())
+{
return !CommandLine()->CheckParm("-norestrictservercommands");
}
// Fix respawn's crappy UTF8 parser so it doesn't crash -_-
// This also means you can launch multiplayer with "communities_enabled 1" and not crash, you're welcome
-KHOOK(CrashFunc_ParseUTF8, ("engine.dll", "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 54 41 55 41 56 41 57 48 83 EC 20 8B 1A"),
- bool, __fastcall, (INT64* a1, DWORD* a2, char* strData)) {
+KHOOK(
+ CrashFunc_ParseUTF8, ("engine.dll", "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 54 41 55 41 56 41 57 48 83 EC 20 8B 1A"), bool,
+ __fastcall, (INT64 * a1, DWORD* a2, char* strData))
+{
static void* targetRetAddr = NSMem::PatternScan("engine.dll", "84 C0 75 2C 49 8B 16");
- if (_ReturnAddress() == targetRetAddr) {
- if (!ExploitFixes_UTF8Parser::CheckValid(a1, a2, strData)) {
+ if (_ReturnAddress() == targetRetAddr)
+ {
+ if (!ExploitFixes_UTF8Parser::CheckValid(a1, a2, strData))
+ {
spdlog::warn("ParseUTF8 Hook: Ignoring potentially-crashing utf8 string");
return false;
}
@@ -162,8 +237,10 @@ KHOOK(CrashFunc_ParseUTF8, ("engine.dll", "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74
//////////////////////////////////////////////////
-void DoBytePatches() {
+void DoBytePatches()
+{
uintptr_t engineBase = (uintptr_t)GetModuleHandleA("engine.dll");
+ uintptr_t serverBase = (uintptr_t)GetModuleHandleA("server.dll");
// patches to make commands run from client/ui script still work
// note: this is likely preventable in a nicer way? test prolly
@@ -176,17 +253,49 @@ void DoBytePatches() {
int val = *(int*)addr | FCVAR_SERVER_CAN_EXECUTE;
NSMem::BytePatch(addr, (BYTE*)&val, sizeof(int));
}
+
+ { // Dumb ANTITAMPER patches (they negatively impact performance and security)
+
+ constexpr const char* ANTITAMPER_EXPORTS[] = {
+ "ANTITAMPER_SPOTCHECK_CODEMARKER",
+ "ANTITAMPER_TESTVALUE_CODEMARKER",
+ "ANTITAMPER_TRIGGER_CODEMARKER",
+ };
+
+ // Prevent thesefrom actually doing anything
+ for (auto exportName : ANTITAMPER_EXPORTS)
+ {
+
+ auto address = (uintptr_t)GetProcAddress(NULL, exportName);
+ if (!address)
+ {
+ spdlog::warn("Failed to find AntiTamper function export \"{}\"", exportName);
+ }
+ else
+ {
+
+ // Just return, none of them have any args or are userpurge
+ NSMem::BytePatch(address, {0xC3});
+
+ spdlog::info("Patched AntiTamper function export \"{}\"", exportName);
+ }
+ }
+ }
}
-void ExploitFixes::LoadCallback(HMODULE unused) {
+void ExploitFixes::LoadCallback(HMODULE unused)
+{
spdlog::info("ExploitFixes::LoadCallback ...");
spdlog::info("\tByte patching...");
DoBytePatches();
- if (KHook::InitAllHooks()) {
+ if (KHook::InitAllHooks())
+ {
spdlog::info("\tInitialized " + std::to_string(KHook::_allHooks.size()) + " exploit-patch hooks.");
- } else {
+ }
+ else
+ {
spdlog::critical("\tFAILED to initialize all exploit patches.");
// Force exit?
diff --git a/NorthstarDedicatedTest/ExploitFixes.h b/NorthstarDedicatedTest/ExploitFixes.h
index 49928640..7a407a3d 100644
--- a/NorthstarDedicatedTest/ExploitFixes.h
+++ b/NorthstarDedicatedTest/ExploitFixes.h
@@ -3,6 +3,7 @@
#pragma once
#include "pch.h"
-namespace ExploitFixes {
+namespace ExploitFixes
+{
void LoadCallback(HMODULE unused);
-}
+} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h b/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h
index 6b767a0c..b06d442b 100644
--- a/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h
+++ b/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h
@@ -8,55 +8,55 @@ namespace ExploitFixes_UTF8Parser
{
bool __fastcall CheckValid(INT64* a1, DWORD* a2, char* strData)
{
- static auto sub_F1320 = (INT64(__fastcall*)(DWORD a1, char* a2)) NSMem::PatternScan("engine.dll", "83 F9 7F 77 08 88 0A");
+ static auto sub_F1320 = (INT64(__fastcall*)(DWORD a1, char* a2))NSMem::PatternScan("engine.dll", "83 F9 7F 77 08 88 0A");
- DWORD v3; // eax
- char* v4; // rbx
- char v5; // si
- char* _strData; // rdi
- char* v7; // rbp
- char v11; // al
- DWORD v12; // er9
- DWORD v13; // ecx
- DWORD v14; // edx
- DWORD v15; // er8
- int v16; // eax
- DWORD v17; // er9
- int v18; // eax
- DWORD v19; // er9
- DWORD v20; // ecx
- int v21; // eax
- int v22; // er9
- DWORD v23; // edx
- int v24; // eax
- int v25; // er9
- DWORD v26; // er9
- DWORD v27; // er10
- DWORD v28; // ecx
- DWORD v29; // edx
- DWORD v30; // er8
- int v31; // eax
- DWORD v32; // er10
- int v33; // eax
- DWORD v34; // er10
- DWORD v35; // ecx
- int v36; // eax
- int v37; // er10
- DWORD v38; // edx
- int v39; // eax
- int v40; // er10
- DWORD v41; // er10
- INT64 v43; // r8
- INT64 v44; // rdx
- INT64 v45; // rcx
- INT64 v46; // rax
- INT64 v47; // rax
- char v48; // al
- INT64 v49; // r8
- INT64 v50; // rdx
- INT64 v51; // rcx
- INT64 v52; // rax
- INT64 v53; // rax
+ DWORD v3; // eax
+ char* v4; // rbx
+ char v5; // si
+ char* _strData; // rdi
+ char* v7; // rbp
+ char v11; // al
+ DWORD v12; // er9
+ DWORD v13; // ecx
+ DWORD v14; // edx
+ DWORD v15; // er8
+ int v16; // eax
+ DWORD v17; // er9
+ int v18; // eax
+ DWORD v19; // er9
+ DWORD v20; // ecx
+ int v21; // eax
+ int v22; // er9
+ DWORD v23; // edx
+ int v24; // eax
+ int v25; // er9
+ DWORD v26; // er9
+ DWORD v27; // er10
+ DWORD v28; // ecx
+ DWORD v29; // edx
+ DWORD v30; // er8
+ int v31; // eax
+ DWORD v32; // er10
+ int v33; // eax
+ DWORD v34; // er10
+ DWORD v35; // ecx
+ int v36; // eax
+ int v37; // er10
+ DWORD v38; // edx
+ int v39; // eax
+ int v40; // er10
+ DWORD v41; // er10
+ INT64 v43; // r8
+ INT64 v44; // rdx
+ INT64 v45; // rcx
+ INT64 v46; // rax
+ INT64 v47; // rax
+ char v48; // al
+ INT64 v49; // r8
+ INT64 v50; // rdx
+ INT64 v51; // rcx
+ INT64 v52; // rax
+ INT64 v53; // rax
v3 = a2[2];
v4 = (char*)(a1[1] + *a2);
@@ -71,7 +71,7 @@ namespace ExploitFixes_UTF8Parser
{
while (1)
{
-
+
if (!NSMem::IsMemoryReadable(v4, 1))
return false; // INVALID
@@ -128,7 +128,7 @@ namespace ExploitFixes_UTF8Parser
return true;
if (*v4 != 92 || v4[1] != 117)
return true;
-
+
v27 = v4[2] | 0x20;
v28 = v4[3] | 0x20;
v29 = v4[4] | 0x20;
@@ -172,4 +172,4 @@ namespace ExploitFixes_UTF8Parser
LABEL_48:
return true;
}
-} \ No newline at end of file
+} // namespace ExploitFixes_UTF8Parser \ No newline at end of file
diff --git a/NorthstarDedicatedTest/NSMem.h b/NorthstarDedicatedTest/NSMem.h
index a6ddf033..a5bbd42f 100644
--- a/NorthstarDedicatedTest/NSMem.h
+++ b/NorthstarDedicatedTest/NSMem.h
@@ -4,8 +4,10 @@
// KittenPopo's memory stuff, made for northstar (because I really can't handle working with northstar's original memory stuff tbh)
#pragma region Pattern Scanning
-namespace NSMem {
- inline void* PatternScan(void* module, const int* pattern, int patternSize, int offset) {
+namespace NSMem
+{
+ inline void* PatternScan(void* module, const int* pattern, int patternSize, int offset)
+ {
if (!module)
return NULL;
@@ -16,16 +18,20 @@ namespace NSMem {
auto scanBytes = (BYTE*)module;
- for (auto i = 0; i < sizeOfImage - patternSize; ++i) {
+ for (auto i = 0; i < sizeOfImage - patternSize; ++i)
+ {
bool found = true;
- for (auto j = 0; j < patternSize; ++j) {
- if (scanBytes[i + j] != pattern[j] && pattern[j] != -1) {
+ for (auto j = 0; j < patternSize; ++j)
+ {
+ if (scanBytes[i + j] != pattern[j] && pattern[j] != -1)
+ {
found = false;
break;
}
}
- if (found) {
+ if (found)
+ {
uintptr_t addressInt = (uintptr_t)(&scanBytes[i]) + offset;
return (uint8_t*)addressInt;
}
@@ -34,31 +40,42 @@ namespace NSMem {
return nullptr;
}
- inline void* PatternScan(const char* moduleName, const char* pattern, int offset = 0) {
+ inline void* PatternScan(const char* moduleName, const char* pattern, int offset = 0)
+ {
std::vector<int> patternNums;
bool lastChar = 0;
int size = strlen(pattern);
- for (int i = 0; i < size; i++) {
+ for (int i = 0; i < size; i++)
+ {
char c = pattern[i];
// If this is a space character, ignore it
if (c == ' ' || c == '\t')
continue;
- if (c == '?') {
+ if (c == '?')
+ {
// Add a wildcard (-1)
patternNums.push_back(-1);
- } else if (i < size - 1) {
+ }
+ else if (i < size - 1)
+ {
BYTE result = 0;
- for (int j = 0; j < 2; j++) {
+ for (int j = 0; j < 2; j++)
+ {
int val = 0;
char c = (pattern + i + j)[0];
- if (c >= 'a') {
+ if (c >= 'a')
+ {
val = c - 'a' + 0xA;
- } else if (c >= 'A') {
+ }
+ else if (c >= 'A')
+ {
val = c - 'A' + 0xA;
- } else {
+ }
+ else
+ {
val = c - '0';
}
@@ -72,24 +89,28 @@ namespace NSMem {
return PatternScan(GetModuleHandleA(moduleName), &patternNums[0], patternNums.size(), offset);
}
- inline void BytePatch(uintptr_t address, const BYTE* vals, int size) {
+ inline void BytePatch(uintptr_t address, const BYTE* vals, int size)
+ {
WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, vals, size, NULL);
}
- inline void BytePatch(uintptr_t address, std::initializer_list<BYTE> vals) {
+ inline void BytePatch(uintptr_t address, std::initializer_list<BYTE> vals)
+ {
std::vector<BYTE> bytes = vals;
if (!bytes.empty())
BytePatch(address, &bytes[0], bytes.size());
}
- inline void NOP(uintptr_t address, int size) {
+ inline void NOP(uintptr_t address, int size)
+ {
BYTE* buf = (BYTE*)malloc(size);
memset(buf, 0x90, size);
BytePatch(address, buf, size);
free(buf);
}
- inline bool IsMemoryReadable(void* ptr, size_t size) {
+ inline bool IsMemoryReadable(void* ptr, size_t size)
+ {
BYTE* buffer = (BYTE*)malloc(size);
size_t numWritten = 0;
@@ -98,18 +119,21 @@ namespace NSMem {
return numWritten == size;
}
-}
+} // namespace NSMem
#pragma region KHOOK
-struct KHookPatternInfo {
- const char* moduleName, *pattern;
+struct KHookPatternInfo
+{
+ const char *moduleName, *pattern;
int offset = 0;
- KHookPatternInfo(const char* moduleName, const char* pattern, int offset = 0)
- : moduleName(moduleName), pattern(pattern), offset(offset) {}
+ KHookPatternInfo(const char* moduleName, const char* pattern, int offset = 0) : moduleName(moduleName), pattern(pattern), offset(offset)
+ {
+ }
};
-struct KHook {
+struct KHook
+{
KHookPatternInfo targetFunc;
void* targetFuncAddr;
void* hookFunc;
@@ -117,13 +141,15 @@ struct KHook {
static inline std::vector<KHook*> _allHooks;
- KHook(KHookPatternInfo targetFunc, void* hookFunc, void** original) : targetFunc(targetFunc) {
+ KHook(KHookPatternInfo targetFunc, void* hookFunc, void** original) : targetFunc(targetFunc)
+ {
this->hookFunc = hookFunc;
this->original = original;
_allHooks.push_back(this);
}
- bool Setup() {
+ bool Setup()
+ {
targetFuncAddr = NSMem::PatternScan(targetFunc.moduleName, targetFunc.pattern, targetFunc.offset);
if (!targetFuncAddr)
return false;
@@ -132,11 +158,16 @@ struct KHook {
}
// Returns true if succeeded
- static bool InitAllHooks() {
- for (KHook* hook : _allHooks) {
- if (hook->Setup()) {
+ static bool InitAllHooks()
+ {
+ for (KHook* hook : _allHooks)
+ {
+ if (hook->Setup())
+ {
spdlog::info("KHook hooked at {}", hook->targetFuncAddr);
- } else {
+ }
+ else
+ {
return false;
}
}
@@ -144,9 +175,9 @@ struct KHook {
return MH_EnableHook(MH_ALL_HOOKS) == MH_OK;
}
};
-#define KHOOK(name, funcPatternInfo, returnType, convention, args) \
+#define KHOOK(name, funcPatternInfo, returnType, convention, args) \
returnType convention hk##name args; \
auto o##name = (returnType(convention*) args)0; \
- KHook k##name = KHook(KHookPatternInfo funcPatternInfo, &hk##name, (void**)&o##name); \
+ KHook k##name = KHook(KHookPatternInfo funcPatternInfo, &hk##name, (void**)&o##name); \
returnType convention hk##name args
#pragma endregion \ No newline at end of file
diff --git a/NorthstarDedicatedTest/concommand.h b/NorthstarDedicatedTest/concommand.h
index b1342163..eb812c32 100644
--- a/NorthstarDedicatedTest/concommand.h
+++ b/NorthstarDedicatedTest/concommand.h
@@ -104,4 +104,7 @@ class ConCommand : public ConCommandBase
}; // Size: 0x0060
void RegisterConCommand(const char* name, void (*callback)(const CCommand&), const char* helpString, int flags);
-void InitialiseConCommands(HMODULE baseAddress); \ No newline at end of file
+void InitialiseConCommands(HMODULE baseAddress);
+
+#define MAKE_CONCMD(name, helpStr, flags, fn) \
+RegisterConCommand(name, fn, helpStr, flags); \ No newline at end of file
diff --git a/NorthstarDedicatedTest/convar.cpp b/NorthstarDedicatedTest/convar.cpp
index 7023e13c..3f2728d0 100644
--- a/NorthstarDedicatedTest/convar.cpp
+++ b/NorthstarDedicatedTest/convar.cpp
@@ -6,10 +6,6 @@
#include "gameutils.h"
#include "sourceinterface.h"
-// should this be in modmanager?
-std::unordered_map<std::string, ConVar*>
- g_CustomConvars; // this is used in modloading code to determine whether we've registered a mod convar already
-
typedef void (*ConVarRegisterType)(
ConVar* pConVar, const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString, bool bMin, float fMin,
bool bMax, float fMax, void* pCallback);
@@ -40,6 +36,8 @@ void InitialiseConVars(HMODULE baseAddress)
HookEnabler hook;
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x417FA0, &ConVar::IsFlagSet, reinterpret_cast<LPVOID*>(&CvarIsFlagSet));
+
+
}
//-----------------------------------------------------------------------------
@@ -54,8 +52,6 @@ ConVar::ConVar(const char* pszName, const char* pszDefaultValue, int nFlags, con
conVarMalloc(&this->m_pMalloc, 0, 0); // Allocate new memory for ConVar.
conVarRegister(this, pszName, pszDefaultValue, nFlags, pszHelpString, 0, 0, 0, 0, 0);
-
- g_CustomConvars.emplace(pszName, this);
}
//-----------------------------------------------------------------------------
@@ -72,8 +68,6 @@ ConVar::ConVar(
conVarMalloc(&this->m_pMalloc, 0, 0); // Allocate new memory for ConVar.
conVarRegister(this, pszName, pszDefaultValue, nFlags, pszHelpString, bMin, fMin, bMax, fMax, pCallback);
-
- g_CustomConvars.emplace(pszName, this);
}
//-----------------------------------------------------------------------------
@@ -82,10 +76,7 @@ ConVar::ConVar(
ConVar::~ConVar(void)
{
if (m_Value.m_pszString)
- {
delete[] m_Value.m_pszString;
- m_Value.m_pszString = NULL;
- }
}
//-----------------------------------------------------------------------------
diff --git a/NorthstarDedicatedTest/convar.h b/NorthstarDedicatedTest/convar.h
index a00c7f0e..fc5b0eb9 100644
--- a/NorthstarDedicatedTest/convar.h
+++ b/NorthstarDedicatedTest/convar.h
@@ -129,7 +129,7 @@ class ConVar
};
ConCommandBase m_ConCommandBase{}; // 0x0000
- ConVar* m_pParent{}; // 0x0040
+ const char* defaultVal{}; // 0x0040
CVValue_t m_Value{}; // 0x0048
bool m_bHasMin{}; // 0x005C
float m_fMinVal{}; // 0x0060
@@ -139,5 +139,4 @@ class ConVar
char m_pPad80[10]{}; // 0x0080
}; // Size: 0x0080
-void InitialiseConVars(HMODULE baseAddress);
-extern std::unordered_map<std::string, ConVar*> g_CustomConvars; \ No newline at end of file
+void InitialiseConVars(HMODULE baseAddress); \ No newline at end of file
diff --git a/NorthstarDedicatedTest/cvar.cpp b/NorthstarDedicatedTest/cvar.cpp
index 7f8c35d7..23d767fd 100644
--- a/NorthstarDedicatedTest/cvar.cpp
+++ b/NorthstarDedicatedTest/cvar.cpp
@@ -4,45 +4,6 @@
#include "concommand.h"
//-----------------------------------------------------------------------------
-// Purpose: finds base commands.
-// Input : *pszCommandName -
-//-----------------------------------------------------------------------------
-ConCommandBase* CCvar::FindCommandBase(const char* pszCommandName)
-{
- static int index = 14;
- return CallVFunc<ConCommandBase*>(index, this, pszCommandName);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: finds ConVars.
-// Input : *pszVarName -
-//-----------------------------------------------------------------------------
-ConVar* CCvar::FindVar(const char* pszVarName)
-{
- static int index = 16;
- return CallVFunc<ConVar*>(index, this, pszVarName);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: finds ConCommands.
-// Input : *pszCommandName -
-//-----------------------------------------------------------------------------
-ConCommand* CCvar::FindCommand(const char* pszCommandName)
-{
- static int index = 18;
- return CallVFunc<ConCommand*>(index, this, pszCommandName);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: iterates over all ConVars
-//-----------------------------------------------------------------------------
-CCVarIteratorInternal* CCvar::FactoryInternalIterator()
-{
- static int index = 41;
- return CallVFunc<CCVarIteratorInternal*>(index, this);
-}
-
-//-----------------------------------------------------------------------------
// Purpose: returns all ConVars
//-----------------------------------------------------------------------------
std::unordered_map<std::string, ConCommandBase*> CCvar::DumpToMap()
@@ -61,5 +22,6 @@ std::unordered_map<std::string, ConCommandBase*> CCvar::DumpToMap()
return allConVars;
}
+
SourceInterface<CCvar>* g_pCVarInterface;
CCvar* g_pCVar; \ No newline at end of file
diff --git a/NorthstarDedicatedTest/cvar.h b/NorthstarDedicatedTest/cvar.h
index 43b13bfb..e254af4e 100644
--- a/NorthstarDedicatedTest/cvar.h
+++ b/NorthstarDedicatedTest/cvar.h
@@ -1,5 +1,6 @@
#pragma once
#include "convar.h"
+#include "pch.h"
//-----------------------------------------------------------------------------
// Forward declarations
@@ -25,11 +26,13 @@ class CCVarIteratorInternal // Fully reversed table, just look at the virtual fu
//-----------------------------------------------------------------------------
class CCvar
{
- public:
- ConCommandBase* FindCommandBase(const char* pszCommandName);
- ConVar* FindVar(const char* pszVarName);
- ConCommand* FindCommand(const char* pszCommandName);
- CCVarIteratorInternal* FactoryInternalIterator();
+ public:
+
+ M_VMETHOD(ConCommandBase*, FindCommandBase, 14, (const char* pszCommandName), (this, pszCommandName));
+ M_VMETHOD(ConVar*, FindVar, 16, (const char* pszVarName), (this, pszVarName));
+ M_VMETHOD(ConCommand*, FindCommand, 18, (const char* pszCommandName), (this, pszCommandName));
+ M_VMETHOD(CCVarIteratorInternal*, FactoryInternalIterator, 41, (), (this));
+
std::unordered_map<std::string, ConCommandBase*> DumpToMap();
};
diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp
index 524235cf..22b9a673 100644
--- a/NorthstarDedicatedTest/dllmain.cpp
+++ b/NorthstarDedicatedTest/dllmain.cpp
@@ -197,7 +197,9 @@ bool InitialiseNorthstar()
parseConfigurables();
InitialiseVersion();
+ // Fix some users' failure to connect to respawn datacenters
SetEnvironmentVariableA("OPENSSL_ia32cap", "~0x200000200000000");
+
curl_global_init_mem(CURL_GLOBAL_DEFAULT, _malloc_base, _free_base, _realloc_base, _strdup_base, _calloc_base);
InitialiseLogging();
diff --git a/NorthstarDedicatedTest/hooks.cpp b/NorthstarDedicatedTest/hooks.cpp
index 4c403872..055fe4df 100644
--- a/NorthstarDedicatedTest/hooks.cpp
+++ b/NorthstarDedicatedTest/hooks.cpp
@@ -36,7 +36,7 @@ LoadLibraryWType LoadLibraryWOriginal;
void InstallInitialHooks()
{
if (MH_Initialize() != MH_OK)
- spdlog::error("MH_Initialize failed");
+ spdlog::error("MH_Initialize (minhook initialization) failed");
HookEnabler hook;
ENABLER_CREATEHOOK(hook, &GetCommandLineA, &GetCommandLineAHook, reinterpret_cast<LPVOID*>(&GetCommandLineAOriginal));
diff --git a/NorthstarDedicatedTest/keyvalues.cpp b/NorthstarDedicatedTest/keyvalues.cpp
index 08c85dc1..2063be62 100644
--- a/NorthstarDedicatedTest/keyvalues.cpp
+++ b/NorthstarDedicatedTest/keyvalues.cpp
@@ -59,7 +59,7 @@ void ModManager::TryBuildKeyValues(const char* filename)
if (!m_loadedMods[i].Enabled)
continue;
- size_t fileHash = std::hash<std::string>{}(normalisedPath);
+ size_t fileHash = STR_HASH(normalisedPath);
auto modKv = m_loadedMods[i].KeyValues.find(fileHash);
if (modKv != m_loadedMods[i].KeyValues.end())
{
diff --git a/NorthstarDedicatedTest/languagehooks.cpp b/NorthstarDedicatedTest/languagehooks.cpp
index 95638ef2..8d60ca22 100644
--- a/NorthstarDedicatedTest/languagehooks.cpp
+++ b/NorthstarDedicatedTest/languagehooks.cpp
@@ -15,7 +15,7 @@ GetGameLanguageType GetGameLanguageOriginal;
bool CheckLangAudioExists(char* lang)
{
- std::string path{"r2\\sound\\general_"};
+ std::string path {"r2\\sound\\general_"};
path += lang;
path += ".mstr";
return fs::exists(path);
@@ -31,7 +31,7 @@ std::vector<std::string> file_list(fs::path dir, std::regex ext_pattern)
using iterator = fs::directory_iterator;
const iterator end;
- for (iterator iter{dir}; iter != end; ++iter)
+ for (iterator iter {dir}; iter != end; ++iter)
{
const std::string filename = iter->path().filename().string();
std::smatch matches;
diff --git a/NorthstarDedicatedTest/latencyflex.cpp b/NorthstarDedicatedTest/latencyflex.cpp
index 623ac06b..4ac1c760 100644
--- a/NorthstarDedicatedTest/latencyflex.cpp
+++ b/NorthstarDedicatedTest/latencyflex.cpp
@@ -8,9 +8,9 @@ OnRenderStartType OnRenderStart;
ConVar* Cvar_r_latencyflex;
-HMODULE m_lfxModule{};
+HMODULE m_lfxModule {};
typedef void (*PFN_winelfx_WaitAndBeginFrame)();
-PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame{};
+PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame {};
void OnRenderStartHook()
{
diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp
index 9b2a0925..98e80592 100644
--- a/NorthstarDedicatedTest/logging.cpp
+++ b/NorthstarDedicatedTest/logging.cpp
@@ -52,74 +52,77 @@ long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo)
return EXCEPTION_CONTINUE_SEARCH;
std::stringstream exceptionCause;
+ exceptionCause << "Cause: ";
switch (exceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_IN_PAGE_ERROR:
{
- exceptionCause << "Cause: Access Violation" << std::endl;
+ exceptionCause << "Access Violation" << std::endl;
auto exceptionInfo0 = exceptionInfo->ExceptionRecord->ExceptionInformation[0];
auto exceptionInfo1 = exceptionInfo->ExceptionRecord->ExceptionInformation[1];
if (!exceptionInfo0)
- exceptionCause << "Attempted to read from: 0x" << std::setw(8) << std::setfill('0') << std::hex << exceptionInfo1;
+ exceptionCause << "Attempted to read from: 0x" << (void*)exceptionInfo1;
else if (exceptionInfo0 == 1)
- exceptionCause << "Attempted to write to: 0x" << std::setw(8) << std::setfill('0') << std::hex << exceptionInfo1;
+ exceptionCause << "Attempted to write to: 0x" << (void*)exceptionInfo1;
else if (exceptionInfo0 == 8)
- exceptionCause << "Data Execution Prevention (DEP) at: 0x" << std::setw(8) << std::setfill('0') << std::hex
+ exceptionCause << "Data Execution Prevention (DEP) at: 0x" << (void*)std::hex
<< exceptionInfo1;
else
- exceptionCause << "Unknown access violation at: 0x" << std::setw(8) << std::setfill('0') << std::hex << exceptionInfo1;
+ exceptionCause << "Unknown access violation at: 0x" << (void*)exceptionInfo1;
break;
}
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
- exceptionCause << "Cause: Array bounds exceeded";
+ exceptionCause << "Array bounds exceeded";
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
- exceptionCause << "Cause: Datatype misalignment";
+ exceptionCause << "Datatype misalignment";
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
- exceptionCause << "Cause: Denormal operand";
+ exceptionCause << "Denormal operand";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ exceptionCause << "Divide by zero (float)";
+ break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
- exceptionCause << "Cause: Divide by zero";
+ exceptionCause << "Divide by zero (int)";
break;
case EXCEPTION_FLT_INEXACT_RESULT:
- exceptionCause << "Cause: Inexact result";
+ exceptionCause << "Inexact result";
break;
case EXCEPTION_FLT_INVALID_OPERATION:
- exceptionCause << "Cause: invalid operation";
+ exceptionCause << "Invalid operation";
break;
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_INT_OVERFLOW:
- exceptionCause << "Cause: Numeric overflow";
+ exceptionCause << "Numeric overflow";
break;
case EXCEPTION_FLT_UNDERFLOW:
- exceptionCause << "Cause: Numeric underflow";
+ exceptionCause << "Numeric underflow";
break;
case EXCEPTION_FLT_STACK_CHECK:
- exceptionCause << "Cause: Stack check";
+ exceptionCause << "Stack check";
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
- exceptionCause << "Cause: Illegal instruction";
+ exceptionCause << "Illegal instruction";
break;
case EXCEPTION_INVALID_DISPOSITION:
- exceptionCause << "Cause: Invalid disposition";
+ exceptionCause << "Invalid disposition";
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
- exceptionCause << "Cause: Noncontinuable exception";
+ exceptionCause << "Noncontinuable exception";
break;
case EXCEPTION_PRIV_INSTRUCTION:
- exceptionCause << "Cause: Priv instruction";
+ exceptionCause << "Priviledged instruction";
break;
case EXCEPTION_STACK_OVERFLOW:
- exceptionCause << "Cause: Stack overflow";
+ exceptionCause << "Stack overflow";
break;
default:
- exceptionCause << "Cause: Unknown";
+ exceptionCause << "Unknown";
break;
}
@@ -206,14 +209,33 @@ long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo)
return EXCEPTION_EXECUTE_HANDLER;
}
+HANDLE hExceptionFilter;
+
+BOOL WINAPI ConsoleHandlerRoutine(DWORD eventCode)
+{
+ switch (eventCode)
+ {
+ case CTRL_CLOSE_EVENT:
+ // User closed console, shut everything down
+ spdlog::info("Exiting due to console close...");
+ RemoveVectoredExceptionHandler(hExceptionFilter);
+ exit(EXIT_SUCCESS);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
void InitialiseLogging()
{
- AddVectoredExceptionHandler(TRUE, ExceptionFilter);
+ hExceptionFilter = AddVectoredExceptionHandler(TRUE, ExceptionFilter);
AllocConsole();
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v");
+
+ SetConsoleCtrlHandler(ConsoleHandlerRoutine, true);
}
ConVar* Cvar_spewlog_enable;
@@ -221,10 +243,12 @@ ConVar* Cvar_spewlog_enable;
enum SpewType_t
{
SPEW_MESSAGE = 0,
+
SPEW_WARNING,
SPEW_ASSERT,
SPEW_ERROR,
SPEW_LOG,
+
SPEW_TYPE_COUNT
};
diff --git a/NorthstarDedicatedTest/misccommands.cpp b/NorthstarDedicatedTest/misccommands.cpp
index 4a556fad..dca87947 100644
--- a/NorthstarDedicatedTest/misccommands.cpp
+++ b/NorthstarDedicatedTest/misccommands.cpp
@@ -6,50 +6,43 @@
#include "serverauthentication.h"
#include "squirrel.h"
-void ForceLoadMapCommand(const CCommand& arg)
+void AddMiscConCommands()
{
- if (arg.ArgC() < 2)
- return;
-
- g_pHostState->m_iNextState = HS_NEW_GAME;
- strncpy(g_pHostState->m_levelName, arg.Arg(1), sizeof(g_pHostState->m_levelName));
-}
+ MAKE_CONCMD(
+ "force_newgame", "forces a map load through directly setting g_pHostState->m_iNextState to HS_NEW_GAME", FCVAR_NONE,
+ [](const CCommand& arg) {
+ if (arg.ArgC() < 2)
+ return;
-void SelfAuthAndLeaveToLobbyCommand(const CCommand& arg)
-{
- // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect
+ g_pHostState->m_iNextState = HS_NEW_GAME;
+ strncpy(g_pHostState->m_levelName, arg.Arg(1), sizeof(g_pHostState->m_levelName));
+ });
- g_MasterServerManager->m_bNewgameAfterSelfAuth = true;
- g_MasterServerManager->AuthenticateWithOwnServer(g_LocalPlayerUserID, g_MasterServerManager->m_ownClientAuthToken);
-}
+ MAKE_CONCMD(
+ "ns_start_reauth_and_leave_to_lobby", "called by the server, used to reauth and return the player to lobby when leaving a game",
+ FCVAR_SERVER_CAN_EXECUTE, [](const CCommand& arg) {
+ // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect
+ g_MasterServerManager->m_bNewgameAfterSelfAuth = true;
+ g_MasterServerManager->AuthenticateWithOwnServer(g_LocalPlayerUserID, g_MasterServerManager->m_ownClientAuthToken);
+ });
-void EndSelfAuthAndLeaveToLobbyCommand(const CCommand& arg)
-{
- Cbuf_AddText(
- Cbuf_GetCurrentPlayer(), fmt::format("serverfilter {}", g_ServerAuthenticationManager->m_authData.begin()->first).c_str(),
- cmd_source_t::kCommandSrcCode);
- Cbuf_Execute();
+ // this is a concommand because we make a deferred call to it from another thread
+ MAKE_CONCMD("ns_end_reauth_and_leave_to_lobby", "", FCVAR_NONE, [](const CCommand& arg) {
+ Cbuf_AddText(
+ Cbuf_GetCurrentPlayer(), fmt::format("serverfilter {}", g_ServerAuthenticationManager->m_authData.begin()->first).c_str(),
+ cmd_source_t::kCommandSrcCode);
+ Cbuf_Execute();
- // weird way of checking, but check if client script vm is initialised, mainly just to allow players to cancel this
- if (g_ClientSquirrelManager->sqvm)
- {
- g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame = true;
- // this won't set playlist correctly on remote clients, don't think they can set playlist until they've left which sorta fucks
- // things should maybe set this in HostState_NewGame?
- SetCurrentPlaylist("tdm");
- strcpy(g_pHostState->m_levelName, "mp_lobby");
- g_pHostState->m_iNextState = HS_NEW_GAME;
- }
-}
+ // weird way of checking, but check if client script vm is initialised, mainly just to allow players to cancel this
+ if (g_ClientSquirrelManager->sqvm)
+ {
+ g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame = true;
-void AddMiscConCommands()
-{
- RegisterConCommand(
- "force_newgame", ForceLoadMapCommand, "forces a map load through directly setting g_pHostState->m_iNextState to HS_NEW_GAME",
- FCVAR_NONE);
- RegisterConCommand(
- "ns_start_reauth_and_leave_to_lobby", SelfAuthAndLeaveToLobbyCommand,
- "called by the server, used to reauth and return the player to lobby when leaving a game", FCVAR_SERVER_CAN_EXECUTE);
- // this is a concommand because we make a deferred call to it from another thread
- RegisterConCommand("ns_end_reauth_and_leave_to_lobby", EndSelfAuthAndLeaveToLobbyCommand, "", FCVAR_NONE);
+ // this won't set playlist correctly on remote clients, don't think they can set playlist until they've left which sorta fucks
+ // things should maybe set this in HostState_NewGame?
+ SetCurrentPlaylist("tdm");
+ strcpy(g_pHostState->m_levelName, "mp_lobby");
+ g_pHostState->m_iNextState = HS_NEW_GAME;
+ }
+ });
} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/miscserverfixes.cpp b/NorthstarDedicatedTest/miscserverfixes.cpp
index 0b9a12db..7d977d64 100644
--- a/NorthstarDedicatedTest/miscserverfixes.cpp
+++ b/NorthstarDedicatedTest/miscserverfixes.cpp
@@ -25,7 +25,4 @@ void InitialiseMiscServerFixes(HMODULE baseAddress)
}
}
-void InitialiseMiscEngineServerFixes(HMODULE baseAddress)
-{
-
-} \ No newline at end of file
+void InitialiseMiscEngineServerFixes(HMODULE baseAddress) {} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp
index 79529c99..99bb5fdb 100644
--- a/NorthstarDedicatedTest/modmanager.cpp
+++ b/NorthstarDedicatedTest/modmanager.cpp
@@ -191,9 +191,10 @@ ModManager::ModManager()
{
// precaculated string hashes
// note: use backslashes for these, since we use lexically_normal for file paths which uses them
- m_hScriptsRsonHash = std::hash<std::string>{}("scripts\\vscripts\\scripts.rson");
- m_hPdefHash = std::hash<std::string>{}(
- "cfg\\server\\persistent_player_data_version_231.pdef"); // this can have multiple versions, but we use 231 so that's what we hash
+ m_hScriptsRsonHash = STR_HASH("scripts\\vscripts\\scripts.rson");
+ m_hPdefHash = STR_HASH(
+ "cfg\\server\\persistent_player_data_version_231.pdef" // this can have multiple versions, but we use 231 so that's what we hash
+ );
LoadMods();
}
@@ -282,9 +283,7 @@ void ModManager::LoadMods()
// preexisting convars note: we don't delete convars if they already exist because they're used for script stuff, unfortunately this
// causes us to leak memory on reload, but not much, potentially find a way to not do this at some point
for (ModConVar* convar : mod.ConVars)
- if (g_CustomConvars.find(convar->Name) ==
- g_CustomConvars.end()) // make sure convar isn't registered yet, unsure if necessary but idk what behaviour is for defining
- // same convar multiple times
+ if (!g_pCVar->FindVar(convar->Name.c_str())) // make sure convar isn't registered yet, unsure if necessary but idk what behaviour is for defining same convar multiple times
new ConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str());
// read vpk paths
@@ -396,7 +395,7 @@ void ModManager::LoadMods()
if (fs::is_regular_file(file))
{
std::string kvStr = file.path().lexically_relative(mod.ModDirectory / "keyvalues").lexically_normal().string();
- mod.KeyValues.emplace(std::hash<std::string>{}(kvStr), kvStr);
+ mod.KeyValues.emplace(STR_HASH(kvStr), kvStr);
}
}
}
@@ -537,7 +536,7 @@ void ModManager::UnloadMods()
void ModManager::CompileAssetsForFile(const char* filename)
{
- size_t fileHash = std::hash<std::string>{}(fs::path(filename).lexically_normal().string());
+ size_t fileHash = STR_HASH(fs::path(filename).lexically_normal().string());
if (fileHash == m_hScriptsRsonHash)
BuildScriptsRson();
diff --git a/NorthstarDedicatedTest/pch.h b/NorthstarDedicatedTest/pch.h
index 29d1fe8d..cc44c107 100644
--- a/NorthstarDedicatedTest/pch.h
+++ b/NorthstarDedicatedTest/pch.h
@@ -16,6 +16,7 @@
#include <Windows.h>
#include <Psapi.h>
#include <set>
+#include <map>
#include <filesystem>
#include <sstream>
@@ -30,4 +31,17 @@ template <typename ReturnType, typename... Args> ReturnType CallVFunc(int index,
return (*reinterpret_cast<ReturnType(__fastcall***)(void*, Args...)>(thisPtr))[index](thisPtr, args...);
}
+template <typename T, size_t index, typename ...Args> constexpr T CallVFunc_Alt(void* classBase, Args... args) noexcept {
+ return ((*(T(__thiscall***)(void*, Args...))(classBase))[index])(classBase, args...);
+}
+
+
+#define STR_HASH(s) (std::hash<std::string>()(s))
+
+// Example usage: M_VMETHOD(int, GetEntityIndex, 8, (CBaseEntity* ent), (this, ent))
+#define M_VMETHOD(returnType, name, index, args, argsRaw) \
+FORCEINLINE returnType name args noexcept { \
+ return CallVFunc_Alt<returnType, index> argsRaw; \
+}
+
#endif \ No newline at end of file
diff --git a/loader_launcher_proxy/Memory.cpp b/loader_launcher_proxy/Memory.cpp
index f00c4d96..200246eb 100644
--- a/loader_launcher_proxy/Memory.cpp
+++ b/loader_launcher_proxy/Memory.cpp
@@ -5,10 +5,12 @@ IMemAlloc** g_ppMemAllocSingleton;
void LoadTier0Handle()
{
- if (!hTier0Module) hTier0Module = GetModuleHandleA("tier0.dll");
- if (!hTier0Module) return;
+ if (!hTier0Module)
+ hTier0Module = GetModuleHandleA("tier0.dll");
+ if (!hTier0Module)
+ return;
- g_ppMemAllocSingleton = (IMemAlloc**)GetProcAddress(hTier0Module, "g_pMemAllocSingleton");
+ g_ppMemAllocSingleton = (IMemAlloc**)GetProcAddress(hTier0Module, "g_pMemAllocSingleton");
}
const int STATIC_ALLOC_SIZE = 4096;
@@ -21,64 +23,60 @@ char pStaticAllocBuf[STATIC_ALLOC_SIZE];
void* malloc(size_t n)
{
- // allocate into static buffer
- if (g_iStaticAllocated + n <= STATIC_ALLOC_SIZE)
- {
- void* ret = pStaticAllocBuf + g_iStaticAllocated;
- g_iStaticAllocated += n;
- return ret;
- }
- else
- {
- // try to fallback to g_pMemAllocSingleton
- if (!hTier0Module || !g_ppMemAllocSingleton) LoadTier0Handle();
- if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton)
- return (*g_ppMemAllocSingleton)->m_vtable->Alloc(*g_ppMemAllocSingleton, n);
- else
- throw "Cannot allocate";
- }
+ // allocate into static buffer
+ if (g_iStaticAllocated + n <= STATIC_ALLOC_SIZE)
+ {
+ void* ret = pStaticAllocBuf + g_iStaticAllocated;
+ g_iStaticAllocated += n;
+ return ret;
+ }
+ else
+ {
+ // try to fallback to g_pMemAllocSingleton
+ if (!hTier0Module || !g_ppMemAllocSingleton)
+ LoadTier0Handle();
+ if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton)
+ return (*g_ppMemAllocSingleton)->m_vtable->Alloc(*g_ppMemAllocSingleton, n);
+ else
+ throw "Cannot allocate";
+ }
}
void free(void* p)
{
- // if it was allocated into the static buffer, just do nothing, safest way to deal with it
- if (p >= pStaticAllocBuf && p <= pStaticAllocBuf + STATIC_ALLOC_SIZE)
- return;
+ // if it was allocated into the static buffer, just do nothing, safest way to deal with it
+ if (p >= pStaticAllocBuf && p <= pStaticAllocBuf + STATIC_ALLOC_SIZE)
+ return;
- if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton)
- (*g_ppMemAllocSingleton)->m_vtable->Free(*g_ppMemAllocSingleton, p);
+ if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton)
+ (*g_ppMemAllocSingleton)->m_vtable->Free(*g_ppMemAllocSingleton, p);
}
-void* realloc(void* old_ptr, size_t size) {
- // it was allocated into the static buffer
- if (old_ptr >= pStaticAllocBuf && old_ptr <= pStaticAllocBuf + STATIC_ALLOC_SIZE)
- {
- if (g_pLastAllocated == old_ptr)
- {
- // nothing was allocated after this
- size_t old_size = g_iStaticAllocated - ((size_t)g_pLastAllocated - (size_t)pStaticAllocBuf);
- size_t diff = size - old_size;
- if (diff > 0)
- g_iStaticAllocated += diff;
- return old_ptr;
- }
- else
- {
- return malloc(size);
- }
- }
+void* realloc(void* old_ptr, size_t size)
+{
+ // it was allocated into the static buffer
+ if (old_ptr >= pStaticAllocBuf && old_ptr <= pStaticAllocBuf + STATIC_ALLOC_SIZE)
+ {
+ if (g_pLastAllocated == old_ptr)
+ {
+ // nothing was allocated after this
+ size_t old_size = g_iStaticAllocated - ((size_t)g_pLastAllocated - (size_t)pStaticAllocBuf);
+ size_t diff = size - old_size;
+ if (diff > 0)
+ g_iStaticAllocated += diff;
+ return old_ptr;
+ }
+ else
+ {
+ return malloc(size);
+ }
+ }
- if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton)
- return (*g_ppMemAllocSingleton)->m_vtable->Realloc(*g_ppMemAllocSingleton, old_ptr, size);
- return nullptr;
+ if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton)
+ return (*g_ppMemAllocSingleton)->m_vtable->Realloc(*g_ppMemAllocSingleton, old_ptr, size);
+ return nullptr;
}
-void* operator new(size_t n)
-{
- return malloc(n);
-}
+void* operator new(size_t n) { return malloc(n); }
-void operator delete(void* p)
-{
- return free(p);
-}
+void operator delete(void* p) { return free(p); }