Add functions to change player clantags/name

This commit is contained in:
Federico Cecchetto 2021-06-20 00:31:56 +02:00
parent 97371d8f44
commit 9ead1676b0
5 changed files with 191 additions and 2 deletions

View File

@ -339,7 +339,7 @@ namespace gsc
{
if (ent.classnum != 0)
{
throw std::runtime_error("Invalid type");
throw std::runtime_error("Invalid entity");
}
const auto client = ent.entnum;

View File

@ -3,6 +3,7 @@
#include "scheduler.hpp"
#include "command.hpp"
#include "userinfo.hpp"
#include "game/scripting/event.hpp"
#include "game/scripting/execution.hpp"
@ -46,7 +47,9 @@ namespace scripting
if (e.name == "connected")
{
scripting::clear_entity_fields(e.entity);
const auto player = e.arguments[0].as<scripting::entity>();
const auto client = player.call("getentitynumber").as<int>();
userinfo::clear_client_overrides(client);
}
}
@ -72,6 +75,7 @@ namespace scripting
void g_shutdown_game_stub(const int free_scripts)
{
userinfo::clear_overrides();
command::clear_script_commands();
gsc::replaced_functions.clear();
g_shutdown_game_hook.invoke<void>(free_scripts);

177
src/component/userinfo.cpp Normal file
View File

@ -0,0 +1,177 @@
#include <stdinc.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "game/scripting/event.hpp"
#include "game/scripting/execution.hpp"
#include "game/scripting/functions.hpp"
#include "game/scripting/array.hpp"
#include "gsc.hpp"
namespace userinfo
{
using userinfo_map = std::unordered_map<std::string, std::string>;
std::unordered_map<int, userinfo_map> userinfo_overrides;
namespace
{
utils::hook::detour sv_getuserinfo_hook;
userinfo_map userinfo_to_map(const std::string& userinfo)
{
userinfo_map map;
const auto args = utils::string::split(userinfo, '\\');
for (auto i = 1; i < args.size() - 1; i += 2)
{
map[args[i]] = args[i + 1];
}
return map;
}
std::string map_to_userinfo(const userinfo_map& map)
{
std::string buffer;
for (const auto& value : map)
{
buffer.append("\\");
buffer.append(value.first);
buffer.append("\\");
buffer.append(value.second);
}
return buffer;
}
void sv_getuserinfo_stub(int index, char* buffer, int bufferSize)
{
char _buffer[1024];
sv_getuserinfo_hook.invoke<void>(index, _buffer, 1024);
auto map = userinfo_to_map(_buffer);
if (userinfo_overrides.find(index) == userinfo_overrides.end())
{
userinfo_overrides[index] = {};
}
for (const auto& values : userinfo_overrides[index])
{
if (values.second.empty())
{
map.erase(values.first);
}
else
{
map[values.first] = values.second;
}
}
const auto userinfo = map_to_userinfo(map);
strcpy_s(buffer, 1024, userinfo.data());
}
}
void clear_client_overrides(int client)
{
userinfo_overrides[client].clear();
}
void clear_overrides()
{
userinfo_overrides.clear();
}
class component final : public component_interface
{
public:
void post_unpack() override
{
sv_getuserinfo_hook.create(0x573E00, sv_getuserinfo_stub);
gsc::method::add("setname", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value
{
const auto name = args[0].as<std::string>();
if (ent.classnum != 0 || ent.entnum > 17)
{
throw std::runtime_error("Invalid entity");
}
userinfo_overrides[ent.entnum]["name"] = name;
game::ClientUserinfoChanged(ent.entnum);
return {};
});
gsc::method::add("resetname", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value
{
const auto name = args[0].as<std::string>();
if (ent.classnum != 0 || ent.entnum > 17)
{
throw std::runtime_error("Invalid entity");
}
userinfo_overrides[ent.entnum].erase("name");
game::ClientUserinfoChanged(ent.entnum);
return {};
});
gsc::method::add("setclantag", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value
{
const auto name = args[0].as<std::string>();
if (ent.classnum != 0 || ent.entnum > 17)
{
throw std::runtime_error("Invalid entity");
}
userinfo_overrides[ent.entnum]["clantag"] = name;
userinfo_overrides[ent.entnum]["ec_TagText"] = name;
userinfo_overrides[ent.entnum]["ec_usingTag"] = "1";
game::ClientUserinfoChanged(ent.entnum);
return {};
});
gsc::method::add("resetclantag", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value
{
const auto name = args[0].as<std::string>();
if (ent.classnum != 0 || ent.entnum > 17)
{
throw std::runtime_error("Invalid entity");
}
userinfo_overrides[ent.entnum].erase("clantag");
userinfo_overrides[ent.entnum].erase("ec_TagText");
userinfo_overrides[ent.entnum].erase("ec_usingTag");
game::ClientUserinfoChanged(ent.entnum);
return {};
});
gsc::method::add("removeclantag", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value
{
if (ent.classnum != 0 || ent.entnum > 17)
{
throw std::runtime_error("Invalid entity");
}
userinfo_overrides[ent.entnum]["clantag"] = "";
userinfo_overrides[ent.entnum]["ec_TagText"] = "";
userinfo_overrides[ent.entnum]["ec_usingTag"] = "0";
game::ClientUserinfoChanged(ent.entnum);
return {};
});
}
};
}
REGISTER_COMPONENT(userinfo::component)

View File

@ -0,0 +1,7 @@
#pragma once
namespace userinfo
{
void clear_client_overrides(int client);
void clear_overrides();
}

View File

@ -16,6 +16,7 @@ namespace game
WEAK symbol<void(unsigned int weapon, bool isAlternate, char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x42F760};
WEAK symbol<void(int client)> ClientUserinfoChanged{0x4FADB0};
WEAK symbol<const char*(int index)> ConcatArgs{0x502150};
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x545680};
WEAK symbol<void(const char* cmdName, void(), cmd_function_t* allocedCmd)> Cmd_AddCommandInternal{0x545DF0};