diff --git a/src/client/component/auth.cpp b/src/client/component/auth.cpp index edac9e7b..45405af4 100644 --- a/src/client/component/auth.cpp +++ b/src/client/component/auth.cpp @@ -2,13 +2,18 @@ #include "loader/component_loader.hpp" #include "auth.hpp" +#include "command.hpp" +#include "network.hpp" +#include "profile_infos.hpp" #include +#include #include #include #include #include +#include #include #include @@ -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(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(buffer.size()); + } return reinterpret_cast(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(game::select(0x142253EFA, 0x14053714A), 0xEB); + //network::on("connect", handle_connect_packet); + (void)&handle_connect_packet; + // Patch steam id bit check std::vector> 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) diff --git a/src/client/component/chat.cpp b/src/client/component/chat.cpp index 7bb8a52e..41b1b44a 100644 --- a/src/client/component/chat.cpp +++ b/src/client/component/chat.cpp @@ -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; diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 0ac70162..07a2a241 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -7,6 +7,7 @@ #include #include +#include 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()); } + + 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) diff --git a/src/client/component/dedicated_info.cpp b/src/client/component/dedicated_info.cpp index c950a247..95089a2d 100644 --- a/src/client/component/dedicated_info.cpp +++ b/src/client/component/dedicated_info.cpp @@ -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); diff --git a/src/client/component/dedicated_patches.cpp b/src/client/component/dedicated_patches.cpp index 06c943ce..6e7f249e 100644 --- a/src/client/component/dedicated_patches.cpp +++ b/src/client/component/dedicated_patches.cpp @@ -36,22 +36,22 @@ namespace dedicated_patches { const std::vector 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((*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 { diff --git a/src/client/component/getinfo.cpp b/src/client/component/getinfo.cpp index 496db001..ee848f15 100644 --- a/src/client/component/getinfo.cpp +++ b/src/client/component/getinfo.cpp @@ -21,21 +21,18 @@ namespace getinfo return game::get_dvar_int("com_maxclients"); } - int get_client_count() + template + int get_client_count(T* client_states) { - int count = 0; - const auto client_states = *reinterpret_cast(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(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(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(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); diff --git a/src/client/component/getinfo.hpp b/src/client/component/getinfo.hpp index 5d9d14be..0bf43b4d 100644 --- a/src/client/component/getinfo.hpp +++ b/src/client/component/getinfo.hpp @@ -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(); } diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index 4dbe5f1a..9a722405 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -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; } diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index bf5caebe..7a80a301 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -6,6 +6,7 @@ #include "network.hpp" #include "scheduler.hpp" #include "workshop.hpp" +#include "profile_infos.hpp" #include #include @@ -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(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 diff --git a/src/client/component/party.hpp b/src/client/component/party.hpp index 4bca8966..f682a14e 100644 --- a/src/client/component/party.hpp +++ b/src/client/component/party.hpp @@ -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); } diff --git a/src/client/component/profile_infos.cpp b/src/client/component/profile_infos.cpp index d03ccc49..b72d192f 100644 --- a/src/client/component/profile_infos.cpp +++ b/src/client/component/profile_infos.cpp @@ -3,6 +3,8 @@ #include "profile_infos.hpp" #include "network.hpp" +#include "party.hpp" +#include "scheduler.hpp" #include #include @@ -11,12 +13,14 @@ #include "../steam/steam.hpp" #include +#include "game/utils.hpp" + namespace profile_infos { namespace { using profile_map = std::unordered_map; - utils::concurrency::container profile_mapping; + utils::concurrency::container profile_mapping{}; std::optional 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 get_connected_client_xuids() + { + std::unordered_set 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 get_profile_info(uint64_t user_id) + profile_info::profile_info(utils::byte_buffer& buffer) + { + this->version = buffer.read(); + 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 get_profile_info() + { + return load_profile_info(); + } + + std::optional 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>([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(); + const profile_info info(buffer); + + add_profile_info(user_id, info); + }); + } } }; } diff --git a/src/client/component/profile_infos.hpp b/src/client/component/profile_infos.hpp index e0f8cf5d..c1cfd1f8 100644 --- a/src/client/component/profile_infos.hpp +++ b/src/client/component/profile_infos.hpp @@ -1,13 +1,26 @@ #pragma once +#include +#include + 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 get_profile_info(); std::optional get_profile_info(uint64_t user_id); void update_profile_info(const profile_info& info); } diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index b3599480..a54fddbf 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -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 { diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 94887b58..22638c6d 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -144,7 +144,7 @@ namespace game WEAK symbol CL_CheckKeepDrawingConnectScreen{0x1413CCAE0}; // Scr - WEAK symbol Scr_AddInt{0x1412E9870, 0x14016F160}; + WEAK symbol Scr_AddInt{0x0, 0x14016F160}; WEAK symbol Scr_AddString{0x0, 0x14016F320}; WEAK symbol Scr_GetString{0x0, 0x140171490}; WEAK symbol Scr_Notify_Canon{ @@ -162,6 +162,9 @@ namespace game 0x141CD98D0 }; + // PCache + WEAK symbol PCache_DeleteEntries{0x141E8D710}; + // SV WEAK symbol SV_Loaded{0x142252250, 0x140535460}; WEAK symbol SV_AddTestClient{0x142248F40, 0x14052E3E0}; @@ -195,6 +198,8 @@ namespace game WEAK symbol s_dvarPool{0x157AC6220, 0x14A3CB620}; WEAK symbol g_dvarCount{0x157AC61CC, 0x14A3CB5FC}; + // Client and dedi struct size differs :( + WEAK symbol svs_clients_cl{0x1576F9318, 0}; WEAK symbol svs_clients{0x0, 0x14A178E98}; // Dvar variables diff --git a/src/client/game/utils.cpp b/src/client/game/utils.cpp index 74c650f1..e374a73a 100644 --- a/src/client/game/utils.cpp +++ b/src/client/game/utils.cpp @@ -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(get_dvar_int("com_maxclients")); + } + + template + static void foreach_client(T* client_states, const std::function& 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& callback) + { + if (is_server()) + { + foreach_client(*svs_clients, callback); + } + else + { + foreach_client(*svs_clients_cl, callback); + } + } + + void foreach_client(const std::function& callback) + { + foreach_client([&](client_s& client, size_t) + { + callback(client); + }); + } + + void foreach_connected_client(const std::function& 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& callback) + { + foreach_connected_client([&](client_s& client, size_t) + { + callback(client); + }); + } } diff --git a/src/client/game/utils.hpp b/src/client/game/utils.hpp index e08b9417..09abd8e7 100644 --- a/src/client/game/utils.hpp +++ b/src/client/game/utils.hpp @@ -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& callback); + void foreach_client(const std::function& callback); + + void foreach_connected_client(const std::function& callback); + void foreach_connected_client(const std::function& callback); } diff --git a/src/common/utils/byte_buffer.cpp b/src/common/utils/byte_buffer.cpp new file mode 100644 index 00000000..7a3d5b94 --- /dev/null +++ b/src/common/utils/byte_buffer.cpp @@ -0,0 +1,53 @@ +#include "byte_buffer.hpp" + +#include + +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(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 byte_buffer::read_data(const size_t length) + { + std::vector result{}; + result.resize(length); + + this->read(result.data(), result.size()); + + return result; + } +} diff --git a/src/common/utils/byte_buffer.hpp b/src/common/utils/byte_buffer.hpp new file mode 100644 index 00000000..c4c33ec7 --- /dev/null +++ b/src/common/utils/byte_buffer.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include +#include +#include + +namespace utils +{ + class byte_buffer + { + public: + byte_buffer(); + byte_buffer(std::string buffer); + + template + byte_buffer(const std::basic_string_view& buffer) + : byte_buffer(std::string(reinterpret_cast(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(static_cast(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 + void write(const T& object) + { + this->write(&object, sizeof(object)); + } + + template + void write(const std::vector& vec) + { + this->write(vec.data(), vec.size() * sizeof(T)); + } + + template + void write_vector(const std::vector& vec) + { + this->write(static_cast(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 + T read() + { + T object{}; + this->read(&object, sizeof(object)); + return object; + } + + template + std::vector read_vector() + { + std::vector result{}; + const auto size = this->read(); + 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(); + + 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 read_data(size_t length); + + private: + bool writing_{false}; + size_t offset_{0}; + std::string buffer_{}; + }; +} diff --git a/src/common/utils/info_string.cpp b/src/common/utils/info_string.cpp index bdb3ad1f..d6f401ea 100644 --- a/src/common/utils/info_string.cpp +++ b/src/common/utils/info_string.cpp @@ -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}) { diff --git a/src/common/utils/info_string.hpp b/src/common/utils/info_string.hpp index 776739ab..4e3e4bc9 100644 --- a/src/common/utils/info_string.hpp +++ b/src/common/utils/info_string.hpp @@ -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& buffer); void set(const std::string& key, const std::string& value);