diff --git a/src/client/component/getinfo.cpp b/src/client/component/getinfo.cpp index 505c0dd6..7514d8cf 100644 --- a/src/client/component/getinfo.cpp +++ b/src/client/component/getinfo.cpp @@ -110,6 +110,7 @@ namespace getinfo info.set("bots", std::to_string(get_bot_count())); info.set("sv_maxclients", std::to_string(get_max_client_count())); info.set("protocol", std::to_string(PROTOCOL)); + info.set("sub_protocol", std::to_string(SUB_PROTOCOL)); info.set("playmode", std::to_string(game::Com_SessionMode_GetMode())); info.set("gamemode", std::to_string(Com_SessionMode_GetGameMode())); info.set("sv_running", std::to_string(game::is_server_running())); diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 403c0c3a..f5d0487a 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -157,6 +157,14 @@ namespace party return; } + const auto sub_protocol = atoi(info.get("sub_protocol").data()); + if (sub_protocol != SUB_PROTOCOL && sub_protocol != (SUB_PROTOCOL - 1)) + { + const auto str = "Invalid sub-protocol."; + printf("%s\n", str); + return; + } + const auto gamename = info.get("gamename"); if (gamename != "T7"s) { diff --git a/src/client/component/server_list.cpp b/src/client/component/server_list.cpp index 7c0ca6ad..9e3fcbfc 100644 --- a/src/client/component/server_list.cpp +++ b/src/client/component/server_list.cpp @@ -1,164 +1,164 @@ -#include -#include "loader/component_loader.hpp" -#include "server_list.hpp" - -#include "game/game.hpp" - -#include -#include -#include - -#include "network.hpp" -#include "scheduler.hpp" - -namespace server_list -{ - namespace - { +#include +#include "loader/component_loader.hpp" +#include "server_list.hpp" + +#include "game/game.hpp" + +#include +#include +#include + +#include "network.hpp" +#include "scheduler.hpp" + +namespace server_list +{ + namespace + { utils::hook::detour lua_serverinfo_to_table_hook; - - struct state - { - game::netadr_t address{}; - bool requesting{false}; - std::chrono::high_resolution_clock::time_point query_start{}; - callback callback{}; - }; - - utils::concurrency::container master_state; - - void handle_server_list_response(const game::netadr_t& target, - const network::data_view& data, state& s) - { - if (!s.requesting || s.address != target) - { - return; - } - - s.requesting = false; - const auto callback = std::move(s.callback); - - std::optional start{}; - - for (size_t i = 0; i + 6 < data.size(); ++i) - { - if (data[i + 6] == '\\') - { - start.emplace(i); - break; - } - } - - if (!start.has_value()) - { - callback(true, {}); - return; - } - - std::unordered_set result{}; - - for (auto i = start.value(); i + 6 < data.size(); i += 7) - { - if (data[i + 6] != '\\') - { - break; - } - - game::netadr_t address{}; - address.type = game::NA_RAWIP; - address.localNetID = game::NS_CLIENT1; - memcpy(&address.ipv4.a, data.data() + i + 0, 4); - memcpy(&address.port, data.data() + i + 4, 2); - address.port = ntohs(address.port); - - result.emplace(address); - } - - callback(true, result); + + struct state + { + game::netadr_t address{}; + bool requesting{false}; + std::chrono::high_resolution_clock::time_point query_start{}; + callback callback{}; + }; + + utils::concurrency::container master_state; + + void handle_server_list_response(const game::netadr_t& target, + const network::data_view& data, state& s) + { + if (!s.requesting || s.address != target) + { + return; + } + + s.requesting = false; + const auto callback = std::move(s.callback); + + std::optional start{}; + + for (size_t i = 0; i + 6 < data.size(); ++i) + { + if (data[i + 6] == '\\') + { + start.emplace(i); + break; + } + } + + if (!start.has_value()) + { + callback(true, {}); + return; + } + + std::unordered_set result{}; + + for (auto i = start.value(); i + 6 < data.size(); i += 7) + { + if (data[i + 6] != '\\') + { + break; + } + + game::netadr_t address{}; + address.type = game::NA_RAWIP; + address.localNetID = game::NS_CLIENT1; + memcpy(&address.ipv4.a, data.data() + i + 0, 4); + memcpy(&address.port, data.data() + i + 4, 2); + address.port = ntohs(address.port); + + result.emplace(address); + } + + callback(true, result); } void lua_serverinfo_to_table_stub(game::hks::lua_State* state, game::ServerInfo serverInfo, int index) - { + { lua_serverinfo_to_table_hook.invoke(state, serverInfo, index); if (state) { auto botCount = atoi(game::Info_ValueForKey(serverInfo.tags, "bots")); game::Lua_SetTableInt("botCount", botCount, state); - } - } - } - - bool get_master_server(game::netadr_t& address) - { - address = network::address_from_string("server.boiii.re:20810"); - return address.type != game::NA_BAD; - } - - void request_servers(callback callback) - { - master_state.access([&callback](state& s) - { - game::netadr_t addr{}; - if (!get_master_server(addr)) - { - return; - } - - s.requesting = true; - s.address = addr; - s.callback = std::move(callback); - s.query_start = std::chrono::high_resolution_clock::now(); - - network::send(s.address, "getservers", utils::string::va("T7 %i full empty", PROTOCOL)); - }); - } - - struct component final : client_component - { - void post_unpack() override - { - network::on("getServersResponse", [](const game::netadr_t& target, const network::data_view& data) - { - master_state.access([&](state& s) - { - handle_server_list_response(target, data, s); - }); - }); - - scheduler::loop([] - { - master_state.access([](state& s) - { - if (!s.requesting) - { - return; - } - - const auto now = std::chrono::high_resolution_clock::now(); - if ((now - s.query_start) < 2s) - { - return; - } - - s.requesting = false; - s.callback(false, {}); - s.callback = {}; - }); + } + } + } + + bool get_master_server(game::netadr_t& address) + { + address = network::address_from_string("server.boiii.re:20810"); + return address.type != game::NA_BAD; + } + + void request_servers(callback callback) + { + master_state.access([&callback](state& s) + { + game::netadr_t addr{}; + if (!get_master_server(addr)) + { + return; + } + + s.requesting = true; + s.address = addr; + s.callback = std::move(callback); + s.query_start = std::chrono::high_resolution_clock::now(); + + network::send(s.address, "getservers", utils::string::va("T7 %i full empty", PROTOCOL)); + }); + } + + struct component final : client_component + { + void post_unpack() override + { + network::on("getServersResponse", [](const game::netadr_t& target, const network::data_view& data) + { + master_state.access([&](state& s) + { + handle_server_list_response(target, data, s); + }); + }); + + scheduler::loop([] + { + master_state.access([](state& s) + { + if (!s.requesting) + { + return; + } + + const auto now = std::chrono::high_resolution_clock::now(); + if ((now - s.query_start) < 2s) + { + return; + } + + s.requesting = false; + s.callback(false, {}); + s.callback = {}; + }); }, scheduler::async, 200ms); - lua_serverinfo_to_table_hook.create(0x141F1FD10_g, lua_serverinfo_to_table_stub); - } - - void pre_destroy() override - { - master_state.access([](state& s) - { - s.requesting = false; - s.callback = {}; - }); - } - }; -} - -REGISTER_COMPONENT(server_list::component) + lua_serverinfo_to_table_hook.create(0x141F1FD10_g, lua_serverinfo_to_table_stub); + } + + void pre_destroy() override + { + master_state.access([](state& s) + { + s.requesting = false; + s.callback = {}; + }); + } + }; +} + +REGISTER_COMPONENT(server_list::component) diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index a9c73f5b..5c13d86e 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1,6 +1,7 @@ #pragma once #define PROTOCOL 4 +#define SUB_PROTOCOL 1 #ifdef __cplusplus namespace game diff --git a/src/client/steam/interfaces/matchmaking_servers.cpp b/src/client/steam/interfaces/matchmaking_servers.cpp index b2996171..594689ce 100644 --- a/src/client/steam/interfaces/matchmaking_servers.cpp +++ b/src/client/steam/interfaces/matchmaking_servers.cpp @@ -1,333 +1,334 @@ -#include -#include "../steam.hpp" - -#include "game/game.hpp" - -#include "component/party.hpp" -#include "component/network.hpp" -#include "component/server_list.hpp" - -#include -#include - -namespace steam -{ - namespace - { - struct server - { - bool handled{false}; - game::netadr_t address{}; - gameserveritem_t server_item{}; - }; - - auto* const internet_request = reinterpret_cast(1); - - using servers = std::vector; - - ::utils::concurrency::container queried_servers{}; - std::atomic current_response{}; - - gameserveritem_t create_server_item(const game::netadr_t& address, const ::utils::info_string& info, - const uint32_t ping, const bool success) - { - gameserveritem_t server{}; - server.m_NetAdr.m_usConnectionPort = address.port; - server.m_NetAdr.m_usQueryPort = address.port; - server.m_NetAdr.m_unIP = ntohl(address.addr); - server.m_nPing = static_cast(ping); - server.m_bHadSuccessfulResponse = success; - server.m_bDoNotRefresh = false; - ::utils::string::copy(server.m_szGameDir, ""); - ::utils::string::copy(server.m_szMap, info.get("mapname").data()); - ::utils::string::copy(server.m_szGameDescription, info.get("description").data()); - server.m_nAppID = 311210; - server.m_nPlayers = atoi(info.get("clients").data()); - server.m_nMaxPlayers = atoi(info.get("sv_maxclients").data()); - server.m_nBotPlayers = atoi(info.get("bots").data()); - server.m_bPassword = info.get("isPrivate") == "1"; - server.m_bSecure = true; - server.m_ulTimeLastPlayed = 0; - server.m_nServerVersion = 1000; - ::utils::string::copy(server.m_szServerName, info.get("hostname").data()); - - const auto playmode = info.get("playmode"); - const auto mode = game::eModes(std::atoi(playmode.data())); - - const auto* tags = ::utils::string::va( - R"(\gametype\%s\dedicated\%s\ranked\false\hardcore\%s\zombies\%s\playerCount\%d\bots\%d\modName\%s\)", - info.get("gametype").data(), +#include +#include "../steam.hpp" + +#include "game/game.hpp" + +#include "component/party.hpp" +#include "component/network.hpp" +#include "component/server_list.hpp" + +#include +#include + +namespace steam +{ + namespace + { + struct server + { + bool handled{false}; + game::netadr_t address{}; + gameserveritem_t server_item{}; + }; + + auto* const internet_request = reinterpret_cast(1); + + using servers = std::vector; + + ::utils::concurrency::container queried_servers{}; + std::atomic current_response{}; + + gameserveritem_t create_server_item(const game::netadr_t& address, const ::utils::info_string& info, + const uint32_t ping, const bool success) + { + const auto sub_protocol = atoi(info.get("sub_protocol").data()); + + gameserveritem_t server{}; + server.m_NetAdr.m_usConnectionPort = address.port; + server.m_NetAdr.m_usQueryPort = address.port; + server.m_NetAdr.m_unIP = ntohl(address.addr); + server.m_nPing = static_cast(ping); + server.m_bHadSuccessfulResponse = success; + server.m_bDoNotRefresh = false; + ::utils::string::copy(server.m_szGameDir, ""); + ::utils::string::copy(server.m_szMap, info.get("mapname").data()); + ::utils::string::copy(server.m_szGameDescription, info.get("description").data()); + server.m_nAppID = (sub_protocol == SUB_PROTOCOL || sub_protocol == (SUB_PROTOCOL - 1)) ? 311210 : 0; + server.m_nPlayers = atoi(info.get("clients").data()); + server.m_nMaxPlayers = atoi(info.get("sv_maxclients").data()); + server.m_nBotPlayers = atoi(info.get("bots").data()); + server.m_bPassword = info.get("isPrivate") == "1"; + server.m_bSecure = true; + server.m_ulTimeLastPlayed = 0; + server.m_nServerVersion = 1000; + ::utils::string::copy(server.m_szServerName, info.get("hostname").data()); + + const auto playmode = info.get("playmode"); + const auto mode = game::eModes(std::atoi(playmode.data())); + + const auto* tags = ::utils::string::va( + R"(\gametype\%s\dedicated\%s\ranked\false\hardcore\%s\zombies\%s\playerCount\%d\bots\%d\modName\%s\)", + info.get("gametype").data(), info.get("dedicated") == "1" ? "true" : "false", - info.get("hc") == "1" ? "true" : "false", + info.get("hc") == "1" ? "true" : "false", mode == game::MODE_ZOMBIES ? "true" : "false", server.m_nPlayers, atoi(info.get("bots").data()), - info.get("modname").data()); - - ::utils::string::copy(server.m_szGameTags, tags); - server.m_steamID.bits = strtoull(info.get("xuid").data(), nullptr, 16); - - return server; - } - - void handle_server_respone(const bool success, const game::netadr_t& host, const ::utils::info_string& info, - const uint32_t ping) - { - bool all_handled = false; - std::optional index{}; - queried_servers.access([&](servers& srvs) - { - size_t i = 0; - for (; i < srvs.size(); ++i) - { - if (srvs[i].address == host) - { - break; - } - } - - if (i >= srvs.size()) - { - return; - } - - index = static_cast(i); - - auto& srv = srvs[i]; - srv.handled = true; - srv.server_item = create_server_item(host, info, ping, success); - - - for (const auto& entry : srvs) - { - if (!entry.handled) - { - return; - } - } - - all_handled = true; - }); - - const auto res = current_response.load(); - if (!index || !res) - { - return; - } - - if (success) - { - res->ServerResponded(internet_request, *index); - } - else - { - res->ServerFailedToRespond(internet_request, *index); - } - - if (all_handled) - { - res->RefreshComplete(internet_request, eServerResponded); - } - } - - void ping_server(const game::netadr_t& server) - { - party::query_server(server, handle_server_respone); - } - } - - void* matchmaking_servers::RequestInternetServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, - matchmaking_server_list_response* pRequestServersResponse) - { - current_response = pRequestServersResponse; - - server_list::request_servers([](const bool success, const std::unordered_set& s) - { - const auto res = current_response.load(); - if (!res) - { - return; - } - - if (!success) - { - res->RefreshComplete(internet_request, eServerFailedToRespond); - return; - } - - if (s.empty()) - { - res->RefreshComplete(internet_request, eNoServersListedOnMasterServer); - return; - } - - queried_servers.access([&s](servers& srvs) - { - srvs = {}; - srvs.reserve(s.size()); - - for (auto& address : s) - { - server new_server{}; - new_server.address = address; - new_server.server_item = create_server_item(address, {}, 0, false); - - srvs.push_back(new_server); - } - }); - - for (auto& srv : s) - { - ping_server(srv); - } - }); - - return internet_request; - } - - void* matchmaking_servers::RequestLANServerList(unsigned int iApp, - matchmaking_server_list_response* pRequestServersResponse) - { - return reinterpret_cast(2); - } - - void* matchmaking_servers::RequestFriendsServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, - matchmaking_server_list_response* pRequestServersResponse) - { - return reinterpret_cast(3); - } - - void* matchmaking_servers::RequestFavoritesServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, - matchmaking_server_list_response* pRequestServersResponse) - { - return reinterpret_cast(4); - } - - void* matchmaking_servers::RequestHistoryServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, - matchmaking_server_list_response* pRequestServersResponse) - { - return reinterpret_cast(5); - } - - void* matchmaking_servers::RequestSpectatorServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, - matchmaking_server_list_response* pRequestServersResponse) - { - return reinterpret_cast(6); - } - - void matchmaking_servers::ReleaseRequest(void* hServerListRequest) - { - if (internet_request == hServerListRequest) - { - current_response = nullptr; - } - } - - gameserveritem_t* matchmaking_servers::GetServerDetails(void* hRequest, int iServer) - { - if (internet_request != hRequest) - { - return nullptr; - } - - static thread_local gameserveritem_t server_item{}; - return queried_servers.access([iServer](const servers& s) -> gameserveritem_t* - { - if (iServer < 0 || static_cast(iServer) >= s.size()) - { - return nullptr; - } - - server_item = s[iServer].server_item; - return &server_item; - }); - } - - void matchmaking_servers::CancelQuery(void* hRequest) - { - } - - void matchmaking_servers::RefreshQuery(void* hRequest) - { - } - - bool matchmaking_servers::IsRefreshing(void* hRequest) - { - return false; - } - - int matchmaking_servers::GetServerCount(void* hRequest) - { - if (internet_request != hRequest) - { - return 0; - } - - return queried_servers.access([](const servers& s) - { - return static_cast(s.size()); - }); - } - - void matchmaking_servers::RefreshServer(void* hRequest, const int iServer) - { - if (internet_request != hRequest) - { - return; - } - - std::optional address{}; - queried_servers.access([&](const servers& s) - { - if (iServer < 0 || static_cast(iServer) >= s.size()) - { - return; - } - - address = s[iServer].address; - }); - - if (address) - { - ping_server(*address); - } - } - - void* matchmaking_servers::PingServer(const unsigned int unIP, const unsigned short usPort, - matchmaking_ping_response* pRequestServersResponse) - { - auto response = pRequestServersResponse; - const auto addr = network::address_from_ip(htonl(unIP), usPort); - - party::query_server( - addr, [response](const bool success, const game::netadr_t& host, const ::utils::info_string& info, - const uint32_t ping) - { - if (success) - { - auto server_item = create_server_item(host, info, ping, success); - response->ServerResponded(server_item); - } - else - { - response->ServerFailedToRespond(); - } - }); - - return reinterpret_cast(static_cast(7 + rand())); - } - - int matchmaking_servers::PlayerDetails(unsigned int unIP, unsigned short usPort, void* pRequestServersResponse) - { - return 0; - } - - int matchmaking_servers::ServerRules(unsigned int unIP, unsigned short usPort, void* pRequestServersResponse) - { - return 0; - } - - void matchmaking_servers::CancelServerQuery(int hServerQuery) - { - } -} + info.get("modname").data()); + + ::utils::string::copy(server.m_szGameTags, tags); + server.m_steamID.bits = strtoull(info.get("xuid").data(), nullptr, 16); + + return server; + } + + void handle_server_respone(const bool success, const game::netadr_t& host, const ::utils::info_string& info, + const uint32_t ping) + { + bool all_handled = false; + std::optional index{}; + queried_servers.access([&](servers& srvs) + { + size_t i = 0; + for (; i < srvs.size(); ++i) + { + if (srvs[i].address == host) + { + break; + } + } + + if (i >= srvs.size()) + { + return; + } + + index = static_cast(i); + + auto& srv = srvs[i]; + srv.handled = true; + srv.server_item = create_server_item(host, info, ping, success); + + + for (const auto& entry : srvs) + { + if (!entry.handled) + { + return; + } + } + + all_handled = true; + }); + + const auto res = current_response.load(); + if (!index || !res) + { + return; + } + + if (success) + { + res->ServerResponded(internet_request, *index); + } + else + { + res->ServerFailedToRespond(internet_request, *index); + } + + if (all_handled) + { + res->RefreshComplete(internet_request, eServerResponded); + } + } + + void ping_server(const game::netadr_t& server) + { + party::query_server(server, handle_server_respone); + } + } + + void* matchmaking_servers::RequestInternetServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + matchmaking_server_list_response* pRequestServersResponse) + { + current_response = pRequestServersResponse; + + server_list::request_servers([](const bool success, const std::unordered_set& s) + { + const auto res = current_response.load(); + if (!res) + { + return; + } + + if (!success) + { + res->RefreshComplete(internet_request, eServerFailedToRespond); + return; + } + + if (s.empty()) + { + res->RefreshComplete(internet_request, eNoServersListedOnMasterServer); + return; + } + + queried_servers.access([&s](servers& srvs) + { + srvs = {}; + srvs.reserve(s.size()); + + for (auto& address : s) + { + server new_server{}; + new_server.address = address; + new_server.server_item = create_server_item(address, {}, 0, false); + + srvs.push_back(new_server); + } + }); + + for (auto& srv : s) + { + ping_server(srv); + } + }); + + return internet_request; + } + + void* matchmaking_servers::RequestLANServerList(unsigned int iApp, + matchmaking_server_list_response* pRequestServersResponse) + { + return reinterpret_cast(2); + } + + void* matchmaking_servers::RequestFriendsServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + matchmaking_server_list_response* pRequestServersResponse) + { + return reinterpret_cast(3); + } + + void* matchmaking_servers::RequestFavoritesServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + matchmaking_server_list_response* pRequestServersResponse) + { + return reinterpret_cast(4); + } + + void* matchmaking_servers::RequestHistoryServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + matchmaking_server_list_response* pRequestServersResponse) + { + return reinterpret_cast(5); + } + + void* matchmaking_servers::RequestSpectatorServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + matchmaking_server_list_response* pRequestServersResponse) + { + return reinterpret_cast(6); + } + + void matchmaking_servers::ReleaseRequest(void* hServerListRequest) + { + if (internet_request == hServerListRequest) + { + current_response = nullptr; + } + } + + gameserveritem_t* matchmaking_servers::GetServerDetails(void* hRequest, int iServer) + { + if (internet_request != hRequest) + { + return nullptr; + } + + static thread_local gameserveritem_t server_item{}; + return queried_servers.access([iServer](const servers& s) -> gameserveritem_t* { + if (iServer < 0 || static_cast(iServer) >= s.size()) + { + return nullptr; + } + + server_item = s[iServer].server_item; + return &server_item; + }); + } + + void matchmaking_servers::CancelQuery(void* hRequest) + { + } + + void matchmaking_servers::RefreshQuery(void* hRequest) + { + } + + bool matchmaking_servers::IsRefreshing(void* hRequest) + { + return false; + } + + int matchmaking_servers::GetServerCount(void* hRequest) + { + if (internet_request != hRequest) + { + return 0; + } + + return queried_servers.access([](const servers& s) + { + return static_cast(s.size()); + }); + } + + void matchmaking_servers::RefreshServer(void* hRequest, const int iServer) + { + if (internet_request != hRequest) + { + return; + } + + std::optional address{}; + queried_servers.access([&](const servers& s) + { + if (iServer < 0 || static_cast(iServer) >= s.size()) + { + return; + } + + address = s[iServer].address; + }); + + if (address) + { + ping_server(*address); + } + } + + void* matchmaking_servers::PingServer(const unsigned int unIP, const unsigned short usPort, + matchmaking_ping_response* pRequestServersResponse) + { + auto response = pRequestServersResponse; + const auto addr = network::address_from_ip(htonl(unIP), usPort); + + party::query_server( + addr, [response](const bool success, const game::netadr_t& host, const ::utils::info_string& info, + const uint32_t ping) + { + if (success) + { + auto server_item = create_server_item(host, info, ping, success); + response->ServerResponded(server_item); + } + else + { + response->ServerFailedToRespond(); + } + }); + + return reinterpret_cast(static_cast(7 + rand())); + } + + int matchmaking_servers::PlayerDetails(unsigned int unIP, unsigned short usPort, void* pRequestServersResponse) + { + return 0; + } + + int matchmaking_servers::ServerRules(unsigned int unIP, unsigned short usPort, void* pRequestServersResponse) + { + return 0; + } + + void matchmaking_servers::CancelServerQuery(int hServerQuery) + { + } +}