feat: add support for favorites in the server browser

This commit is contained in:
Jari van der Kaap 2023-04-12 23:41:45 +02:00
parent e8dba61553
commit b58c514261
4 changed files with 159 additions and 18 deletions

View File

@ -6,7 +6,8 @@
#include <utils/string.hpp> #include <utils/string.hpp>
#include <utils/concurrency.hpp> #include <utils/concurrency.hpp>
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/io.hpp>
#include "network.hpp" #include "network.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
@ -15,7 +16,7 @@ namespace server_list
{ {
namespace namespace
{ {
utils::hook::detour lua_serverinfo_to_table_hook; utils::hook::detour lua_serverinfo_to_table_hook;
struct state struct state
{ {
@ -25,7 +26,9 @@ namespace server_list
callback callback{}; callback callback{};
}; };
utils::concurrency::container<state> master_state; utils::concurrency::container<state> master_state;
std::vector<game::netadr_t> favorite_servers{};
void handle_server_list_response(const game::netadr_t& target, void handle_server_list_response(const game::netadr_t& target,
const network::data_view& data, state& s) const network::data_view& data, state& s)
@ -75,17 +78,63 @@ namespace server_list
} }
callback(true, result); 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);
}
} }
void lua_serverinfo_to_table_stub(game::hks::lua_State* state, game::ServerInfo serverInfo, int index) std::string get_favorite_servers_file_path()
{ {
lua_serverinfo_to_table_hook.invoke(state, serverInfo, index); return "players/user/favorite_servers.csv";
}
if (state) void write_favorite_servers()
{
std::string servers_buffer = "";
for (auto itr : favorite_servers)
{ {
auto botCount = atoi(game::Info_ValueForKey(serverInfo.tags, "bots")); servers_buffer.append(utils::string::va("%u,%u\n", itr.addr, itr.port));
game::Lua_SetTableInt("botCount", botCount, state); }
}
if (servers_buffer.empty())
{
return;
}
utils::io::write_file(get_favorite_servers_file_path(), servers_buffer);
}
void read_favorite_servers()
{
const std::string path = get_favorite_servers_file_path();
if (!utils::io::file_exists(path))
{
return;
}
favorite_servers.clear();
std::string filedata;
if (utils::io::read_file(path, &filedata))
{
auto servers = utils::string::split(filedata, '\n');
for (auto server_data : servers)
{
auto data = utils::string::split(server_data, ',');
auto addr = std::stoul(data[0].c_str());
auto port = (uint16_t)atoi(data[1].c_str());
auto server = network::address_from_ip(addr, port);
favorite_servers.push_back(server);
}
}
} }
} }
@ -112,6 +161,37 @@ namespace server_list
network::send(s.address, "getservers", utils::string::va("T7 %i full empty", PROTOCOL)); network::send(s.address, "getservers", utils::string::va("T7 %i full empty", PROTOCOL));
}); });
}
void add_favorite_server(game::netadr_t addr)
{
if (has_favorited_server(addr))
{
return;
}
favorite_servers.push_back(addr);
write_favorite_servers();
}
void remove_favorite_server(game::netadr_t addr)
{
for (auto it = favorite_servers.begin(); it != favorite_servers.end(); ++it)
{
if (network::are_addresses_equal(*it, addr))
{
favorite_servers.erase(it);
break;
}
}
write_favorite_servers();
}
bool has_favorited_server(game::netadr_t addr)
{
auto it = std::find_if(favorite_servers.begin(), favorite_servers.end(), [&addr](const game::netadr_t& obj) { return network::are_addresses_equal(addr, obj); });
return it != favorite_servers.end();
} }
struct component final : client_component struct component final : client_component
@ -145,9 +225,11 @@ namespace server_list
s.callback(false, {}); s.callback(false, {});
s.callback = {}; s.callback = {};
}); });
}, scheduler::async, 200ms); }, scheduler::async, 200ms);
lua_serverinfo_to_table_hook.create(0x141F1FD10_g, lua_serverinfo_to_table_stub);
lua_serverinfo_to_table_hook.create(0x141F1FD10_g, lua_serverinfo_to_table_stub); read_favorite_servers();
} }
void pre_destroy() override void pre_destroy() override

View File

@ -6,5 +6,9 @@ namespace server_list
bool get_master_server(game::netadr_t& address); bool get_master_server(game::netadr_t& address);
using callback = std::function<void(bool, const std::unordered_set<game::netadr_t>&)>; using callback = std::function<void(bool, const std::unordered_set<game::netadr_t>&)>;
void request_servers(callback callback); void request_servers(callback callback);
void add_favorite_server(game::netadr_t addr);
void remove_favorite_server(game::netadr_t addr);
bool has_favorited_server(game::netadr_t addr);
} }

View File

@ -1,5 +1,8 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "../steam.hpp" #include "../steam.hpp"
#include "component/network.hpp"
#include "component/server_list.hpp"
namespace steam namespace steam
{ {
@ -18,13 +21,17 @@ namespace steam
int matchmaking::AddFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort, int matchmaking::AddFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort,
unsigned short nQueryPort, unsigned int unFlags, unsigned short nQueryPort, unsigned int unFlags,
unsigned int rTime32LastPlayedOnServer) unsigned int rTime32LastPlayedOnServer)
{ {
auto addr = network::address_from_ip(htonl(nIP), nConnPort);
server_list::add_favorite_server(addr);
return 0; return 0;
} }
bool matchmaking::RemoveFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort, bool matchmaking::RemoveFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort,
unsigned short nQueryPort, unsigned int unFlags) unsigned short nQueryPort, unsigned int unFlags)
{ {
auto addr = network::address_from_ip(htonl(nIP), nConnPort);
server_list::remove_favorite_server(addr);
return false; return false;
} }

View File

@ -21,7 +21,8 @@ namespace steam
gameserveritem_t server_item{}; gameserveritem_t server_item{};
}; };
auto* const internet_request = reinterpret_cast<void*>(1); auto* const internet_request = reinterpret_cast<void*>(1);
auto* const favorites_request = reinterpret_cast<void*>(4);
using servers = std::vector<server>; using servers = std::vector<server>;
@ -198,7 +199,54 @@ namespace steam
void* matchmaking_servers::RequestFavoritesServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, void* matchmaking_servers::RequestFavoritesServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters,
matchmaking_server_list_response* pRequestServersResponse) matchmaking_server_list_response* pRequestServersResponse)
{ {
return reinterpret_cast<void*>(4); current_response = pRequestServersResponse;
server_list::request_servers([](const bool success, const std::unordered_set<game::netadr_t>& s)
{
const auto res = current_response.load();
if (!res)
{
return;
}
if (!success)
{
res->RefreshComplete(favorites_request, eServerFailedToRespond);
return;
}
if (s.empty())
{
res->RefreshComplete(favorites_request, eNoServersListedOnMasterServer);
return;
}
queried_servers.access([&s](servers& srvs)
{
srvs = {};
srvs.reserve(s.size());
for (auto& address : s)
{
if (!server_list::has_favorited_server(address))
{
continue;
}
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 favorites_request;
} }
void* matchmaking_servers::RequestHistoryServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, void* matchmaking_servers::RequestHistoryServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters,