r5sdk/r5dedicated/gameclasses.cpp

470 lines
17 KiB
C++
Raw Normal View History

#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