Merge pull request #288 from diamante0018/userinfo

[Script] Add way to modify player name
This commit is contained in:
Dss0 2022-06-04 19:39:41 +02:00 committed by GitHub
commit 9eb6bc0cda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 144 additions and 20 deletions

View File

@ -15,6 +15,7 @@ end
function json11.project()
project "json11"
language "C++"
cppdialect "C++11"
files
{

View File

@ -109,6 +109,7 @@ namespace Components
Loader::Register(new RawMouse());
Loader::Register(new Bullet());
Loader::Register(new Ceg());
Loader::Register(new UserInfo());
Loader::Pregame = false;
}

View File

@ -140,3 +140,4 @@ namespace Components
#include "Modules/RawMouse.hpp"
#include "Modules/Bullet.hpp"
#include "Modules/Ceg.hpp"
#include "Modules/UserInfo.hpp"

View File

@ -311,10 +311,13 @@ namespace Components
* Should be called when a client drops from the server
* but not "between levels" (Quake-III-Arena)
*/
void Bots::ClientDisconnect_Hk(int clientNum)
void Bots::ClientDisconnect_Hk(const int clientNum)
{
g_botai[clientNum].active = false;
// Clear the overrides for UserInfo
UserInfo::ClearClientOverrides(clientNum);
// Call original function
Utils::Hook::Call<void(int)>(0x4AA430)(clientNum);
}

View File

@ -451,7 +451,7 @@ namespace Components
}
}
void ServerList::Insert(Network::Address address, Utils::InfoString info)
void ServerList::Insert(const Network::Address& address, const Utils::InfoString& info)
{
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);

View File

@ -38,7 +38,7 @@ namespace Components
static void RefreshVisibleListInternal(UIScript::Token, bool refresh = false);
static void UpdateVisibleList(UIScript::Token);
static void InsertRequest(Network::Address address);
static void Insert(Network::Address address, Utils::InfoString info);
static void Insert(const Network::Address& address, const Utils::InfoString& info);
static ServerInfo* GetCurrentServer();

View File

@ -0,0 +1,76 @@
#include <STDInclude.hpp>
namespace Components
{
std::unordered_map<int, UserInfo::userInfoMap> UserInfo::UserInfoOverrides;
void UserInfo::SV_GetUserInfo_Stub(int index, char* buffer, int bufferSize)
{
Utils::Hook::Call<void(int, char*, int)>(0x49A160)(index, buffer, bufferSize);
Utils::InfoString map(buffer);
if (!UserInfoOverrides.contains(index))
{
UserInfoOverrides[index] = {};
}
for (const auto& [key, val] : UserInfoOverrides[index])
{
if (val.empty())
{
map.remove(key);
}
else
{
map.set(key, val);
}
}
const auto userInfo = map.build();
strncpy_s(buffer, bufferSize, userInfo.data(), _TRUNCATE);
}
void UserInfo::ClearClientOverrides(const int client)
{
UserInfoOverrides[client].clear();
}
void UserInfo::ClearAllOverrides()
{
UserInfoOverrides.clear();
}
void UserInfo::AddScriptMethods()
{
Script::AddMethod("SetName", [](Game::scr_entref_t entref) // gsc: self SetName(<string>)
{
const auto* ent = Game::GetPlayerEntity(entref);
const auto* name = Game::Scr_GetString(0);
UserInfoOverrides[ent->s.number]["name"] = name;
Game::ClientUserinfoChanged(ent->s.number);
});
Script::AddMethod("ResetName", [](Game::scr_entref_t entref) // gsc: self ResetName()
{
const auto* ent = Game::GetPlayerEntity(entref);
UserInfoOverrides[ent->s.number].erase("name");
Game::ClientUserinfoChanged(ent->s.number);
});
}
UserInfo::UserInfo()
{
Utils::Hook(0x445268, SV_GetUserInfo_Stub, HOOK_CALL).install()->quick();
Utils::Hook(0x478B04, SV_GetUserInfo_Stub, HOOK_CALL).install()->quick();
AddScriptMethods();
Script::OnVMShutdown([]
{
ClearAllOverrides();
});
}
}

View File

