diff --git a/r5dedicated/console.cpp b/r5dedicated/console.cpp index a9be9e56..6cdc996a 100644 --- a/r5dedicated/console.cpp +++ b/r5dedicated/console.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "hooks.h" +#include "opcodes.h" #include "console.h" //############################################################################# @@ -80,6 +81,7 @@ DWORD __stdcall ProcessConsoleWorker(LPVOID) // Exec toggles if (sCommand == "1") { addr_CommandExecute(NULL, "exec autoexec_dev"); } if (sCommand == "2") { g_bDebugLoading = !g_bDebugLoading; continue; } + if (sCommand == "3") { SetCHostState(); continue; } // TEST /////////////////////////////////////////////////////////////////////// // Execute the command in the r5 SQVM diff --git a/r5dedicated/csourceappsystemgroup.cpp b/r5dedicated/csourceappsystemgroup.cpp new file mode 100644 index 00000000..bffa7cc3 --- /dev/null +++ b/r5dedicated/csourceappsystemgroup.cpp @@ -0,0 +1,36 @@ +#include "pch.h" +#include "hooks.h" + +namespace Hooks +{ + CSourceAppSystemGroup_CreateFn originalCSourceAppSystemGroup_Create = nullptr; +} +auto g_bIsDedicated = (uint8_t*)0x162C61208; + +//----------------------------------------------------------------------------- +// Purpose: sets 'EbisuSDK' globals required in certain engine callbacks. +//----------------------------------------------------------------------------- +void HEbisuSDK_Init() +{ + auto ofs000 = (uint8_t*)0x1634F1690; + auto ofs001 = (uint8_t*)0x1634F16B0; + auto ofs002 = (uint8_t*)0x1634F1695; + auto ofs003 = (uint8_t*)0x1634F30D8; + auto ofs004 = (uint8_t*)0x1634F31D8; + + *(char*)(ofs000) = (char)0x1; // <-- | 1st EbisuSDK boolean to be checked. + *(char*)(ofs001) = (char)0x1; // <-- | 2nd EbisuSDK boolean to be checked. + *(char*)(ofs002) = (char)0x1; // <-- | 3rd EbisuSDK boolean to be checked. + *(char*)(ofs003) = (char)0x1; // <-- | Gets tested on listenserver for certain ConCommands. + *(char*)(ofs004) = (char)0x0; // <-- | TODO: enforces Necleus cvars when not equal to NULL. +} + +//----------------------------------------------------------------------------- +// Purpose: hook 'SourceAppSystemGroup::Create' and set m_bIsDedicated to true. +//----------------------------------------------------------------------------- +char __fastcall Hooks::CSourceAppSystemGroup_Create(__int64 a1) +{ + *g_bIsDedicated = 1; // HAS TO BE HERE!!! + HEbisuSDK_Init(); + return originalCSourceAppSystemGroup_Create(a1); +} \ No newline at end of file diff --git a/r5dedicated/dllmain.cpp b/r5dedicated/dllmain.cpp index 89df758c..4dc7cc24 100644 --- a/r5dedicated/dllmain.cpp +++ b/r5dedicated/dllmain.cpp @@ -11,6 +11,7 @@ void InitializeR5Dedicated() { SetupConsole(); Hooks::InstallHooks(); + Hooks::DedicatedPatch(); printf("+-----------------------------------------------------------------------------+\n"); printf("| R5 DEDICATED SERVER --------------------------------------------------- |\n"); printf("+-----------------------------------------------------------------------------+\n"); diff --git a/r5dedicated/enums.h b/r5dedicated/enums.h new file mode 100644 index 00000000..f3ff58b5 --- /dev/null +++ b/r5dedicated/enums.h @@ -0,0 +1,422 @@ +#pragma once + +/* Enumerations */ +enum class D3D11DeviceVTbl : short +{ + // IUnknown + QueryInterface = 0, + AddRef = 1, + Release = 2, + + // ID3D11Device + CreateBuffer = 3, + CreateTexture1D = 4, + CreateTexture2D = 5, + CreateTexture3D = 6, + CreateShaderResourceView = 7, + CreateUnorderedAccessView = 8, + CreateRenderTargetView = 9, + CreateDepthStencilView = 10, + CreateInputLayout = 11, + CreateVertexShader = 12, + CreateGeometryShader = 13, + CreateGeometryShaderWithStreamOutput = 14, + CreatePixelShader = 15, + CreateHullShader = 16, + CreateDomainShader = 17, + CreateComputeShader = 18, + CreateClassLinkage = 19, + CreateBlendState = 20, + CreateDepthStencilState = 21, + CreateRasterizerState = 22, + CreateSamplerState = 23, + CreateQuery = 24, + CreatePredicate = 25, + CreateCounter = 26, + CreateDeferredContext = 27, + OpenSharedResource = 28, + CheckFormatSupport = 29, + CheckMultisampleQualityLevels = 30, + CheckCounterInfo = 31, + CheckCounter = 32, + CheckFeatureSupport = 33, + GetPrivateData = 34, + SetPrivateData = 35, + SetPrivateDataInterface = 36, + GetFeatureLevel = 37, + GetCreationFlags = 38, + GetDeviceRemovedReason = 39, + GetImmediateContext = 40, + SetExceptionMode = 41, + GetExceptionMode = 42, +}; + +enum class DXGISwapChainVTbl : short +{ + // IUnknown + QueryInterface = 0, + AddRef = 1, + Release = 2, + + // IDXGIObject + SetPrivateData = 3, + SetPrivateDataInterface = 4, + GetPrivateData = 5, + GetParent = 6, + + // IDXGIDeviceSubObject + GetDevice = 7, + + // IDXGISwapChain + Present = 8, + GetBuffer = 9, + SetFullscreenState = 10, + GetFullscreenState = 11, + GetDesc = 12, + ResizeBuffers = 13, + ResizeTarget = 14, + GetContainingOutput = 15, + GetFrameStatistics = 16, + GetLastPresentCount = 17, +}; + +#define MAX_SPLITSCREEN_CLIENT_BITS 2 +#define MAX_SPLITSCREEN_CLIENTS ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) // 4 + +enum +{ + MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, + MOUSE_BUTTON_COUNT = 5, +}; + +enum JoystickAxis_t +{ + JOY_AXIS_X = 0, + JOY_AXIS_Y, + JOY_AXIS_Z, + JOY_AXIS_R, + JOY_AXIS_U, + JOY_AXIS_V, + MAX_JOYSTICK_AXES, +}; + +enum +{ + JOYSTICK_MAX_BUTTON_COUNT = 32, + JOYSTICK_POV_BUTTON_COUNT = 4, + JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, +}; + +#define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) +#define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) +#define JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick) * JOYSTICK_AXIS_BUTTON_COUNT) + (_button) ) + +#define JOYSTICK_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ) +#define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) +#define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) + +enum ButtonCode_t +{ + BUTTON_CODE_INVALID = -1, + BUTTON_CODE_NONE = 0, + + KEY_FIRST = 0, + + KEY_NONE = KEY_FIRST, + KEY_0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_A, + KEY_B, + KEY_C, + KEY_D, + KEY_E, + KEY_F, + KEY_G, + KEY_H, + KEY_I, + KEY_J, + KEY_K, + KEY_L, + KEY_M, + KEY_N, + KEY_O, + KEY_P, + KEY_Q, + KEY_R, + KEY_S, + KEY_T, + KEY_U, + KEY_V, + KEY_W, + KEY_X, + KEY_Y, + KEY_Z, + KEY_PAD_0, + KEY_PAD_1, + KEY_PAD_2, + KEY_PAD_3, + KEY_PAD_4, + KEY_PAD_5, + KEY_PAD_6, + KEY_PAD_7, + KEY_PAD_8, + KEY_PAD_9, + KEY_PAD_DIVIDE, + KEY_PAD_MULTIPLY, + KEY_PAD_MINUS, + KEY_PAD_PLUS, + KEY_PAD_ENTER, + KEY_PAD_DECIMAL, + KEY_LBRACKET, + KEY_RBRACKET, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_BACKQUOTE, + KEY_COMMA, + KEY_PERIOD, + KEY_SLASH, + KEY_BACKSLASH, + KEY_MINUS, + KEY_EQUAL, + KEY_ENTER, + KEY_SPACE, + KEY_BACKSPACE, + KEY_TAB, + KEY_CAPSLOCK, + KEY_NUMLOCK, + KEY_ESCAPE, + KEY_SCROLLLOCK, + KEY_INSERT, + KEY_DELETE, + KEY_HOME, + KEY_END, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_BREAK, + KEY_LSHIFT, + KEY_RSHIFT, + KEY_LALT, + KEY_RALT, + KEY_LCONTROL, + KEY_RCONTROL, + KEY_LWIN, + KEY_RWIN, + KEY_APP, + KEY_UP, + KEY_LEFT, + KEY_DOWN, + KEY_RIGHT, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_CAPSLOCKTOGGLE, + KEY_NUMLOCKTOGGLE, + KEY_SCROLLLOCKTOGGLE, + + KEY_LAST = KEY_SCROLLLOCKTOGGLE, + KEY_COUNT = KEY_LAST - KEY_FIRST + 1, + + // Mouse + MOUSE_FIRST = KEY_LAST + 1, + + MOUSE_LEFT = MOUSE_FIRST, + MOUSE_RIGHT, + MOUSE_MIDDLE, + MOUSE_4, + MOUSE_5, + MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up + MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down + + MOUSE_LAST = MOUSE_WHEEL_DOWN, + MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, + + // Joystick + JOYSTICK_FIRST = MOUSE_LAST + 1, + + JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, + JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_MAX_BUTTON_COUNT - 1), + JOYSTICK_FIRST_POV_BUTTON, + JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_POV_BUTTON_COUNT - 1), + JOYSTICK_FIRST_AXIS_BUTTON, + JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), + + JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, + + BUTTON_CODE_LAST, + BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, + + // Helpers for XBox 360 + KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons + KEY_XBUTTON_RIGHT, + KEY_XBUTTON_DOWN, + KEY_XBUTTON_LEFT, + + KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons + KEY_XBUTTON_B, + KEY_XBUTTON_X, + KEY_XBUTTON_Y, + KEY_XBUTTON_LEFT_SHOULDER, + KEY_XBUTTON_RIGHT_SHOULDER, + KEY_XBUTTON_BACK, + KEY_XBUTTON_START, + KEY_XBUTTON_STICK1, + KEY_XBUTTON_STICK2, + KEY_XBUTTON_INACTIVE_START, + + KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE + KEY_XSTICK1_LEFT, // XAXIS NEGATIVE + KEY_XSTICK1_DOWN, // YAXIS POSITIVE + KEY_XSTICK1_UP, // YAXIS NEGATIVE + KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE + KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE + KEY_XSTICK2_RIGHT, // UAXIS POSITIVE + KEY_XSTICK2_LEFT, // UAXIS NEGATIVE + KEY_XSTICK2_DOWN, // VAXIS POSITIVE + KEY_XSTICK2_UP, // VAXIS NEGATIVE +}; + +// Buttons are not confirmed to be the same. They have been always the same throughout the source engine. Lets hope they did not change them. + +enum KeyValuesTypes +{ + TYPE_NONE = 0x0, + TYPE_STRING = 0x1, + TYPE_INT = 0x2, + TYPE_FLOAT = 0x3, + TYPE_PTR = 0x4, + TYPE_WSTRING = 0x5, + TYPE_COLOR = 0x6, + TYPE_UINT64 = 0x7, + TYPE_COMPILED_INT_BYTE = 0x8, + TYPE_COMPILED_INT_0 = 0x9, + TYPE_COMPILED_INT_1 = 0xA, + TYPE_NUMTYPES = 0xB, +}; + +enum ClientFrameStage_t +{ + FRAME_UNDEFINED = -1, // (haven't run any frames yet) + FRAME_START, + + // A network packet is being recieved + FRAME_NET_UPDATE_START, + // Data has been received and we're going to start calling PostDataUpdate + FRAME_NET_UPDATE_POSTDATAUPDATE_START, + // Data has been received and we've called PostDataUpdate on all data recipients + FRAME_NET_UPDATE_POSTDATAUPDATE_END, + // We've received all packets, we can now do interpolation, prediction, etc.. + FRAME_NET_UPDATE_END, + + // We're about to start rendering the scene + FRAME_RENDER_START, + // We've finished rendering the scene. + FRAME_RENDER_END, + + FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE +}; + +enum HostStates_t +{ + HS_NEW_GAME = 0x0, + HS_LOAD_GAME = 0x1, + HS_CHANGE_LEVEL_SP = 0x2, + HS_CHANGE_LEVEL_MP = 0x3, + HS_RUN = 0x4, + HS_GAME_SHUTDOWN = 0x5, + HS_SHUTDOWN = 0x6, + HS_RESTART = 0x7, +}; + +enum SIGNONSTATE +{ + SIGNONSTATE_NONE = 0, // no state yet; about to connect + SIGNONSTATE_CHALLENGE = 1, // client challenging server; all OOB packets + SIGNONSTATE_CONNECTED = 2, // client is connected to server; netchans ready + SIGNONSTATE_NEW = 3, // just got serverinfo and string tables + SIGNONSTATE_PRESPAWN = 4, // received signon buffers + SIGNONSTATE_GETTING_DATA = 5, // getting persistence data I assume? + SIGNONSTATE_SPAWN = 6, // ready to receive entity packets + SIGNONSTATE_FIRST_SNAP = 7, // ??? + SIGNONSTATE_FULL = 8, // we are fully connected; first non-delta packet received + SIGNONSTATE_CHANGELEVEL = 9, // server is changing level; please wait +}; + +enum FileWarningLevel_t +{ + FILESYSTEM_WARNING = -1, + FILESYSTEM_WARNING_QUIET = 0, + FILESYSTEM_WARNING_REPORTUNCLOSED, + FILESYSTEM_WARNING_REPORTUSAGE, + FILESYSTEM_WARNING_REPORTALLACCESSES, + FILESYSTEM_WARNING_REPORTALLACCESSES_READ, + FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, + FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC +}; + +#define FCVAR_NONE 0 + +// Command to ConVars and ConCommands +// ConVar Systems +#define FCVAR_UNREGISTERED (1<<0) // If this is set, don't add to linked list, etc. +#define FCVAR_DEVELOPMENTONLY (1<<1) // Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. +#define FCVAR_GAMEDLL (1<<2) // defined by the game DLL +#define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL +#define FCVAR_HIDDEN (1<<4) // Hidden. Doesn't appear in find or auto complete. Like DEVELOPMENTONLY, but can't be compiled out. + +// ConVar only +#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value +#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. +#define FCVAR_ARCHIVE (1<<7) // set to cause it to be saved to vars.rc +#define FCVAR_NOTIFY (1<<8) // notifies players when changed +#define FCVAR_USERINFO (1<<9) // changes the client's info string + +#define FCVAR_PRINTABLEONLY (1<<10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). + +#define FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS (1<<10) // When on concommands this allows remote clients to execute this cmd on the server. + // We are changing the default behavior of concommands to disallow execution by remote clients without + // this flag due to the number existing concommands that can lag or crash the server when clients abuse them. + +#define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log +#define FCVAR_NEVER_AS_STRING (1<<12) // never try to print that cvar + +// It's a ConVar that's shared between the client and the server. +// At signon, the values of all such ConVars are sent from the server to the client (skipped for local +// client, of course ) +// If a change is requested it must come from the console (i.e., no remote client changes) +// If a value is changed while a server is active, it's replicated to all connected clients +#define FCVAR_REPLICATED (1<<13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time +#define FCVAR_CHEAT (1<<14) // Only useable in singleplayer / debug / multiplayer & sv_cheats +#define FCVAR_SS (1<<15) // causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated +#define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file +#define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles +#define FCVAR_SS_ADDED (1<<18) // This is one of the "added" FCVAR_SS variables for the splitscreen players +#define FCVAR_RELEASE (1<<19) // Cvars tagged with this are the only cvars avaliable to customers +#define FCVAR_RELOAD_MATERIALS (1<<20) // If this cvar changes, it forces a material reload +#define FCVAR_RELOAD_TEXTURES (1<<21) // If this cvar changes, if forces a texture reload + +#define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server +#define FCVAR_MATERIAL_SYSTEM_THREAD (1<<23) // Indicates this cvar is read from the material system thread +#define FCVAR_ARCHIVE_GAMECONSOLE (1<<24) // cvar written to config.cfg on the Xbox + +#define FCVAR_SERVER_CAN_EXECUTE (1<<28)// the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd. +#define FCVAR_SERVER_CANNOT_QUERY (1<<29)// If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). +#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. + +#define MAX_PLAYERS 128 // Max R5 players. diff --git a/r5dedicated/gameclasses.cpp b/r5dedicated/gameclasses.cpp new file mode 100644 index 00000000..9f9cf1e4 --- /dev/null +++ b/r5dedicated/gameclasses.cpp @@ -0,0 +1,470 @@ +#include "pch.h" +#include "enums.h" +#include "gameclasses.h" + +// Need this for a re-factor later. +// Interface* interfaces = *reinterpret_cast<Interface**>(0x167F4FA48); + +// for (Interface* current = interfaces; current; current = reinterpret_cast<Interface*>(current->NextInterfacePtr)) +// { +// printf("%s: %p\n", current->InterfaceName, current->InterfacePtr); +// } + +namespace GameGlobals +{ + bool IsInitialized = false; + CHostState* HostState = nullptr; + CInputSystem* InputSystem = nullptr; + CCVar* Cvar = nullptr; + CClient* Client = nullptr; + BanList* BanSystem = new BanList(); + + CKeyValuesSystem* KeyValuesSystem = nullptr; + KeyValues** PlaylistKeyValues = nullptr; + + std::vector<std::string> allPlaylists = { "none" }; + + namespace CustomCommandVariations + { + + void Kick_Callback(CCommand* cmd) + { + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + return; + + CCommand& cmdReference = *cmd; // Get reference. + const char* firstArg = cmdReference[1]; // Get first arg. + + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. + if (!client) + continue; + + if (!client->GetNetChan()) // Netchan valid? + continue; + + void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. + std::string clientName((char*)clientNamePtr, 32); // Get full name. + + if (clientName.empty()) // Empty name? + continue; + + if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? + continue; + + DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. + } + } + + void KickID_Callback(CCommand* cmd) + { + static auto HasOnlyDigits = [](const std::string& string) + { + for (const char& character : string) + { + if (std::isdigit(character) == 0) + return false; + } + return true; + }; + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + return; + + CCommand& cmdReference = *cmd; // Get reference. + std::string firstArg = cmdReference[1]; // Get first arg. + + try + { + bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. + if (!client) + continue; + + if (!client->GetNetChan()) // Netchan valid? + continue; + + std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. + MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>()); + + finalIPAddress = ss.str(); + } + + if (onlyDigits) + { + std::int64_t ID = static_cast<std::int64_t>(std::stoll(firstArg)); + if (ID > MAX_PLAYERS) // Is it a possible originID? + { + std::int64_t originID = client->m_iOriginID; + if (originID != ID) // See if they match. + continue; + } + else // If its not try by userID. + { + std::int64_t clientID = static_cast<std::int64_t>(client->m_iUserID + 1); // Get UserID + 1. + if (clientID != ID) // See if they match. + continue; + } + + DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. + } + else + { + if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? + continue; + + DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. + } + } + } + catch (std::exception& e) + { + std::cout << "Kick UID asked for a userID or originID :( You can get the userID with the 'status' command. Error: " << e.what() << std::endl; + return; + } + } + + void Unban_Callback(CCommand* cmd) + { + static auto HasOnlyDigits = [](const std::string& string) + { + for (const char& character : string) + { + if (std::isdigit(character) == 0) + return false; + } + return true; + }; + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + return; + + CCommand& cmdReference = *cmd; // Get reference. + + try + { + const char* firstArg = cmdReference[1]; + if (HasOnlyDigits(firstArg)) // Check if we have an ip address or origin ID. + { + GameGlobals::BanSystem->DeleteEntry("noIP", std::stoll(firstArg)); // Delete ban entry. + GameGlobals::BanSystem->Save(); // Save modified vector to file. + } + else + { + GameGlobals::BanSystem->DeleteEntry(firstArg, 1); // Delete ban entry. + GameGlobals::BanSystem->Save(); // Save modified vector to file. + } + } + catch (std::exception& e) + { + std::cout << "Unban Error: " << e.what() << std::endl; + return; + } + } + + void ReloadBanList_Callback(CCommand* cmd) + { + GameGlobals::BanSystem->Load(); // Reload banlist. + } + + void Ban_Callback(CCommand* cmd) + { + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + return; + + CCommand& cmdReference = *cmd; // Get reference. + const char* firstArg = cmdReference[1]; // Get first arg. + + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. + if (!client) + continue; + + if (!client->GetNetChan()) // Netchan valid? + continue; + + void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. + std::string clientName((char*)clientNamePtr, 32); // Get full name. + + if (clientName.empty()) // Empty name? + continue; + + if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? + continue; + + std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. + MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField && ipAddressField.GetValue<int>() != 0x0) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>()); + + finalIPAddress = ss.str(); + } + + GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. + GameGlobals::BanSystem->Save(); // Save ban list. + DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. + } + } + + void BanID_Callback(CCommand* cmd) + { + static auto HasOnlyDigits = [](const std::string& string) + { + for (const char& character : string) + { + if (std::isdigit(character) == 0) + return false; + } + return true; + }; + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + return; + + CCommand& cmdReference = *cmd; // Get reference. + std::string firstArg = cmdReference[1]; + + try + { + bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. + if (!client) + continue; + + if (!client->GetNetChan()) // Netchan valid? + continue; + + std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. + MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>()); + + finalIPAddress = ss.str(); + } + + if (onlyDigits) + { + std::int64_t ID = static_cast<std::int64_t>(std::stoll(firstArg)); + if (ID > MAX_PLAYERS) // Is it a possible originID? + { + std::int64_t originID = client->m_iOriginID; + if (originID != ID) // See if they match. + continue; + } + else // If its not try by userID. + { + std::int64_t clientID = static_cast<std::int64_t>(client->m_iUserID + 1); // Get UserID + 1. + if (clientID != ID) // See if they match. + continue; + } + + GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. + GameGlobals::BanSystem->Save(); // Save ban list. + DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. + } + else + { + if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? + continue; + + GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. + GameGlobals::BanSystem->Save(); // Save ban list. + DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. + } + } + } + catch (std::exception& e) + { + std::cout << "Banid Error: " << e.what() << std::endl; + return; + } + } + } + + void NullHostNames() + { + const char* hostnameArray[] = + { + "pin_telemetry_hostname", + "assetdownloads_hostname", + "users_hostname", + "persistence_hostname", + "speechtotexttoken_hostname", + "communities_hostname", + "persistenceDef_hostname", + "party_hostname", + "speechtotext_hostname", + "serverReports_hostname", + "subscription_hostname", + "steamlink_hostname", + "staticfile_hostname", + "matchmaking_hostname", + "skill_hostname", + "publication_hostname", + "stats_hostname" + }; + + for (int i = 0; i < 17; i++) + { + const char* name = hostnameArray[i]; + Cvar->FindVar(name)->m_pzsCurrentValue = "0.0.0.0"; + } + } + + void InitGameGlobals() + { + HostState = reinterpret_cast<CHostState*>(0x141736120); // Get CHostState from memory. + InputSystem = *reinterpret_cast<CInputSystem**>(0x14D40B380); // Get IInputSystem from memory. + Cvar = *reinterpret_cast<CCVar**>(0x14D40B348); // Get CCVar from memory. + //KeyValuesSystem = reinterpret_cast<CKeyValuesSystem*>(0x141F105C0); // Get CKeyValuesSystem from memory. + //PlaylistKeyValues = reinterpret_cast<KeyValues**>(0x16705B980); // Get the KeyValue for the playlist file. + //Client = reinterpret_cast<CClient*>(0x16073B200); + + //NullHostNames(); // Null all hostnames. + //InitAllCommandVariations(); // Initialize our custom ConVars. + //*(char*)addr_m_bRestrictServerCommands = true; // Restrict commands. + //void* disconnect = Cvar->FindCommand("disconnect"); + //*(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this. + + //std::thread t1(InitPlaylist); // Start thread to grab playlists. + //t1.detach(); // Detach thread from current one. + + IsInitialized = true; + } + + void InitPlaylist() + { + while (true) + { + if ((*PlaylistKeyValues)) + { + KeyValues* playlists = (*PlaylistKeyValues)->FindKey("Playlists", false); // Find playlists key. + if (playlists) + { + allPlaylists.clear(); + for (KeyValues* dat = playlists->m_pSub; dat != nullptr; dat = dat->m_pPeer) // Parse through all sub keys. + { + allPlaylists.push_back(dat->GetName()); // Get all playlist names. + } + + break; // Break if playlist got filled. + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + } + + void InitAllCommandVariations() + { + void* KickConCommand = CreateCustomConCommand("kick", "Kick a client from the Server via name. | Usage: kick (name).", 0, CustomCommandVariations::Kick_Callback, nullptr); + void* KickIDConCommand = CreateCustomConCommand("kickid", "Kick a client from the Server via userID or originID | Usage: kickid (originID/userID)", 0, CustomCommandVariations::KickID_Callback, nullptr); + void* UnbanConCommand = CreateCustomConCommand("unban", "Unbans a client from the Server via IP or originID | Usage: unban (originID/ipAddress)", 0, CustomCommandVariations::Unban_Callback, nullptr); + void* ReloadBanListConCommand = CreateCustomConCommand("reloadbanlist", "Reloads the ban list from disk.", 0, CustomCommandVariations::ReloadBanList_Callback, nullptr); + void* BanConCommand = CreateCustomConCommand("ban", "Bans a client from the Server via name. | Usage: ban (name)", 0, CustomCommandVariations::Ban_Callback, nullptr); + void* BanIDConCommand = CreateCustomConCommand("banid", "Bans a client from the Server via originID, userID or IP | Usage: banid (originID/ipAddress/userID)", 0, CustomCommandVariations::BanID_Callback, nullptr); + } + + void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution) + { + static MemoryAddress ConCommandVtable = MemoryAddress(0x14136BD70); + static MemoryAddress NullSub = MemoryAddress(0x1401B3280); + static MemoryAddress CallbackCompletion = MemoryAddress(0x1401E3990); + static MemoryAddress RegisterConCommand = MemoryAddress(0x14046F470); + + void* command = reinterpret_cast<void*>(addr_MemAlloc_Wrapper(0x68)); // Allocate new memory with StdMemAlloc else we crash. + memset(command, 0, 0x68); // Set all to null. + std::uintptr_t commandPtr = reinterpret_cast<std::uintptr_t>(command); // To ptr. + + *(void**)commandPtr = ConCommandVtable.RCast<void*>(); // 0x0 to ConCommand vtable. + *(const char**)(commandPtr + 0x18) = name; // 0x18 to ConCommand Name. + *(const char**)(commandPtr + 0x20) = helpString; // 0x20 to ConCommand help string. + *(std::int32_t*)(commandPtr + 0x38) = flags; // 0x38 to ConCommand Flags. + *(void**)(commandPtr + 0x40) = NullSub.RCast<void*>(); // 0x40 Nullsub since every concommand has it. + *(void**)(commandPtr + 0x50) = callback; // 0x50 has function callback. + *(std::int32_t*)(commandPtr + 0x60) = 2; // 0x60 Set to use callback and newcommand callback. + + if (callbackAfterExecution) // Do we wanna have a callback after execution? + { + *(void**)(commandPtr + 0x58) = callbackAfterExecution; // 0x58 to our callback after execution. + } + else + { + *(void**)(commandPtr + 0x58) = CallbackCompletion.RCast<void*>(); // 0x58 nullsub. + } + + RegisterConCommand.RCast<void(*)(void*)>()((void*)commandPtr); // Register command in ConVarAccessor. + + return command; + } + + ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk) + { + static MemoryAddress ConVarVtable = MemoryAddress(0x14046FB50).Offset(0x12).ResolveRelativeAddress(); // Get vtable ptr for ConVar table. + static MemoryAddress ICvarVtable = MemoryAddress(0x14046FB50).Offset(0x29).ResolveRelativeAddress(); // Get vtable ptr for ICvar table. + static MemoryAddress CreateConVar = MemoryAddress(0x140470540); // Get CreateConvar address. + + ConVar* allocatedConvar = reinterpret_cast<ConVar*>(addr_MemAlloc_Wrapper(0xA0)); // Allocate new memory with StdMemAlloc else we crash. + memset(allocatedConvar, 0, 0xA0); // Set all to null. + std::uintptr_t cvarPtr = reinterpret_cast<std::uintptr_t>(allocatedConvar); // To ptr. + + *(void**)(cvarPtr + 0x40) = ICvarVtable.RCast<void*>(); // 0x40 to ICvar table. + *(void**)cvarPtr = ConVarVtable.RCast<void*>(); // 0x0 to ConVar vtable. + + CreateConVar.RCast<void(*)(ConVar*, const char*, const char*, int, const char*, bool, float, bool, float, void*, void*)>() // Call to create ConVar. + (allocatedConvar, name, defaultValue, flags, helpString, bMin, fMin, bMax, fMax, callback, unk); + + return allocatedConvar; // Return allocated ConVar. + } + + void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2) + { + if (!client) // Client valid? + return; + + if (std::strlen(reason) == NULL) // Is reason null? + return; + + if (!client->GetNetChan()) + return; + + addr_NetChan_Shutdown(client->GetNetChan(), reason, unk1, unk2); // Shutdown netchan. + client->GetNetChan() = nullptr; // Null netchan. + MemoryAddress(0x140302FD0).RCast<void(*)(CClient*)>()(client); // Reset CClient instance for client. + } +} + +#pragma region KeyValues +const char* KeyValues::GetName() +{ + return GameGlobals::KeyValuesSystem->GetStringForSymbol(MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive, m_iKeyNameCaseSensitive2)); +} +#pragma endregion \ No newline at end of file diff --git a/r5dedicated/gameclasses.h b/r5dedicated/gameclasses.h new file mode 100644 index 00000000..39ebf687 --- /dev/null +++ b/r5dedicated/gameclasses.h @@ -0,0 +1,487 @@ +#pragma once +#include "pch.h" +#include "hooks.h" +#include "enums.h" +#include "banlist.h" + +///////////////////////////////////////////////////////////////////////////// +// Classes and Structs +class CInputSystem +{ +public: + void EnableInput(bool bEnabled)// @0x14039F100 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CInputSystem*, bool); + (*reinterpret_cast<OriginalFn**>(this))[10](this, bEnabled); + } + + void EnableMessagePump(bool bEnabled) // @0x14039F110 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CInputSystem*, bool); + (*reinterpret_cast<OriginalFn**>(this))[11](this, bEnabled); + } + + bool IsButtonDown(ButtonCode_t Button) // @0x1403A0140 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = bool(__thiscall*)(CInputSystem*, ButtonCode_t); + return (*reinterpret_cast<OriginalFn**>(this))[13](this, Button); + } + +private: + char pad_0000[16]; //0x0000 +public: + bool m_bEnabled; //0x0010 IsInputEnabled variable. + bool m_bPumpEnabled; //0x0011 EnabledMessagePump variable. +}; + +typedef int HKeySymbol; + +#define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( std::uint16_t )x2) << 8 ) | (std::uint8_t)(x1)) + +class CKeyValuesSystem // VTABLE @ 0x1413AA1E8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM +{ +public: + + void RegisterSizeofKeyValues(__int64 size) //@0x1413AA1F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, __int64); + (*reinterpret_cast<OriginalFn**>(this))[0](this, size); + } + + void* AllocKeyValuesMemory(__int64 size) // @0x1413AA1F8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void* (__thiscall*)(CKeyValuesSystem*, __int64); + return (*reinterpret_cast<OriginalFn**>(this))[1](this, size); + } + + void FreeKeyValuesMemory(void* pMem) // @0x1413AA200 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, void*); + (*reinterpret_cast<OriginalFn**>(this))[2](this, pMem); + } + + HKeySymbol GetSymbolForString(const char* name, bool bCreate) // @0x1413AA208 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, const char*, bool); + return (*reinterpret_cast<OriginalFn**>(this))[3](this, name, bCreate); + } + + const char* GetStringForSymbol(HKeySymbol symbol) // @0x1413AA210 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = const char* (__thiscall*)(CKeyValuesSystem*, HKeySymbol); + return (*reinterpret_cast<OriginalFn**>(this))[4](this, symbol); + } + + // void __fastcall CKeyValuesSystem::FreeKeyValuesMemory(CKeyValuesSystem* this_arg, void* ptr_mem_arg) + // { + // __int64* v2; // rax + // __int64 v4; // rax + // __int64* v5; // rax + // + // v2 = qword_14D40B538; + // if (!qword_14D40B538) + // { + // v2 = sub_140462930(); + // qword_14D40B538 = v2; + // } + // v4 = (*(*v2 + 48))(v2, ptr_mem_arg); + // if (v4 > 0) + // CKeyValuesSystem::m_pMemPool -= v4; + // v5 = qword_14D40B538; + // if (!qword_14D40B538) + // { + // v5 = sub_140462930(); + // qword_14D40B538 = v5; + // } + // (*(*v5 + 40))(v5, ptr_mem_arg); + // } + + // GetMemPool return a global variable called m_pMemPool it gets modified by AllocKeyValuesMemory and FreeKeyValuesMemory above you can see where the find it in FreeKeyValuesMemory. + void* GetMemPool() // @0x1413AA228 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + return reinterpret_cast<void*>(0x14D412768); // May need to dereference is once more not sure right now. + } + + void SetKeyValuesExpressionSymbol(const char* name, bool bValue) // @0x1413AA230 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, const char*, bool); + (*reinterpret_cast<OriginalFn**>(this))[8](this, name, bValue); + } + + bool GetKeyValuesExpressionSymbol(const char* name) // @0x1413AA238 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = bool(__thiscall*)(CKeyValuesSystem*, const char*); + return (*reinterpret_cast<OriginalFn**>(this))[9](this, name); + } + + HKeySymbol GetSymbolForStringCaseSensitive(HKeySymbol& hCaseInsensitiveSymbol, const char* name, bool bCreate) // @0x1413AA240 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, HKeySymbol&, const char*, bool); + return (*reinterpret_cast<OriginalFn**>(this))[10](this, hCaseInsensitiveSymbol, name, bCreate); + } + + // Datatypes aren't accurate. But full fill the actual byte distance. +public: + void* vtable; // 0x0000 + __int64 m_iMaxKeyValuesSize; // 0x0008 +private: + char gap10[240]; // 0x0010 +public: + __int32 m_KvConditionalSymbolTable; // 0x0100 +private: + char gap104[4]; // 0x0104 +public: + __int64 field_108; // 0x0108 +private: + char gap110[32]; // 0x0110 +public: + int m_mutex; // 0x0130 +}; + +class KeyValues +{ +public: + + KeyValues* FindKey(const char* keyName, bool bCreate) + { + static auto func = reinterpret_cast<KeyValues*(__thiscall*)(KeyValues*, const char*, bool)>(addr_KeyValues_FindKey); + return func(this, keyName, bCreate); + } + + const char* GetName(); + + int GetInt(const char* keyName, int defaultValue) + { + KeyValues* dat = FindKey(keyName, false); + + if (!dat) + return defaultValue; + + switch (dat->m_iDataType) + { + case TYPE_STRING: + return atoi(dat->m_sValue); + case TYPE_FLOAT: + return static_cast<int>(m_flValue()); + case TYPE_WSTRING: + return _wtoi(dat->m_wsValue); + case TYPE_UINT64: + return 0; + default: + return dat->m_iValue(); + } + + return defaultValue; + } + + void SetInt(const char* keyName, int iValue) + { + KeyValues* dat = FindKey(keyName, true); + if (dat) + { + dat->m_iValue() = iValue; + dat->m_iDataType = TYPE_INT; + } + } + + void SetFloat(const char* keyName, float flValue) + { + KeyValues* dat = FindKey(keyName, true); + if (dat) + { + dat->m_flValue() = flValue; + dat->m_iDataType = TYPE_FLOAT; + } + } + + // Compiler makes it so m_Value shares the offset spot with m_flValue that why we cast it like this. + float& m_flValue() + { + static __int32 offset = 0x18; + return *(float*)((std::uintptr_t)this + offset); + } + + int& m_iValue() + { + static __int32 offset = 0x18; + return *(int*)((std::uintptr_t)this + offset); + } + +public: + unsigned __int32 m_iKeyName : 24; // 0x0000 + unsigned __int32 m_iKeyNameCaseSensitive : 8; // 0x0003 + char* m_sValue; // 0x0008 + wchar_t* m_wsValue; // 0x0010 + int m_Value; // 0x0018 +private: + char gap1C[12]; // 0x0020 +public: + char m_iDataType; // 0x0028 + unsigned __int16 m_iKeyNameCaseSensitive2; // 0x002A + KeyValues* m_pPeer; // 0x0030 + KeyValues* m_pSub; // 0x0038 + KeyValues* m_pChain; // 0x0040 +}; + +struct Vector3 // Implement the proper class of this at some point. +{ + float x; // 0x0000 + float y; // 0x0004 + float z; // 0x0008 +}; + +struct QAngle // Implement the proper class of this at some point. +{ + float pitch; //0x0000 + float yaw; // 0x0004 + float roll; // 0x0008 +}; + +class CHostState +{ +public: + int m_iCurrentState; //0x0000 + int m_iNextState; //0x0004 + Vector3 m_vecLocation; //0x0008 + QAngle m_angLocation; //0x0014 + char m_levelName[64]; //0x0020 + char m_mapGroupName[256]; //0x0060 + char m_landMarkName[256]; //0x0160 + float m_flShortFrameTime; //0x0260 + bool m_bActiveGame; //0x0264 + bool m_bRememberLocation; //0x0265 + bool m_bBackgroundLevel; //0x0266 + bool m_bWaitingForConnection; //0x0267 + bool m_bSplitScreenConnect; //0x0268 + bool m_bGameHasShutDownAndFlushedMemory; //0x0269 + bool m_bWorkshopMapDownloadPending; //0x026A +}; + +class CHLClient +{ +public: + void FrameStageNotify(ClientFrameStage_t curStage) // @0x1405C0740 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CHLClient*, ClientFrameStage_t); + (*reinterpret_cast<OriginalFn**>(this))[58](this, curStage); /* 48 83 EC 28 89 15 ? ? ? ? */ + } +}; + +class CClient +{ +public: + inline CClient* GetClientInstance(int index) + { + return (CClient*)(std::uintptr_t)(0x16073B200 + (index * (std::uintptr_t)0x4A4C0)); + } + + void*& GetNetChan() + { + return m_nNetChannel; + } +private: + char pad_0000[16]; //0x0000 +public: + int m_iUserID; //0x0010 +private: + char pad_0014[908]; //0x0014 +public: + void* m_nNetChannel; //0x03A0 +private: + char pad_03A8[8]; //0x03A8 +public: + int m_iSignonstate; //0x03B0 +private: + char pad_03B4[4]; //0x03B4 +public: + std::int64_t m_iOriginID; //0x03B8 +private: + char pad_03C0[303360]; //0x03C0 +}; + +class CCommand +{ +private: + enum + { + COMMAND_MAX_ARGC = 64, + COMMAND_MAX_LENGTH = 512, + }; + +public: + CCommand() = delete; + + inline int MaxCommandLength() + { + return COMMAND_MAX_LENGTH - 1; + } + + inline int64_t ArgC() const + { + return m_nArgc; + } + + inline const char** ArgV() const + { + return m_nArgc ? (const char**)m_ppArgv : NULL; + } + + inline const char* ArgS() const + { + return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; + } + + inline const char* GetCommandString() const + { + return m_nArgc ? m_pArgSBuffer : ""; + } + + inline const char* Arg(int nIndex) const + { + // FIXME: Many command handlers appear to not be particularly careful + // about checking for valid argc range. For now, we're going to + // do the extra check and return an empty string if it's out of range + if (nIndex < 0 || nIndex >= m_nArgc) + return ""; + return m_ppArgv[nIndex]; + } + + inline const char* operator[](int nIndex) const + { + return Arg(nIndex); + } + +private: + std::int64_t m_nArgc; + std::int64_t m_nArgv0Size; + char m_pArgSBuffer[COMMAND_MAX_LENGTH]; + char m_pArgvBuffer[COMMAND_MAX_LENGTH]; + const char* m_ppArgv[COMMAND_MAX_ARGC]; +}; + +class ConCommandBase +{ +public: + void* m_pConCommandBaseVTable; //0x0000 + ConCommandBase* m_pNext; //0x0008 + bool m_bRegistered; //0x0010 +private: + char pad_0011[7]; //0x0011 +public: + const char* m_pszName; //0x0018 + const char* m_pszHelpString; //0x0020 +private: + char pad_0028[16]; //0x0028 +public: + __int32 m_nFlags; //0x0038 +private: + char pad_003C[4]; //0x003C +}; //Size: 0x0038 + +class ConVar +{ +public: + ConCommandBase m_ConCommandBase; // 0x0000 + void* m_pConVarVTable; //0x0040 + ConVar* m_pParent; //0x0048 + const char* n_pszDefaultValue; //0x0050 + const char* m_pzsCurrentValue; //0x0058 + __int64 m_iStringLength; //0x0060 + float m_flValue; //0x0068 + int m_iValue; //0x006C + bool m_bHasMin; //0x0070 + float m_flMinValue; //0x0074 + bool m_bHasMax; //0x0078 + float m_flMaxValue; //0x007C + char pad_0080[32]; //0x0080 +}; //Size: 0x00A0 + +class CCVarIteratorInternal // Fully reversed table, just look at the virtual function table and rename the function. +{ +public: + virtual void SetFirst(void) = 0; //0 + virtual void Next(void) = 0; //1 + virtual bool IsValid(void) = 0; //2 + virtual ConCommandBase* Get(void) = 0; //3 +}; + +class CCVar +{ +public: + ConCommandBase* FindCommandBase(const char* szCommandName) // @0x1405983A0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = ConCommandBase*(__thiscall*)(CCVar*, const char*); + return (*reinterpret_cast<OriginalFn**>(this))[14](this, szCommandName); + } + + ConVar* FindVar(const char* szVarName) // @0x1405983B0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = ConVar*(__thiscall*)(CCVar*, const char*); + return (*reinterpret_cast<OriginalFn**>(this))[16](this, szVarName); + } + + void* /*Implement ConCommand class.*/ FindCommand(const char* szCommandName) // @0x1405983F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void*(__thiscall*)(CCVar*, const char*); + return (*reinterpret_cast<OriginalFn**>(this))[18](this, szCommandName); + } + + CCVarIteratorInternal* FactoryInternalIterator() // @0x140597C10 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = CCVarIteratorInternal*(__thiscall*)(CCVar*); + return (*reinterpret_cast<OriginalFn**>(this))[41](this); + } + + std::unordered_map<std::string, ConCommandBase*> DumpToMap() + { + std::stringstream ss; + CCVarIteratorInternal* itint = FactoryInternalIterator(); // Allocatd new InternalIterator. + + std::unordered_map<std::string, ConCommandBase*> allConVars; + + for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances. + { + ConCommandBase* command = itint->Get(); + const char* commandName = command->m_pszName; + allConVars[commandName] = command; + } + + return allConVars; + } +}; + +struct Interface +{ + __int64 (*InterfacePtr)(void); + const char* InterfaceName; + __int64* NextInterfacePtr; +}; + +///////////////////////////////////////////////////////////////////////////// +// Initialize Game Globals +namespace GameGlobals +{ + // Class Instances + extern CHostState* HostState; + extern CInputSystem* InputSystem; + extern CCVar* Cvar; + extern KeyValues** PlaylistKeyValues; + extern CKeyValuesSystem* KeyValuesSystem; + extern CClient* Client; + extern BanList* BanSystem; + + // Var + ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk); + void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution); + + // Init + void InitGameGlobals(); + void InitAllCommandVariations(); + void InitPlaylist(); + + extern std::vector<std::string> allPlaylists; + extern bool IsInitialized; + + // Utility + void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2); +} \ No newline at end of file diff --git a/r5dedicated/hooks.cpp b/r5dedicated/hooks.cpp index 2b590d4b..ad488c56 100644 --- a/r5dedicated/hooks.cpp +++ b/r5dedicated/hooks.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "hooks.h" #include "opcodes.h" +#include "gameclasses.h" void Hooks::InstallHooks() { @@ -8,6 +9,11 @@ void Hooks::InstallHooks() // Initialize Minhook MH_Initialize(); + /////////////////////////////////////////////////////////////////////////////// + // Hook SourceAppSystemGroup functions + MH_CreateHook(addr_CSourceAppSystemGroup_Create, &Hooks::CSourceAppSystemGroup_Create, reinterpret_cast<void**>(&originalCSourceAppSystemGroup_Create)); + + /////////////////////////////////////////////////////////////////////////////// // Hook Squirrel functions MH_CreateHook(addr_SQVM_Print, &Hooks::SQVM_Print, NULL); MH_CreateHook(addr_SQVM_LoadRson, &Hooks::SQVM_LoadRson, reinterpret_cast<void**>(&originalSQVM_LoadRson)); @@ -32,6 +38,10 @@ void Hooks::InstallHooks() // Hook Utility functions MH_CreateHook(addr_MSG_EngineError, &Hooks::MSG_EngineError, reinterpret_cast<void**>(&originalMSG_EngineError)); + /////////////////////////////////////////////////////////////////////////////// + // Enable SourceAppSystemGroup hooks + MH_EnableHook(addr_CSourceAppSystemGroup_Create); + /////////////////////////////////////////////////////////////////////////////// // Enable Squirrel hooks MH_EnableHook(addr_SQVM_Print); @@ -52,11 +62,17 @@ void Hooks::InstallHooks() // Enabled Utility hooks MH_EnableHook(addr_MSG_EngineError); - InstallOpcodes(); + /////////////////////////////////////////////////////////////////////////////// + // Set global variables + GameGlobals::InitGameGlobals(); } void Hooks::RemoveHooks() { + /////////////////////////////////////////////////////////////////////////////// + // Hook SourceAppSystemGroup functions + MH_RemoveHook(addr_CSourceAppSystemGroup_Create); + /////////////////////////////////////////////////////////////////////////////// // Unhook Squirrel functions MH_RemoveHook(addr_SQVM_Print); diff --git a/r5dedicated/hooks.h b/r5dedicated/hooks.h index be69c6ca..360bddc7 100644 --- a/r5dedicated/hooks.h +++ b/r5dedicated/hooks.h @@ -5,6 +5,10 @@ namespace { Module r5_patterns = Module("r5apex.exe"); // Create module class instance. +#pragma region CSourceAppSystemGroup + FUNC_AT_ADDRESS(addr_CSourceAppSystemGroup_Create, char(*)(__int64), r5_patterns.PatternSearch("48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B F9 E8 ? ? ? ? 33 C9").GetPtr()); +#pragma endregion + #pragma region Console /*0x140202090*/ FUNC_AT_ADDRESS(addr_CommandExecute, void(*)(void*, const char*), r5_patterns.PatternSearch("48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 41 8B D8").GetPtr()); @@ -36,6 +40,9 @@ namespace /*0x1402662D0*/ FUNC_AT_ADDRESS(addr_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr()); + + /*0x14025F190*/ + FUNC_AT_ADDRESS(addr_NetChan_Shutdown, void(*)(void*, const char*, unsigned __int8, char), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("E8 ? ? ? ? 4C 89 B3 ? ? ? ?", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr()); #pragma endregion #pragma region CHLClient @@ -43,6 +50,11 @@ namespace FUNC_AT_ADDRESS(addr_CHLClient_FrameStageNotify, void(*)(void* rcx, int curStage), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr()); #pragma endregion +#pragma region CClientState + /*0x1418223E4*/ + FUNC_AT_ADDRESS(addr_m_bRestrictServerCommands, void*, r5_patterns.StringSearch("DevShotGenerator_Init()").FindPatternSelf("88 05", MemoryAddress::Direction::UP).ResolveRelativeAddressSelf(0x2).OffsetSelf(0x2).GetPtr()); +#pragma endregion + #pragma region CVEngineServer /*0x140315CF0*/ FUNC_AT_ADDRESS(addr_CVEngineServer_IsPersistenceDataAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr()); @@ -51,13 +63,23 @@ namespace #pragma region Utility /*0x140295600*/ FUNC_AT_ADDRESS(addr_MSG_EngineError, int(*)(char*, va_list), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 00 00 48 8B DA").GetPtr()); + + /*0x1401B31C0*/ + FUNC_AT_ADDRESS(addr_MemAlloc_Wrapper, void* (*)(__int64), r5_patterns.StringSearch("ConversionModeMenu").FindPatternSelf("E8 ? ? ? ? 48", MemoryAddress::Direction::UP).FollowNearCallSelf().GetPtr()); #pragma endregion // Un-used atm. // DWORD64 p_KeyValues_FindKey = /*1404744E0*/ reinterpret_cast<DWORD64>(PatternScan("r5apex.exe", "40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45")); +#pragma region KeyValues +/*0x1404744E0*/ + FUNC_AT_ADDRESS(addr_KeyValues_FindKey, void* (*)(void*, const char*, bool), r5_patterns.PatternSearch("40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45").GetPtr()); +#pragma endregion + + void PrintHAddress() // Test the sigscan results { std::cout << "+--------------------------------------------------------+" << std::endl; + PRINT_ADDRESS("CSourceAppSystemGroup::Create", addr_CSourceAppSystemGroup_Create); PRINT_ADDRESS("CommandExecute", addr_CommandExecute); PRINT_ADDRESS("ConVar_IsFlagSet", addr_ConVar_IsFlagSet); PRINT_ADDRESS("ConCommand_IsFlagSet", addr_ConCommand_IsFlagSet); @@ -66,9 +88,12 @@ namespace PRINT_ADDRESS("SQVM_LoadRson", addr_SQVM_LoadRson); PRINT_ADDRESS("NET_ReceiveDatagram", addr_NET_ReceiveDatagram); PRINT_ADDRESS("NET_SendDatagram ", addr_NET_SendDatagram); + PRINT_ADDRESS("NetChan_Shutdown ", addr_NetChan_Shutdown); PRINT_ADDRESS("CHLClient::FrameStageNotify", addr_CHLClient_FrameStageNotify); PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", addr_CVEngineServer_IsPersistenceDataAvailable); PRINT_ADDRESS("MSG_EngineError", addr_MSG_EngineError); + PRINT_ADDRESS("MemAlloc_Wrapper", addr_MemAlloc_Wrapper); + PRINT_ADDRESS("KeyValues_FindKey", addr_KeyValues_FindKey); std::cout << "+--------------------------------------------------------+" << std::endl; // TODO implement error handling when sigscan fails or result is 0 } @@ -80,6 +105,13 @@ inline bool g_bDebugConsole = false; namespace Hooks { +#pragma region CSourceAppSystemGroup + char __fastcall CSourceAppSystemGroup_Create(__int64 a1); + + using CSourceAppSystemGroup_CreateFn = char(*)(__int64); + extern CSourceAppSystemGroup_CreateFn originalCSourceAppSystemGroup_Create; +#pragma endregion + #pragma region CHLClient // void __fastcall FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage); @@ -122,7 +154,7 @@ namespace Hooks bool ConCommand_IsFlagSet(int* cmd, int flag); #pragma endregion -#pragma region Other +#pragma region Utility int MSG_EngineError(char* fmt, va_list args); using MSG_EngineErrorFn = int(*)(char*, va_list); @@ -133,4 +165,5 @@ namespace Hooks void RemoveHooks(); void ToggleNetTrace(); void ToggleDevCommands(); + void DedicatedPatch(); } \ No newline at end of file diff --git a/r5dedicated/opcodes.cpp b/r5dedicated/opcodes.cpp index d91b6b44..dbcd45fd 100644 --- a/r5dedicated/opcodes.cpp +++ b/r5dedicated/opcodes.cpp @@ -1,29 +1,152 @@ #include "pch.h" +#include "hooks.h" +#include "enums.h" #include "opcodes.h" +#include "gameclasses.h" /*----------------------------------------------------------------------------- * _opcodes.cpp *-----------------------------------------------------------------------------*/ -void InstallOpcodes() /* .TEXT */ +void DisableRenderer() { //------------------------------------------------------------------------- - // JNZ --> JMP | Prevent OriginSDK from initializing on the client - //Origin_Init.Offset(0x0B).Patch({ 0xE9, 0x63, 0x02, 0x00, 0x00, 0x00 }); - //Origin_SetState.Offset(0x0E).Patch({ 0xE9, 0xCB, 0x03, 0x00, 0x00 }); + // FUN --> RET | Called from CEngineClient and CEngineVGUI (Init()?). + r0.Patch({ 0xC3 }); // This patch is likely not required if client.dll isn't initialized. //------------------------------------------------------------------------- - // JNE --> JMP | Allow games to be loaded without the optional texture streaming file - dst002.Offset(0x8E5).Patch({ 0xEB, 0x19 }); + // FUN --> RET | Skip ShaderSetup(). CShaderGlue. + r1.Patch({ 0xC3 }); //------------------------------------------------------------------------- - // JNE --> JMP | Prevent connect command from crashing by invalid call to UI function - dst004.Offset(0x1D6).Patch({ 0xEB, 0x27 }); + // FUN --> RET | Skip Matsync. Called from CMaterialSystem. + r2.Patch({ 0xC3 }); //------------------------------------------------------------------------- - // JNE --> JMP | Prevent connect localhost from being executed after listenserver init - //Host_NewGame.Offset(0x637).Patch({ 0xE9, 0xC1, 0x00, 0x00, 0x00}); + // JE --> JMP | Matsys mode init (CMaterialSystem). + r3.Offset(0x22).Patch({ 0xEB, 0x66 }); //------------------------------------------------------------------------- - // JA --> JMP | Disable server-side verification for duplicate accounts on the server - CServer_Auth.Offset(0x284).Patch({ 0x90, 0x90 }); + // FUN --> RET | Clear render buffer? Called from CMatRenderContext and CTexture. + r4.Patch({ 0xC3 }); //------------------------------------------------------------------------- - // JA --> JMP | Prevent FairFight anti-cheat from initializing on the server + // FUN --> RET | Heavy render stuff. Called from CMatRenderContext. + r5.Patch({ 0xC3 }); + //------------------------------------------------------------------------- + // FUN --> RET | Set shader resource. + r6.Patch({ 0xC3 }); + //------------------------------------------------------------------------- + // FUN --> RET | Begin. + r7.Patch({ 0xC3, 0x90, 0x90, 0x90, 0x90 }); + //------------------------------------------------------------------------- + // FUN --> RET | End. + r8.Patch({ 0xC3, 0x90, 0x90, 0x90, 0x90, 0x90 }); +} + +void DisableClient() +{ + //------------------------------------------------------------------------- + // JNZ --> JMP | Prevent EbisuSDK from initializing on the engine and server. + Origin_Init.Offset(0x0B).Patch({ 0xE9, 0x63, 0x02, 0x00, 0x00, 0x00 }); + Origin_SetState.Offset(0x0E).Patch({ 0xE9, 0xCB, 0x03, 0x00, 0x00 }); + //------------------------------------------------------------------------- + // JE --> JMP | Skip CreateGameWindow initialization code. + CreateGameWindow.Offset(0x3DD).Patch({ 0xEB, 0x6D }); + //------------------------------------------------------------------------- + // JNZ --> JMP | Skip CreateGameWindow validation code. + CreateGameWindow.Offset(0x44C).Patch({ 0xEB, 0x49 }); + //------------------------------------------------------------------------- + // PUS --> XOR | Prevent ShowWindow and CreateGameWindow from being initialized. + c1.Patch({ 0x30, 0xC0, 0xC3 }); + //------------------------------------------------------------------------- + // PUS --> XOR | Prevent ShowWindow and CreateGameWindow from being initialized. + c1.Patch({ 0x30, 0xC0, 0xC3 }); + //------------------------------------------------------------------------- + // JNE --> NOP | TODO: NOP 'particle_script' instead. + c2.Offset(0x23C).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); + //------------------------------------------------------------------------- + // MOV --> NOP | TODO: NOP 'particle_script' instead. + c2.Offset(0x2BD).Patch({ 0x90, 0x90, 0x90 }); + //------------------------------------------------------------------------- + // MOV --> NOP | TODO: NOP 'highlight_system' instead. + c3.Offset(0xA9).Patch({ 0x90, 0x90, 0x90, 0x90 }); +} + +void DisableVGUI() +{ + //------------------------------------------------------------------------- + // CMP --> XOR | Skip VGUI initialization jumptable. + v0.Patch({ 0x48, 0x33, 0xC0, 0xC3, 0x90, 0x90, 0x90 }); + //------------------------------------------------------------------------- + // JNE --> JMP | Skip call to VGUI loadscreen func. + SCR_BeginLoadingPlaque.Offset(0x427).Patch({ 0xEB, 0x09 }); +} + +void Hooks::DedicatedPatch() +{ + //Sleep(10000); + // for future reference 14171A9B4 - matsys mode + + *(uintptr_t*)0x14D415040 = 0x1417304E8; + *(uintptr_t*)0x14B37C3C0 = 0x141F10CA0; + + *(uintptr_t*)0x14B3800D7 = 0x1; // bDedicated + + DisableRenderer(); + DisableClient(); + DisableVGUI(); + + //------------------------------------------------------------------------- + // CAL --> NOP | HLClient call inside eng->frame. + addr_CEngine_Frame.Offset(0x410).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); + //------------------------------------------------------------------------- + // JA --> JMP | Prevent FairFight anti-cheat from initializing on the server. + // TODO: fix and re-enable this. FairFight_Init.Offset(0x61).Patch({ 0xE9, 0xED, 0x00, 0x00, 0x00, 0x00 }); + //------------------------------------------------------------------------- + // JNE --> JMP | Take dedicated initialization routine instead. + s0.Offset(0x19).Patch({ 0xEB, 0x6E }); + //------------------------------------------------------------------------- + // JE --> JMP | Skip client.dll Init_PostVideo() validation code. + s0.Offset(0x609).Patch({ 0xEB, 0x2B }); + //------------------------------------------------------------------------- + // JNE --> JMP | Skip client.dll Init_PostVideo() validation code. + s0.Offset(0x621).Patch({ 0xEB, 0x0C }); + //------------------------------------------------------------------------- + // JE --> JMP | Skip NULL call as client is never initialized. + s0.Offset(0x658).Patch({ 0xE9, 0x8C, 0x00, 0x00, 0x00 }); + //------------------------------------------------------------------------- + // JNE --> JMP | Skip shader preloading as cvar can't be checked due to client being NULL. + s0.Offset(0x6E9).Patch({ 0xE9, 0xB0, 0x00, 0x00, 0x00 }); + //------------------------------------------------------------------------- + // JNE --> JMP | Return early in _Host_RunFrame() for debugging perposes. + //s1.Offset(0x1C6).Patch({ 0xE9, 0xAD, 0x11, 0x00, 0x00 }); // <-- this one was only used to debug. + //------------------------------------------------------------------------- + // JNE --> JMP | Return early in _Host_RunFrame() for debugging perposes. + s1.Offset(0x1010).Patch({ 0xEB, 0x14 }); + //------------------------------------------------------------------------- + // CAL --> NOP | NOP NULL call as client is never initialized. + s1.Offset(0x1023).Patch({ 0x90, 0x90, 0x90 }); + //------------------------------------------------------------------------- + // JS --> JMP | Skip ListenServer HeartBeat. + s2.Offset(0xF).Patch({ 0xE9, 0x22, 0x01, 0x00, 0x00 }); + + //------------------------------------------------------------------------- + // CAL --> NOP | NOP call to UI texture asset preloading. + e0.Offset(0x182).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); + //------------------------------------------------------------------------- + // JNE --> JNP | Skip client.dll library initialization. + e0.Offset(0xA7D).Patch({ 0xE9, 0xF0, 0x01, 0x00, 0x00 }); + //------------------------------------------------------------------------- + // JNE --> NOP | Skip settings field loading for client texture assets. + // TODO: this is also used by server.dll library. + e1.Offset(0x213).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); + + //------------------------------------------------------------------------- + // ??? 1403DFC30 = 0x94490 ??? // an expensive stuff that wasted many CPU cycles, this one seems to be the best candidate to return +} + +// TEST +void SetCHostState() +{ + static std::string ServerMap = std::string(); + ServerMap = "mp_rr_canyonlands_64k_x_64k"; + strncpy_s(GameGlobals::HostState->m_levelName, ServerMap.c_str(), 64); // Copy new map into hoststate levelname. 64 is size of m_levelname. + GameGlobals::HostState->m_iNextState = HostStates_t::HS_NEW_GAME; // Force CHostState::FrameUpdate to start a server. } \ No newline at end of file diff --git a/r5dedicated/opcodes.h b/r5dedicated/opcodes.h index 4c44dcd5..48d4c2f0 100644 --- a/r5dedicated/opcodes.h +++ b/r5dedicated/opcodes.h @@ -1,7 +1,7 @@ #pragma once -void InstallOpcodes(); inline HANDLE GameProcess = GetCurrentProcess(); +void SetCHostState(); namespace { @@ -36,6 +36,42 @@ namespace MemoryAddress FairFight_Init = r5_op.PatternSearch("40 53 48 83 EC 20 8B 81 B0 03 ? ? 48 8B D9 C6"); #pragma endregion + + // TODO: create patterns instead and rename to function names. + // Renderer + MemoryAddress r0 = 0x00000001402FE280; // + MemoryAddress r1 = 0x00000001403B3A50; // + MemoryAddress r2 = 0x00000001403DEE90; // + MemoryAddress r3 = 0x00000001403BD120; // + MemoryAddress r4 = 0x0000000140404380; // + MemoryAddress r5 = 0x000000014040D850; // + MemoryAddress r6 = 0x0000000140413260; // + MemoryAddress r7 = 0x00000001404093F0; // + MemoryAddress r8 = 0x00000001403D2E60; // + MemoryAddress d3d11init = 0x000000014043CDF0; // + + // Engine + MemoryAddress e0 = 0x0000000140236E40; // main Host_Init()? + MemoryAddress e1 = 0x0000000140FB2F10; // also used by CServerGameDLL + MemoryAddress addr_CEngine_Frame = 0x00000001402970E0; + + // SERVER + MemoryAddress s0 = 0x0000000140237B00; // server Host_Init()? + MemoryAddress s1 = 0x0000000140231C00; // _Host_RunFrame() with inlined CFrameTimer::MarkFrame()? + MemoryAddress s2 = 0x00000001402312A0; // server HeartBeat? (baseserver.cpp) + + // CLIENT + MemoryAddress c0 = 0x0000000140236640; // client Host_Init()? + MemoryAddress c1 = 0x0000000140299100; // CreateGameWindowInit()? + MemoryAddress c2 = 0x00000001403F4360; // 1403DF870 --> 1403F4360 + MemoryAddress c3 = 0x00000001403F8A80; // 1403DF870 --> 1403F8A40 + MemoryAddress CreateGameWindow = 0x0000000140343DE0; + + // VGUI + MemoryAddress v0 = 0x0000000140282E40; // jumptable + MemoryAddress SCR_BeginLoadingPlaque = 0x000000014023E870; + + void PrintOAddress() // Test the sigscan results { std::cout << "+--------------------------------------------------------+" << std::endl; diff --git a/r5dedicated/pch.h b/r5dedicated/pch.h index d81d5c4d..9a8b7cc6 100644 --- a/r5dedicated/pch.h +++ b/r5dedicated/pch.h @@ -1,5 +1,5 @@ #pragma once -#pragma message("Pre-compiling headers.\n") +#pragma message("[DEDICATED] pre-compiling headers.\n") #define WIN32_LEAN_AND_MEAN // Prevent winsock2 redefinition. #include <windows.h> @@ -27,8 +27,8 @@ #include "sinks/stdout_sinks.h" #include "sinks/ostream_sink.h" #include "utility.h" -//#include "httplib.h" -//#include "json.hpp" +#include "httplib.h" +#include "json.hpp" #include "address.h" diff --git a/r5dedicated/r5dedicated.vcxproj b/r5dedicated/r5dedicated.vcxproj index 961013f0..5a98ecbe 100644 --- a/r5dedicated/r5dedicated.vcxproj +++ b/r5dedicated/r5dedicated.vcxproj @@ -185,7 +185,9 @@ <ItemGroup> <ClCompile Include="..\shared\utility.cpp" /> <ClCompile Include="console.cpp" /> + <ClCompile Include="csourceappsystemgroup.cpp" /> <ClCompile Include="dllmain.cpp" /> + <ClCompile Include="gameclasses.cpp" /> <ClCompile Include="hooks.cpp" /> <ClCompile Include="iconvar.cpp" /> <ClCompile Include="msgbox.cpp" /> @@ -300,6 +302,8 @@ <ClInclude Include="..\shared\include\utility.h" /> <ClInclude Include="console.h" /> <ClInclude Include="dllmain.h" /> + <ClInclude Include="enums.h" /> + <ClInclude Include="gameclasses.h" /> <ClInclude Include="hooks.h" /> <ClInclude Include="opcodes.h" /> <ClInclude Include="pch.h" /> diff --git a/r5dedicated/r5dedicated.vcxproj.filters b/r5dedicated/r5dedicated.vcxproj.filters index c95890fb..43adb38d 100644 --- a/r5dedicated/r5dedicated.vcxproj.filters +++ b/r5dedicated/r5dedicated.vcxproj.filters @@ -50,36 +50,36 @@ <Filter Include="shared\libraries\minhook\include"> <UniqueIdentifier>{485b5648-149f-4664-a961-be9cd520e9e3}</UniqueIdentifier> </Filter> - <Filter Include="r5-sdk"> - <UniqueIdentifier>{f5326cf2-826e-4499-98de-e818e096939d}</UniqueIdentifier> - </Filter> - <Filter Include="r5-sdk\include"> - <UniqueIdentifier>{4c680991-cc41-4265-a6f3-b46d698bd72f}</UniqueIdentifier> - </Filter> - <Filter Include="r5-sdk\src"> - <UniqueIdentifier>{8aac7eb6-9810-4fa2-bbfe-499fb2950f01}</UniqueIdentifier> - </Filter> - <Filter Include="hooks\src"> - <UniqueIdentifier>{f28b1a49-9b41-48d2-9462-1674af3d72a2}</UniqueIdentifier> - </Filter> - <Filter Include="hooks\src\other"> - <UniqueIdentifier>{cc424eef-0c7a-4fb0-9d84-30bf8db2e253}</UniqueIdentifier> - </Filter> - <Filter Include="hooks\src\squirrel"> - <UniqueIdentifier>{74afa89f-72af-4e13-aa90-70f7a1957154}</UniqueIdentifier> - </Filter> - <Filter Include="hooks\src\netchannel"> - <UniqueIdentifier>{05e6e9a7-801b-49b0-9c5a-21c4868befb7}</UniqueIdentifier> - </Filter> - <Filter Include="hooks\src\iconvar"> - <UniqueIdentifier>{06affed3-5a59-4b95-88ca-72d92c91909b}</UniqueIdentifier> - </Filter> - <Filter Include="hooks\src\cvengineserver"> - <UniqueIdentifier>{338a4fb7-7519-4628-9206-679d33824965}</UniqueIdentifier> - </Filter> <Filter Include="hooks\include"> <UniqueIdentifier>{31cdde4d-3641-497c-9b34-20d3d7c89d87}</UniqueIdentifier> </Filter> + <Filter Include="hooks\cvengineserver"> + <UniqueIdentifier>{338a4fb7-7519-4628-9206-679d33824965}</UniqueIdentifier> + </Filter> + <Filter Include="hooks\iconvar"> + <UniqueIdentifier>{06affed3-5a59-4b95-88ca-72d92c91909b}</UniqueIdentifier> + </Filter> + <Filter Include="hooks\netchannel"> + <UniqueIdentifier>{05e6e9a7-801b-49b0-9c5a-21c4868befb7}</UniqueIdentifier> + </Filter> + <Filter Include="hooks\other"> + <UniqueIdentifier>{cc424eef-0c7a-4fb0-9d84-30bf8db2e253}</UniqueIdentifier> + </Filter> + <Filter Include="hooks\squirrel"> + <UniqueIdentifier>{74afa89f-72af-4e13-aa90-70f7a1957154}</UniqueIdentifier> + </Filter> + <Filter Include="hooks\csourceappsystemgroup"> + <UniqueIdentifier>{9381fa63-cf89-4980-8e5a-bf6e43cb2283}</UniqueIdentifier> + </Filter> + <Filter Include="r5-sdk"> + <UniqueIdentifier>{7fd080e8-390a-430b-a94c-e19c5792bf10}</UniqueIdentifier> + </Filter> + <Filter Include="r5-sdk\src"> + <UniqueIdentifier>{e7e154b6-398e-42f9-bfb9-e80cd306254e}</UniqueIdentifier> + </Filter> + <Filter Include="r5-sdk\include"> + <UniqueIdentifier>{3c89e0ef-e415-4a91-86b7-2a04a5f03340}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClInclude Include="..\shared\include\address.h"> @@ -388,12 +388,18 @@ <ClInclude Include="..\external\minhook\include\MinHook.h"> <Filter>shared\libraries\minhook\include</Filter> </ClInclude> - <ClInclude Include="opcodes.h"> - <Filter>r5-sdk\include</Filter> - </ClInclude> <ClInclude Include="hooks.h"> <Filter>hooks\include</Filter> </ClInclude> + <ClInclude Include="opcodes.h"> + <Filter>hooks\include</Filter> + </ClInclude> + <ClInclude Include="gameclasses.h"> + <Filter>r5-sdk\include</Filter> + </ClInclude> + <ClInclude Include="enums.h"> + <Filter>r5-sdk\include</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="console.cpp"> @@ -406,28 +412,34 @@ <Filter>core</Filter> </ClCompile> <ClCompile Include="net.cpp"> - <Filter>hooks\src\netchannel</Filter> + <Filter>hooks\netchannel</Filter> </ClCompile> <ClCompile Include="cvengineserver.cpp"> - <Filter>hooks\src\cvengineserver</Filter> + <Filter>hooks\cvengineserver</Filter> </ClCompile> <ClCompile Include="sqvm.cpp"> - <Filter>hooks\src\squirrel</Filter> + <Filter>hooks\squirrel</Filter> </ClCompile> <ClCompile Include="msgbox.cpp"> - <Filter>hooks\src\other</Filter> + <Filter>hooks\other</Filter> </ClCompile> <ClCompile Include="..\shared\utility.cpp"> <Filter>shared</Filter> </ClCompile> <ClCompile Include="iconvar.cpp"> - <Filter>hooks\src\iconvar</Filter> + <Filter>hooks\iconvar</Filter> </ClCompile> <ClCompile Include="opcodes.cpp"> - <Filter>r5-sdk\src</Filter> + <Filter>hooks</Filter> </ClCompile> <ClCompile Include="hooks.cpp"> - <Filter>hooks\src</Filter> + <Filter>hooks</Filter> + </ClCompile> + <ClCompile Include="csourceappsystemgroup.cpp"> + <Filter>hooks\csourceappsystemgroup</Filter> + </ClCompile> + <ClCompile Include="gameclasses.cpp"> + <Filter>r5-sdk\src</Filter> </ClCompile> </ItemGroup> <ItemGroup> diff --git a/r5dev/include/pch.h b/r5dev/include/pch.h index b42160e0..de793c13 100644 --- a/r5dev/include/pch.h +++ b/r5dev/include/pch.h @@ -1,5 +1,5 @@ #pragma once -#pragma message("Pre-compiling headers.\n") +#pragma message("[DEV] pre-compiling headers.\n") #define WIN32_LEAN_AND_MEAN // Prevent winsock2 redefinition. #include <windows.h> diff --git a/shared/include/banlist.h b/shared/include/banlist.h index 71889965..3ed13f42 100644 --- a/shared/include/banlist.h +++ b/shared/include/banlist.h @@ -1,4 +1,5 @@ #pragma once +#include "pch.h" class BanList {