Merge pull request #456 from momo5502/feature/auth-protocol
Distribute profile infos to connected clients
This commit is contained in:
commit
48b5ad815f
@ -2,13 +2,18 @@
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "auth.hpp"
|
||||
#include "command.hpp"
|
||||
#include "network.hpp"
|
||||
#include "profile_infos.hpp"
|
||||
|
||||
#include <game/game.hpp>
|
||||
#include <game/utils.hpp>
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/smbios.hpp>
|
||||
#include <utils/byte_buffer.hpp>
|
||||
#include <utils/info_string.hpp>
|
||||
#include <utils/cryptography.hpp>
|
||||
|
||||
@ -92,16 +97,59 @@ namespace auth
|
||||
return !is_first;
|
||||
}
|
||||
|
||||
int send_connect_data_stub(const game::netsrc_t sock, game::netadr_t* adr, const char* data, const int len)
|
||||
std::string serialize_connect_data(const char* data, const int length)
|
||||
{
|
||||
/*const auto is_connect_sequence = len >= 7 && strncmp("connect", data, 7) == 0;
|
||||
utils::byte_buffer buffer{};
|
||||
profile_infos::get_profile_info().value_or(profile_infos::profile_info{}).serialize(buffer);
|
||||
|
||||
buffer.write_string(data, static_cast<size_t>(length));
|
||||
|
||||
return buffer.move_buffer();
|
||||
}
|
||||
|
||||
int send_connect_data_stub(const game::netsrc_t sock, game::netadr_t* adr, const char* data, int len)
|
||||
{
|
||||
std::string buffer{};
|
||||
|
||||
const auto is_connect_sequence = len >= 7 && strncmp("connect", data, 7) == 0;
|
||||
if (is_connect_sequence)
|
||||
{
|
||||
MessageBoxA(0, "CONNECT", 0, 0);
|
||||
}*/
|
||||
buffer.append("connect");
|
||||
buffer.push_back(' ');
|
||||
buffer.append(serialize_connect_data(data, len));
|
||||
|
||||
data = buffer.data();
|
||||
len = static_cast<int>(buffer.size());
|
||||
}
|
||||
|
||||
return reinterpret_cast<decltype(&send_connect_data_stub)>(0x142173600_g)(sock, adr, data, len);
|
||||
}
|
||||
|
||||
void handle_connect_packet(const game::netadr_t& target, const network::data_view& data)
|
||||
{
|
||||
if (!game::is_server_running())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::byte_buffer buffer(data);
|
||||
const profile_infos::profile_info info(buffer);
|
||||
|
||||
const auto connect_data = buffer.read_string();
|
||||
const command::params_sv params(connect_data);
|
||||
|
||||
if (params.size() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const utils::info_string info_string(params[1]);
|
||||
const auto xuid = strtoull(info_string.get("xuid").data(), nullptr, 16);
|
||||
|
||||
profile_infos::add_and_distribute_profile_info(target, xuid, info);
|
||||
|
||||
game::SV_DirectConnect(target);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t get_guid()
|
||||
@ -123,6 +171,11 @@ namespace auth
|
||||
{
|
||||
void post_unpack() override
|
||||
{
|
||||
// Skip connect handler
|
||||
//utils::hook::set<uint8_t>(game::select(0x142253EFA, 0x14053714A), 0xEB);
|
||||
//network::on("connect", handle_connect_packet);
|
||||
(void)&handle_connect_packet;
|
||||
|
||||
// Patch steam id bit check
|
||||
std::vector<std::pair<size_t, size_t>> patches{};
|
||||
const auto p = [&patches](const size_t a, const size_t b)
|
||||
@ -168,7 +221,8 @@ namespace auth
|
||||
p(0x141EB5992_g, 0x141EB59D5_g);
|
||||
p(0x141EB74D2_g, 0x141EB7515_g); // ?
|
||||
|
||||
utils::hook::call(0x14134BF7D_g, send_connect_data_stub);
|
||||
//utils::hook::call(0x14134BF7D_g, send_connect_data_stub);
|
||||
(void)&send_connect_data_stub;
|
||||
}
|
||||
|
||||
for (const auto& patch : patches)
|
||||
|
@ -122,7 +122,7 @@ namespace chat
|
||||
// Overwrite say command
|
||||
utils::hook::jump(0x14052A6C0_g, +[]
|
||||
{
|
||||
if (!game::get_dvar_bool("sv_running"))
|
||||
if (!game::is_server_running())
|
||||
{
|
||||
printf("Server is not running\n");
|
||||
return;
|
||||
@ -138,7 +138,7 @@ namespace chat
|
||||
// Overwrite tell command
|
||||
utils::hook::jump(0x14052A7E0_g, +[]
|
||||
{
|
||||
if (!game::get_dvar_bool("sv_running"))
|
||||
if (!game::is_server_running())
|
||||
{
|
||||
printf("Server is not running\n");
|
||||
return;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <utils/memory.hpp>
|
||||
|
||||
#include <game/game.hpp>
|
||||
#include <steam/steam.hpp>
|
||||
|
||||
namespace command
|
||||
{
|
||||
@ -66,15 +67,6 @@ namespace command
|
||||
}
|
||||
}
|
||||
|
||||
struct component final : generic_component
|
||||
{
|
||||
void post_unpack() override
|
||||
{
|
||||
// Disable whitelist
|
||||
utils::hook::jump(game::select(0x1420EE860, 0x1404F9CD0), update_whitelist_stub);
|
||||
}
|
||||
};
|
||||
|
||||
params::params()
|
||||
: nesting_(get_cmd_args()->nesting)
|
||||
{
|
||||
@ -227,6 +219,15 @@ namespace command
|
||||
game::Cmd_AddServerCommandInternal(cmd_string, execute_custom_sv_command,
|
||||
allocator.allocate<game::cmd_function_s>());
|
||||
}
|
||||
|
||||
struct component final : generic_component
|
||||
{
|
||||
void post_unpack() override
|
||||
{
|
||||
// Disable whitelist
|
||||
utils::hook::jump(game::select(0x1420EE860, 0x1404F9CD0), update_whitelist_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(command::component)
|
||||
|
@ -27,11 +27,11 @@ namespace dedicated_info
|
||||
|
||||
const auto mapname = game::get_dvar_string("mapname");
|
||||
|
||||
const std::string window_text = utils::string::va("%s on %s [%d/%d] (%d)",
|
||||
const std::string window_text = utils::string::va("%s on %s [%zu/%zu] (%zu)",
|
||||
clean_server_name,
|
||||
mapname.data(),
|
||||
getinfo::get_client_count(),
|
||||
getinfo::get_max_client_count(),
|
||||
game::get_max_client_count(),
|
||||
getinfo::get_bot_count());
|
||||
|
||||
console::set_title(window_text);
|
||||
|
@ -36,22 +36,22 @@ namespace dedicated_patches
|
||||
{
|
||||
const std::vector<uintptr_t> is_mod_loaded_addresses =
|
||||
{
|
||||
{ 0x14019CFC4_g },
|
||||
{ 0x14024D4A0_g },
|
||||
{ 0x14024D669_g },
|
||||
{ 0x14024D939_g },
|
||||
{ 0x14024DC64_g },
|
||||
{ 0x14024E13A_g },
|
||||
{ 0x14024E5A3_g },
|
||||
{ 0x14024FFB9_g },
|
||||
{ 0x140251E9E_g },
|
||||
{ 0x140253680_g },
|
||||
{ 0x140257BF6_g },
|
||||
{ 0x1402D296D_g },
|
||||
{ 0x1402D58E9_g },
|
||||
{ 0x140468374_g },
|
||||
{ 0x14046B796_g },
|
||||
{ 0x14048003D_g },
|
||||
{0x14019CFC4_g},
|
||||
{0x14024D4A0_g},
|
||||
{0x14024D669_g},
|
||||
{0x14024D939_g},
|
||||
{0x14024DC64_g},
|
||||
{0x14024E13A_g},
|
||||
{0x14024E5A3_g},
|
||||
{0x14024FFB9_g},
|
||||
{0x140251E9E_g},
|
||||
{0x140253680_g},
|
||||
{0x140257BF6_g},
|
||||
{0x1402D296D_g},
|
||||
{0x1402D58E9_g},
|
||||
{0x140468374_g},
|
||||
{0x14046B796_g},
|
||||
{0x14048003D_g},
|
||||
};
|
||||
|
||||
for (const auto& address : is_mod_loaded_addresses)
|
||||
@ -68,15 +68,20 @@ namespace dedicated_patches
|
||||
spawn_server_hook.invoke(controllerIndex, server, preload, savegame);
|
||||
}
|
||||
|
||||
uint64_t sv_get_player_xuid_stub(int client_num)
|
||||
uint64_t sv_get_player_xuid_stub(const int client_num)
|
||||
{
|
||||
return static_cast<uint64_t>((*game::svs_clients)[client_num].xuid);
|
||||
const auto* clients = *game::svs_clients;
|
||||
if (!clients)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return clients[client_num].xuid;
|
||||
}
|
||||
}
|
||||
|
||||
struct component final : server_component
|
||||
{
|
||||
static_assert(offsetof(game::client_s, xuid) == 0xBB354);
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
|
@ -21,21 +21,18 @@ namespace getinfo
|
||||
return game::get_dvar_int("com_maxclients");
|
||||
}
|
||||
|
||||
int get_client_count()
|
||||
template <typename T>
|
||||
int get_client_count(T* client_states)
|
||||
{
|
||||
int count = 0;
|
||||
const auto client_states = *reinterpret_cast<uint64_t*>(game::select(0x1576F9318, 0x14A178E98));
|
||||
if (!client_states)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto object_length = game::is_server() ? 0xE5110 : 0xE5170;
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; i < get_max_client_count(); ++i)
|
||||
{
|
||||
const auto client_state = *reinterpret_cast<int*>(client_states + (i * object_length));
|
||||
if (client_state > 0)
|
||||
if (client_states[i].client_state > 0)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
@ -44,23 +41,28 @@ namespace getinfo
|
||||
return count;
|
||||
}
|
||||
|
||||
int get_bot_count()
|
||||
size_t get_client_count()
|
||||
{
|
||||
const auto client_states = *reinterpret_cast<uint64_t*>(game::select(0x1576F9318, 0x14A178E98));
|
||||
if (!client_states)
|
||||
size_t count = 0;
|
||||
game::foreach_connected_client([&count](const game::client_s&)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
++count;
|
||||
});
|
||||
|
||||
int count = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
for (int i = 0; i < get_max_client_count(); ++i)
|
||||
size_t get_bot_count()
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
game::foreach_connected_client([&count](const game::client_s&, const size_t index)
|
||||
{
|
||||
if (game::SV_IsTestClient(i))
|
||||
if (game::SV_IsTestClient(static_cast<int>(index)))
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -103,13 +105,13 @@ namespace getinfo
|
||||
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
|
||||
info.set("mapname", game::get_dvar_string("mapname"));
|
||||
info.set("isPrivate", game::get_dvar_string("g_password").empty() ? "0" : "1");
|
||||
info.set("clients", utils::string::va("%i", get_client_count()));
|
||||
info.set("bots", utils::string::va("%i", get_bot_count()));
|
||||
info.set("sv_maxclients", utils::string::va("%i", get_max_client_count()));
|
||||
info.set("clients", utils::string::va("%zu", get_client_count()));
|
||||
info.set("bots", utils::string::va("%zu", get_bot_count()));
|
||||
info.set("sv_maxclients", utils::string::va("%zu", get_max_client_count()));
|
||||
info.set("protocol", utils::string::va("%i", PROTOCOL));
|
||||
info.set("playmode", utils::string::va("%i", game::Com_SessionMode_GetMode()));
|
||||
info.set("gamemode", utils::string::va("%i", Com_SessionMode_GetGameMode()));
|
||||
info.set("sv_running", utils::string::va("%i", game::get_dvar_bool("sv_running")));
|
||||
info.set("sv_running", utils::string::va("%i", game::is_server_running()));
|
||||
info.set("dedicated", utils::string::va("%i", game::is_server() ? 1 : 0));
|
||||
info.set("shortversion", SHORTVERSION);
|
||||
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
namespace getinfo
|
||||
{
|
||||
int get_max_client_count();
|
||||
int get_client_count();
|
||||
int get_bot_count();
|
||||
size_t get_client_count();
|
||||
size_t get_bot_count();
|
||||
bool is_host();
|
||||
}
|
||||
|
@ -35,7 +35,18 @@ namespace network
|
||||
|
||||
const std::basic_string_view data(message->data + offset, message->cursize - offset);
|
||||
|
||||
handler->second(*address, data);
|
||||
try
|
||||
{
|
||||
handler->second(*address, data);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
printf("Error: %s\n", e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "network.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "workshop.hpp"
|
||||
#include "profile_infos.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
@ -36,7 +37,7 @@ namespace party
|
||||
}
|
||||
|
||||
void connect_to_lobby(const game::netadr_t& addr, const std::string& mapname, const std::string& gamemode,
|
||||
const std::string& pub_id)
|
||||
const std::string& pub_id)
|
||||
{
|
||||
workshop::load_usermap_mod_if_needed(pub_id);
|
||||
|
||||
@ -55,7 +56,8 @@ namespace party
|
||||
}
|
||||
|
||||
void connect_to_lobby_with_mode(const game::netadr_t& addr, const game::eModes mode, const std::string& mapname,
|
||||
const std::string& gametype, const std::string& pub_id, const bool was_retried = false)
|
||||
const std::string& gametype, const std::string& pub_id,
|
||||
const bool was_retried = false)
|
||||
{
|
||||
if (game::Com_SessionMode_IsMode(mode))
|
||||
{
|
||||
@ -144,6 +146,13 @@ namespace party
|
||||
|
||||
is_connecting_to_dedi = info.get("dedicated") == "1";
|
||||
|
||||
if (atoi(info.get("protocol").data()) != PROTOCOL)
|
||||
{
|
||||
const auto str = "Invalid protocol.";
|
||||
printf("%s\n", str);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto gamename = info.get("gamename");
|
||||
if (gamename != "T7"s)
|
||||
{
|
||||
@ -203,6 +212,7 @@ namespace party
|
||||
connect_host = target;
|
||||
}
|
||||
|
||||
profile_infos::clear_profile_infos();
|
||||
query_server(connect_host, handle_connect_query_response);
|
||||
}
|
||||
|
||||
@ -306,6 +316,11 @@ namespace party
|
||||
return *reinterpret_cast<game::netadr_t*>(address);
|
||||
}
|
||||
|
||||
bool is_host(const game::netadr_t& addr)
|
||||
{
|
||||
return get_connected_server() == addr || connect_host == addr;
|
||||
}
|
||||
|
||||
struct component final : client_component
|
||||
{
|
||||
void post_unpack() override
|
||||
|
@ -9,4 +9,6 @@ namespace party
|
||||
void query_server(const game::netadr_t& host, query_callback callback);
|
||||
|
||||
game::netadr_t get_connected_server();
|
||||
|
||||
bool is_host(const game::netadr_t& addr);
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "profile_infos.hpp"
|
||||
#include "network.hpp"
|
||||
#include "party.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/properties.hpp>
|
||||
@ -11,12 +13,14 @@
|
||||
#include "../steam/steam.hpp"
|
||||
#include <utils/io.hpp>
|
||||
|
||||
#include "game/utils.hpp"
|
||||
|
||||
namespace profile_infos
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using profile_map = std::unordered_map<uint64_t, profile_info>;
|
||||
utils::concurrency::container<profile_map> profile_mapping;
|
||||
utils::concurrency::container<profile_map> profile_mapping{};
|
||||
|
||||
std::optional<profile_info> load_profile_info()
|
||||
{
|
||||
@ -29,7 +33,7 @@ namespace profile_infos
|
||||
profile_info info{};
|
||||
constexpr auto version_size = sizeof(info.version);
|
||||
|
||||
if(data.size() < sizeof(version_size))
|
||||
if (data.size() < sizeof(version_size))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@ -37,15 +41,171 @@ namespace profile_infos
|
||||
memcpy(&info.version, data.data(), version_size);
|
||||
info.ddl.assign(data.begin() + version_size, data.end());
|
||||
|
||||
return { std::move(info) };
|
||||
return {std::move(info)};
|
||||
}
|
||||
|
||||
void send_profile_info(const game::netadr_t& address, const std::string& buffer)
|
||||
{
|
||||
network::send(address, "profileInfo", buffer);
|
||||
}
|
||||
|
||||
void distribute_profile_info(const uint64_t user_id, const profile_info& info)
|
||||
{
|
||||
if (user_id == steam::SteamUser()->GetSteamID().bits)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::byte_buffer buffer{};
|
||||
buffer.write(user_id);
|
||||
info.serialize(buffer);
|
||||
|
||||
const std::string data = buffer.move_buffer();
|
||||
|
||||
game::foreach_connected_client([&](const game::client_s& client)
|
||||
{
|
||||
send_profile_info(client.address, data);
|
||||
});
|
||||
}
|
||||
|
||||
void schedule_pcache_update()
|
||||
{
|
||||
static std::atomic_bool update_triggered{false};
|
||||
if (game::is_server() || update_triggered.exchange(true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler::once([]
|
||||
{
|
||||
game::PCache_DeleteEntries(game::CONTROLLER_INDEX_FIRST);
|
||||
update_triggered = false;
|
||||
}, scheduler::main, 5s);
|
||||
}
|
||||
|
||||
std::unordered_set<uint64_t> get_connected_client_xuids()
|
||||
{
|
||||
std::unordered_set<uint64_t> connected_clients{};
|
||||
connected_clients.reserve(game::get_max_client_count());
|
||||
|
||||
game::foreach_connected_client([&](const game::client_s& client)
|
||||
{
|
||||
connected_clients.emplace(client.xuid);
|
||||
});
|
||||
|
||||
return connected_clients;
|
||||
}
|
||||
|
||||
void clean_cached_profile_infos()
|
||||
{
|
||||
if (!game::is_server_running())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto xuids = get_connected_client_xuids();
|
||||
|
||||
profile_mapping.access([&](profile_map& profiles)
|
||||
{
|
||||
for (auto i = profiles.begin(); i != profiles.end();)
|
||||
{
|
||||
if (xuids.contains(i->first))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = profiles.erase(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<profile_info> get_profile_info(uint64_t user_id)
|
||||
profile_info::profile_info(utils::byte_buffer& buffer)
|
||||
{
|
||||
this->version = buffer.read<int32_t>();
|
||||
this->ddl = buffer.read_string();
|
||||
}
|
||||
|
||||
void profile_info::serialize(utils::byte_buffer& buffer) const
|
||||
{
|
||||
buffer.write(this->version);
|
||||
buffer.write_string(this->ddl);
|
||||
}
|
||||
|
||||
void add_profile_info(const uint64_t user_id, const profile_info& info)
|
||||
{
|
||||
if (user_id == steam::SteamUser()->GetSteamID().bits)
|
||||
{
|
||||
return load_profile_info();
|
||||
return;
|
||||
}
|
||||
|
||||
profile_mapping.access([&](profile_map& profiles)
|
||||
{
|
||||
profiles[user_id] = info;
|
||||
});
|
||||
|
||||
schedule_pcache_update();
|
||||
}
|
||||
|
||||
void distribute_profile_info_to_user(const game::netadr_t& addr, const uint64_t user_id, const profile_info& info)
|
||||
{
|
||||
utils::byte_buffer buffer{};
|
||||
buffer.write(user_id);
|
||||
info.serialize(buffer);
|
||||
|
||||
send_profile_info(addr, buffer.get_buffer());
|
||||
}
|
||||
|
||||
void distribute_profile_infos_to_user(const game::netadr_t& addr)
|
||||
{
|
||||
profile_mapping.access([&](const profile_map& profiles)
|
||||
{
|
||||
for (const auto& entry : profiles)
|
||||
{
|
||||
distribute_profile_info_to_user(addr, entry.first, entry.second);
|
||||
}
|
||||
});
|
||||
|
||||
if (!game::is_server())
|
||||
{
|
||||
const auto info = get_profile_info();
|
||||
if (info)
|
||||
{
|
||||
distribute_profile_info_to_user(addr, steam::SteamUser()->GetSteamID().bits, *info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_and_distribute_profile_info(const game::netadr_t& addr, const uint64_t user_id, const profile_info& info)
|
||||
{
|
||||
distribute_profile_infos_to_user(addr);
|
||||
|
||||
add_profile_info(user_id, info);
|
||||
distribute_profile_info(user_id, info);
|
||||
}
|
||||
|
||||
void clear_profile_infos()
|
||||
{
|
||||
profile_mapping.access([&](profile_map& profiles)
|
||||
{
|
||||
profiles = {};
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<profile_info> get_profile_info()
|
||||
{
|
||||
return load_profile_info();
|
||||
}
|
||||
|
||||
std::optional<profile_info> get_profile_info(const uint64_t user_id)
|
||||
{
|
||||
printf("Requesting profile info: %llX\n", user_id);
|
||||
|
||||
if (user_id == steam::SteamUser()->GetSteamID().bits)
|
||||
{
|
||||
return get_profile_info();
|
||||
}
|
||||
|
||||
return profile_mapping.access<std::optional<profile_info>>([user_id](const profile_map& profiles)
|
||||
@ -75,15 +235,26 @@ namespace profile_infos
|
||||
|
||||
struct component final : generic_component
|
||||
{
|
||||
void post_load() override
|
||||
{
|
||||
}
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
/*network::on("profileInfo", [](const game::netadr_t& server, const network::data_view& data)
|
||||
scheduler::loop(clean_cached_profile_infos, scheduler::main, 5s);
|
||||
|
||||
if (game::is_client())
|
||||
{
|
||||
});*/
|
||||
network::on("profileInfo", [](const game::netadr_t& server, const network::data_view& data)
|
||||
{
|
||||
if (!party::is_host(server))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::byte_buffer buffer(data);
|
||||
const auto user_id = buffer.read<uint64_t>();
|
||||
const profile_info info(buffer);
|
||||
|
||||
add_profile_info(user_id, info);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,13 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <game/game.hpp>
|
||||
#include <utils/byte_buffer.hpp>
|
||||
|
||||
namespace profile_infos
|
||||
{
|
||||
struct profile_info
|
||||
{
|
||||
int32_t version;
|
||||
std::string ddl;
|
||||
int32_t version{3};
|
||||
std::string ddl{};
|
||||
|
||||
profile_info() = default;
|
||||
profile_info(utils::byte_buffer& buffer);
|
||||
void serialize(utils::byte_buffer& buffer) const;
|
||||
};
|
||||
|
||||
void add_profile_info(uint64_t user_id, const profile_info& info);
|
||||
void add_and_distribute_profile_info(const game::netadr_t& addr, uint64_t user_id, const profile_info& info);
|
||||
void clear_profile_infos();
|
||||
|
||||
|
||||
std::optional<profile_info> get_profile_info();
|
||||
std::optional<profile_info> get_profile_info(uint64_t user_id);
|
||||
void update_profile_info(const profile_info& info);
|
||||
}
|
||||
|
@ -1544,14 +1544,31 @@ namespace game
|
||||
|
||||
struct client_s
|
||||
{
|
||||
char __pad0[0xBB354];
|
||||
int xuid;
|
||||
char __pad1[0x8];
|
||||
int client_state;
|
||||
char __pad0[0x28];
|
||||
netadr_t address;
|
||||
char __pad1[0x5588];
|
||||
uint64_t xuid;
|
||||
char __pad2[0xB5D84];
|
||||
int guid;
|
||||
char __pad3[0x8];
|
||||
bool bIsTestClient;
|
||||
char __pad2[0x29DAC];
|
||||
char __pad4[0x29DAC];
|
||||
};
|
||||
|
||||
static_assert(sizeof(client_s) == 0xE5110);
|
||||
static_assert(sizeof(client_s) <= 0xE5110);
|
||||
|
||||
static_assert(offsetof(game::client_s, address) == 0x2C);
|
||||
static_assert(offsetof(game::client_s, xuid) == 0x55C8);
|
||||
static_assert(offsetof(game::client_s, guid) == 0xBB354);
|
||||
static_assert(offsetof(game::client_s, bIsTestClient) == 0xBB360);
|
||||
|
||||
struct client_s_cl : client_s
|
||||
{
|
||||
char __pad1_0[0x60];
|
||||
};
|
||||
|
||||
static_assert(sizeof(client_s_cl) == 0xE5170);
|
||||
|
||||
enum scriptInstance_t
|
||||
{
|
||||
|
@ -144,7 +144,7 @@ namespace game
|
||||
WEAK symbol<void(int localClientNum)> CL_CheckKeepDrawingConnectScreen{0x1413CCAE0};
|
||||
|
||||
// Scr
|
||||
WEAK symbol<void(scriptInstance_t inst, int value)> Scr_AddInt{0x1412E9870, 0x14016F160};
|
||||
WEAK symbol<void(scriptInstance_t inst, int value)> Scr_AddInt{0x0, 0x14016F160};
|
||||
WEAK symbol<void(scriptInstance_t inst, const char* value)> Scr_AddString{0x0, 0x14016F320};
|
||||
WEAK symbol<const char*(scriptInstance_t inst, unsigned int index)> Scr_GetString{0x0, 0x140171490};
|
||||
WEAK symbol<void(gentity_s* ent, ScrVarCanonicalName_t stringValue, unsigned int paramcount)> Scr_Notify_Canon{
|
||||
@ -162,6 +162,9 @@ namespace game
|
||||
0x141CD98D0
|
||||
};
|
||||
|
||||
// PCache
|
||||
WEAK symbol<void(ControllerIndex_t controllerIndex)> PCache_DeleteEntries{0x141E8D710};
|
||||
|
||||
// SV
|
||||
WEAK symbol<bool()> SV_Loaded{0x142252250, 0x140535460};
|
||||
WEAK symbol<void*()> SV_AddTestClient{0x142248F40, 0x14052E3E0};
|
||||
@ -195,6 +198,8 @@ namespace game
|
||||
WEAK symbol<char> s_dvarPool{0x157AC6220, 0x14A3CB620};
|
||||
WEAK symbol<int> g_dvarCount{0x157AC61CC, 0x14A3CB5FC};
|
||||
|
||||
// Client and dedi struct size differs :(
|
||||
WEAK symbol<client_s_cl*> svs_clients_cl{0x1576F9318, 0};
|
||||
WEAK symbol<client_s*> svs_clients{0x0, 0x14A178E98};
|
||||
|
||||
// Dvar variables
|
||||
|
@ -45,7 +45,8 @@ namespace game
|
||||
return dvar->current.value.enabled;
|
||||
}
|
||||
|
||||
const dvar_t* register_sessionmode_dvar_bool(const char* dvar_name, const bool value, const int flags, const char* description, const eModes mode)
|
||||
const dvar_t* register_sessionmode_dvar_bool(const char* dvar_name, const bool value, const int flags,
|
||||
const char* description, const eModes mode)
|
||||
{
|
||||
const auto hash = Dvar_GenerateHash(dvar_name);
|
||||
auto* registered_dvar = Dvar_SessionModeRegisterBool(hash, dvar_name, value, flags, description);
|
||||
@ -83,7 +84,8 @@ namespace game
|
||||
return registered_dvar;
|
||||
}
|
||||
|
||||
const dvar_t* register_dvar_float(const char* dvar_name, float value, float min, float max, const int flags, const char* description)
|
||||
const dvar_t* register_dvar_float(const char* dvar_name, float value, float min, float max, const int flags,
|
||||
const char* description)
|
||||
{
|
||||
const auto hash = Dvar_GenerateHash(dvar_name);
|
||||
auto* registered_dvar = Dvar_RegisterFloat(hash, dvar_name, value, min, max, flags, description);
|
||||
@ -96,7 +98,8 @@ namespace game
|
||||
return registered_dvar;
|
||||
}
|
||||
|
||||
const dvar_t* register_dvar_string(const char* dvar_name, const char* value, const int flags, const char* description)
|
||||
const dvar_t* register_dvar_string(const char* dvar_name, const char* value, const int flags,
|
||||
const char* description)
|
||||
{
|
||||
const auto hash = Dvar_GenerateHash(dvar_name);
|
||||
auto* registered_dvar = Dvar_RegisterString(hash, dvar_name, value, flags, description);
|
||||
@ -148,4 +151,67 @@ namespace game
|
||||
|
||||
dvar_to_change->flags = flags;
|
||||
}
|
||||
|
||||
bool is_server_running()
|
||||
{
|
||||
return get_dvar_bool("sv_running");
|
||||
}
|
||||
|
||||
size_t get_max_client_count()
|
||||
{
|
||||
return static_cast<size_t>(get_dvar_int("com_maxclients"));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void foreach_client(T* client_states, const std::function<void(client_s&, size_t index)>& callback)
|
||||
{
|
||||
if (!client_states || !callback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < get_max_client_count(); ++i)
|
||||
{
|
||||
callback(client_states[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
void foreach_client(const std::function<void(client_s&, size_t index)>& callback)
|
||||
{
|
||||
if (is_server())
|
||||
{
|
||||
foreach_client(*svs_clients, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach_client(*svs_clients_cl, callback);
|
||||
}
|
||||
}
|
||||
|
||||
void foreach_client(const std::function<void(client_s&)>& callback)
|
||||
{
|
||||
foreach_client([&](client_s& client, size_t)
|
||||
{
|
||||
callback(client);
|
||||
});
|
||||
}
|
||||
|
||||
void foreach_connected_client(const std::function<void(client_s&, size_t index)>& callback)
|
||||
{
|
||||
foreach_client([&](client_s& client, const size_t index)
|
||||
{
|
||||
if (client.client_state > 0)
|
||||
{
|
||||
callback(client, index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void foreach_connected_client(const std::function<void(client_s&)>& callback)
|
||||
{
|
||||
foreach_connected_client([&](client_s& client, size_t)
|
||||
{
|
||||
callback(client);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,16 @@ namespace game
|
||||
const dvar_t* register_dvar_float(const char* dvar_name, float value, float min, float max, const int flags, const char* description);
|
||||
const dvar_t* register_sessionmode_dvar_bool(const char* dvar_name, bool value, int flags, const char* description, eModes mode = MODE_COUNT);
|
||||
const dvar_t* register_dvar_string(const char* dvar_name, const char* value, int flags, const char* description);
|
||||
|
||||
void dvar_add_flags(const char* dvar, dvarFlags_e flags);
|
||||
void dvar_set_flags(const char* dvar_name, dvarFlags_e flags);
|
||||
|
||||
bool is_server_running();
|
||||
size_t get_max_client_count();
|
||||
|
||||
void foreach_client(const std::function<void(client_s&, size_t index)>& callback);
|
||||
void foreach_client(const std::function<void(client_s&)>& callback);
|
||||
|
||||
void foreach_connected_client(const std::function<void(client_s&, size_t index)>& callback);
|
||||
void foreach_connected_client(const std::function<void(client_s&)>& callback);
|
||||
}
|
||||
|
53
src/common/utils/byte_buffer.cpp
Normal file
53
src/common/utils/byte_buffer.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "byte_buffer.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
byte_buffer::byte_buffer()
|
||||
: writing_(true)
|
||||
{
|
||||
}
|
||||
|
||||
byte_buffer::byte_buffer(std::string buffer)
|
||||
: writing_(false)
|
||||
, buffer_(std::move(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
void byte_buffer::write(const void* buffer, const size_t length)
|
||||
{
|
||||
if (!this->writing_)
|
||||
{
|
||||
throw std::runtime_error("Writing to readable byte buffer");
|
||||
}
|
||||
|
||||
this->buffer_.append(static_cast<const char*>(buffer), length);
|
||||
}
|
||||
|
||||
void byte_buffer::read(void* data, const size_t length)
|
||||
{
|
||||
if (this->writing_)
|
||||
{
|
||||
throw std::runtime_error("Reading from writable byte buffer");
|
||||
}
|
||||
|
||||
if (this->offset_ + length > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
memcpy(data, this->buffer_.data() + this->offset_, length);
|
||||
this->offset_ += length;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> byte_buffer::read_data(const size_t length)
|
||||
{
|
||||
std::vector<uint8_t> result{};
|
||||
result.resize(length);
|
||||
|
||||
this->read(result.data(), result.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
119
src/common/utils/byte_buffer.hpp
Normal file
119
src/common/utils/byte_buffer.hpp
Normal file
@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class byte_buffer
|
||||
{
|
||||
public:
|
||||
byte_buffer();
|
||||
byte_buffer(std::string buffer);
|
||||
|
||||
template <typename T>
|
||||
byte_buffer(const std::basic_string_view<T>& buffer)
|
||||
: byte_buffer(std::string(reinterpret_cast<const char*>(buffer.data()), buffer.size() * sizeof(T)))
|
||||
{
|
||||
}
|
||||
|
||||
void write(const void* buffer, size_t length);
|
||||
|
||||
void write_string(const char* str, const size_t length)
|
||||
{
|
||||
this->write<uint32_t>(static_cast<uint32_t>(length));
|
||||
this->write(str, length);
|
||||
}
|
||||
|
||||
void write_string(const std::string& str)
|
||||
{
|
||||
this->write_string(str.data(), str.size());
|
||||
}
|
||||
|
||||
void write_string(const char* str)
|
||||
{
|
||||
this->write_string(str, strlen(str));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const T& object)
|
||||
{
|
||||
this->write(&object, sizeof(object));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const std::vector<T>& vec)
|
||||
{
|
||||
this->write(vec.data(), vec.size() * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_vector(const std::vector<T>& vec)
|
||||
{
|
||||
this->write(static_cast<uint32_t>(vec.size()));
|
||||
this->write(vec);
|
||||
}
|
||||
|
||||
const std::string& get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
|
||||
std::string move_buffer()
|
||||
{
|
||||
return std::move(this->buffer_);
|
||||
}
|
||||
|
||||
void read(void* data, size_t length);
|
||||
|
||||
template <typename T>
|
||||
T read()
|
||||
{
|
||||
T object{};
|
||||
this->read(&object, sizeof(object));
|
||||
return object;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> read_vector()
|
||||
{
|
||||
std::vector<T> result{};
|
||||
const auto size = this->read<uint32_t>();
|
||||
const auto totalSize = size * sizeof(T);
|
||||
|
||||
if (this->offset_ + totalSize > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
result.resize(size);
|
||||
this->read(result.data(), totalSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string read_string()
|
||||
{
|
||||
std::string result{};
|
||||
const auto size = this->read<uint32_t>();
|
||||
|
||||
if (this->offset_ + size > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
result.resize(size);
|
||||
this->read(result.data(), size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> read_data(size_t length);
|
||||
|
||||
private:
|
||||
bool writing_{false};
|
||||
size_t offset_{0};
|
||||
std::string buffer_{};
|
||||
};
|
||||
}
|
@ -8,6 +8,11 @@ namespace utils
|
||||
this->parse(buffer);
|
||||
}
|
||||
|
||||
info_string::info_string(const char* buffer)
|
||||
: info_string(std::string{buffer})
|
||||
{
|
||||
}
|
||||
|
||||
info_string::info_string(const std::string_view& buffer)
|
||||
: info_string(std::string{buffer})
|
||||
{
|
||||
|
@ -9,8 +9,9 @@ namespace utils
|
||||
{
|
||||
public:
|
||||
info_string() = default;
|
||||
info_string(const std::string& buffer);
|
||||
info_string(const std::string_view& buffer);
|
||||
explicit info_string(const std::string& buffer);
|
||||
explicit info_string(const char* buffer);
|
||||
explicit info_string(const std::string_view& buffer);
|
||||
info_string(const std::basic_string_view<uint8_t>& buffer);
|
||||
|
||||
void set(const std::string& key, const std::string& value);
|
||||
|
Loading…
Reference in New Issue
Block a user