#include #include "loader/component_loader.hpp" #include "game/game.hpp" #include "gsc.hpp" #include #include namespace user_info { namespace { using user_info_map = std::unordered_map; std::array user_info_overrides; utils::hook::detour scr_shutdown_system_hook; void clear_client_overrides(unsigned int client_num) { user_info_overrides[client_num].clear(); } void clear_all_overrides() { for (auto& entry : user_info_overrides) { entry.clear(); } } void scr_shutdown_system_stub(const game::scriptInstance_t inst, const unsigned char sys, const int b_complete) { clear_all_overrides(); scr_shutdown_system_hook.invoke(inst, sys, b_complete); } void sv_get_user_info_stub(const int index, char* buffer, const int buffer_size) { game::SV_GetUserinfo(index, buffer, buffer_size); utils::info_string map(buffer); for (const auto& [key, val] : user_info_overrides[index]) { if (val.empty()) { map.remove(key); } else { map.set(key, val); } } const auto user_info = map.build(); strncpy_s(buffer, buffer_size, user_info.data(), _TRUNCATE); } } class component final : public component_interface { public: void on_startup([[maybe_unused]] plugin::plugin* plugin) override { utils::hook::call(SELECT_VALUE(0x5D38EB, 0x4A75E2), sv_get_user_info_stub); utils::hook::call(SELECT_VALUE(0x67FFE9, 0x548DB0), sv_get_user_info_stub); plugin->get_interface()->callbacks()->on_player_connect(clear_client_overrides); plugin->get_interface()->callbacks()->on_player_disconnect(clear_client_overrides); scr_shutdown_system_hook.create(SELECT_VALUE(0x596D40, 0x540780), scr_shutdown_system_stub); gsc::method::add_multiple([](const scripting::entity& player, const std::string& name) -> void { const auto entref = player.get_entity_reference(); if (entref.classnum) { throw std::runtime_error("Not a player entity"); } if (name.empty()) { throw std::runtime_error("set_name: Illegal parameter!"); } user_info_overrides[entref.entnum]["name"] = name; game::ClientUserinfoChanged(entref.entnum); }, "user_info::set_name", "set_name"); gsc::method::add_multiple([](const scripting::entity& player) -> void { const auto entref = player.get_entity_reference(); if (entref.classnum) { throw std::runtime_error("Not a player entity"); } user_info_overrides[entref.entnum].erase("name"); game::ClientUserinfoChanged(entref.entnum); }, "user_info::reset_name", "reset_name"); gsc::method::add_multiple([](const scripting::entity& player, const std::string& tag) -> void { const auto entref = player.get_entity_reference(); if (entref.classnum) { throw std::runtime_error("Not a player entity"); } if (tag.empty()) { throw std::runtime_error("set_clantag: Illegal parameter!"); } user_info_overrides[entref.entnum]["clanAbbrev"] = tag; game::ClientUserinfoChanged(entref.entnum); }, "user_info::set_clantag", "set_clantag"); gsc::method::add_multiple([](const scripting::entity& player) -> void { const auto entref = player.get_entity_reference(); if (entref.classnum) { throw std::runtime_error("Not a player entity"); } user_info_overrides[entref.entnum].erase("clanAbbrev"); game::ClientUserinfoChanged(entref.entnum); }, "user_info::reset_clantag", "reset_clantag"); } }; } REGISTER_COMPONENT(user_info::component)