aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDLL/dedicated.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDLL/dedicated.cpp')
-rw-r--r--NorthstarDLL/dedicated.cpp286
1 files changed, 125 insertions, 161 deletions
diff --git a/NorthstarDLL/dedicated.cpp b/NorthstarDLL/dedicated.cpp
index e6394aee..251e2437 100644
--- a/NorthstarDLL/dedicated.cpp
+++ b/NorthstarDLL/dedicated.cpp
@@ -1,9 +1,16 @@
#include "pch.h"
#include "dedicated.h"
-#include "hookutils.h"
-#include "gameutils.h"
+#include "tier0.h"
+#include "playlist.h"
+#include "r2engine.h"
+#include "hoststate.h"
#include "serverauthentication.h"
#include "masterserver.h"
+#include "printcommand.h"
+
+AUTOHOOK_INIT()
+
+using namespace R2;
bool IsDedicatedServer()
{
@@ -33,32 +40,23 @@ void Sys_Printf(CDedicatedExports* dedicated, const char* msg)
spdlog::info("[DEDICATED SERVER] {}", msg);
}
-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());
- // 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();
- // ensure playlist initialises right, if we've not explicitly called setplaylist
- SetCurrentPlaylist(GetCurrentPlaylistName());
-
- // note: we no longer manually set map and hoststate to start server in g_pHostState, we just use +map which seems to initialise stuff
- // better
-
// get tickinterval
ConVar* Cvar_base_tickinterval_mp = g_pCVar->FindVar("base_tickinterval_mp");
@@ -66,43 +64,31 @@ 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
- if ((frameStart - frameTitle) > 0.5)
- {
- 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);
- if (!maxPlayers)
- maxPlayers = "6";
-
- SetConsoleTitleA(fmt::format(
- "{} - {} {}/{} players ({})",
- g_MasterServerManager->m_sUnicodeServerName,
- g_pHostState->m_levelName,
- g_ServerAuthenticationManager->m_additionalPlayerData.size(),
- maxPlayers,
- GetCurrentPlaylistName())
- .c_str());
- }
-
std::this_thread::sleep_for(std::chrono::duration<double, std::ratio<1>>(
- Cvar_base_tickinterval_mp->GetFloat() - fmin(Plat_FloatTime() - frameStart, 0.25)));
+ Cvar_base_tickinterval_mp->GetFloat() - fmin(Tier0::Plat_FloatTime() - frameStart, 0.25)));
}
}
-typedef bool (*IsGameActiveWindowType)();
-IsGameActiveWindowType IsGameActiveWindow;
-bool IsGameActiveWindowHook()
+// use server presence to update window title
+class DedicatedConsoleServerPresence : public ServerPresenceReporter
{
- return true;
-}
+ void ReportPresence(const ServerPresence* pServerPresence) override
+ {
+ SetConsoleTitleA(fmt::format(
+ "{} - {} {}/{} players ({})",
+ pServerPresence->m_sServerName,
+ pServerPresence->m_MapName,
+ pServerPresence->m_iPlayerCount,
+ pServerPresence->m_iMaxPlayers,
+ pServerPresence->m_PlaylistName)
+ .c_str());
+ }
+};
HANDLE consoleInputThreadHandle = NULL;
-
DWORD WINAPI ConsoleInputThread(PVOID pThreadParameter)
{
while (!g_pEngine || !g_pHostState || g_pHostState->m_iCurrentState != HostState_t::HS_RUN)
@@ -121,139 +107,106 @@ DWORD WINAPI ConsoleInputThread(PVOID pThreadParameter)
{
input += "\n";
Cbuf_AddText(Cbuf_GetCurrentPlayer(), input.c_str(), cmd_source_t::kCommandSrcCode);
+ TryPrintCvarHelpForCommand(input.c_str()); // this needs to be done on main thread, unstable in this one
}
}
return 0;
}
-#include "nsmem.h"
-void InitialiseDedicated(HMODULE engineAddress)
+// clang-format off
+AUTOHOOK(IsGameActiveWindow, engine.dll + 0x1CDC80,
+bool,, ())
+// clang-format on
+{
+ return true;
+}
+
+ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModule module))
{
spdlog::info("InitialiseDedicated");
- uintptr_t ea = (uintptr_t)engineAddress;
+ AUTOHOOK_DISPATCH_MODULE("engine.dll")
- {
- // Host_Init
- // prevent a particle init that relies on client dll
- NSMem::NOP(ea + 0x156799, 5);
- }
+ // Host_Init
+ // prevent a particle init that relies on client dll
+ module.Offset(0x156799).NOP(5);
+
+ // Host_Init
+ // don't call Key_Init to avoid loading some extra rsons from rpak (will be necessary to boot if we ever wanna disable rpaks entirely on
+ // dedi)
+ module.Offset(0x1565B0).NOP(5);
{
// CModAppSystemGroup::Create
// force the engine into dedicated mode by changing the first comparison to IsServerOnly to an assignment
- auto ptr = ea + 0x1C4EBD;
+ MemoryAddress base = module.Offset(0x1C4EBD);
// cmp => mov
- NSMem::BytePatch(ptr + 1, "C6 87");
+ base.Offset(1).Patch("C6 87");
// 00 => 01
- NSMem::BytePatch(ptr + 7, "01");
+ base.Offset(7).Patch("01");
}
- {
- // Some init that i'm not sure of that crashes
- // nop the call to it
- NSMem::NOP(ea + 0x156A63, 5);
- }
-
- {
- // runframeserver
- // nop some access violations
- NSMem::NOP(ea + 0x159819, 17);
- }
-
- {
- NSMem::NOP(ea + 0x156B4C, 7);
+ // Some init that i'm not sure of that crashes
+ // nop the call to it
+ module.Offset(0x156A63).NOP(5);
- // previously patched these, took me a couple weeks to figure out they were the issue
- // 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);
+ // runframeserver
+ // nop some access violations
+ module.Offset(0x159819).NOP(17);
- NSMem::NOP(ea + 0x156B4C + 15, 9);
- }
+ module.Offset(0x156B4C).NOP(7);
- {
- // HostState_State_NewGame
- // nop an access violation
- NSMem::NOP(ea + 0xB934C, 9);
- }
+ // previously patched these, took me a couple weeks to figure out they were the issue
+ // 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);
+ module.Offset(0x156B4C).Offset(15).NOP(9);
- {
- // CEngineAPI::Connect
- // remove call to Shader_Connect
- NSMem::NOP(ea + 0x1C4D7D, 5);
- }
+ // HostState_State_NewGame
+ // nop an access violation
+ module.Offset(0xB934C).NOP(9);
- // currently does not work, crashes stuff, likely gotta keep this here
- //{
- // // CEngineAPI::Connect
- // // remove calls to register ui rpak asset types
- // NSMem::NOP(ea + 0x1C4E07, 5);
- //}
+ // CEngineAPI::Connect
+ // remove call to Shader_Connect
+ module.Offset(0x1C4D7D).NOP(5);
- {
- // Host_Init
- // remove call to ui loading stuff
- NSMem::NOP(ea + 0x156595, 5);
- }
+ // Host_Init
+ // remove call to ui loading stuff
+ module.Offset(0x156595).NOP(5);
- {
- // some function that gets called from RunFrameServer
- // nop a function that makes requests to stryder, this will eventually access violation if left alone and isn't necessary anyway
- NSMem::NOP(ea + 0x15A0BB, 5);
- }
+ // some function that gets called from RunFrameServer
+ // nop a function that makes requests to stryder, this will eventually access violation if left alone and isn't necessary anyway
+ module.Offset(0x15A0BB).NOP(5);
- {
- // RunFrameServer
- // nop a function that access violations
- NSMem::NOP(ea + 0x159BF3, 5);
- }
+ // RunFrameServer
+ // nop a function that access violations
+ module.Offset(0x159BF3).NOP(5);
- {
- // func that checks if origin is inited
- // always return 1
- NSMem::BytePatch(
- ea + 0x183B70,
- {
- 0xB0,
- 0x01, // mov al,01
- 0xC3 // ret
- });
- }
+ // func that checks if origin is inited
+ // always return 1
+ module.Offset(0x183B70).Patch("B0 01 C3"); // mov al,01 ret
- {
- // HostState_State_ChangeLevel
- // nop clientinterface call
- NSMem::NOP(ea + 0x1552ED, 16);
- }
+ // HostState_State_ChangeLevel
+ // nop clientinterface call
+ module.Offset(0x1552ED).NOP(16);
- {
- // HostState_State_ChangeLevel
- // nop clientinterface call
- NSMem::NOP(ea + 0x155363, 16);
- }
+ // HostState_State_ChangeLevel
+ // nop clientinterface call
+ module.Offset(0x155363).NOP(16);
- // note: previously had DisableDedicatedWindowCreation patches here, but removing those rn since they're all shit and unstable and bad
- // and such check commit history if any are needed for reimplementation
- {
- // IVideoMode::CreateGameWindow
- // nop call to ShowWindow
- NSMem::NOP(ea + 0x1CD146, 5);
- }
+ // IVideoMode::CreateGameWindow
+ // nop call to ShowWindow
+ module.Offset(0x1CD146).NOP(5);
CDedicatedExports* dedicatedExports = new CDedicatedExports;
dedicatedExports->vtable = dedicatedExports;
dedicatedExports->Sys_Printf = Sys_Printf;
dedicatedExports->RunServer = RunServer;
- CDedicatedExports** exports = (CDedicatedExports**)((char*)engineAddress + 0x13F0B668);
- *exports = dedicatedExports;
-
- HookEnabler hook;
- ENABLER_CREATEHOOK(hook, (char*)engineAddress + 0x1CDC80, &IsGameActiveWindowHook, reinterpret_cast<LPVOID*>(&IsGameActiveWindow));
+ *module.Offset(0x13F0B668).As<CDedicatedExports**>() = dedicatedExports;
// extra potential patches:
// nop engine.dll+1c67d1 and +1c67d8 to skip videomode creategamewindow
@@ -265,15 +218,19 @@ void InitialiseDedicated(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");
+
+ // use presence reporter for console title
+ DedicatedConsoleServerPresence* presenceReporter = new DedicatedConsoleServerPresence;
+ g_pServerPresence->AddPresenceReporter(presenceReporter);
// 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;
@@ -295,35 +252,42 @@ void InitialiseDedicated(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");
}
-void InitialiseDedicatedOrigin(HMODULE baseAddress)
+ON_DLL_LOAD_DEDI("tier0.dll", DedicatedServerOrigin, (CModule module))
{
// disable origin on dedicated
// 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
- });
+ module.GetExport("Tier0_InitOrigin").Patch("C3");
}
-typedef void (*PrintFatalSquirrelErrorType)(void* sqvm);
-PrintFatalSquirrelErrorType PrintFatalSquirrelError;
-void PrintFatalSquirrelErrorHook(void* sqvm)
+// clang-format off
+AUTOHOOK(PrintSquirrelError, server.dll + 0x794D0,
+void, __fastcall, (void* sqvm))
+// clang-format on
{
- PrintFatalSquirrelError(sqvm);
- g_pEngine->m_nQuitting = EngineQuitState::QUIT_TODESKTOP;
+ PrintSquirrelError(sqvm);
+
+ // close dedicated server if a fatal error is hit
+ static ConVar* Cvar_fatal_script_errors = g_pCVar->FindVar("fatal_script_errors");
+ if (Cvar_fatal_script_errors->GetBool())
+ abort();
}
-void InitialiseDedicatedServerGameDLL(HMODULE baseAddress)
+ON_DLL_LOAD_DEDI("server.dll", DedicatedServerGameDLL, (CModule module))
{
- HookEnabler hook;
- ENABLER_CREATEHOOK(hook, baseAddress + 0x794D0, &PrintFatalSquirrelErrorHook, reinterpret_cast<LPVOID*>(&PrintFatalSquirrelError));
+ AUTOHOOK_DISPATCH_MODULE(server.dll)
+
+ if (Tier0::CommandLine()->CheckParm("-nopakdedi"))
+ {
+ module.Offset(0x6BA350).Patch("C3"); // dont load skins.rson from rpak if we don't have rpaks, as loading it will cause a crash
+ module.Offset(0x6BA300).Patch(
+ "B8 C8 00 00 00 C3"); // return 200 as the number of skins from server.dll + 6BA300, this is the normal value read from
+ // skins.rson and should be updated when we need it more modular
+ }
}