From 661874025f53e42982606076311e71a794bf30c8 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 17 May 2024 11:12:57 +0200 Subject: [PATCH] Engine: add functionality to run game without the platform system Unbind the game from the platform system, which is useful when developing for or debugging the game. This was supposed to make it into the SDK back in 2021, but wasn't due to the ability to spoof usernames. This is no longer possible on servers requiring authentication as the player's name is actually checked along with the Nucleus ID. --- r5dev/common/global.cpp | 8 +++++ r5dev/common/global.h | 6 ++++ r5dev/ebisusdk/EbisuSDK.cpp | 50 ++++++++++++++++++++++++++--- r5dev/ebisusdk/EbisuSDK.h | 22 +++++++++---- r5dev/engine/client/clientstate.cpp | 30 +++++++++++++++++ 5 files changed, 104 insertions(+), 12 deletions(-) diff --git a/r5dev/common/global.cpp b/r5dev/common/global.cpp index d12ae85e..fe73e35e 100644 --- a/r5dev/common/global.cpp +++ b/r5dev/common/global.cpp @@ -65,6 +65,12 @@ ConVar* language_cvar = nullptr; ConVar* voice_noxplat = nullptr; +ConVar* platform_user_id = nullptr; + +#ifndef DEDICATED +ConVar* name_cvar = nullptr; +#endif // !DEDICATED + //----------------------------------------------------------------------------- // SERVER | #ifndef CLIENT_DLL @@ -156,7 +162,9 @@ void ConVar_InitShipped(void) language_cvar = g_pCVar->FindVar("language"); voice_noxplat = g_pCVar->FindVar("voice_noxplat"); + platform_user_id = g_pCVar->FindVar("platform_user_id"); #ifndef DEDICATED + name_cvar = g_pCVar->FindVar("name"); cl_updaterate_mp = g_pCVar->FindVar("cl_updaterate_mp"); cl_threaded_bone_setup = g_pCVar->FindVar("cl_threaded_bone_setup"); #endif // !DEDICATED diff --git a/r5dev/common/global.h b/r5dev/common/global.h index 139ad327..5df2c062 100644 --- a/r5dev/common/global.h +++ b/r5dev/common/global.h @@ -52,6 +52,12 @@ extern ConVar* language_cvar; extern ConVar* voice_noxplat; +extern ConVar* platform_user_id; + +#ifndef DEDICATED +extern ConVar* name_cvar; +#endif // !DEDICATED + //------------------------------------------------------------------------- // SERVER | #ifndef CLIENT_DLL diff --git a/r5dev/ebisusdk/EbisuSDK.cpp b/r5dev/ebisusdk/EbisuSDK.cpp index a082356f..1487a250 100644 --- a/r5dev/ebisusdk/EbisuSDK.cpp +++ b/r5dev/ebisusdk/EbisuSDK.cpp @@ -4,18 +4,47 @@ #include "engine/server/sv_main.h" //----------------------------------------------------------------------------- -// Purpose: sets the EbisuSDK globals for dedicated to satisfy command callbacks +// Purpose: initialize the EbisuSDK //----------------------------------------------------------------------------- void HEbisuSDK_Init() { - if (IsDedicated()) + const bool isDedicated = IsDedicated(); + const bool noOrigin = IsOriginDisabled(); + + // Fill with default data if this is a dedicated server, or if the game was + // launched with the platform system disabled. Engine code requires these + // to be set for the game to function, else stuff like the "map" command + // won't run as 'IsOriginInitialized()' returns false (which got inlined in + // every place this was called in the game's executable). + if (isDedicated || noOrigin) { - *g_EbisuSDKInit = true; // <- 1st EbisuSDK - *g_EbisuProfileInit = true; // <- 2nd EbisuSDK - *g_NucleusID = 9990000; // <- 3rd EbisuSDK + *g_EbisuSDKInit = true; + *g_EbisuProfileInit = true; + *g_NucleusID = FAKE_BASE_NUCLEUD_ID; + + Q_snprintf(g_OriginAuthCode, 256, "%s", "INVALID_OAUTH_CODE"); + Q_snprintf(g_NucleusToken, 1024, "%s", "INVALID_NUCLEUS_TOKEN"); + + if (!isDedicated) + { + platform_user_id->SetValue(FAKE_BASE_NUCLEUD_ID); + } } } +//----------------------------------------------------------------------------- +// Purpose: runs the EbisuSDK state machine +//----------------------------------------------------------------------------- +void HEbisuSDK_RunFrame() +{ + if (IsOriginDisabled()) + { + return; + } + + EbisuSDK_RunFrame(); +} + //----------------------------------------------------------------------------- // Purpose: returns the currently set language //----------------------------------------------------------------------------- @@ -52,6 +81,16 @@ const char* HEbisuSDK_GetLanguage() return languageName; } +//----------------------------------------------------------------------------- +// Purpose: checks if the EbisuSDK is disabled +// Output : true on success, false on failure +//----------------------------------------------------------------------------- +bool IsOriginDisabled() +{ + const static bool isDisabled = CommandLine()->CheckParm("-noorigin"); + return isDisabled; +} + //----------------------------------------------------------------------------- // Purpose: checks if the EbisuSDK is initialized // Output : true on success, false on failure @@ -97,5 +136,6 @@ bool IsValidPersonaName(const char* pszName, int nMinLen, int nMaxLen) void VEbisuSDK::Detour(const bool bAttach) const { + DetourSetup(&EbisuSDK_RunFrame, &HEbisuSDK_RunFrame, bAttach); DetourSetup(&EbisuSDK_GetLanguage, &HEbisuSDK_GetLanguage, bAttach); } diff --git a/r5dev/ebisusdk/EbisuSDK.h b/r5dev/ebisusdk/EbisuSDK.h index caf1f16c..79dbaaa7 100644 --- a/r5dev/ebisusdk/EbisuSDK.h +++ b/r5dev/ebisusdk/EbisuSDK.h @@ -1,13 +1,17 @@ #pragma once +#define MAX_PERSONA_NAME_LEN 64 // sizeof( g_PersonaName ) +#define FAKE_BASE_NUCLEUD_ID 9990000 + inline void(*EbisuSDK_Tier0_Init)(void); inline void(*EbisuSDK_CVar_Init)(void); -inline void(*EbisuSDK_SetState)(void); +inline void(*EbisuSDK_RunFrame)(void); inline const char*(*EbisuSDK_GetLanguage)(void); inline uint64_t* g_NucleusID = nullptr; inline char* g_NucleusToken = nullptr; /*SIZE = 1024*/ inline char* g_OriginAuthCode = nullptr; /*SIZE = 256*/ +inline char* g_PersonaName = nullptr; /*SIZE = 64*/ inline int* g_OriginErrorLevel = nullptr; inline bool* g_EbisuSDKInit = nullptr; inline bool* g_EbisuProfileInit = nullptr; @@ -16,7 +20,9 @@ inline bool* g_EbisuProfileInit = nullptr; void HEbisuSDK_Init(); const char* HEbisuSDK_GetLanguage(); +bool IsOriginDisabled(); bool IsOriginInitialized(); + bool IsValidPersonaName(const char* pszName, int nMinLen, int nMaxLen); /////////////////////////////////////////////////////////////////////////////// @@ -26,11 +32,12 @@ class VEbisuSDK : public IDetour { LogFunAdr("EbisuSDK_Tier0_Init", EbisuSDK_Tier0_Init); LogFunAdr("EbisuSDK_CVar_Init", EbisuSDK_CVar_Init); - LogFunAdr("EbisuSDK_SetState", EbisuSDK_SetState); - LogFunAdr("EbisuSDK_SetState", EbisuSDK_GetLanguage); + LogFunAdr("EbisuSDK_RunFrame", EbisuSDK_RunFrame); + LogFunAdr("EbisuSDK_GetLanguage", EbisuSDK_GetLanguage); LogVarAdr("g_NucleusID", g_NucleusID); LogVarAdr("g_NucleusToken", g_NucleusToken); LogVarAdr("g_OriginAuthCode", g_OriginAuthCode); + LogVarAdr("g_PersonaName", g_PersonaName); LogVarAdr("g_OriginErrorLevel", g_OriginErrorLevel); LogVarAdr("g_EbisuProfileInit", g_EbisuProfileInit); LogVarAdr("g_EbisuSDKInit", g_EbisuSDKInit); @@ -39,15 +46,16 @@ class VEbisuSDK : public IDetour { g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 0F 85 ?? 02 ?? ?? 48 89 5C 24 20").GetPtr(EbisuSDK_Tier0_Init); g_GameDll.FindPatternSIMD("40 57 48 83 EC 40 83 3D").GetPtr(EbisuSDK_CVar_Init); - g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 5B").GetPtr(EbisuSDK_SetState); + g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 5B").GetPtr(EbisuSDK_RunFrame); g_GameDll.FindPatternSIMD("48 8B C4 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ??").GetPtr(EbisuSDK_GetLanguage); } virtual void GetVar(void) const { g_NucleusID = CMemory(EbisuSDK_CVar_Init).Offset(0x20).FindPatternSelf("4C 89 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_NucleusToken = CMemory(EbisuSDK_SetState).Offset(0x1EF).FindPatternSelf("80 3D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); - g_OriginAuthCode = CMemory(EbisuSDK_SetState).Offset(0x1BF).FindPatternSelf("0F B6", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_OriginErrorLevel = CMemory(EbisuSDK_SetState).Offset(0x20).FindPatternSelf("89 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); + g_NucleusToken = CMemory(EbisuSDK_RunFrame).Offset(0x1EF).FindPatternSelf("80 3D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); + g_OriginAuthCode = CMemory(EbisuSDK_RunFrame).Offset(0x1BF).FindPatternSelf("0F B6", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_PersonaName = CMemory(EbisuSDK_CVar_Init).Offset(0x120).FindPatternSelf("48 8D 0D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_OriginErrorLevel = CMemory(EbisuSDK_RunFrame).Offset(0x20).FindPatternSelf("89 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); g_EbisuProfileInit = CMemory(EbisuSDK_CVar_Init).Offset(0x12A).FindPatternSelf("C6 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); g_EbisuSDKInit = CMemory(EbisuSDK_Tier0_Init).Offset(0x0).FindPatternSelf("80 3D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); } diff --git a/r5dev/engine/client/clientstate.cpp b/r5dev/engine/client/clientstate.cpp index 2a2a57c0..f8064e02 100644 --- a/r5dev/engine/client/clientstate.cpp +++ b/r5dev/engine/client/clientstate.cpp @@ -25,6 +25,36 @@ #include #include +//------------------------------------------------------------------------------ +// Purpose: console command callbacks +//------------------------------------------------------------------------------ +static void SetName_f(const CCommand& args) +{ + if (args.ArgC() < 2) + return; + + if (!IsOriginDisabled()) + return; + + const char* pszName = args.Arg(1); + + if (!pszName[0]) + pszName = "unnamed"; + + const size_t nLen = strlen(pszName); + + if (nLen > MAX_PERSONA_NAME_LEN) + return; + + // Update nucleus name. + memset(g_PersonaName, '\0', MAX_PERSONA_NAME_LEN); + strncpy(g_PersonaName, pszName, nLen); +} + +//------------------------------------------------------------------------------ +// Purpose: console commands +//------------------------------------------------------------------------------ +static ConCommand cl_setname("cl_setname", SetName_f, "Sets the client's persona name", FCVAR_RELEASE); //------------------------------------------------------------------------------ // Purpose: returns true if client simulation is paused