@ -0,0 +1,21 @@
#pragma once
namespace Components
{
class UserInfo : public Component
{
public:
UserInfo();
static void ClearClientOverrides(int client);
static void ClearAllOverrides();
private:
using userInfoMap = std::unordered_map<std::string, std::string>;
static std::unordered_map<int, userInfoMap> UserInfoOverrides;
static void SV_GetUserInfo_Stub(int index, char* buffer, int bufferSize);
static void AddScriptMethods();
};
}

View File

@ -431,6 +431,8 @@ namespace Game
IN_Init_t IN_Init = IN_Init_t(0x45D620);
IN_Shutdown_t IN_Shutdown = IN_Shutdown_t(0x426360);
ClientUserinfoChanged_t ClientUserinfoChanged = ClientUserinfoChanged_t(0x445240);
XAssetHeader* DB_XAssetPool = reinterpret_cast<XAssetHeader*>(0x7998A8);
unsigned int* g_poolSize = reinterpret_cast<unsigned int*>(0x7995E8);

View File

@ -1029,6 +1029,9 @@ namespace Game
typedef void(__cdecl * IN_Shutdown_t)();
extern IN_Shutdown_t IN_Shutdown;
typedef void(__cdecl * ClientUserinfoChanged_t)(int clientNum);
extern ClientUserinfoChanged_t ClientUserinfoChanged;
extern XAssetHeader* DB_XAssetPool;
extern unsigned int* g_poolSize;

View File

@ -2,12 +2,22 @@
namespace Utils
{
InfoString::InfoString(const std::string& buffer)
{
this->parse(buffer);
}
void InfoString::set(const std::string& key, const std::string& value)
{
this->keyValuePairs[key] = value;
}
std::string InfoString::get(const std::string& key)
void InfoString::remove(const std::string& key)
{
this->keyValuePairs.erase(key);
}
std::string InfoString::get(const std::string& key) const
{
const auto value = this->keyValuePairs.find(key);
if (value != this->keyValuePairs.end())
@ -15,7 +25,7 @@ namespace Utils
return value->second;
}
return "";
return {};
}
void InfoString::parse(std::string buffer)
@ -25,17 +35,17 @@ namespace Utils
buffer = buffer.substr(1);
}
auto KeyValues = Utils::String::Split(buffer, '\\');
const auto keyValues = Utils::String::Split(buffer, '\\');
for (size_t i = 0; !KeyValues.empty() && i < (KeyValues.size() - 1); i += 2)
for (std::size_t i = 0; !keyValues.empty() && i < (keyValues.size() - 1); i += 2)
{
const auto& key = KeyValues[i];
const auto& value = KeyValues[i + 1];
const auto& key = keyValues[i];
const auto& value = keyValues[i + 1];
this->keyValuePairs[key] = value;
}
}
std::string InfoString::build()
std::string InfoString::build() const
{
std::string infoString;
@ -54,16 +64,18 @@ namespace Utils
return infoString;
}
#ifdef _DEBUG
void InfoString::dump()
{
for (const auto& [key, value] : this->keyValuePairs)
{
OutputDebugStringA(Utils::String::VA("%s: %s\n", key.data(), value.data()));
OutputDebugStringA(String::VA("%s: %s\n", key.data(), value.data()));
}
}
#endif
json11::Json InfoString::to_json()
json11::Json InfoString::to_json() const
{
return this->keyValuePairs;
return {this->keyValuePairs};
}
}

View File

@ -5,19 +5,23 @@ namespace Utils
class InfoString
{
public:
InfoString() {};
InfoString(const std::string& buffer) : InfoString() { this->parse(buffer); };
InfoString() = default;
explicit InfoString(const std::string& buffer);
void set(const std::string& key, const std::string& value);
std::string get(const std::string& key);
std::string build();
void remove(const std::string& key);
[[nodiscard]] std::string get(const std::string& key) const;
[[nodiscard]] std::string build() const;
#ifdef _DEBUG
void dump();
#endif
json11::Json to_json();
[[nodiscard]] json11::Json to_json() const;
private:
std::map<std::string, std::string> keyValuePairs;
std::unordered_map<std::string, std::string> keyValuePairs;
void parse(std::string buffer);
};
}