From 2497b700379205ea01c1aa0c55a19be523233be1 Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Thu, 30 Sep 2021 20:48:53 +0100 Subject: add spewfunc, setplaylistvaroverride, dedicated changes --- NorthstarDedicatedTest/dedicated.cpp | 128 +++++++++++++++++++-- NorthstarDedicatedTest/dedicated.h | 1 + NorthstarDedicatedTest/dedicatedmaterialsystem.cpp | 16 +++ NorthstarDedicatedTest/dllmain.cpp | 1 + NorthstarDedicatedTest/gameutils.cpp | 2 + NorthstarDedicatedTest/gameutils.h | 3 + NorthstarDedicatedTest/logging.cpp | 127 ++++++++++++++++++++ NorthstarDedicatedTest/logging.h | 3 +- NorthstarDedicatedTest/playlist.cpp | 25 ++++ NorthstarDedicatedTest/playlist.h | 1 + 10 files changed, 294 insertions(+), 13 deletions(-) diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp index 1d59cc91..c09797f8 100644 --- a/NorthstarDedicatedTest/dedicated.cpp +++ b/NorthstarDedicatedTest/dedicated.cpp @@ -3,12 +3,18 @@ #include "hookutils.h" #include "tier0.h" #include "gameutils.h" +#include "serverauthentication.h" bool IsDedicated() { return CommandLine()->CheckParm("-dedicated"); } +bool DisableDedicatedWindowCreation() +{ + return !CommandLine()->CheckParm("-windoweddedi"); +} + // CDedidcatedExports defs struct CDedicatedExports; // forward declare @@ -52,7 +58,7 @@ void RunServer(CDedicatedExports* dedicated) { g_pEngine->Frame(); - SetConsoleTitleA(fmt::format("Titanfall 2 dedicated server - {} {}/{} players", g_pHostState->m_levelName, "0", "0").c_str()); + SetConsoleTitleA(fmt::format("Titanfall 2 dedicated server - {} {}/{} players", g_pHostState->m_levelName, g_ServerAuthenticationManager->m_additionalPlayerData.size(), "0").c_str()); Sleep(50); } } @@ -204,13 +210,13 @@ void InitialiseDedicated(HMODULE engineAddress) *(ptr + 4) = (char)0x90; } - // not currently using this because it's for nopping renderthread/gamewindow stuff i.e. very hard + // currently does not work, crashes stuff, likely gotta keep this here //{ - // // CEngineAPI::Init - // char* ptr = (char*)engineAddress + 0x1C60CE; + // // CEngineAPI::Connect + // char* ptr = (char*)engineAddress + 0x1C4E07; // TempReadWrite rw(ptr); - // - // // remove call to something or other that reads video settings + // + // // remove calls to register ui rpak asset types // *ptr = 0x90; // *(ptr + 1) = (char)0x90; // *(ptr + 2) = (char)0x90; @@ -218,15 +224,27 @@ void InitialiseDedicated(HMODULE engineAddress) // *(ptr + 4) = (char)0x90; //} + // not sure if this should be done, not loading ui at least is good, but should everything be gone? { - // some inputsystem bullshit - char* ptr = (char*)engineAddress + 0x1CEE28; + // Host_Init + char* ptr = (char*)engineAddress + 0x15653B; TempReadWrite rw(ptr); - // nop an accessviolation: temp because we still create game window atm - *ptr = (char)0x90; + // change the number of rpaks to load from 6 to 1, so we only load common.rpak + *(ptr + 1) = (char)0x01; + } + + { + // Host_Init + char* ptr = (char*)engineAddress + 0x156595; + TempReadWrite rw(ptr); + + // remove call to ui loading stuff + *ptr = 0x90; *(ptr + 1) = (char)0x90; *(ptr + 2) = (char)0x90; + *(ptr + 3) = (char)0x90; + *(ptr + 4) = (char)0x90; } { @@ -243,11 +261,11 @@ void InitialiseDedicated(HMODULE engineAddress) } { - // some function that gets called from RunFrameServer + // RunFrameServer char* ptr = (char*)engineAddress + 0x159BF3; TempReadWrite rw(ptr); - // nop a function that makes requests to stryder, this will eventually access violation if left alone and isn't necessary anyway + // nop a function that access violations *ptr = (char)0x90; *(ptr + 1) = (char)0x90; *(ptr + 2) = (char)0x90; @@ -255,6 +273,89 @@ void InitialiseDedicated(HMODULE engineAddress) *(ptr + 4) = (char)0x90; } + // stuff that disables renderer/window creation: unstable atm + if (DisableDedicatedWindowCreation()) + { + { + // CEngineAPI::Init + char* ptr = (char*)engineAddress + 0x1C60CE; + TempReadWrite rw(ptr); + + // remove call to something or other that reads video settings + *ptr = (char)0x90; + *(ptr + 1) = (char)0x90; + *(ptr + 2) = (char)0x90; + *(ptr + 3) = (char)0x90; + *(ptr + 4) = (char)0x90; + } + + { + // some inputsystem bullshit + char* ptr = (char*)engineAddress + 0x1CEE28; + TempReadWrite rw(ptr); + + // nop an accessviolation: temp because we still create game window atm + *ptr = (char)0x90; + *(ptr + 1) = (char)0x90; + *(ptr + 2) = (char)0x90; + } + + //{ + // // CEngineAPI::ModInit + // char* ptr = (char*)engineAddress + 0x1C67D1; + // TempReadWrite rw(ptr); + // + // // prevent game window from being created + // *ptr = (char)0x90; + // *(ptr + 1) = (char)0x90; + // *(ptr + 2) = (char)0x90; + // *(ptr + 3) = (char)0x90; + // *(ptr + 4) = (char)0x90; + // + // *(ptr + 7) = (char)0xEB; // jnz => jmp + //} + + // note: this is a different way of nopping window creation, i'm assuming there are like a shitload of inits here we shouldn't skip + // i know at the very least it registers datatables which are important + { + // IVideoMode::CreateGameWindow + char* ptr = (char*)engineAddress + 0x1CD0ED; + TempReadWrite rw(ptr); + + // prevent game window from being created + *ptr = (char)0x90; + *(ptr + 1) = (char)0x90; + *(ptr + 2) = (char)0x90; + *(ptr + 3) = (char)0x90; + *(ptr + 4) = (char)0x90; + } + + { + // IVideoMode::CreateGameWindow + char* ptr = (char*)engineAddress + 0x1CD146; + TempReadWrite rw(ptr); + + // prevent game from calling a matsystem function that will crash here + //*ptr = (char)0x90; + //*(ptr + 1) = (char)0x90; + //*(ptr + 2) = (char)0x90; + //*(ptr + 3) = (char)0x90; + //*(ptr + 4) = (char)0x90; + } + + { + // IVideoMode::CreateGameWindow + char* ptr = (char*)engineAddress + 0x1CD160; + TempReadWrite rw(ptr); + + // prevent game from complaining about window not being created + *ptr = (char)0x90; + *(ptr + 1) = (char)0x90; + } + + CommandLine()->AppendParm("-noshaderapi", 0); + } + CDedicatedExports* dedicatedExports = new CDedicatedExports; dedicatedExports->vtable = dedicatedExports; dedicatedExports->Sys_Printf = Sys_Printf; @@ -272,6 +373,9 @@ void InitialiseDedicated(HMODULE engineAddress) // this crashes HARD if no window which makes sense tbh // also look into materialsystem + 5B344 since it seems to be the base of all the renderthread stuff + // big note: datatable gets registered in window creation + // make sure it still gets registered + // add cmdline args that are good for dedi CommandLine()->AppendParm("-nomenuvid", 0); CommandLine()->AppendParm("-nosound", 0); diff --git a/NorthstarDedicatedTest/dedicated.h b/NorthstarDedicatedTest/dedicated.h index ded6af70..459f47ee 100644 --- a/NorthstarDedicatedTest/dedicated.h +++ b/NorthstarDedicatedTest/dedicated.h @@ -1,6 +1,7 @@ #pragma once bool IsDedicated(); +bool DisableDedicatedWindowCreation(); void InitialiseDedicated(HMODULE moduleAddress); void InitialiseDedicatedOrigin(HMODULE baseAddress); \ No newline at end of file diff --git a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp index e334e8fe..531885a7 100644 --- a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp +++ b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp @@ -8,6 +8,9 @@ void InitialiseDedicatedMaterialSystem(HMODULE baseAddress) { if (!IsDedicated()) return; + + //while (!IsDebuggerPresent()) + // Sleep(100); // not using these for now since they're related to nopping renderthread/gamewindow i.e. very hard //{ @@ -44,4 +47,17 @@ void InitialiseDedicatedMaterialSystem(HMODULE baseAddress) *(ptr + 2) = (char)0x03; *(ptr + 3) = (char)0x00; } + + if (DisableDedicatedWindowCreation()) + { + { + // some renderthread stuff + char* ptr = (char*)baseAddress + 0x8C10; + TempReadWrite rw(ptr); + + // call => nop + *ptr = (char)0x90; + *(ptr + 1) = (char)0x90; + } + } } \ No newline at end of file diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp index 33b8c920..684f968e 100644 --- a/NorthstarDedicatedTest/dllmain.cpp +++ b/NorthstarDedicatedTest/dllmain.cpp @@ -64,6 +64,7 @@ void InitialiseNorthstar() AddDllLoadCallback("engine.dll", WaitForDebugger); AddDllLoadCallback("engine.dll", InitialiseEngineGameUtilFunctions); + AddDllLoadCallback("engine.dll", InitialiseEngineSpewFuncHooks); // dedi patches { diff --git a/NorthstarDedicatedTest/gameutils.cpp b/NorthstarDedicatedTest/gameutils.cpp index e63c8786..013ae61a 100644 --- a/NorthstarDedicatedTest/gameutils.cpp +++ b/NorthstarDedicatedTest/gameutils.cpp @@ -20,6 +20,7 @@ ConVar* Cvar_hostport; // playlist stuff GetCurrentPlaylistType GetCurrentPlaylistName; SetCurrentPlaylistType SetCurrentPlaylist; +SetPlaylistVarOverrideType SetPlaylistVarOverride; // uid char* g_LocalPlayerUserID; @@ -37,6 +38,7 @@ void InitialiseEngineGameUtilFunctions(HMODULE baseAddress) GetCurrentPlaylistName = (GetCurrentPlaylistType)((char*)baseAddress + 0x18C640); SetCurrentPlaylist = (SetCurrentPlaylistType)((char*)baseAddress + 0x18EB20); + SetPlaylistVarOverride = (SetPlaylistVarOverrideType)((char*)baseAddress + 0x18ED17); g_LocalPlayerUserID = (char*)baseAddress + 0x13F8E688; } diff --git a/NorthstarDedicatedTest/gameutils.h b/NorthstarDedicatedTest/gameutils.h index cabd3edb..0f59a773 100644 --- a/NorthstarDedicatedTest/gameutils.h +++ b/NorthstarDedicatedTest/gameutils.h @@ -180,6 +180,9 @@ extern GetCurrentPlaylistType GetCurrentPlaylistName; typedef void(*SetCurrentPlaylistType)(const char* playlistName); extern SetCurrentPlaylistType SetCurrentPlaylist; +typedef void(*SetPlaylistVarOverrideType)(const char* varName, const char* value); +extern SetPlaylistVarOverrideType SetPlaylistVarOverride; + // uid extern char* g_LocalPlayerUserID; diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp index 721c00f5..f94a878f 100644 --- a/NorthstarDedicatedTest/logging.cpp +++ b/NorthstarDedicatedTest/logging.cpp @@ -2,6 +2,7 @@ #include "logging.h" #include "sourceconsole.h" #include "spdlog/sinks/basic_file_sink.h" +#include "hookutils.h" #include #include @@ -23,4 +24,130 @@ void InitialiseLogging() // create logger spdlog::default_logger()->sinks().push_back(std::make_shared(stream.str(), false)); +} + +enum SpewType_t +{ + SPEW_MESSAGE = 0, + SPEW_WARNING, + SPEW_ASSERT, + SPEW_ERROR, + SPEW_LOG, + SPEW_TYPE_COUNT +}; + +typedef void(*EngineSpewFuncType)(); +EngineSpewFuncType EngineSpewFunc; + +void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format, va_list args) +{ + const char* typeStr; + switch (type) + { + case SPEW_MESSAGE: + { + typeStr = "SPEW_MESSAGE"; + break; + } + + case SPEW_WARNING: + { + typeStr = "SPEW_WARNING"; + break; + } + + case SPEW_ASSERT: + { + typeStr = "SPEW_ASSERT"; + break; + } + + case SPEW_ERROR: + { + typeStr = "SPEW_ERROR"; + break; + } + + case SPEW_LOG: + { + typeStr = "SPEW_LOG"; + break; + } + + default: + { + typeStr = "SPEW_UNKNOWN"; + break; + } + } + + char formatted[2048]; + bool shouldFormat = true; + + // because titanfall 2 is quite possibly the worst thing to yet exist, it sometimes gives invalid specifiers which will crash + // ttf2sdk had a way to prevent them from crashing but it doesnt work in debug builds + // so we use this instead + for (int i = 0; format[i]; i++) + { + if (format[i] == '%') + { + switch (format[i + 1]) + { + // this is fucking awful lol + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'f': + case 'F': + case 'g': + case 'G': + case 'a': + case 'A': + case 'c': + case 's': + case 'p': + case 'n': + case '%': + case '-': + case '+': + case ' ': + case '#': + case '*': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + + default: + { + shouldFormat = false; + break; + } + } + } + } + + if (shouldFormat) + vsnprintf(formatted, sizeof(formatted), format, args); + else + { + spdlog::warn("Failed to format {} \"{}\"", typeStr, format); + } + + spdlog::info("[SERVER {}] {}", typeStr, formatted); +} + +void InitialiseEngineSpewFuncHooks(HMODULE baseAddress) +{ + HookEnabler hook; + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x11CA80, EngineSpewFuncHook, reinterpret_cast(&EngineSpewFunc)); } \ No newline at end of file diff --git a/NorthstarDedicatedTest/logging.h b/NorthstarDedicatedTest/logging.h index a9ec3c9a..5a4522b4 100644 --- a/NorthstarDedicatedTest/logging.h +++ b/NorthstarDedicatedTest/logging.h @@ -1,4 +1,5 @@ #pragma once #include "context.h" -void InitialiseLogging(); \ No newline at end of file +void InitialiseLogging(); +void InitialiseEngineSpewFuncHooks(HMODULE baseAddress); \ No newline at end of file diff --git a/NorthstarDedicatedTest/playlist.cpp b/NorthstarDedicatedTest/playlist.cpp index 2a730ee1..096f4f3d 100644 --- a/NorthstarDedicatedTest/playlist.cpp +++ b/NorthstarDedicatedTest/playlist.cpp @@ -4,6 +4,7 @@ #include "convar.h" #include "gameutils.h" #include "hookutils.h" +#include "dedicated.h" typedef char(*Onclc_SetPlaylistVarOverrideType)(void* a1, void* a2); Onclc_SetPlaylistVarOverrideType Onclc_SetPlaylistVarOverride; @@ -18,6 +19,14 @@ void SetPlaylistCommand(const CCommand& args) SetCurrentPlaylist(args.Arg(1)); } +void SetPlaylistVarOverrideCommand(const CCommand& args) +{ + if (args.ArgC() < 3) + return; + + SetPlaylistVarOverride(args.Arg(1), args.Arg(2)); +} + char Onclc_SetPlaylistVarOverrideHook(void* a1, void* a2) { // the private_match playlist is the only situation where there should be any legitimate sending of this netmessage @@ -31,6 +40,7 @@ char Onclc_SetPlaylistVarOverrideHook(void* a1, void* a2) void InitialisePlaylistHooks(HMODULE baseAddress) { RegisterConCommand("setplaylist", SetPlaylistCommand, "Sets the current playlist", FCVAR_NONE); + RegisterConCommand("setplaylistvaroverride", SetPlaylistVarOverrideCommand, "Sets a playlist var override", FCVAR_NONE); // 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 compatibility @@ -46,4 +56,19 @@ void InitialisePlaylistHooks(HMODULE baseAddress) TempReadWrite rw(ptr); *((char*)ptr) = 0xC3; // jmp => ret } + + // on dedicated servers, force SetPlaylistVarOverride to always set the server's override list, irregardless of whether server is running yet + if (IsDedicated()) + { + // SetPlaylistVarOverride + void* ptr = (char*)baseAddress + 0x18ED17; + TempReadWrite rw(ptr); + + *((char*)ptr) = 0x90; // jl => nop + *((char*)ptr + 1) = 0x90; + *((char*)ptr + 2) = 0x90; + *((char*)ptr + 3) = 0x90; + *((char*)ptr + 4) = 0x90; + *((char*)ptr + 5) = 0x90; + } } \ No newline at end of file diff --git a/NorthstarDedicatedTest/playlist.h b/NorthstarDedicatedTest/playlist.h index d96a77ee..bf827ff4 100644 --- a/NorthstarDedicatedTest/playlist.h +++ b/NorthstarDedicatedTest/playlist.h @@ -1 +1,2 @@ +#pragma once void InitialisePlaylistHooks(HMODULE baseAddress); \ No newline at end of file -- cgit v1.2.3