From cc7b0ded08e3dc26fc970169a79c74c54e4f923b Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Thu, 26 May 2022 03:16:46 +0100 Subject: lots of cleanup and starting moving to new hooking macros --- LauncherInjector/LauncherInjector.vcxproj | 6 + LauncherInjector/main.cpp | 17 +- NorthstarDedicatedTest/ExploitFixes.cpp | 3 +- .../NorthstarDedicatedTest.vcxproj | 8 +- .../NorthstarDedicatedTest.vcxproj.filters | 8 +- NorthstarDedicatedTest/audio.cpp | 74 ++++---- NorthstarDedicatedTest/bansystem.cpp | 4 +- NorthstarDedicatedTest/buildainfile.cpp | 31 +--- NorthstarDedicatedTest/chatcommand.cpp | 1 - NorthstarDedicatedTest/clientauthhooks.cpp | 20 +-- NorthstarDedicatedTest/clientchathooks.cpp | 21 +-- NorthstarDedicatedTest/clientruihooks.cpp | 1 - NorthstarDedicatedTest/clientvideooverrides.cpp | 1 - NorthstarDedicatedTest/commandprint.cpp | 161 ----------------- NorthstarDedicatedTest/commandprint.h | 6 - NorthstarDedicatedTest/concommand.cpp | 4 +- NorthstarDedicatedTest/concommand.h | 2 +- NorthstarDedicatedTest/convar.cpp | 6 +- NorthstarDedicatedTest/cvar.cpp | 8 +- NorthstarDedicatedTest/cvar.h | 8 +- NorthstarDedicatedTest/debugoverlay.cpp | 1 - NorthstarDedicatedTest/dedicated.cpp | 49 +++-- NorthstarDedicatedTest/dedicatedmaterialsystem.cpp | 2 - NorthstarDedicatedTest/demofixes.cpp | 6 +- NorthstarDedicatedTest/dllmain.cpp | 1 - NorthstarDedicatedTest/filesystem.cpp | 15 +- NorthstarDedicatedTest/filesystem.h | 5 +- NorthstarDedicatedTest/hooks.cpp | 198 ++++++++++---------- NorthstarDedicatedTest/hooks.h | 199 +++++++++++++++++++-- NorthstarDedicatedTest/host.cpp | 30 ++-- NorthstarDedicatedTest/keyvalues.cpp | 4 +- NorthstarDedicatedTest/languagehooks.cpp | 2 +- NorthstarDedicatedTest/latencyflex.cpp | 2 - NorthstarDedicatedTest/localchatwriter.cpp | 1 - NorthstarDedicatedTest/logging.cpp | 2 - NorthstarDedicatedTest/mapsprint.cpp | 172 ------------------ NorthstarDedicatedTest/mapsprint.h | 2 - NorthstarDedicatedTest/maxplayers.cpp | 1 - NorthstarDedicatedTest/miscclientfixes.cpp | 2 - NorthstarDedicatedTest/misccommands.cpp | 2 +- NorthstarDedicatedTest/miscserverfixes.cpp | 2 - NorthstarDedicatedTest/miscserverscript.cpp | 2 +- NorthstarDedicatedTest/modlocalisation.cpp | 11 +- NorthstarDedicatedTest/modmanager.cpp | 12 +- NorthstarDedicatedTest/pdef.cpp | 4 +- NorthstarDedicatedTest/playlist.cpp | 52 +++--- NorthstarDedicatedTest/playlist.h | 2 +- NorthstarDedicatedTest/plugins.cpp | 4 +- NorthstarDedicatedTest/printcommand.h | 6 + NorthstarDedicatedTest/printcommands.cpp | 161 +++++++++++++++++ NorthstarDedicatedTest/printmaps.cpp | 169 +++++++++++++++++ NorthstarDedicatedTest/printmaps.h | 2 + NorthstarDedicatedTest/r2client.cpp | 8 +- NorthstarDedicatedTest/r2client.h | 4 +- NorthstarDedicatedTest/rpakfilesystem.cpp | 2 - NorthstarDedicatedTest/scriptbrowserhooks.cpp | 21 +-- NorthstarDedicatedTest/scriptmainmenupromos.cpp | 1 - NorthstarDedicatedTest/scriptmodmenu.cpp | 1 - NorthstarDedicatedTest/scriptserverbrowser.cpp | 4 +- .../scriptservertoclientstringcommand.cpp | 1 - NorthstarDedicatedTest/scriptsrson.cpp | 2 +- NorthstarDedicatedTest/serverauthentication.cpp | 37 ++-- NorthstarDedicatedTest/serverchathooks.cpp | 9 +- NorthstarDedicatedTest/sourceconsole.cpp | 3 +- NorthstarDedicatedTest/sourceinterface.cpp | 2 - NorthstarDedicatedTest/squirrel.h | 2 +- 66 files changed, 872 insertions(+), 738 deletions(-) delete mode 100644 NorthstarDedicatedTest/commandprint.cpp delete mode 100644 NorthstarDedicatedTest/commandprint.h delete mode 100644 NorthstarDedicatedTest/mapsprint.cpp delete mode 100644 NorthstarDedicatedTest/mapsprint.h create mode 100644 NorthstarDedicatedTest/printcommand.h create mode 100644 NorthstarDedicatedTest/printcommands.cpp create mode 100644 NorthstarDedicatedTest/printmaps.cpp create mode 100644 NorthstarDedicatedTest/printmaps.h diff --git a/LauncherInjector/LauncherInjector.vcxproj b/LauncherInjector/LauncherInjector.vcxproj index 8870c732..0c727918 100644 --- a/LauncherInjector/LauncherInjector.vcxproj +++ b/LauncherInjector/LauncherInjector.vcxproj @@ -65,6 +65,9 @@ shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 8000000 + + copy /Y "$(TargetPath)" "D:\origin\titanfall\Titanfall2" + @@ -85,6 +88,9 @@ shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 8000000 + + copy /Y "$(TargetPath)" "D:\origin\titanfall\Titanfall2" + diff --git a/LauncherInjector/main.cpp b/LauncherInjector/main.cpp index 0fb025a7..99e5ba15 100644 --- a/LauncherInjector/main.cpp +++ b/LauncherInjector/main.cpp @@ -147,15 +147,22 @@ void AwaitOriginStartup() std::cout << "LSX: connect()" << std::endl; connect(sock, (struct sockaddr*)&lsxAddr, sizeof(lsxAddr)); - + char buf[4096]; - recv(sock, buf, 4096, 0); - std::cout << buf << std::endl; + memset(buf, 0, sizeof(buf)); + + do + { + recv(sock, buf, 4096, 0); + std::cout << buf << std::endl; - Sleep(8000); + // honestly really shit, this isn't needed for origin due to being able to check OriginClientService + // but for ea desktop we don't have anything like this, so atm we just have to wait to ensure that we start after logging in + Sleep(8000); + } while (!strstr(buf, "")); // ensure we're actually getting data from lsx } - WSACleanup(); + WSACleanup(); // cleanup sockets and such so game can contact lsx itself } void EnsureOriginStarted() diff --git a/NorthstarDedicatedTest/ExploitFixes.cpp b/NorthstarDedicatedTest/ExploitFixes.cpp index ff0af8f9..154091df 100644 --- a/NorthstarDedicatedTest/ExploitFixes.cpp +++ b/NorthstarDedicatedTest/ExploitFixes.cpp @@ -2,7 +2,6 @@ #include "ExploitFixes.h" #include "ExploitFixes_UTF8Parser.h" -#include "hooks.h" #include "NSMem.h" #include "cvar.h" #include "tier0.h" @@ -141,7 +140,7 @@ KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 if (!nameValid || !valValid) return BLOCKED_INFO("Missing null terminators"); - auto realVar = g_pCVar->FindVar(entry->name); + auto realVar = R2::g_pCVar->FindVar(entry->name); if (realVar) memcpy( diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj index 0c012880..d6c77de4 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj @@ -117,10 +117,10 @@ - + - + @@ -585,7 +585,7 @@ - + @@ -603,7 +603,7 @@ - + diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters index c31b5045..3ab95494 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters @@ -1467,10 +1467,10 @@ Header Files\Server\Dedicated - + Header Files\Shared\Console - + Header Files\Shared\Console @@ -1652,13 +1652,13 @@ Source Files\Server\Scripted - + Source Files\Shared\Console Source Files\Client - + Source Files\Shared\Console diff --git a/NorthstarDedicatedTest/audio.cpp b/NorthstarDedicatedTest/audio.cpp index eb493779..6f47786f 100644 --- a/NorthstarDedicatedTest/audio.cpp +++ b/NorthstarDedicatedTest/audio.cpp @@ -1,14 +1,15 @@ #include "pch.h" -#include "hooks.h" #include "audio.h" #include "dedicated.h" +#include "convar.h" #include "rapidjson/error/en.h" #include #include #include #include -#include "convar.h" + +AUTOHOOK_INIT() extern "C" { @@ -328,9 +329,6 @@ void CustomAudioManager::ClearAudioOverrides() m_loadedAudioOverridesRegex.clear(); } -typedef bool (*LoadSampleMetadata_Type)(void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType); -LoadSampleMetadata_Type LoadSampleMetadata_Original; - template Iter select_randomly(Iter start, Iter end, RandomGenerator& g) { std::uniform_int_distribution<> dis(0, std::distance(start, end) - 1); @@ -367,6 +365,24 @@ bool ShouldPlayAudioEvent(const char* eventName, const std::shared_ptrsecond; if (!ShouldPlayAudioEvent(eventName, overrideData)) - return LoadSampleMetadata_Original(sample, audioBuffer, audioBufferLength, audioType); + return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType); void* data = 0; unsigned int dataLength = 0; @@ -453,7 +469,7 @@ bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal( if (!data) { spdlog::warn("Could not fetch override sample data for event {}! Using original data instead.", eventName); - return LoadSampleMetadata_Original(sample, audioBuffer, audioBufferLength, audioType); + return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType); } audioBuffer = data; @@ -464,51 +480,23 @@ bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal( *(unsigned int*)((uintptr_t)sample + 0xF0) = audioBufferLength; // 64 - Auto-detect sample type - bool res = LoadSampleMetadata_Original(sample, audioBuffer, audioBufferLength, 64); + bool res = LoadSampleMetadata(sample, audioBuffer, audioBufferLength, 64); if (!res) spdlog::error("LoadSampleMetadata failed! The game will crash :("); return res; } -// DO NOT TOUCH THIS FUNCTION -// The actual logic of it in a separate function (forcefully not inlined) to preserve the r12 register, which holds the event pointer. -bool __fastcall LoadSampleMetadata_Hook(void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType) -{ - uintptr_t parentEvent = (uintptr_t)Audio_GetParentEvent(); - - // Raw source, used for voice data only - if (audioType == 0) - return LoadSampleMetadata_Original(sample, audioBuffer, audioBufferLength, audioType); - - return LoadSampleMetadata_Internal(parentEvent, sample, audioBuffer, audioBufferLength, audioType); -} - -typedef bool (*MilesLog_Type)(int level, const char* string); -MilesLog_Type MilesLog_Original; - -void __fastcall MilesLog_Hook(int level, const char* string) +AUTOHOOK(MilesLog, mileswin64.dll + 0x57DAD0, +void, __fastcall, (int level, const char* string), { spdlog::info("[MSS] {} - {}", level, string); -} +}) ON_DLL_LOAD_CLIENT_RELIESON("client.dll", AudioHooks, ConVar, [](HMODULE baseAddress) { - Cvar_ns_print_played_sounds = new ConVar("ns_print_played_sounds", "0", FCVAR_NONE, ""); - - if (IsDedicatedServer()) - return; - - uintptr_t milesAudioBase = (uintptr_t)GetModuleHandleA("mileswin64.dll"); + AUTOHOOK_DISPATCH() - if (!milesAudioBase) - return spdlog::error("miles audio not found :terror:"); - - HookEnabler hook; - - ENABLER_CREATEHOOK( - hook, (char*)milesAudioBase + 0xF110, &LoadSampleMetadata_Hook, reinterpret_cast(&LoadSampleMetadata_Original)); - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x57DAD0, &MilesLog_Hook, reinterpret_cast(&MilesLog_Original)); - - MilesStopAll = (MilesStopAll_Type)((char*)baseAddress + 0x580850); + Cvar_ns_print_played_sounds = new ConVar("ns_print_played_sounds", "0", FCVAR_NONE, ""); + MilesStopAll = (MilesStopAll_Type)((char*)GetModuleHandleA("mileswin64.dll") + 0x580850); }) \ No newline at end of file diff --git a/NorthstarDedicatedTest/bansystem.cpp b/NorthstarDedicatedTest/bansystem.cpp index 70f4d7db..08115998 100644 --- a/NorthstarDedicatedTest/bansystem.cpp +++ b/NorthstarDedicatedTest/bansystem.cpp @@ -1,13 +1,13 @@ #pragma once #include "pch.h" #include "bansystem.h" -#include "hooks.h" #include "serverauthentication.h" #include "concommand.h" #include "miscserverscript.h" -#include #include "configurables.h" +#include + const char* BANLIST_PATH_SUFFIX = "/banlist.txt"; ServerBanSystem* g_ServerBanSystem; diff --git a/NorthstarDedicatedTest/buildainfile.cpp b/NorthstarDedicatedTest/buildainfile.cpp index 39ce0323..ea9d69ef 100644 --- a/NorthstarDedicatedTest/buildainfile.cpp +++ b/NorthstarDedicatedTest/buildainfile.cpp @@ -1,16 +1,15 @@ #include "pch.h" #include "convar.h" -#include "hooks.h" -#include "hookutils.h" #include #include #include "NSMem.h" +AUTOHOOK_INIT() + namespace fs = std::filesystem; const int AINET_VERSION_NUMBER = 57; const int AINET_SCRIPT_VERSION_NUMBER = 21; -const int MAP_VERSION_TEMP = 30; const int PLACEHOLDER_CRC = 0; const int MAX_HULLS = 5; @@ -349,18 +348,16 @@ void DumpAINInfo(CAI_Network* aiNetwork) writeStream.close(); } -typedef void (*CAI_NetworkBuilder__BuildType)(void* builder, CAI_Network* aiNetwork, void* unknown); -CAI_NetworkBuilder__BuildType CAI_NetworkBuilder__Build; -void CAI_NetworkBuilder__BuildHook(void* builder, CAI_Network* aiNetwork, void* unknown) +AUTOHOOK(CAI_NetworkBuilder__Build, server.dll + 0x385E20, +void,, (void* builder, CAI_Network* aiNetwork, void* unknown), { CAI_NetworkBuilder__Build(builder, aiNetwork, unknown); DumpAINInfo(aiNetwork); -} +}) -typedef void (*LoadAINFileType)(void* aimanager, void* buf, const char* filename); -LoadAINFileType LoadAINFile; -void LoadAINFileHook(void* aimanager, void* buf, const char* filename) +AUTOHOOK(LoadAINFile, server.dll + 0x3933A0, +void,, (void* aimanager, void* buf, const char* filename), { LoadAINFile(aimanager, buf, filename); @@ -369,18 +366,13 @@ void LoadAINFileHook(void* aimanager, void* buf, const char* filename) spdlog::info("running DumpAINInfo for loaded file {}", filename); DumpAINInfo(*(CAI_Network**)((char*)aimanager + 2536)); } -} +}) ON_DLL_LOAD("server.dll", BuildAINFile, [](HMODULE baseAddress) { Cvar_ns_ai_dumpAINfileFromLoad = new ConVar( "ns_ai_dumpAINfileFromLoad", "0", FCVAR_NONE, "For debugging: whether we should dump ain data for ains loaded from disk"); - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x385E20, &CAI_NetworkBuilder__BuildHook, reinterpret_cast(&CAI_NetworkBuilder__Build)); - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x3933A0, &LoadAINFileHook, reinterpret_cast(&LoadAINFile)); - pUnkStruct0Count = (int*)((char*)baseAddress + 0x1063BF8); pppUnkNodeStruct0s = (UnkNodeStruct0***)((char*)baseAddress + 0x1063BE0); @@ -388,11 +380,4 @@ ON_DLL_LOAD("server.dll", BuildAINFile, [](HMODULE baseAddress) pppUnkStruct1s = (UnkLinkStruct1***)((char*)baseAddress + 0x1063A90); pUnkServerMapversionGlobal = (char**)((char*)baseAddress + 0xBFBE08); pMapName = (char*)baseAddress + 0x1053370; - - uintptr_t base = (uintptr_t)baseAddress; - - // remove a check that prevents a logging function in link generation from working - // due to the sheer amount of logging this is a massive perf hit to generation, but spewlog_enable 0 exists so whatever - NSMem::NOP(base + 0x3889B6, 6); - NSMem::NOP(base + 0x3889BF, 6); }); \ No newline at end of file diff --git a/NorthstarDedicatedTest/chatcommand.cpp b/NorthstarDedicatedTest/chatcommand.cpp index 78f4477d..86060a91 100644 --- a/NorthstarDedicatedTest/chatcommand.cpp +++ b/NorthstarDedicatedTest/chatcommand.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "hooks.h" #include "convar.h" #include "concommand.h" #include "localchatwriter.h" diff --git a/NorthstarDedicatedTest/clientauthhooks.cpp b/NorthstarDedicatedTest/clientauthhooks.cpp index 6e1da395..7463028b 100644 --- a/NorthstarDedicatedTest/clientauthhooks.cpp +++ b/NorthstarDedicatedTest/clientauthhooks.cpp @@ -1,10 +1,10 @@ #include "pch.h" -#include "hooks.h" -#include "hookutils.h" #include "masterserver.h" #include "convar.h" #include "r2client.h" +AUTOHOOK_INIT() + ConVar* Cvar_ns_has_agreed_to_send_token; // mirrored in script @@ -12,9 +12,8 @@ const int NOT_DECIDED_TO_SEND_TOKEN = 0; const int AGREED_TO_SEND_TOKEN = 1; const int DISAGREED_TO_SEND_TOKEN = 2; -typedef void (*AuthWithStryderType)(void* a1); -AuthWithStryderType AuthWithStryder; -void AuthWithStryderHook(void* a1) +AUTOHOOK(AuthWithStryder, engine.dll + 0x1843A0, +void,, (void* a1), { // game will call this forever, until it gets a valid auth key // so, we need to manually invalidate our key until we're authed with northstar, then we'll allow game to auth with stryder @@ -23,24 +22,23 @@ void AuthWithStryderHook(void* a1) // if player has agreed to send token and we aren't already authing, try to auth if (Cvar_ns_has_agreed_to_send_token->GetInt() == AGREED_TO_SEND_TOKEN && !g_MasterServerManager->m_bOriginAuthWithMasterServerInProgress) - g_MasterServerManager->AuthenticateOriginWithMasterServer(R2::g_LocalPlayerUserID, R2::g_LocalPlayerOriginToken); + g_MasterServerManager->AuthenticateOriginWithMasterServer(R2::g_pLocalPlayerUserID, R2::g_pLocalPlayerOriginToken); // invalidate key so auth will fail - *R2::g_LocalPlayerOriginToken = 0; + *R2::g_pLocalPlayerOriginToken = 0; } AuthWithStryder(a1); -} +}) ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", ClientAuthHooks, ConVar, [](HMODULE baseAddress) { + AUTOHOOK_DISPATCH() + // this cvar will save to cfg once initially agreed with Cvar_ns_has_agreed_to_send_token = new ConVar( "ns_has_agreed_to_send_token", "0", FCVAR_ARCHIVE_PLAYERPROFILE, "whether the user has agreed to send their origin token to the northstar masterserver"); - - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1843A0, &AuthWithStryderHook, reinterpret_cast(&AuthWithStryder)); }) \ No newline at end of file diff --git a/NorthstarDedicatedTest/clientchathooks.cpp b/NorthstarDedicatedTest/clientchathooks.cpp index a5daa2be..f778c6ee 100644 --- a/NorthstarDedicatedTest/clientchathooks.cpp +++ b/NorthstarDedicatedTest/clientchathooks.cpp @@ -1,20 +1,14 @@ #include "pch.h" -#include "hooks.h" -#include #include "squirrel.h" #include "serverchathooks.h" #include "localchatwriter.h" -struct ChatTags -{ - bool whisper; - bool team; - bool dead; -}; +#include -typedef void(__fastcall* CHudChat__AddGameLineType)(void* self, const char* message, int fromPlayerId, bool isteam, bool isdead); -CHudChat__AddGameLineType CHudChat__AddGameLine; -static void CHudChat__AddGameLineHook(void* self, const char* message, int inboxId, bool isTeam, bool isDead) +AUTOHOOK_INIT() + +AUTOHOOK(CHudChat__AddGameLine, client.dll + 0x22E580, +void,, (void* self, const char* message, int inboxId, bool isTeam, bool isDead), { // This hook is called for each HUD, but we only want our logic to run once. if (self != *CHudChat::allHuds) @@ -51,7 +45,7 @@ static void CHudChat__AddGameLineHook(void* self, const char* message, int inbox CHudChat__AddGameLine(hud, message, inboxId, isTeam, isDead); } } -} +}) // void NSChatWrite( int context, string str ) static SQRESULT SQ_ChatWrite(void* sqvm) @@ -85,8 +79,7 @@ static SQRESULT SQ_ChatWriteLine(void* sqvm) ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientChatHooks, ClientSquirrel, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x22E580, &CHudChat__AddGameLineHook, reinterpret_cast(&CHudChat__AddGameLine)); + AUTOHOOK_DISPATCH() g_pClientSquirrel->AddFuncRegistration("void", "NSChatWrite", "int context, string text", "", SQ_ChatWrite); g_pClientSquirrel->AddFuncRegistration("void", "NSChatWriteRaw", "int context, string text", "", SQ_ChatWriteRaw); diff --git a/NorthstarDedicatedTest/clientruihooks.cpp b/NorthstarDedicatedTest/clientruihooks.cpp index 65c85511..da556d26 100644 --- a/NorthstarDedicatedTest/clientruihooks.cpp +++ b/NorthstarDedicatedTest/clientruihooks.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "hooks.h" #include "convar.h" ConVar* Cvar_rui_drawEnable; diff --git a/NorthstarDedicatedTest/clientvideooverrides.cpp b/NorthstarDedicatedTest/clientvideooverrides.cpp index 8bed49e9..b3609fc2 100644 --- a/NorthstarDedicatedTest/clientvideooverrides.cpp +++ b/NorthstarDedicatedTest/clientvideooverrides.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "hooks.h" #include "modmanager.h" typedef void* (*BinkOpenType)(const char* path, uint32_t flags); diff --git a/NorthstarDedicatedTest/commandprint.cpp b/NorthstarDedicatedTest/commandprint.cpp deleted file mode 100644 index 5d78a1b4..00000000 --- a/NorthstarDedicatedTest/commandprint.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "pch.h" -#include "commandprint.h" -#include "convar.h" -#include "concommand.h" - -void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name) -{ - if (!command) - { - spdlog::info("unknown command {}", name); - return; - } - - // build string for flags if not FCVAR_NONE - std::string flagString; - if (command->GetFlags() != FCVAR_NONE) - { - flagString = "( "; - - for (auto& flagPair : g_PrintCommandFlags) - { - if (command->GetFlags() & flagPair.first) - { - flagString += flagPair.second; - flagString += " "; - } - } - - flagString += ") "; - } - - // temp because command->IsCommand does not currently work - ConVar* cvar = g_pCVar->FindVar(command->m_pszName); - if (cvar) - spdlog::info("\"{}\" = \"{}\" {}- {}", cvar->GetBaseName(), cvar->GetString(), flagString, cvar->GetHelpText()); - else - spdlog::info("\"{}\" {} - {}", command->m_pszName, flagString, command->GetHelpText()); -} - -void TryPrintCvarHelpForCommand(const char* pCommand) -{ - // try to display help text for an inputted command string from the console - int pCommandLen = strlen(pCommand); - char* pCvarStr = new char[pCommandLen]; - strcpy(pCvarStr, pCommand); - - // trim whitespace from right - for (int i = pCommandLen - 1; i; i--) - { - if (isspace(pCvarStr[i])) - pCvarStr[i] = '\0'; - else - break; - } - - // check if we're inputting a cvar, but not setting it at all - ConVar* cvar = g_pCVar->FindVar(pCvarStr); - if (cvar) - PrintCommandHelpDialogue(&cvar->m_ConCommandBase, pCvarStr); - - delete[] pCvarStr; -} - -void ConCommand_help(const CCommand& arg) -{ - if (arg.ArgC() < 2) - { - spdlog::info("Usage: help "); - return; - } - - PrintCommandHelpDialogue(g_pCVar->FindCommandBase(arg.Arg(1)), arg.Arg(1)); -} - -void ConCommand_find(const CCommand& arg) -{ - if (arg.ArgC() < 2) - { - spdlog::info("Usage: find [...]"); - return; - } - - char pTempName[256]; - char pTempSearchTerm[256]; - - for (auto& map : g_pCVar->DumpToMap()) - { - bool bPrintCommand = true; - for (int i = 0; i < arg.ArgC() - 1; i++) - { - // make lowercase to avoid case sensitivity - strncpy(pTempName, map.second->m_pszName, sizeof(pTempName)); - strncpy(pTempSearchTerm, arg.Arg(i + 1), sizeof(pTempSearchTerm)); - - for (int i = 0; pTempName[i]; i++) - pTempName[i] = tolower(pTempName[i]); - - for (int i = 0; pTempSearchTerm[i]; i++) - pTempSearchTerm[i] = tolower(pTempSearchTerm[i]); - - if (!strstr(pTempName, pTempSearchTerm)) - { - bPrintCommand = false; - break; - } - } - - if (bPrintCommand) - PrintCommandHelpDialogue(map.second, map.second->m_pszName); - } -} - -void ConCommand_findflags(const CCommand& arg) -{ - if (arg.ArgC() < 2) - { - spdlog::info("Usage: findflags "); - for (auto& flagPair : g_PrintCommandFlags) - spdlog::info(" - {}", flagPair.second); - - return; - } - - // convert input flag to uppercase - char* upperFlag = new char[strlen(arg.Arg(1))]; - strcpy(upperFlag, arg.Arg(1)); - - for (int i = 0; upperFlag[i]; i++) - upperFlag[i] = toupper(upperFlag[i]); - - // resolve flag name => int flags - int resolvedFlag = FCVAR_NONE; - for (auto& flagPair : g_PrintCommandFlags) - { - if (!strcmp(flagPair.second, upperFlag)) - { - resolvedFlag = flagPair.first; - break; - } - } - - // print cvars - for (auto& map : g_pCVar->DumpToMap()) - { - if (map.second->m_nFlags & resolvedFlag) - PrintCommandHelpDialogue(map.second, map.second->m_pszName); - } - - delete[] upperFlag; -} - -void InitialiseCommandPrint() -{ - RegisterConCommand("find", ConCommand_find, "Find concommands with the specified string in their name/help text.", FCVAR_NONE); - RegisterConCommand("findflags", ConCommand_findflags, "Find concommands by flags.", FCVAR_NONE); - // help is already a command, so we need to modify the preexisting command to use our func instead - // and clear the flags also - ConCommand* helpCommand = g_pCVar->FindCommand("help"); - helpCommand->m_nFlags = FCVAR_NONE; - helpCommand->m_pCommandCallback = ConCommand_help; -} \ No newline at end of file diff --git a/NorthstarDedicatedTest/commandprint.h b/NorthstarDedicatedTest/commandprint.h deleted file mode 100644 index 6c3ef850..00000000 --- a/NorthstarDedicatedTest/commandprint.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include "concommand.h" - -void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name); -void TryPrintCvarHelpForCommand(const char* pCommand); -void InitialiseCommandPrint(); diff --git a/NorthstarDedicatedTest/concommand.cpp b/NorthstarDedicatedTest/concommand.cpp index b5da2118..fddad088 100644 --- a/NorthstarDedicatedTest/concommand.cpp +++ b/NorthstarDedicatedTest/concommand.cpp @@ -1,7 +1,7 @@ #include "pch.h" -#include "hooks.h" #include "concommand.h" #include "misccommands.h" + #include //----------------------------------------------------------------------------- @@ -38,7 +38,7 @@ bool ConCommandBase::IsRegistered(void) const //----------------------------------------------------------------------------- bool ConCommandBase::IsFlagSet(int nFlags) const { - return false; // !TODO: Returning false on every query? (original implementation in Northstar before ConCommandBase refactor) + return m_nFlags & nFlags; } //----------------------------------------------------------------------------- diff --git a/NorthstarDedicatedTest/concommand.h b/NorthstarDedicatedTest/concommand.h index c11c7ea4..a02604f2 100644 --- a/NorthstarDedicatedTest/concommand.h +++ b/NorthstarDedicatedTest/concommand.h @@ -83,7 +83,7 @@ typedef void (*FnCommandCallback_t)(const CCommand& command); //----------------------------------------------------------------------------- // Returns 0 to COMMAND_COMPLETION_MAXITEMS worth of completion strings //----------------------------------------------------------------------------- -typedef int (*FnCommandCompletionCallback)(const char* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]); +typedef int (*__fastcall FnCommandCompletionCallback)(const char* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]); // From r5reloaded class ConCommandBase diff --git a/NorthstarDedicatedTest/convar.cpp b/NorthstarDedicatedTest/convar.cpp index df570da9..1b2e0679 100644 --- a/NorthstarDedicatedTest/convar.cpp +++ b/NorthstarDedicatedTest/convar.cpp @@ -1,9 +1,7 @@ #include "pch.h" -#include "hooks.h" #include "bits.h" #include "cvar.h" #include "convar.h" -#include "hookutils.h" #include "sourceinterface.h" typedef void (*ConVarRegisterType)( @@ -36,8 +34,8 @@ ON_DLL_LOAD("engine.dll", ConVar, [](HMODULE baseAddress) g_pConVar_Vtable = (char*)baseAddress + 0x67FD28; g_pIConVar_Vtable = (char*)baseAddress + 0x67FDC8; - g_pCVarInterface = new SourceInterface("vstdlib.dll", "VEngineCvar007"); - g_pCVar = *g_pCVarInterface; + R2::g_pCVarInterface = new SourceInterface("vstdlib.dll", "VEngineCvar007"); + R2::g_pCVar = *R2::g_pCVarInterface; }) //----------------------------------------------------------------------------- diff --git a/NorthstarDedicatedTest/cvar.cpp b/NorthstarDedicatedTest/cvar.cpp index 23d767fd..787790be 100644 --- a/NorthstarDedicatedTest/cvar.cpp +++ b/NorthstarDedicatedTest/cvar.cpp @@ -23,5 +23,9 @@ std::unordered_map CCvar::DumpToMap() return allConVars; } -SourceInterface* g_pCVarInterface; -CCvar* g_pCVar; \ No newline at end of file +// use the R2 namespace for game funcs +namespace R2 +{ + SourceInterface* g_pCVarInterface; + CCvar* g_pCVar; +} // namespace R2 diff --git a/NorthstarDedicatedTest/cvar.h b/NorthstarDedicatedTest/cvar.h index a39df387..ead30d3e 100644 --- a/NorthstarDedicatedTest/cvar.h +++ b/NorthstarDedicatedTest/cvar.h @@ -35,5 +35,9 @@ class CCvar std::unordered_map DumpToMap(); }; -extern SourceInterface* g_pCVarInterface; -extern CCvar* g_pCVar; +// use the R2 namespace for game funcs +namespace R2 +{ + extern SourceInterface* g_pCVarInterface; + extern CCvar* g_pCVar; +} // namespace R2 diff --git a/NorthstarDedicatedTest/debugoverlay.cpp b/NorthstarDedicatedTest/debugoverlay.cpp index 64e0d065..6cf7703f 100644 --- a/NorthstarDedicatedTest/debugoverlay.cpp +++ b/NorthstarDedicatedTest/debugoverlay.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "hooks.h" #include "dedicated.h" #include "cvar.h" diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp index 5d8fedf0..25e6d24b 100644 --- a/NorthstarDedicatedTest/dedicated.cpp +++ b/NorthstarDedicatedTest/dedicated.cpp @@ -1,16 +1,13 @@ #include "pch.h" -#include "hooks.h" #include "dedicated.h" -#include "hookutils.h" #include "tier0.h" #include "playlist.h" #include "r2engine.h" #include "hoststate.h" #include "serverauthentication.h" #include "masterserver.h" -#include "commandprint.h" +#include "printcommand.h" -using namespace Tier0; using namespace R2; bool IsDedicatedServer() @@ -46,21 +43,17 @@ typedef void (*CHostState__InitType)(CHostState* self); void RunServer(CDedicatedExports* dedicated) { spdlog::info("CDedicatedExports::RunServer(): starting"); - spdlog::info(CommandLine()->GetCmdLine()); + spdlog::info(Tier0::CommandLine()->GetCmdLine()); // initialise engine g_pEngine->Frame(); // add +map if not present // don't manually execute this from cbuf as users may have it in their startup args anyway, easier just to run from stuffcmds if present - if (!CommandLine()->CheckParm("+map")) - CommandLine()->AppendParm("+map", g_pCVar->FindVar("match_defaultMap")->GetString()); + if (!Tier0::CommandLine()->CheckParm("+map")) + Tier0::CommandLine()->AppendParm("+map", g_pCVar->FindVar("match_defaultMap")->GetString()); - // ensure playlist initialises right, if we've not explicitly called setplaylist - SetCurrentPlaylist(GetCurrentPlaylistName()); - - // run server autoexec and re-run commandline - Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode); + // re-run commandline Cbuf_AddText(Cbuf_GetCurrentPlayer(), "stuffcmds", cmd_source_t::kCommandSrcCode); Cbuf_Execute(); @@ -71,7 +64,7 @@ void RunServer(CDedicatedExports* dedicated) double frameTitle = 0; while (g_pEngine->m_nQuitting == EngineQuitState::QUIT_NOTQUITTING) { - double frameStart = Plat_FloatTime(); + double frameStart = Tier0::Plat_FloatTime(); g_pEngine->Frame(); // only update the title after at least 500ms since the last update @@ -80,7 +73,7 @@ void RunServer(CDedicatedExports* dedicated) frameTitle = frameStart; // this way of getting playercount/maxplayers honestly really sucks, but not got any other methods of doing it rn - const char* maxPlayers = GetCurrentPlaylistVar("max_players", false); + const char* maxPlayers = GetCurrentPlaylistVar("max_players", true); if (!maxPlayers) maxPlayers = "6"; @@ -95,7 +88,7 @@ void RunServer(CDedicatedExports* dedicated) } std::this_thread::sleep_for(std::chrono::duration>( - Cvar_base_tickinterval_mp->GetFloat() - fmin(Plat_FloatTime() - frameStart, 0.25))); + Cvar_base_tickinterval_mp->GetFloat() - fmin(Tier0::Plat_FloatTime() - frameStart, 0.25))); } } @@ -173,7 +166,6 @@ ON_DLL_LOAD_DEDI("engine.dll", DedicatedServer, [](HMODULE engineAddress) // removing these will mess up register state when this function is over, so we'll write HS_RUN to the wrong address // so uhh, don't do that // NSMem::NOP(ea + 0x156B4C + 7, 8); - NSMem::NOP(ea + 0x156B4C + 15, 9); // HostState_State_NewGame @@ -243,15 +235,15 @@ ON_DLL_LOAD_DEDI("engine.dll", DedicatedServer, [](HMODULE engineAddress) // make sure it still gets registered // add cmdline args that are good for dedi - CommandLine()->AppendParm("-nomenuvid", 0); - CommandLine()->AppendParm("-nosound", 0); - CommandLine()->AppendParm("-windowed", 0); - CommandLine()->AppendParm("-nomessagebox", 0); - CommandLine()->AppendParm("+host_preload_shaders", "0"); - CommandLine()->AppendParm("+net_usesocketsforloopback", "1"); + Tier0::CommandLine()->AppendParm("-nomenuvid", 0); + Tier0::CommandLine()->AppendParm("-nosound", 0); + Tier0::CommandLine()->AppendParm("-windowed", 0); + Tier0::CommandLine()->AppendParm("-nomessagebox", 0); + Tier0::CommandLine()->AppendParm("+host_preload_shaders", "0"); + Tier0::CommandLine()->AppendParm("+net_usesocketsforloopback", "1"); // Disable Quick Edit mode to reduce chance of user unintentionally hanging their server by selecting something. - if (!CommandLine()->CheckParm("-bringbackquickedit")) + if (!Tier0::CommandLine()->CheckParm("-bringbackquickedit")) { HANDLE stdIn = GetStdHandle(STD_INPUT_HANDLE); DWORD mode = 0; @@ -273,7 +265,7 @@ ON_DLL_LOAD_DEDI("engine.dll", DedicatedServer, [](HMODULE engineAddress) spdlog::info("Quick Edit enabled by user request"); // create console input thread - if (!CommandLine()->CheckParm("-noconsoleinput")) + if (!Tier0::CommandLine()->CheckParm("-noconsoleinput")) consoleInputThreadHandle = CreateThread(0, 0, ConsoleInputThread, 0, 0, NULL); else spdlog::info("Console input disabled by user request"); @@ -285,11 +277,10 @@ ON_DLL_LOAD_DEDI("tier0.dll", DedicatedServerOrigin, [](HMODULE baseAddress) // for any big ea lawyers, this can't be used to play the game without origin, game will throw a fit if you try to do anything without // an origin id as a client for dedi it's fine though, game doesn't care if origin is disabled as long as there's only a server - NSMem::BytePatch( - (uintptr_t)GetProcAddress(GetModuleHandleA("tier0.dll"), "Tier0_InitOrigin"), - { - 0xC3 // ret - }); + NSMem::BytePatch((uintptr_t)GetProcAddress(GetModuleHandleA("tier0.dll"), "Tier0_InitOrigin"), + { + 0xC3 // ret + }); }) typedef void (*PrintFatalSquirrelErrorType)(void* sqvm); diff --git a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp index 7142e44d..d01d381f 100644 --- a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp +++ b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp @@ -1,8 +1,6 @@ #pragma once #include "pch.h" -#include "hooks.h" #include "dedicated.h" -#include "hookutils.h" #include "tier0.h" #include "NSMem.h" diff --git a/NorthstarDedicatedTest/demofixes.cpp b/NorthstarDedicatedTest/demofixes.cpp index c2eb9b09..fba94183 100644 --- a/NorthstarDedicatedTest/demofixes.cpp +++ b/NorthstarDedicatedTest/demofixes.cpp @@ -10,15 +10,15 @@ ON_DLL_LOAD_CLIENT_RELIESON("client.dll", DemoFixes, ConVar, [](HMODULE baseAddr // change default values of demo cvars to enable them by default, but not autorecord // this is before Host_Init, the setvalue calls here will get overwritten by custom cfgs/launch options - ConVar* Cvar_demo_enableDemos = g_pCVar->FindVar("demo_enabledemos"); + ConVar* Cvar_demo_enableDemos = R2::g_pCVar->FindVar("demo_enabledemos"); Cvar_demo_enableDemos->m_pszDefaultValue = "1"; Cvar_demo_enableDemos->SetValue(true); - ConVar* Cvar_demo_writeLocalFile = g_pCVar->FindVar("demo_writeLocalFile"); + ConVar* Cvar_demo_writeLocalFile = R2::g_pCVar->FindVar("demo_writeLocalFile"); Cvar_demo_writeLocalFile->m_pszDefaultValue = "1"; Cvar_demo_writeLocalFile->SetValue(true); - ConVar* Cvar_demo_autoRecord = g_pCVar->FindVar("demo_autoRecord"); + ConVar* Cvar_demo_autoRecord = R2::g_pCVar->FindVar("demo_autoRecord"); Cvar_demo_autoRecord->m_pszDefaultValue = "0"; Cvar_demo_autoRecord->SetValue(false); }) \ No newline at end of file diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp index f097aa20..718e9413 100644 --- a/NorthstarDedicatedTest/dllmain.cpp +++ b/NorthstarDedicatedTest/dllmain.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "hooks.h" #include "main.h" #include "logging.h" #include "memalloc.h" diff --git a/NorthstarDedicatedTest/filesystem.cpp b/NorthstarDedicatedTest/filesystem.cpp index 09bf8e97..f55fca82 100644 --- a/NorthstarDedicatedTest/filesystem.cpp +++ b/NorthstarDedicatedTest/filesystem.cpp @@ -1,22 +1,23 @@ #include "pch.h" #include "filesystem.h" -#include "hooks.h" -#include "hookutils.h" #include "sourceinterface.h" #include "modmanager.h" #include #include -using namespace R2FS; +using namespace R2; bool bReadingOriginalFile = false; std::string sCurrentModPath; ConVar* Cvar_ns_fs_log_reads; -namespace R2FS +// use the R2 namespace for game funcs +namespace R2 { + SourceInterface* g_pFilesystem; + std::string ReadVPKFile(const char* path) { // read scripts.rson file, todo: check if this can be overwritten @@ -44,9 +45,7 @@ namespace R2FS return ret; } - - SourceInterface* g_pFilesystem; -} +} // namespace R2 typedef void (*AddSearchPathType)(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType); AddSearchPathType AddSearchPath; @@ -172,7 +171,7 @@ VPKData* MountVPKHook(IFileSystem* fileSystem, const char* vpkPath) ON_DLL_LOAD("filesystem_stdio.dll", Filesystem, [](HMODULE baseAddress) { - R2FS::g_pFilesystem = new SourceInterface("filesystem_stdio.dll", "VFileSystem017"); + R2::g_pFilesystem = new SourceInterface("filesystem_stdio.dll", "VFileSystem017"); // create hooks HookEnabler hook; diff --git a/NorthstarDedicatedTest/filesystem.h b/NorthstarDedicatedTest/filesystem.h index 1aa4fb0c..abfdd14b 100644 --- a/NorthstarDedicatedTest/filesystem.h +++ b/NorthstarDedicatedTest/filesystem.h @@ -67,10 +67,11 @@ class IFileSystem VTable2* m_vtable2; }; -namespace R2FS +// use the R2 namespace for game funcs +namespace R2 { extern SourceInterface* g_pFilesystem; std::string ReadVPKFile(const char* path); std::string ReadVPKOriginalFile(const char* path); -} // namespace R2FS +} // namespace R2 diff --git a/NorthstarDedicatedTest/hooks.cpp b/NorthstarDedicatedTest/hooks.cpp index 32e4530c..d4608f85 100644 --- a/NorthstarDedicatedTest/hooks.cpp +++ b/NorthstarDedicatedTest/hooks.cpp @@ -1,7 +1,4 @@ #include "pch.h" -#include "hooks.h" -#include "hookutils.h" -#include "sigscanning.h" #include "dedicated.h" #include @@ -15,6 +12,8 @@ namespace fs = std::filesystem; +AUTOHOOK_INIT() + // called from the ON_DLL_LOAD macros __dllLoadCallback::__dllLoadCallback( eDllLoadCallbackSide side, const std::string dllName, DllLoadCallbackFuncType callback, std::string uniqueStr, std::string reliesOn) @@ -41,6 +40,84 @@ __dllLoadCallback::__dllLoadCallback( } } +void __fileAutohook::Dispatch() +{ + for (__autohook* hook : hooks) + hook->Dispatch(); +} + +ManualHook::ManualHook(const char* funcName, LPVOID* orig, LPVOID func) : pHookFunc(func), ppOrigFunc(orig) +{ + const int iFuncNameStrlen = strlen(funcName); + pFuncName = new char[iFuncNameStrlen]; + memcpy(pFuncName, funcName, iFuncNameStrlen); +} + +bool ManualHook::Dispatch(LPVOID addr) +{ + if (MH_CreateHook(addr, pHookFunc, ppOrigFunc) == MH_OK) + { + if (MH_EnableHook(addr) == MH_OK) + { + spdlog::info("Enabling hook {}", pFuncName); + return true; + } + else + spdlog::error("MH_EnableHook failed for function {}", pFuncName); + } + else + spdlog::error("MH_CreateHook failed for function {}", pFuncName); + + return false; +} + +// dll load callback stuff +// this allows for code to register callbacks to be run as soon as a dll is loaded, mainly to allow for patches to be made on dll load +struct DllLoadCallback +{ + std::string dll; + DllLoadCallbackFuncType callback; + std::string tag; + std::string reliesOn; + bool called; +}; + +// HACK: declaring and initialising this vector at file scope crashes on debug builds due to static initialisation order +// using a static var like this ensures that the vector is initialised lazily when it's used +std::vector& GetDllLoadCallbacks() +{ + static std::vector vec = std::vector(); + return vec; +} + +void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::string reliesOn) +{ + DllLoadCallback& callbackStruct = GetDllLoadCallbacks().emplace_back(); + + callbackStruct.dll = dll; + callbackStruct.callback = callback; + callbackStruct.tag = tag; + callbackStruct.reliesOn = reliesOn; + callbackStruct.called = false; +} + +void AddDllLoadCallbackForDedicatedServer( + std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::string reliesOn) +{ + if (!IsDedicatedServer()) + return; + + AddDllLoadCallback(dll, callback, tag, reliesOn); +} + +void AddDllLoadCallbackForClient(std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::string reliesOn) +{ + if (IsDedicatedServer()) + return; + + AddDllLoadCallback(dll, callback, tag, reliesOn); +} + typedef LPSTR (*GetCommandLineAType)(); GetCommandLineAType GetCommandLineAOriginal; LPSTR GetCommandLineAHook() @@ -106,60 +183,12 @@ LPSTR GetCommandLineAHook() return cmdlineModified; } -// dll load callback stuff -// this allows for code to register callbacks to be run as soon as a dll is loaded, mainly to allow for patches to be made on dll load -struct DllLoadCallback -{ - std::string dll; - DllLoadCallbackFuncType callback; - std::string tag; - std::string reliesOn; - bool called; -}; - -// for whatever reason, just declaring and initialising the vector at file scope crashes on debug builds -// but this works, idk sucks but just how it is -std::vector& GetDllLoadCallbacks() -{ - static std::vector vec = std::vector(); - return vec; -} - -void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::string reliesOn) -{ - DllLoadCallback& callbackStruct = GetDllLoadCallbacks().emplace_back(); // <-- crashes here - - callbackStruct.dll = dll; - callbackStruct.callback = callback; - callbackStruct.tag = tag; - callbackStruct.reliesOn = reliesOn; - callbackStruct.called = false; -} - -void AddDllLoadCallbackForDedicatedServer( - std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::string reliesOn) -{ - if (!IsDedicatedServer()) - return; - - AddDllLoadCallback(dll, callback, tag, reliesOn); -} - -void AddDllLoadCallbackForClient(std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::string reliesOn) -{ - if (IsDedicatedServer()) - return; - - AddDllLoadCallback(dll, callback, tag, reliesOn); -} - std::vector calledTags; - void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress) { while (true) { - bool doneCalling = true; + bool bDoneCalling = true; for (auto& callbackStruct : GetDllLoadCallbacks()) { @@ -168,7 +197,7 @@ void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress) if (callbackStruct.reliesOn != "" && std::find(calledTags.begin(), calledTags.end(), callbackStruct.reliesOn) == calledTags.end()) { - doneCalling = false; + bDoneCalling = false; continue; } @@ -178,7 +207,7 @@ void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress) } } - if (doneCalling) + if (bDoneCalling) break; } } @@ -187,7 +216,7 @@ void CallLoadLibraryWCallbacks(LPCWSTR lpLibFileName, HMODULE moduleAddress) { while (true) { - bool doneCalling = true; + bool bDoneCalling = true; for (auto& callbackStruct : GetDllLoadCallbacks()) { @@ -196,7 +225,7 @@ void CallLoadLibraryWCallbacks(LPCWSTR lpLibFileName, HMODULE moduleAddress) if (callbackStruct.reliesOn != "" && std::find(calledTags.begin(), calledTags.end(), callbackStruct.reliesOn) == calledTags.end()) { - doneCalling = false; + bDoneCalling = false; continue; } @@ -206,7 +235,7 @@ void CallLoadLibraryWCallbacks(LPCWSTR lpLibFileName, HMODULE moduleAddress) } } - if (doneCalling) + if (bDoneCalling) break; } } @@ -234,72 +263,55 @@ void CallAllPendingDLLLoadCallbacks() } } - -typedef HMODULE (*LoadLibraryExAType)(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); -LoadLibraryExAType LoadLibraryExAOriginal; -HMODULE LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) -{ - HMODULE moduleAddress = LoadLibraryExAOriginal(lpLibFileName, hFile, dwFlags); +AUTOHOOK_ABSOLUTEADDR(_LoadLibraryExA, LoadLibraryExA, +HMODULE, WINAPI, (LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags), + { + HMODULE moduleAddress = _LoadLibraryExA(lpLibFileName, hFile, dwFlags); if (moduleAddress) - { CallLoadLibraryACallbacks(lpLibFileName, moduleAddress); - } return moduleAddress; -} +}) + -typedef HMODULE (*LoadLibraryAType)(LPCSTR lpLibFileName); -LoadLibraryAType LoadLibraryAOriginal; -HMODULE LoadLibraryAHook(LPCSTR lpLibFileName) +AUTOHOOK_ABSOLUTEADDR(_LoadLibraryA, LoadLibraryA, +HMODULE, WINAPI, (LPCSTR lpLibFileName), { - HMODULE moduleAddress = LoadLibraryAOriginal(lpLibFileName); + HMODULE moduleAddress = _LoadLibraryA(lpLibFileName); if (moduleAddress) - { CallLoadLibraryACallbacks(lpLibFileName, moduleAddress); - } return moduleAddress; -} +}) -typedef HMODULE (*LoadLibraryExWType)(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); -LoadLibraryExWType LoadLibraryExWOriginal; -HMODULE LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +AUTOHOOK_ABSOLUTEADDR(_LoadLibraryExW, LoadLibraryExW, +HMODULE, WINAPI, (LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags), { - HMODULE moduleAddress = LoadLibraryExWOriginal(lpLibFileName, hFile, dwFlags); + HMODULE moduleAddress = _LoadLibraryExW(lpLibFileName, hFile, dwFlags); if (moduleAddress) - { CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress); - } return moduleAddress; -} +}) -typedef HMODULE (*LoadLibraryWType)(LPCWSTR lpLibFileName); -LoadLibraryWType LoadLibraryWOriginal; -HMODULE LoadLibraryWHook(LPCWSTR lpLibFileName) +AUTOHOOK_ABSOLUTEADDR(_LoadLibraryW, LoadLibraryW, +HMODULE, WINAPI, (LPCWSTR lpLibFileName), { - HMODULE moduleAddress = LoadLibraryWOriginal(lpLibFileName); + HMODULE moduleAddress = _LoadLibraryW(lpLibFileName); if (moduleAddress) - { CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress); - } return moduleAddress; -} +}) void InstallInitialHooks() { if (MH_Initialize() != MH_OK) spdlog::error("MH_Initialize (minhook initialization) failed"); - HookEnabler hook; - ENABLER_CREATEHOOK(hook, &GetCommandLineA, &GetCommandLineAHook, reinterpret_cast(&GetCommandLineAOriginal)); - ENABLER_CREATEHOOK(hook, &LoadLibraryExA, &LoadLibraryExAHook, reinterpret_cast(&LoadLibraryExAOriginal)); - ENABLER_CREATEHOOK(hook, &LoadLibraryA, &LoadLibraryAHook, reinterpret_cast(&LoadLibraryAOriginal)); - ENABLER_CREATEHOOK(hook, &LoadLibraryExW, &LoadLibraryExWHook, reinterpret_cast(&LoadLibraryExWOriginal)); - ENABLER_CREATEHOOK(hook, &LoadLibraryW, &LoadLibraryWHook, reinterpret_cast(&LoadLibraryWOriginal)); + AUTOHOOK_DISPATCH() } \ No newline at end of file diff --git a/NorthstarDedicatedTest/hooks.h b/NorthstarDedicatedTest/hooks.h index 182cdbc4..36c99b39 100644 --- a/NorthstarDedicatedTest/hooks.h +++ b/NorthstarDedicatedTest/hooks.h @@ -32,16 +32,195 @@ class __dllLoadCallback std::string reliesOn); }; -#define CONCAT_(x, y, z) x##y##z -#define CONCAT(x, y, z) CONCAT_(x, y, z) +#define __CONCAT3(x, y, z) x##y##z +#define CONCAT3(x, y, z) __CONCAT3(x, y, z) +#define __CONCAT2(x, y) x##y +#define CONCAT2(x, y) __CONCAT2(x, y) #define __STR(s) #s -#define __ON_DLL_LOAD(dllName, func, side, counter, uniquestr, reliesOn) \ -__dllLoadCallback CONCAT(__dllLoadCallbackInstance, uniquestr, counter)(side, dllName, func, __STR(uniquestr), reliesOn); +// adds a callback to be called when a given dll is loaded, for creating hooks and such +#define __ON_DLL_LOAD(dllName, func, side, uniquestr, reliesOn) \ +namespace { __dllLoadCallback CONCAT2(__dllLoadCallbackInstance, __LINE__)(side, dllName, func, __STR(uniquestr), reliesOn); } -#define ON_DLL_LOAD(dllName, uniquestr, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::UNSIDED, __LINE__, uniquestr, "") -#define ON_DLL_LOAD_RELIESON(dllName, uniquestr, reliesOn, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::UNSIDED, __LINE__, uniquestr, __STR(reliesOn)) -#define ON_DLL_LOAD_CLIENT(dllName, uniquestr, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::CLIENT, __LINE__, uniquestr, "") -#define ON_DLL_LOAD_CLIENT_RELIESON(dllName, uniquestr, reliesOn, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::CLIENT, __LINE__, uniquestr, __STR(reliesOn)) -#define ON_DLL_LOAD_DEDI(dllName, uniquestr, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::DEDICATED_SERVER, __LINE__, uniquestr, "") -#define ON_DLL_LOAD_DEDI_RELIESON(dllName, uniquestr, reliesOn, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::DEDICATED_SERVER, __LINE__, uniquestr, __STR(reliesOn)) +#define ON_DLL_LOAD(dllName, uniquestr, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::UNSIDED, uniquestr, "") +#define ON_DLL_LOAD_RELIESON(dllName, uniquestr, reliesOn, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::UNSIDED, uniquestr, __STR(reliesOn)) +#define ON_DLL_LOAD_CLIENT(dllName, uniquestr, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::CLIENT, uniquestr, "") +#define ON_DLL_LOAD_CLIENT_RELIESON(dllName, uniquestr, reliesOn, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::CLIENT, uniquestr, __STR(reliesOn)) +#define ON_DLL_LOAD_DEDI(dllName, uniquestr, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::DEDICATED_SERVER, uniquestr, "") +#define ON_DLL_LOAD_DEDI_RELIESON(dllName, uniquestr, reliesOn, func) __ON_DLL_LOAD(dllName, func, eDllLoadCallbackSide::DEDICATED_SERVER, uniquestr, __STR(reliesOn)) + +// new macro hook stuff +class __autohook; + +class __fileAutohook +{ + public: + std::vector<__autohook*> hooks; + + void Dispatch(); +}; + +// initialise autohooks for this file +#define AUTOHOOK_INIT() \ +namespace { __fileAutohook __FILEAUTOHOOK; } \ + +// dispatch all autohooks in this file +#define AUTOHOOK_DISPATCH() \ +__FILEAUTOHOOK.Dispatch(); \ + +class __autohook +{ + public: + enum AddressResolutionMode + { + OFFSET_STRING, // we're using a string that of the format dllname.dll + offset + ABSOLUTE_ADDR, // we're using an absolute address, we don't need to process it at all + }; + + char* pFuncName; + + LPVOID pHookFunc; + LPVOID* ppOrigFunc; + + // address resolution props + AddressResolutionMode iAddressResolutionMode; + char* pAddrString = nullptr; // for OFFSET_STRING + LPVOID iAbsoluteAddress = nullptr; // for ABSOLUTE_ADDR + + public: + __autohook() = delete; + + __autohook(__fileAutohook* autohook, const char* funcName, LPVOID absoluteAddress, LPVOID* orig, LPVOID func) + : pHookFunc(func), ppOrigFunc(orig), iAbsoluteAddress(absoluteAddress) + { + iAddressResolutionMode = ABSOLUTE_ADDR; + + const int iFuncNameStrlen = strlen(funcName) + 1; + pFuncName = new char[iFuncNameStrlen]; + memcpy(pFuncName, funcName, iFuncNameStrlen); + + autohook->hooks.push_back(this); + } + + __autohook(__fileAutohook* autohook, const char* funcName, const char* addrString, LPVOID* orig, LPVOID func) + : pHookFunc(func), ppOrigFunc(orig) + { + iAddressResolutionMode = OFFSET_STRING; + + const int iFuncNameStrlen = strlen(funcName) + 1; + pFuncName = new char[iFuncNameStrlen]; + memcpy(pFuncName, funcName, iFuncNameStrlen); + + const int iAddrStrlen = strlen(addrString) + 1; + pAddrString = new char[iAddrStrlen]; + memcpy(pAddrString, addrString, iAddrStrlen); + + autohook->hooks.push_back(this); + } + + ~__autohook() + { + delete[] pFuncName; + + if (pAddrString) + delete[] pAddrString; + } + + void Dispatch() + { + LPVOID targetAddr = nullptr; + + // determine the address of the function we're hooking + switch (iAddressResolutionMode) + { + case ABSOLUTE_ADDR: + { + targetAddr = iAbsoluteAddress; + break; + } + + case OFFSET_STRING: + { + // in the format server.dll + 0xDEADBEEF + int iDllNameEnd = 0; + for (; !isspace(pAddrString[iDllNameEnd]) && pAddrString[iDllNameEnd] != '+'; iDllNameEnd++); + + char* pModuleName = new char[iDllNameEnd + 1]; + memcpy(pModuleName, pAddrString, iDllNameEnd); + pModuleName[iDllNameEnd] = '\0'; + + // get the module address + const HMODULE pModuleAddr = GetModuleHandleA(pModuleName); + + if (!pModuleAddr) + break; + + // get the offset string + uintptr_t iOffset = 0; + + int iOffsetBegin = iDllNameEnd; + int iOffsetEnd = strlen(pAddrString); + + // seek until we hit the start of the number offset + for (; !(pAddrString[iOffsetBegin] >= '0' && pAddrString[iOffsetBegin] <= '9') && pAddrString[iOffsetBegin]; iOffsetBegin++); + + bool bIsHex = pAddrString[iOffsetBegin] == '0' && (pAddrString[iOffsetBegin + 1] == 'X' || pAddrString[iOffsetBegin + 1] == 'x'); + if (bIsHex) + iOffset = std::stoi(pAddrString + iOffsetBegin + 2, 0, 16); + else + iOffset = std::stoi(pAddrString + iOffsetBegin); + + targetAddr = (char*)pModuleAddr + iOffset; + break; + } + } + + if (MH_CreateHook(targetAddr, pHookFunc, ppOrigFunc) == MH_OK) + { + if (MH_EnableHook(targetAddr) == MH_OK) + spdlog::info("Enabling hook {}", pFuncName); + else + spdlog::error("MH_EnableHook failed for function {}", pFuncName); + } + else + spdlog::error("MH_CreateHook failed for function {}", pFuncName); + } +}; + +// hook a function at a given offset from a dll to be dispatched with AUTOHOOK_DISPATCH() +#define AUTOHOOK(name, addrString, type, callingConvention, args, func) \ +namespace { \ + type(*callingConvention name) args; \ + type callingConvention CONCAT2(__autohookfunc, name) args func \ + __autohook CONCAT2(__autohook, __LINE__)(&__FILEAUTOHOOK, __STR(name), __STR(addrString), (LPVOID*)&name, (LPVOID)CONCAT2(__autohookfunc, name)); \ +} \ + +// hook a function at a given absolute constant address to be dispatched with AUTOHOOK_DISPATCH() +#define AUTOHOOK_ABSOLUTEADDR(name, addr, type, callingConvention, args, func) \ +namespace { \ + type(*callingConvention name) args; \ + type callingConvention CONCAT2(__autohookfunc, name) args func \ + __autohook CONCAT2(__autohook, __LINE__)(&__FILEAUTOHOOK, __STR(name), addr, (LPVOID*)&name, (LPVOID)CONCAT2(__autohookfunc, name)); \ +} \ + +class ManualHook +{ + public: + char* pFuncName; + + LPVOID pHookFunc; + LPVOID* ppOrigFunc; + + public: + ManualHook() = delete; + ManualHook(const char* funcName, LPVOID* orig, LPVOID func); + bool Dispatch(LPVOID addr); +}; + +// hook a function to be dispatched manually later +#define HOOK(varName, originalFunc, type, callingConvention, args, func) \ +namespace { \ + type(*callingConvention originalFunc) args; \ + type callingConvention CONCAT2(__manualhookfunc, varName) args func \ +} \ +ManualHook varName = ManualHook(varName, (LPVOID*)&originalFunc, (LPVOID)CONCAT2(__manualhookfunc, varName)); \ diff --git a/NorthstarDedicatedTest/host.cpp b/NorthstarDedicatedTest/host.cpp index d3a78877..fcc148b0 100644 --- a/NorthstarDedicatedTest/host.cpp +++ b/NorthstarDedicatedTest/host.cpp @@ -1,14 +1,15 @@ #include "pch.h" #include "convar.h" #include "modmanager.h" -#include "commandprint.h" -#include "mapsprint.h" +#include "printcommand.h" +#include "printmaps.h" #include "r2engine.h" typedef void (*Host_InitType)(bool bDedicated); Host_InitType Host_Init; void Host_InitHook(bool bDedicated) { + spdlog::info("Host_Init()"); Host_Init(bDedicated); // get all mod convars @@ -18,8 +19,8 @@ void Host_InitHook(bool bDedicated) vModConvarNames.push_back(cvar->Name); // strip hidden and devonly cvar flags - int iCvarsAltered = 0; - for (auto& pair : g_pCVar->DumpToMap()) + int iNumCvarsAltered = 0; + for (auto& pair : R2::g_pCVar->DumpToMap()) { // don't remove from mod cvars if (std::find(vModConvarNames.begin(), vModConvarNames.end(), pair.second->m_pszName) != vModConvarNames.end()) @@ -30,29 +31,36 @@ void Host_InitHook(bool bDedicated) if (flags & FCVAR_DEVELOPMENTONLY) { flags &= ~FCVAR_DEVELOPMENTONLY; - iCvarsAltered++; + iNumCvarsAltered++; } if (flags & FCVAR_HIDDEN) { flags &= ~FCVAR_HIDDEN; - iCvarsAltered++; + iNumCvarsAltered++; } pair.second->m_nFlags = flags; } - spdlog::info("Removed {} hidden/devonly cvar flags", iCvarsAltered); + spdlog::info("Removed {} hidden/devonly cvar flags", iNumCvarsAltered); // make servers able to run disconnect on clients - g_pCVar->FindCommand("disconnect")->m_nFlags |= FCVAR_SERVER_CAN_EXECUTE; + R2::g_pCVar->FindCommand("disconnect")->m_nFlags |= FCVAR_SERVER_CAN_EXECUTE; - // need to initialise these after host_init since they do stuff to preexisting concommands/convars + // make clients able to run status and ping + R2::g_pCVar->FindCommand("status")->m_nFlags |= FCVAR_CLIENTCMD_CAN_EXECUTE; + R2::g_pCVar->FindCommand("ping")->m_nFlags |= FCVAR_CLIENTCMD_CAN_EXECUTE; + + // need to initialise these after host_init since they do stuff to preexisting concommands/convars without being client/server specific InitialiseCommandPrint(); InitialiseMapsPrint(); - // run client autoexec if on client - if (!bDedicated) + // client/server autoexecs on necessary platforms + // dedi needs autoexec_ns_server on boot, while non-dedi will run it on on listen server start + if (bDedicated) + R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", R2::cmd_source_t::kCommandSrcCode); + else R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "exec autoexec_ns_client", R2::cmd_source_t::kCommandSrcCode); } diff --git a/NorthstarDedicatedTest/keyvalues.cpp b/NorthstarDedicatedTest/keyvalues.cpp index 86cda483..4ba11dad 100644 --- a/NorthstarDedicatedTest/keyvalues.cpp +++ b/NorthstarDedicatedTest/keyvalues.cpp @@ -1,8 +1,6 @@ #include "pch.h" #include "modmanager.h" -#include "hooks.h" #include "filesystem.h" -#include "hookutils.h" #include @@ -75,7 +73,7 @@ void ModManager::TryBuildKeyValues(const char* filename) newKvs += "\"\n"; // load original file, so we can parse out the name of the root obj (e.g. WeaponData for weapons) - std::string originalFile = R2FS::ReadVPKOriginalFile(filename); + std::string originalFile = R2::ReadVPKOriginalFile(filename); if (!originalFile.length()) { diff --git a/NorthstarDedicatedTest/languagehooks.cpp b/NorthstarDedicatedTest/languagehooks.cpp index 42ac9b6a..ac421077 100644 --- a/NorthstarDedicatedTest/languagehooks.cpp +++ b/NorthstarDedicatedTest/languagehooks.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "hooks.h" #include "tier0.h" + #include #include diff --git a/NorthstarDedicatedTest/latencyflex.cpp b/NorthstarDedicatedTest/latencyflex.cpp index c5868aa7..87057a00 100644 --- a/NorthstarDedicatedTest/latencyflex.cpp +++ b/NorthstarDedicatedTest/latencyflex.cpp @@ -1,6 +1,4 @@ #include "pch.h" -#include "hooks.h" -#include "hookutils.h" #include "convar.h" ConVar* Cvar_r_latencyflex; diff --git a/NorthstarDedicatedTest/localchatwriter.cpp b/NorthstarDedicatedTest/localchatwriter.cpp index 81a0286a..c4bf0ea9 100644 --- a/NorthstarDedicatedTest/localchatwriter.cpp +++ b/NorthstarDedicatedTest/localchatwriter.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "hooks.h" #include "localchatwriter.h" class vgui_BaseRichText_vtable; diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp index 014ef642..909d34ce 100644 --- a/NorthstarDedicatedTest/logging.cpp +++ b/NorthstarDedicatedTest/logging.cpp @@ -1,9 +1,7 @@ #include "pch.h" #include "logging.h" -#include "hooks.h" #include "sourceconsole.h" #include "spdlog/sinks/basic_file_sink.h" -#include "hookutils.h" #include "dedicated.h" #include "convar.h" #include diff --git a/NorthstarDedicatedTest/mapsprint.cpp b/NorthstarDedicatedTest/mapsprint.cpp deleted file mode 100644 index bf94c548..00000000 --- a/NorthstarDedicatedTest/mapsprint.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include "pch.h" -#include "mapsprint.h" -#include "convar.h" -#include "concommand.h" -#include "modmanager.h" -#include "tier0.h" -#include -#include - -enum class MapSource_t -{ - VPK, - GAMEDIR, - MOD -}; - -const std::unordered_map PrintMapSource = { - {MapSource_t::VPK, "VPK"}, - {MapSource_t::MOD, "MOD"}, - {MapSource_t::GAMEDIR, "R2"} -}; - -struct MapVPKInfo -{ - std::string name; - std::string parent; - MapSource_t source; -}; - -// our current list of maps in the game -std::vector vMapList; - -void RefreshMapList() -{ - vMapList.clear(); - - // get modded maps - // TODO: could probably check mod vpks to get mapnames from there too? - for (auto& modFilePair : g_pModManager->m_modFiles) - { - ModOverrideFile file = modFilePair.second; - if (file.path.extension() == ".bsp" && file.path.parent_path().string() == "maps") // only allow mods actually in /maps atm - { - MapVPKInfo& map = vMapList.emplace_back(); - map.name = file.path.stem().string(); - map.parent = file.owningMod->Name; - map.source = MapSource_t::MOD; - } - } - - // get maps in vpk - { - const int iNumRetailNonMapVpks = 1; - static const char* ppRetailNonMapVpks[] = { - "englishclient_frontend.bsp.pak000_dir.vpk"}; // don't include mp_common here as it contains mp_lobby - - // matches directory vpks, and captures their map name in the first group - static const std::regex rVpkMapRegex("englishclient_([a-zA-Z_]+)\\.bsp\\.pak000_dir\\.vpk", std::regex::icase); - - for (fs::directory_entry file : fs::directory_iterator("./vpk")) - { - std::string pathString = file.path().filename().string(); - - bool bIsValidMapVpk = true; - for (int i = 0; i < iNumRetailNonMapVpks; i++) - { - if (!pathString.compare(ppRetailNonMapVpks[i])) - { - bIsValidMapVpk = false; - break; - } - } - - if (!bIsValidMapVpk) - continue; - - // run our map vpk regex on the filename - std::smatch match; - std::regex_match(pathString, match, rVpkMapRegex); - - if (match.length() < 2) - continue; - - std::string mapName = match[1].str(); - // special case: englishclient_mp_common contains mp_lobby, so hardcode the name here - if (mapName == "mp_common") - mapName = "mp_lobby"; - - MapVPKInfo& map = vMapList.emplace_back(); - map.name = mapName; - map.parent = pathString; - map.source = MapSource_t::VPK; - } - } - - // get maps in game dir - for (fs::directory_entry file : fs::directory_iterator("./r2/maps")) - { - if (file.path().extension() == ".bsp") - { - MapVPKInfo& map = vMapList.emplace_back(); - map.name = file.path().stem().string(); - map.parent = "R2"; - map.source = MapSource_t::GAMEDIR; - } - } -} - -typedef int (*__fastcall _Host_Map_f_CompletionFuncType)( - char const* cmdname, char const* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]); -_Host_Map_f_CompletionFuncType _Host_Map_f_CompletionFunc; -int __fastcall _Host_Map_f_CompletionFuncHook( - char const* cmdname, char const* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]) -{ - // don't update our map list often from this func, only refresh every 10 seconds so we avoid constantly reading fs - static double flLastAutocompleteRefresh = -999; - - if (flLastAutocompleteRefresh + 10.0 < Tier0::Plat_FloatTime()) - { - RefreshMapList(); - flLastAutocompleteRefresh = Tier0::Plat_FloatTime(); - } - - // use a custom autocomplete func for all map loading command - const int cmdLength = strlen(cmdname); - const char* query = partial + cmdLength; - const int queryLength = strlen(query); - - int numMaps = 0; - for (int i = 0; i < COMMAND_COMPLETION_MAXITEMS && i < vMapList.size(); i++) - { - if (!strncmp(query, vMapList[i].name.c_str(), queryLength)) - { - strcpy(commands[numMaps], cmdname); - strncpy_s( - commands[numMaps++] + cmdLength, - COMMAND_COMPLETION_ITEM_LENGTH, &vMapList[i].name[0], - COMMAND_COMPLETION_ITEM_LENGTH - cmdLength); - } - } - - return numMaps; -} - -void ConCommand_maps(const CCommand& args) -{ - if (args.ArgC() < 2) - { - spdlog::info("Usage: maps "); - spdlog::info("maps * for full listing"); - return; - } - - RefreshMapList(); - - for (MapVPKInfo& map : vMapList) // need to figure out a nice way to include parent path without making the formatting awful - if ((*args.Arg(1) == '*' && !args.Arg(1)[1]) || strstr(map.name.c_str(), args.Arg(1))) - spdlog::info("({}) {}", PrintMapSource.at(map.source), map.name); -} - -void InitialiseMapsPrint() -{ - ConCommand* mapsCommand = g_pCVar->FindCommand("maps"); - mapsCommand->m_pCommandCallback = ConCommand_maps; - - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - (char*)GetModuleHandleA("engine.dll") + 0x161AE0, - &_Host_Map_f_CompletionFuncHook, - reinterpret_cast(&_Host_Map_f_CompletionFunc)); -} \ No newline at end of file diff --git a/NorthstarDedicatedTest/mapsprint.h b/NorthstarDedicatedTest/mapsprint.h deleted file mode 100644 index b01761c0..00000000 --- a/NorthstarDedicatedTest/mapsprint.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -void InitialiseMapsPrint(); diff --git a/NorthstarDedicatedTest/maxplayers.cpp b/NorthstarDedicatedTest/maxplayers.cpp index 5fa684d1..22f2f59a 100644 --- a/NorthstarDedicatedTest/maxplayers.cpp +++ b/NorthstarDedicatedTest/maxplayers.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "hooks.h" #include "tier0.h" // never set this to anything below 32 diff --git a/NorthstarDedicatedTest/miscclientfixes.cpp b/NorthstarDedicatedTest/miscclientfixes.cpp index dce6b6c8..4d0e2508 100644 --- a/NorthstarDedicatedTest/miscclientfixes.cpp +++ b/NorthstarDedicatedTest/miscclientfixes.cpp @@ -1,7 +1,5 @@ #include "pch.h" -#include "hooks.h" #include "convar.h" -#include "hookutils.h" #include "NSMem.h" ON_DLL_LOAD_CLIENT_RELIESON("client.dll", MiscClientFixes, ConVar, [](HMODULE baseAddress) diff --git a/NorthstarDedicatedTest/misccommands.cpp b/NorthstarDedicatedTest/misccommands.cpp index fd04a7fe..5a77a4f9 100644 --- a/NorthstarDedicatedTest/misccommands.cpp +++ b/NorthstarDedicatedTest/misccommands.cpp @@ -22,7 +22,7 @@ void ConCommand_ns_start_reauth_and_leave_to_lobby(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(R2::g_LocalPlayerUserID, g_MasterServerManager->m_sOwnClientAuthToken); + g_MasterServerManager->AuthenticateWithOwnServer(R2::g_pLocalPlayerUserID, g_MasterServerManager->m_sOwnClientAuthToken); } void ConCommand_ns_end_reauth_and_leave_to_lobby(const CCommand& arg) diff --git a/NorthstarDedicatedTest/miscserverfixes.cpp b/NorthstarDedicatedTest/miscserverfixes.cpp index 455a3686..3abb28f4 100644 --- a/NorthstarDedicatedTest/miscserverfixes.cpp +++ b/NorthstarDedicatedTest/miscserverfixes.cpp @@ -1,6 +1,4 @@ #include "pch.h" -#include "hooks.h" -#include "hookutils.h" #include "NSMem.h" diff --git a/NorthstarDedicatedTest/miscserverscript.cpp b/NorthstarDedicatedTest/miscserverscript.cpp index 9e8d26ff..488e071a 100644 --- a/NorthstarDedicatedTest/miscserverscript.cpp +++ b/NorthstarDedicatedTest/miscserverscript.cpp @@ -53,7 +53,7 @@ SQRESULT SQ_IsPlayerIndexLocalPlayer(void* sqvm) return SQRESULT_ERROR; } - g_pServerSquirrel->pushbool(sqvm, !strcmp(R2::g_LocalPlayerUserID, (char*)player + 0xF500)); + g_pServerSquirrel->pushbool(sqvm, !strcmp(R2::g_pLocalPlayerUserID, (char*)player + 0xF500)); return SQRESULT_NOTNULL; } diff --git a/NorthstarDedicatedTest/modlocalisation.cpp b/NorthstarDedicatedTest/modlocalisation.cpp index 3fd96000..85df8902 100644 --- a/NorthstarDedicatedTest/modlocalisation.cpp +++ b/NorthstarDedicatedTest/modlocalisation.cpp @@ -1,15 +1,12 @@ #include "pch.h" -#include "hooks.h" -#include "hookutils.h" #include "modmanager.h" - -typedef bool (*AddLocalisationFileType)(void* g_pVguiLocalize, const char* path, const char* pathId, char unknown); +typedef bool (*AddLocalisationFileType)(void* pVguiLocalize, const char* path, const char* pathId, char unknown); AddLocalisationFileType AddLocalisationFile; -bool AddLocalisationFileHook(void* g_pVguiLocalize, const char* path, const char* pathId, char unknown) +bool AddLocalisationFileHook(void* pVguiLocalize, const char* path, const char* pathId, char unknown) { static bool bLoadModLocalisationFiles = true; - bool ret = AddLocalisationFile(g_pVguiLocalize, path, pathId, unknown); + bool ret = AddLocalisationFile(pVguiLocalize, path, pathId, unknown); if (ret) spdlog::info("Loaded localisation file {} successfully", path); @@ -22,7 +19,7 @@ bool AddLocalisationFileHook(void* g_pVguiLocalize, const char* path, const char for (Mod mod : g_pModManager->m_loadedMods) if (mod.Enabled) for (std::string& localisationFile : mod.LocalisationFiles) - AddLocalisationFile(g_pVguiLocalize, localisationFile.c_str(), pathId, unknown); + AddLocalisationFile(pVguiLocalize, localisationFile.c_str(), pathId, unknown); bLoadModLocalisationFiles = true; diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp index 27576456..1a9ff1ab 100644 --- a/NorthstarDedicatedTest/modmanager.cpp +++ b/NorthstarDedicatedTest/modmanager.cpp @@ -1,10 +1,13 @@ #include "pch.h" #include "modmanager.h" -#include "hooks.h" #include "convar.h" #include "concommand.h" #include "audio.h" #include "masterserver.h" +#include "filesystem.h" +#include "rpakfilesystem.h" +#include "configurables.h" + #include "rapidjson/error/en.h" #include "rapidjson/document.h" #include "rapidjson/ostreamwrapper.h" @@ -14,9 +17,6 @@ #include #include #include -#include "filesystem.h" -#include "rpakfilesystem.h" -#include "configurables.h" ModManager* g_pModManager; @@ -330,7 +330,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_pCVar->FindVar(convar->Name.c_str())) // make sure convar isn't registered yet, unsure if necessary but idk what + if (!R2::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()); @@ -376,7 +376,7 @@ void ModManager::LoadMods() modVpk.m_sVpkPath = vpkName; if (m_hasLoadedMods && modVpk.m_bAutoLoad) - (*R2FS::g_pFilesystem)->m_vtable->MountVPK(*R2FS::g_pFilesystem, vpkName.c_str()); + (*R2::g_pFilesystem)->m_vtable->MountVPK(*R2::g_pFilesystem, vpkName.c_str()); } } } diff --git a/NorthstarDedicatedTest/pdef.cpp b/NorthstarDedicatedTest/pdef.cpp index fdd1dfa4..367d1bbf 100644 --- a/NorthstarDedicatedTest/pdef.cpp +++ b/NorthstarDedicatedTest/pdef.cpp @@ -1,8 +1,8 @@ #include "pch.h" #include "modmanager.h" #include "filesystem.h" -#include "hookutils.h" #include "pdef.h" + #include #include #include @@ -14,7 +14,7 @@ void ModManager::BuildPdef() fs::path MOD_PDEF_PATH = fs::path(GetCompiledAssetsPath() / MOD_PDEF_SUFFIX); fs::remove(MOD_PDEF_PATH); - std::string pdef = R2FS::ReadVPKOriginalFile(VPK_PDEF_PATH); + std::string pdef = R2::ReadVPKOriginalFile(VPK_PDEF_PATH); for (Mod& mod : m_loadedMods) { diff --git a/NorthstarDedicatedTest/playlist.cpp b/NorthstarDedicatedTest/playlist.cpp index e3acc490..0e0ed3c2 100644 --- a/NorthstarDedicatedTest/playlist.cpp +++ b/NorthstarDedicatedTest/playlist.cpp @@ -1,12 +1,12 @@ #include "pch.h" #include "playlist.h" #include "NSMem.h" -#include "hooks.h" #include "concommand.h" #include "convar.h" -#include "hookutils.h" #include "squirrel.h" +AUTOHOOK_INIT() + // use the R2 namespace for game funcs namespace R2 { @@ -16,23 +16,6 @@ namespace R2 GetCurrentPlaylistVarType GetCurrentPlaylistVar; } // namespace R2 -void ConCommand_playlist(const CCommand& args) -{ - if (args.ArgC() < 2) - return; - - R2::SetCurrentPlaylist(args.Arg(1)); -} - -void ConCommand_setplaylistvaroverride(const CCommand& args) -{ - if (args.ArgC() < 3) - return; - - for (int i = 1; i < args.ArgC(); i += 2) - R2::SetPlaylistVarOverride(args.Arg(i), args.Arg(i + 1)); -} - ConVar* Cvar_ns_use_clc_SetPlaylistVarOverride; typedef char (*Onclc_SetPlaylistVarOverrideType)(void* a1, void* a2); @@ -67,7 +50,7 @@ typedef int (*GetCurrentGamemodeMaxPlayersType)(); GetCurrentGamemodeMaxPlayersType GetCurrentGamemodeMaxPlayers; int GetCurrentGamemodeMaxPlayersHook() { - char* maxPlayersStr = R2::GetCurrentPlaylistVar("max_players", 0); + const char* maxPlayersStr = R2::GetCurrentPlaylistVar("max_players", 0); if (!maxPlayersStr) return GetCurrentGamemodeMaxPlayers(); @@ -75,13 +58,35 @@ int GetCurrentGamemodeMaxPlayersHook() return maxPlayers; } +void ConCommand_playlist(const CCommand& args) +{ + if (args.ArgC() < 2) + return; + + R2::SetCurrentPlaylist(args.Arg(1)); +} + +void ConCommand_setplaylistvaroverride(const CCommand& args) +{ + if (args.ArgC() < 3) + return; + + for (int i = 1; i < args.ArgC(); i += 2) + R2::SetPlaylistVarOverride(args.Arg(i), args.Arg(i + 1)); +} + ON_DLL_LOAD_RELIESON("engine.dll", PlaylistHooks, ConCommand, [](HMODULE baseAddress) { // playlist is the name of the command on respawn servers, but we already use setplaylist so can't get rid of it RegisterConCommand("playlist", ConCommand_playlist, "Sets the current playlist", FCVAR_NONE); RegisterConCommand("setplaylist", ConCommand_playlist, "Sets the current playlist", FCVAR_NONE); RegisterConCommand("setplaylistvaroverrides", ConCommand_setplaylistvaroverride, "sets a playlist var override", FCVAR_NONE); - + + R2::GetCurrentPlaylistName = (R2::GetCurrentPlaylistNameType)((char*)baseAddress + 0x18C640); + R2::SetCurrentPlaylist = (R2::SetCurrentPlaylistType)((char*)baseAddress + 0x18EB20); + R2::SetPlaylistVarOverride = (R2::SetPlaylistVarOverrideType)((char*)baseAddress + 0x18ED00); + R2::GetCurrentPlaylistVar = (R2::GetCurrentPlaylistVarType)((char*)baseAddress + 0x18C680); + // note: clc_SetPlaylistVarOverride is pretty insecure, since it allows for entirely arbitrary playlist var overrides to be sent to the // server, this is somewhat restricted on custom servers to prevent it being done outside of private matches, but ideally it should be // disabled altogether, since the custom menus won't use it anyway this should only really be accepted if you want vanilla client @@ -89,11 +94,6 @@ ON_DLL_LOAD_RELIESON("engine.dll", PlaylistHooks, ConCommand, [](HMODULE baseAdd Cvar_ns_use_clc_SetPlaylistVarOverride = new ConVar( "ns_use_clc_SetPlaylistVarOverride", "0", FCVAR_GAMEDLL, "Whether the server should accept clc_SetPlaylistVarOverride messages"); - R2::GetCurrentPlaylistName = (R2::GetCurrentPlaylistNameType)((char*)baseAddress + 0x18C640); - R2::SetCurrentPlaylist = (R2::SetCurrentPlaylistType)((char*)baseAddress + 0x18EB20); - R2::SetPlaylistVarOverride = (R2::SetPlaylistVarOverrideType)((char*)baseAddress + 0x18ED00); - R2::GetCurrentPlaylistVar = (R2::GetCurrentPlaylistVarType)((char*)baseAddress + 0x18C680); - HookEnabler hook; ENABLER_CREATEHOOK( hook, (char*)baseAddress + 0x222180, &Onclc_SetPlaylistVarOverrideHook, reinterpret_cast(&Onclc_SetPlaylistVarOverride)); diff --git a/NorthstarDedicatedTest/playlist.h b/NorthstarDedicatedTest/playlist.h index 28e5aa9d..4ad48ee9 100644 --- a/NorthstarDedicatedTest/playlist.h +++ b/NorthstarDedicatedTest/playlist.h @@ -12,6 +12,6 @@ namespace R2 typedef void (*SetPlaylistVarOverrideType)(const char* varName, const char* value); extern SetPlaylistVarOverrideType SetPlaylistVarOverride; - typedef char* (*GetCurrentPlaylistVarType)(const char* varName, bool useOverrides); + typedef const char* (*GetCurrentPlaylistVarType)(const char* varName, bool useOverrides); extern GetCurrentPlaylistVarType GetCurrentPlaylistVar; } // namespace R2 diff --git a/NorthstarDedicatedTest/plugins.cpp b/NorthstarDedicatedTest/plugins.cpp index 157ac416..e7e40dad 100644 --- a/NorthstarDedicatedTest/plugins.cpp +++ b/NorthstarDedicatedTest/plugins.cpp @@ -1,10 +1,10 @@ #include "pch.h" -#include "hooks.h" #include "squirrel.h" #include "plugins.h" -#include #include "masterserver.h" #include "convar.h" + +#include #include /// diff --git a/NorthstarDedicatedTest/printcommand.h b/NorthstarDedicatedTest/printcommand.h new file mode 100644 index 00000000..6c3ef850 --- /dev/null +++ b/NorthstarDedicatedTest/printcommand.h @@ -0,0 +1,6 @@ +#pragma once +#include "concommand.h" + +void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name); +void TryPrintCvarHelpForCommand(const char* pCommand); +void InitialiseCommandPrint(); diff --git a/NorthstarDedicatedTest/printcommands.cpp b/NorthstarDedicatedTest/printcommands.cpp new file mode 100644 index 00000000..c56283a1 --- /dev/null +++ b/NorthstarDedicatedTest/printcommands.cpp @@ -0,0 +1,161 @@ +#include "pch.h" +#include "printcommand.h" +#include "convar.h" +#include "concommand.h" + +void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name) +{ + if (!command) + { + spdlog::info("unknown command {}", name); + return; + } + + // build string for flags if not FCVAR_NONE + std::string flagString; + if (command->GetFlags() != FCVAR_NONE) + { + flagString = "( "; + + for (auto& flagPair : g_PrintCommandFlags) + { + if (command->GetFlags() & flagPair.first) + { + flagString += flagPair.second; + flagString += " "; + } + } + + flagString += ") "; + } + + // temp because command->IsCommand does not currently work + ConVar* cvar = R2::g_pCVar->FindVar(command->m_pszName); + if (cvar) + spdlog::info("\"{}\" = \"{}\" {}- {}", cvar->GetBaseName(), cvar->GetString(), flagString, cvar->GetHelpText()); + else + spdlog::info("\"{}\" {} - {}", command->m_pszName, flagString, command->GetHelpText()); +} + +void TryPrintCvarHelpForCommand(const char* pCommand) +{ + // try to display help text for an inputted command string from the console + int pCommandLen = strlen(pCommand); + char* pCvarStr = new char[pCommandLen]; + strcpy(pCvarStr, pCommand); + + // trim whitespace from right + for (int i = pCommandLen - 1; i; i--) + { + if (isspace(pCvarStr[i])) + pCvarStr[i] = '\0'; + else + break; + } + + // check if we're inputting a cvar, but not setting it at all + ConVar* cvar = R2::g_pCVar->FindVar(pCvarStr); + if (cvar) + PrintCommandHelpDialogue(&cvar->m_ConCommandBase, pCvarStr); + + delete[] pCvarStr; +} + +void ConCommand_help(const CCommand& arg) +{ + if (arg.ArgC() < 2) + { + spdlog::info("Usage: help "); + return; + } + + PrintCommandHelpDialogue(R2::g_pCVar->FindCommandBase(arg.Arg(1)), arg.Arg(1)); +} + +void ConCommand_find(const CCommand& arg) +{ + if (arg.ArgC() < 2) + { + spdlog::info("Usage: find [...]"); + return; + } + + char pTempName[256]; + char pTempSearchTerm[256]; + + for (auto& map : R2::g_pCVar->DumpToMap()) + { + bool bPrintCommand = true; + for (int i = 0; i < arg.ArgC() - 1; i++) + { + // make lowercase to avoid case sensitivity + strncpy(pTempName, map.second->m_pszName, sizeof(pTempName)); + strncpy(pTempSearchTerm, arg.Arg(i + 1), sizeof(pTempSearchTerm)); + + for (int i = 0; pTempName[i]; i++) + pTempName[i] = tolower(pTempName[i]); + + for (int i = 0; pTempSearchTerm[i]; i++) + pTempSearchTerm[i] = tolower(pTempSearchTerm[i]); + + if (!strstr(pTempName, pTempSearchTerm)) + { + bPrintCommand = false; + break; + } + } + + if (bPrintCommand) + PrintCommandHelpDialogue(map.second, map.second->m_pszName); + } +} + +void ConCommand_findflags(const CCommand& arg) +{ + if (arg.ArgC() < 2) + { + spdlog::info("Usage: findflags "); + for (auto& flagPair : g_PrintCommandFlags) + spdlog::info(" - {}", flagPair.second); + + return; + } + + // convert input flag to uppercase + char* upperFlag = new char[strlen(arg.Arg(1))]; + strcpy(upperFlag, arg.Arg(1)); + + for (int i = 0; upperFlag[i]; i++) + upperFlag[i] = toupper(upperFlag[i]); + + // resolve flag name => int flags + int resolvedFlag = FCVAR_NONE; + for (auto& flagPair : g_PrintCommandFlags) + { + if (!strcmp(flagPair.second, upperFlag)) + { + resolvedFlag = flagPair.first; + break; + } + } + + // print cvars + for (auto& map : R2::g_pCVar->DumpToMap()) + { + if (map.second->m_nFlags & resolvedFlag) + PrintCommandHelpDialogue(map.second, map.second->m_pszName); + } + + delete[] upperFlag; +} + +void InitialiseCommandPrint() +{ + RegisterConCommand("find", ConCommand_find, "Find concommands with the specified string in their name/help text.", FCVAR_NONE); + RegisterConCommand("findflags", ConCommand_findflags, "Find concommands by flags.", FCVAR_NONE); + // help is already a command, so we need to modify the preexisting command to use our func instead + // and clear the flags also + ConCommand* helpCommand = R2::g_pCVar->FindCommand("help"); + helpCommand->m_nFlags = FCVAR_NONE; + helpCommand->m_pCommandCallback = ConCommand_help; +} \ No newline at end of file diff --git a/NorthstarDedicatedTest/printmaps.cpp b/NorthstarDedicatedTest/printmaps.cpp new file mode 100644 index 00000000..7fbe088a --- /dev/null +++ b/NorthstarDedicatedTest/printmaps.cpp @@ -0,0 +1,169 @@ +#include "pch.h" +#include "printmaps.h" +#include "convar.h" +#include "concommand.h" +#include "modmanager.h" +#include "tier0.h" + +#include +#include + +AUTOHOOK_INIT() + +enum class MapSource_t +{ + VPK, + GAMEDIR, + MOD +}; + +const std::unordered_map PrintMapSource = { + {MapSource_t::VPK, "VPK"}, + {MapSource_t::MOD, "MOD"}, + {MapSource_t::GAMEDIR, "R2"} +}; + +struct MapVPKInfo +{ + std::string name; + std::string parent; + MapSource_t source; +}; + +// our current list of maps in the game +std::vector vMapList; + +void RefreshMapList() +{ + vMapList.clear(); + + // get modded maps + // TODO: could probably check mod vpks to get mapnames from there too? + for (auto& modFilePair : g_pModManager->m_modFiles) + { + ModOverrideFile file = modFilePair.second; + if (file.path.extension() == ".bsp" && file.path.parent_path().string() == "maps") // only allow mod maps actually in /maps atm + { + MapVPKInfo& map = vMapList.emplace_back(); + map.name = file.path.stem().string(); + map.parent = file.owningMod->Name; + map.source = MapSource_t::MOD; + } + } + + // get maps in vpk + { + const int iNumRetailNonMapVpks = 1; + static const char* const ppRetailNonMapVpks[] = { + "englishclient_frontend.bsp.pak000_dir.vpk"}; // don't include mp_common here as it contains mp_lobby + + // matches directory vpks, and captures their map name in the first group + static const std::regex rVpkMapRegex("englishclient_([a-zA-Z_]+)\\.bsp\\.pak000_dir\\.vpk", std::regex::icase); + + for (fs::directory_entry file : fs::directory_iterator("./vpk")) + { + std::string pathString = file.path().filename().string(); + + bool bIsValidMapVpk = true; + for (int i = 0; i < iNumRetailNonMapVpks; i++) + { + if (!pathString.compare(ppRetailNonMapVpks[i])) + { + bIsValidMapVpk = false; + break; + } + } + + if (!bIsValidMapVpk) + continue; + + // run our map vpk regex on the filename + std::smatch match; + std::regex_match(pathString, match, rVpkMapRegex); + + if (match.length() < 2) + continue; + + std::string mapName = match[1].str(); + // special case: englishclient_mp_common contains mp_lobby, so hardcode the name here + if (mapName == "mp_common") + mapName = "mp_lobby"; + + MapVPKInfo& map = vMapList.emplace_back(); + map.name = mapName; + map.parent = pathString; + map.source = MapSource_t::VPK; + } + } + + // get maps in game dir + for (fs::directory_entry file : fs::directory_iterator("./r2/maps")) + { + if (file.path().extension() == ".bsp") + { + MapVPKInfo& map = vMapList.emplace_back(); + map.name = file.path().stem().string(); + map.parent = "R2"; + map.source = MapSource_t::GAMEDIR; + } + } +} + +AUTOHOOK(_Host_Map_f_CompletionFunc, engine.dll + 0x161AE0, +int, __fastcall, (const char const* cmdname, const char const* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]), +{ + // don't update our map list often from this func, only refresh every 10 seconds so we avoid constantly reading fs + static double flLastAutocompleteRefresh = -999; + + if (flLastAutocompleteRefresh + 10.0 < Tier0::Plat_FloatTime()) + { + RefreshMapList(); + flLastAutocompleteRefresh = Tier0::Plat_FloatTime(); + } + + // use a custom autocomplete func for all map loading command + const int cmdLength = strlen(cmdname); + const char* query = partial + cmdLength; + const int queryLength = strlen(query); + + int numMaps = 0; + for (int i = 0; i < COMMAND_COMPLETION_MAXITEMS && i < vMapList.size(); i++) + { + if (!strncmp(query, vMapList[i].name.c_str(), queryLength)) + { + strcpy(commands[numMaps], cmdname); + strncpy_s( + commands[numMaps++] + cmdLength, + COMMAND_COMPLETION_ITEM_LENGTH, + &vMapList[i].name[0], + COMMAND_COMPLETION_ITEM_LENGTH - cmdLength); + } + } + + return numMaps; +}) + + +void ConCommand_maps(const CCommand& args) +{ + if (args.ArgC() < 2) + { + spdlog::info("Usage: maps "); + spdlog::info("maps * for full listing"); + return; + } + + RefreshMapList(); + + for (MapVPKInfo& map : vMapList) // need to figure out a nice way to include parent path without making the formatting awful + if ((*args.Arg(1) == '*' && !args.Arg(1)[1]) || strstr(map.name.c_str(), args.Arg(1))) + spdlog::info("({}) {}", PrintMapSource.at(map.source), map.name); +} + +void InitialiseMapsPrint() +{ + AUTOHOOK_DISPATCH() + + ConCommand* mapsCommand = R2::g_pCVar->FindCommand("maps"); + mapsCommand->m_pCommandCallback = ConCommand_maps; +} \ No newline at end of file diff --git a/NorthstarDedicatedTest/printmaps.h b/NorthstarDedicatedTest/printmaps.h new file mode 100644 index 00000000..b01761c0 --- /dev/null +++ b/NorthstarDedicatedTest/printmaps.h @@ -0,0 +1,2 @@ +#pragma once +void InitialiseMapsPrint(); diff --git a/NorthstarDedicatedTest/r2client.cpp b/NorthstarDedicatedTest/r2client.cpp index 845eebf1..0d56e16f 100644 --- a/NorthstarDedicatedTest/r2client.cpp +++ b/NorthstarDedicatedTest/r2client.cpp @@ -6,15 +6,15 @@ using namespace R2; // use the R2 namespace for game funcs namespace R2 { - char* g_LocalPlayerUserID; - char* g_LocalPlayerOriginToken; + char* g_pLocalPlayerUserID; + char* g_pLocalPlayerOriginToken; GetBaseLocalClientType GetBaseLocalClient; } // namespace R2 ON_DLL_LOAD("engine.dll", R2EngineClient, [](HMODULE baseAddress) { - g_LocalPlayerUserID = (char*)baseAddress + 0x13F8E688; - g_LocalPlayerOriginToken = (char*)baseAddress + 0x13979C80; + g_pLocalPlayerUserID = (char*)baseAddress + 0x13F8E688; + g_pLocalPlayerOriginToken = (char*)baseAddress + 0x13979C80; GetBaseLocalClient = (GetBaseLocalClientType)((char*)baseAddress + 0x78200); }) \ No newline at end of file diff --git a/NorthstarDedicatedTest/r2client.h b/NorthstarDedicatedTest/r2client.h index f4e0c8a8..c2de3397 100644 --- a/NorthstarDedicatedTest/r2client.h +++ b/NorthstarDedicatedTest/r2client.h @@ -3,8 +3,8 @@ // use the R2 namespace for game funcs namespace R2 { - extern char* g_LocalPlayerUserID; - extern char* g_LocalPlayerOriginToken; + extern char* g_pLocalPlayerUserID; + extern char* g_pLocalPlayerOriginToken; typedef void* (*GetBaseLocalClientType)(); extern GetBaseLocalClientType GetBaseLocalClient; diff --git a/NorthstarDedicatedTest/rpakfilesystem.cpp b/NorthstarDedicatedTest/rpakfilesystem.cpp index 4da59836..e7c88674 100644 --- a/NorthstarDedicatedTest/rpakfilesystem.cpp +++ b/NorthstarDedicatedTest/rpakfilesystem.cpp @@ -1,7 +1,5 @@ #include "pch.h" #include "rpakfilesystem.h" -#include "hooks.h" -#include "hookutils.h" #include "modmanager.h" #include "dedicated.h" diff --git a/NorthstarDedicatedTest/scriptbrowserhooks.cpp b/NorthstarDedicatedTest/scriptbrowserhooks.cpp index bb917899..de78daf9 100644 --- a/NorthstarDedicatedTest/scriptbrowserhooks.cpp +++ b/NorthstarDedicatedTest/scriptbrowserhooks.cpp @@ -1,26 +1,23 @@ #include "pch.h" -#include "hooks.h" -#include "hookutils.h" + +AUTOHOOK_INIT() bool* bIsOriginOverlayEnabled; -typedef void (*OpenExternalWebBrowserType)(char* url, char flags); -OpenExternalWebBrowserType OpenExternalWebBrowser; -void OpenExternalWebBrowserHook(char* url, char flags) +AUTOHOOK(OpenExternalWebBrowser, engine.dll + 0x184E40, +void,, (char* pUrl, char flags), { bool bIsOriginOverlayEnabledOriginal = *bIsOriginOverlayEnabled; - if (flags & 2 && !strncmp(url, "http", 4)) // custom force external browser flag + if (flags & 2 && !strncmp(pUrl, "http", 4)) // custom force external browser flag *bIsOriginOverlayEnabled = false; // if this bool is false, game will use an external browser rather than the origin overlay one - OpenExternalWebBrowser(url, flags); + OpenExternalWebBrowser(pUrl, flags); *bIsOriginOverlayEnabled = bIsOriginOverlayEnabledOriginal; -} +}) ON_DLL_LOAD_CLIENT("engine.dll", ScriptExternalBrowserHooks, [](HMODULE baseAddress) { - bIsOriginOverlayEnabled = (bool*)baseAddress + 0x13978255; + AUTOHOOK_DISPATCH() - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x184E40, &OpenExternalWebBrowserHook, reinterpret_cast(&OpenExternalWebBrowser)); + bIsOriginOverlayEnabled = (bool*)baseAddress + 0x13978255; }) \ No newline at end of file diff --git a/NorthstarDedicatedTest/scriptmainmenupromos.cpp b/NorthstarDedicatedTest/scriptmainmenupromos.cpp index 013aa8b3..a4f65dd8 100644 --- a/NorthstarDedicatedTest/scriptmainmenupromos.cpp +++ b/NorthstarDedicatedTest/scriptmainmenupromos.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "hooks.h" #include "squirrel.h" #include "masterserver.h" diff --git a/NorthstarDedicatedTest/scriptmodmenu.cpp b/NorthstarDedicatedTest/scriptmodmenu.cpp index 96789068..e1e8c7bb 100644 --- a/NorthstarDedicatedTest/scriptmodmenu.cpp +++ b/NorthstarDedicatedTest/scriptmodmenu.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "hooks.h" #include "modmanager.h" #include "squirrel.h" diff --git a/NorthstarDedicatedTest/scriptserverbrowser.cpp b/NorthstarDedicatedTest/scriptserverbrowser.cpp index 5956d0bb..e1139474 100644 --- a/NorthstarDedicatedTest/scriptserverbrowser.cpp +++ b/NorthstarDedicatedTest/scriptserverbrowser.cpp @@ -333,7 +333,7 @@ SQRESULT SQ_TryAuthWithServer(void* sqvm) // do auth g_MasterServerManager->AuthenticateWithServer( - R2::g_LocalPlayerUserID, + R2::g_pLocalPlayerUserID, g_MasterServerManager->m_sOwnClientAuthToken, g_MasterServerManager->m_vRemoteServers[serverIndex].id, (char*)password); @@ -391,7 +391,7 @@ SQRESULT SQ_ConnectToAuthedServer(void* sqvm) SQRESULT SQ_TryAuthWithLocalServer(void* sqvm) { // do auth request - g_MasterServerManager->AuthenticateWithOwnServer(R2::g_LocalPlayerUserID, g_MasterServerManager->m_sOwnClientAuthToken); + g_MasterServerManager->AuthenticateWithOwnServer(R2::g_pLocalPlayerUserID, g_MasterServerManager->m_sOwnClientAuthToken); return SQRESULT_NULL; } diff --git a/NorthstarDedicatedTest/scriptservertoclientstringcommand.cpp b/NorthstarDedicatedTest/scriptservertoclientstringcommand.cpp index 26660c27..3bb7f60d 100644 --- a/NorthstarDedicatedTest/scriptservertoclientstringcommand.cpp +++ b/NorthstarDedicatedTest/scriptservertoclientstringcommand.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "hooks.h" #include "squirrel.h" #include "convar.h" #include "concommand.h" diff --git a/NorthstarDedicatedTest/scriptsrson.cpp b/NorthstarDedicatedTest/scriptsrson.cpp index 6de78d12..d7df84e8 100644 --- a/NorthstarDedicatedTest/scriptsrson.cpp +++ b/NorthstarDedicatedTest/scriptsrson.cpp @@ -12,7 +12,7 @@ void ModManager::BuildScriptsRson() fs::path MOD_SCRIPTS_RSON_PATH = fs::path(GetCompiledAssetsPath() / MOD_SCRIPTS_RSON_SUFFIX); fs::remove(MOD_SCRIPTS_RSON_PATH); - std::string scriptsRson = R2FS::ReadVPKOriginalFile(VPK_SCRIPTS_RSON_PATH); + std::string scriptsRson = R2::ReadVPKOriginalFile(VPK_SCRIPTS_RSON_PATH); scriptsRson += "\n\n// START MODDED SCRIPT CONTENT\n\n"; // newline before we start custom stuff for (Mod& mod : m_loadedMods) diff --git a/NorthstarDedicatedTest/serverauthentication.cpp b/NorthstarDedicatedTest/serverauthentication.cpp index a8de1a63..efecd692 100644 --- a/NorthstarDedicatedTest/serverauthentication.cpp +++ b/NorthstarDedicatedTest/serverauthentication.cpp @@ -21,8 +21,6 @@ #include #include -using namespace Tier0; - const char* AUTHSERVER_VERIFY_STRING = "I am a northstar server!"; // global vars @@ -161,7 +159,7 @@ bool ServerAuthenticationManager::AuthenticatePlayer(void* player, int64_t uid, strcpy((char*)player + 0xF500, strUid.c_str()); // reset from disk if we're doing that - if (m_bForceReadLocalPlayerPersistenceFromDisk && !strcmp(authData.uid, R2::g_LocalPlayerUserID)) + if (m_bForceReadLocalPlayerPersistenceFromDisk && !strcmp(authData.uid, R2::g_pLocalPlayerUserID)) { std::fstream pdataStream(GetNorthstarPrefix() + "/placeholder_playerdata.pdata", std::ios_base::in); @@ -230,7 +228,7 @@ bool ServerAuthenticationManager::RemovePlayerAuthData(void* player) return false; // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect - if (m_bNeedLocalAuthForNewgame && !strcmp((char*)player + 0xF500, R2::g_LocalPlayerUserID)) + if (m_bNeedLocalAuthForNewgame && !strcmp((char*)player + 0xF500, R2::g_pLocalPlayerUserID)) return false; // we don't have our auth token at this point, so lookup authdata by uid @@ -267,9 +265,9 @@ void ServerAuthenticationManager::WritePersistentData(void* player) bool ServerAuthenticationManager::CheckPlayerChatRatelimit(void* player) { - if (Plat_FloatTime() - m_additionalPlayerData[player].lastSayTextLimitStart >= 1.0) + if (Tier0::Plat_FloatTime() - m_additionalPlayerData[player].lastSayTextLimitStart >= 1.0) { - m_additionalPlayerData[player].lastSayTextLimitStart = Plat_FloatTime(); + m_additionalPlayerData[player].lastSayTextLimitStart = Tier0::Plat_FloatTime(); m_additionalPlayerData[player].sayTextLimitCount = 0; } @@ -432,10 +430,10 @@ char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const c { // note: this isn't super perfect, legit clients can trigger it in lobby, mostly good enough tho imo // https://github.com/perilouswithadollarsign/cstrike15_src/blob/f82112a2388b841d72cb62ca48ab1846dfcc11c8/engine/sv_client.cpp#L1513 - if (Plat_FloatTime() - g_ServerAuthenticationManager->m_additionalPlayerData[self].lastClientCommandQuotaStart >= 1.0) + if (Tier0::Plat_FloatTime() - g_ServerAuthenticationManager->m_additionalPlayerData[self].lastClientCommandQuotaStart >= 1.0) { // reset quota - g_ServerAuthenticationManager->m_additionalPlayerData[self].lastClientCommandQuotaStart = Plat_FloatTime(); + g_ServerAuthenticationManager->m_additionalPlayerData[self].lastClientCommandQuotaStart = Tier0::Plat_FloatTime(); g_ServerAuthenticationManager->m_additionalPlayerData[self].numClientCommandsInQuota = 0; } @@ -457,7 +455,7 @@ char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const c if (!CCommand__Tokenize(tempCommand, pCommandString, R2::cmd_source_t::kCommandSrcCode) || !tempCommand.ArgC()) return false; - ConCommand* command = g_pCVar->FindCommand(tempCommand.Arg(0)); + ConCommand* command = R2::g_pCVar->FindCommand(tempCommand.Arg(0)); // if the command doesn't exist pass it on to ExecuteStringCommand for script clientcommands and stuff if (command && !command->IsFlagSet(FCVAR_CLIENTCMD_CAN_EXECUTE)) @@ -466,11 +464,10 @@ char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const c if (IsDedicatedServer()) return false; - if (strcmp((char*)self + 0xF500, R2::g_LocalPlayerUserID)) + if (strcmp((char*)self + 0xF500, R2::g_pLocalPlayerUserID)) return false; } - // todo later, basically just limit to CVar_sv_quota_stringcmdspersecond->GetInt() stringcmds per client per second return CGameClient__ExecuteStringCommand(self, unknown, pCommandString); } @@ -478,11 +475,11 @@ typedef char (*__fastcall CNetChan___ProcessMessagesType)(void* self, void* buf) CNetChan___ProcessMessagesType CNetChan___ProcessMessages; char __fastcall CNetChan___ProcessMessagesHook(void* self, void* buf) { - double startTime = Plat_FloatTime(); + double startTime = Tier0::Plat_FloatTime(); char ret = CNetChan___ProcessMessages(self, buf); // check processing limits, unless we're in a level transition - if (R2::g_pHostState->m_iCurrentState == R2::HostState_t::HS_RUN && ThreadInServerFrameThread()) + if (R2::g_pHostState->m_iCurrentState == R2::HostState_t::HS_RUN && Tier0::ThreadInServerFrameThread()) { // player that sent the message void* sender = *(void**)((char*)self + 368); @@ -500,7 +497,7 @@ char __fastcall CNetChan___ProcessMessagesHook(void* self, void* buf) g_ServerAuthenticationManager->m_additionalPlayerData[sender].netChanProcessingLimitTime = 0.0; } g_ServerAuthenticationManager->m_additionalPlayerData[sender].netChanProcessingLimitTime += - (Plat_FloatTime() * 1000) - (startTime * 1000); + (Tier0::Plat_FloatTime() * 1000) - (startTime * 1000); if (g_ServerAuthenticationManager->m_additionalPlayerData[sender].netChanProcessingLimitTime >= Cvar_net_chan_limit_msec_per_sec->GetInt()) @@ -512,7 +509,7 @@ char __fastcall CNetChan___ProcessMessagesHook(void* self, void* buf) Cvar_net_chan_limit_msec_per_sec->GetInt()); // nonzero = kick, 0 = warn, but never kick local player - if (Cvar_net_chan_limit_mode->GetInt() && strcmp(R2::g_LocalPlayerUserID, (char*)sender + 0xF500)) + if (Cvar_net_chan_limit_mode->GetInt() && strcmp(R2::g_pLocalPlayerUserID, (char*)sender + 0xF500)) { CBaseClient__Disconnect(sender, 1, "Exceeded net channel processing limit"); return false; @@ -560,12 +557,12 @@ bool ProcessConnectionlessPacketHook(void* a1, netpacket_t* packet) memcpy(sendData->ip, packet->adr.ip, 16); } - if (Plat_FloatTime() < sendData->timeoutEnd) + if (Tier0::Plat_FloatTime() < sendData->timeoutEnd) return false; - if (Plat_FloatTime() - sendData->lastQuotaStart >= 1.0) + if (Tier0::Plat_FloatTime() - sendData->lastQuotaStart >= 1.0) { - sendData->lastQuotaStart = Plat_FloatTime(); + sendData->lastQuotaStart = Tier0::Plat_FloatTime(); sendData->packetCount = 0; } @@ -579,7 +576,7 @@ bool ProcessConnectionlessPacketHook(void* a1, netpacket_t* packet) packet->data[4]); // timeout for a minute - sendData->timeoutEnd = Plat_FloatTime() + 60.0; + sendData->timeoutEnd = Tier0::Plat_FloatTime() + 60.0; return false; } } @@ -630,7 +627,7 @@ ON_DLL_LOAD_RELIESON("engine.dll", ServerAuthentication, ConCommand, [](HMODULE Cvar_sv_querylimit_per_sec = new ConVar("sv_querylimit_per_sec", "15", FCVAR_GAMEDLL, ""); Cvar_sv_max_chat_messages_per_sec = new ConVar("sv_max_chat_messages_per_sec", "5", FCVAR_GAMEDLL, ""); - Cvar_net_datablock_enabled = g_pCVar->FindVar("net_datablock_enabled"); + Cvar_net_datablock_enabled = R2::g_pCVar->FindVar("net_datablock_enabled"); RegisterConCommand( "ns_resetpersistence", ConCommand_ns_resetpersistence, "resets your pdata when you next enter the lobby", FCVAR_NONE); diff --git a/NorthstarDedicatedTest/serverchathooks.cpp b/NorthstarDedicatedTest/serverchathooks.cpp index b2fd7111..145d691e 100644 --- a/NorthstarDedicatedTest/serverchathooks.cpp +++ b/NorthstarDedicatedTest/serverchathooks.cpp @@ -1,13 +1,14 @@ #include "pch.h" #include "serverchathooks.h" -#include "hooks.h" -#include -#include -#include #include "serverauthentication.h" #include "squirrel.h" #include "miscserverscript.h" +#include +#include +#include + + class CServerGameDLL; class CBasePlayer; diff --git a/NorthstarDedicatedTest/sourceconsole.cpp b/NorthstarDedicatedTest/sourceconsole.cpp index e6035ac5..7627a2f1 100644 --- a/NorthstarDedicatedTest/sourceconsole.cpp +++ b/NorthstarDedicatedTest/sourceconsole.cpp @@ -1,10 +1,9 @@ #include "pch.h" -#include "hooks.h" #include "convar.h" #include "sourceconsole.h" #include "sourceinterface.h" #include "concommand.h" -#include "commandprint.h" +#include "printcommand.h" SourceInterface* g_SourceGameConsole; diff --git a/NorthstarDedicatedTest/sourceinterface.cpp b/NorthstarDedicatedTest/sourceinterface.cpp index 2ed3f592..05e83856 100644 --- a/NorthstarDedicatedTest/sourceinterface.cpp +++ b/NorthstarDedicatedTest/sourceinterface.cpp @@ -1,7 +1,5 @@ #include "pch.h" #include "sourceinterface.h" -#include "hooks.h" -#include "hookutils.h" #include "sourceconsole.h" // really wanted to do a modular callback system here but honestly couldn't be bothered so hardcoding stuff for now: todo later diff --git a/NorthstarDedicatedTest/squirrel.h b/NorthstarDedicatedTest/squirrel.h index c654a599..9d55d8a1 100644 --- a/NorthstarDedicatedTest/squirrel.h +++ b/NorthstarDedicatedTest/squirrel.h @@ -162,7 +162,7 @@ template class SquirrelManager if (compileResult != SQRESULT_ERROR) { pushroottable(sqvm2); - SQRESULT callResult = call(sqvm2, 1); + SQRESULT callResult = call(sqvm2, 0); spdlog::info("sq_call returned {}", callResult); } } -- cgit v1.2.3