Merge pull request #519 from JariKCoding/favorites
feat: add support for favorites in the server browser
This commit is contained in:
commit
71265cc5de
@ -7,6 +7,7 @@
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/concurrency.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
#include "network.hpp"
|
||||
#include "scheduler.hpp"
|
||||
@ -20,15 +21,17 @@ namespace server_list
|
||||
struct state
|
||||
{
|
||||
game::netadr_t address{};
|
||||
bool requesting{false};
|
||||
bool requesting{ false };
|
||||
std::chrono::high_resolution_clock::time_point query_start{};
|
||||
callback callback{};
|
||||
};
|
||||
|
||||
utils::concurrency::container<state> master_state;
|
||||
|
||||
utils::concurrency::container<server_list> favorite_servers{};
|
||||
|
||||
void handle_server_list_response(const game::netadr_t& target,
|
||||
const network::data_view& data, state& s)
|
||||
const network::data_view& data, state& s)
|
||||
{
|
||||
if (!s.requesting || s.address != target)
|
||||
{
|
||||
@ -87,6 +90,48 @@ namespace server_list
|
||||
game::Lua_SetTableInt("botCount", botCount, state);
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_favorite_servers_file_path()
|
||||
{
|
||||
return "players/user/favorite_servers.txt";
|
||||
}
|
||||
|
||||
void write_favorite_servers()
|
||||
{
|
||||
favorite_servers.access([&](std::unordered_set<game::netadr_t>& servers)
|
||||
{
|
||||
std::string servers_buffer = "";
|
||||
for (auto itr : servers)
|
||||
{
|
||||
servers_buffer.append(utils::string::va("%i.%i.%i.%i:%u\n", itr.ipv4.a, itr.ipv4.b, itr.ipv4.c, itr.ipv4.d, itr.port));
|
||||
}
|
||||
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.access([&path](std::unordered_set<game::netadr_t>& servers)
|
||||
{
|
||||
servers.clear();
|
||||
std::string filedata;
|
||||
if (utils::io::read_file(path, &filedata))
|
||||
{
|
||||
auto srv = utils::string::split(filedata, '\n');
|
||||
for (auto server_address : srv)
|
||||
{
|
||||
auto server = network::address_from_string(server_address);
|
||||
servers.insert(server);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool get_master_server(game::netadr_t& address)
|
||||
@ -98,20 +143,50 @@ namespace server_list
|
||||
void request_servers(callback callback)
|
||||
{
|
||||
master_state.access([&callback](state& s)
|
||||
{
|
||||
game::netadr_t addr{};
|
||||
if (!get_master_server(addr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
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();
|
||||
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));
|
||||
network::send(s.address, "getservers", utils::string::va("T7 %i full empty", PROTOCOL));
|
||||
});
|
||||
}
|
||||
|
||||
void add_favorite_server(game::netadr_t addr)
|
||||
{
|
||||
favorite_servers.access([&addr](std::unordered_set<game::netadr_t>& servers)
|
||||
{
|
||||
servers.insert(addr);
|
||||
});
|
||||
write_favorite_servers();
|
||||
}
|
||||
|
||||
void remove_favorite_server(game::netadr_t addr)
|
||||
{
|
||||
favorite_servers.access([&addr](std::unordered_set<game::netadr_t>& servers)
|
||||
{
|
||||
for (auto it = servers.begin(); it != servers.end(); ++it)
|
||||
{
|
||||
if (network::are_addresses_equal(*it, addr))
|
||||
{
|
||||
servers.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
write_favorite_servers();
|
||||
}
|
||||
|
||||
utils::concurrency::container<server_list>& get_favorite_servers()
|
||||
{
|
||||
return favorite_servers;
|
||||
}
|
||||
|
||||
struct component final : client_component
|
||||
@ -119,44 +194,49 @@ namespace server_list
|
||||
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);
|
||||
master_state.access([&](state& s)
|
||||
{
|
||||
handle_server_list_response(target, data, s);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
scheduler::loop([]
|
||||
{
|
||||
master_state.access([](state& s)
|
||||
{
|
||||
if (!s.requesting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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);
|
||||
s.requesting = false;
|
||||
s.callback(false, {});
|
||||
s.callback = {};
|
||||
});
|
||||
}, scheduler::async, 200ms);
|
||||
|
||||
lua_serverinfo_to_table_hook.create(0x141F1FD10_g, lua_serverinfo_to_table_stub);
|
||||
|
||||
scheduler::once([]
|
||||
{
|
||||
read_favorite_servers();
|
||||
}, scheduler::main);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
{
|
||||
master_state.access([](state& s)
|
||||
{
|
||||
s.requesting = false;
|
||||
s.callback = {};
|
||||
});
|
||||
{
|
||||
s.requesting = false;
|
||||
s.callback = {};
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,10 +1,17 @@
|
||||
#pragma once
|
||||
#include <game/game.hpp>
|
||||
|
||||
#include <utils/concurrency.hpp>
|
||||
|
||||
namespace server_list
|
||||
{
|
||||
bool get_master_server(game::netadr_t& address);
|
||||
|
||||
using callback = std::function<void(bool, const std::unordered_set<game::netadr_t>&)>;
|
||||
void request_servers(callback callback);
|
||||
|
||||
void add_favorite_server(game::netadr_t addr);
|
||||
void remove_favorite_server(game::netadr_t addr);
|
||||
using server_list = std::unordered_set<game::netadr_t>;
|
||||
utils::concurrency::container<server_list>& get_favorite_servers();
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
#include <std_include.hpp>
|
||||
#include "../steam.hpp"
|
||||
|
||||
#include "component/network.hpp"
|
||||
#include "component/server_list.hpp"
|
||||
|
||||
namespace steam
|
||||
{
|
||||
int matchmaking::GetFavoriteGameCount()
|
||||
@ -19,12 +22,16 @@ namespace steam
|
||||
unsigned short nQueryPort, unsigned int unFlags,
|
||||
unsigned int rTime32LastPlayedOnServer)
|
||||
{
|
||||
auto addr = network::address_from_ip(htonl(nIP), nConnPort);
|
||||
server_list::add_favorite_server(addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool matchmaking::RemoveFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort,
|
||||
unsigned short nQueryPort, unsigned int unFlags)
|
||||
{
|
||||
auto addr = network::address_from_ip(htonl(nIP), nConnPort);
|
||||
server_list::remove_favorite_server(addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -22,11 +22,14 @@ namespace steam
|
||||
};
|
||||
|
||||
auto* const internet_request = reinterpret_cast<void*>(1);
|
||||
auto* const favorites_request = reinterpret_cast<void*>(4);
|
||||
|
||||
using servers = std::vector<server>;
|
||||
|
||||
::utils::concurrency::container<servers> queried_servers{};
|
||||
std::atomic<matchmaking_server_list_response*> current_response{};
|
||||
::utils::concurrency::container<servers> internet_servers{};
|
||||
::utils::concurrency::container<servers> favorites_servers{};
|
||||
std::atomic<matchmaking_server_list_response*> internet_response{};
|
||||
std::atomic<matchmaking_server_list_response*> favorites_response{};
|
||||
|
||||
template <typename T>
|
||||
void copy_safe(T& dest, const char* in)
|
||||
@ -84,11 +87,12 @@ namespace steam
|
||||
}
|
||||
|
||||
void handle_server_respone(const bool success, const game::netadr_t& host, const ::utils::info_string& info,
|
||||
const uint32_t ping)
|
||||
const uint32_t ping, ::utils::concurrency::container<servers>& server_list,
|
||||
std::atomic<matchmaking_server_list_response*>& response, void* request)
|
||||
{
|
||||
bool all_handled = false;
|
||||
std::optional<int> index{};
|
||||
queried_servers.access([&](servers& srvs)
|
||||
server_list.access([&](servers& srvs)
|
||||
{
|
||||
size_t i = 0;
|
||||
for (; i < srvs.size(); ++i)
|
||||
@ -122,7 +126,7 @@ namespace steam
|
||||
all_handled = true;
|
||||
});
|
||||
|
||||
const auto res = current_response.load();
|
||||
const auto res = response.load();
|
||||
if (!index || !res)
|
||||
{
|
||||
return;
|
||||
@ -130,33 +134,46 @@ namespace steam
|
||||
|
||||
if (success)
|
||||
{
|
||||
res->ServerResponded(internet_request, *index);
|
||||
res->ServerResponded(request, *index);
|
||||
}
|
||||
else
|
||||
{
|
||||
res->ServerFailedToRespond(internet_request, *index);
|
||||
res->ServerFailedToRespond(request, *index);
|
||||
}
|
||||
|
||||
if (all_handled)
|
||||
{
|
||||
res->RefreshComplete(internet_request, eServerResponded);
|
||||
res->RefreshComplete(request, eServerResponded);
|
||||
}
|
||||
}
|
||||
|
||||
void ping_server(const game::netadr_t& server)
|
||||
void handle_internet_server_response(const bool success, const game::netadr_t& host, const ::utils::info_string& info,
|
||||
const uint32_t ping)
|
||||
{
|
||||
party::query_server(server, handle_server_respone);
|
||||
handle_server_respone(success, host, info, ping, internet_servers, internet_response, internet_request);
|
||||
}
|
||||
|
||||
|
||||
void handle_favorites_server_response(const bool success, const game::netadr_t& host, const ::utils::info_string& info,
|
||||
const uint32_t ping)
|
||||
{
|
||||
handle_server_respone(success, host, info, ping, favorites_servers, favorites_response, favorites_request);
|
||||
}
|
||||
|
||||
void ping_server(const game::netadr_t& server, party::query_callback callback)
|
||||
{
|
||||
party::query_server(server, callback);
|
||||
}
|
||||
}
|
||||
|
||||
void* matchmaking_servers::RequestInternetServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters,
|
||||
matchmaking_server_list_response* pRequestServersResponse)
|
||||
{
|
||||
current_response = pRequestServersResponse;
|
||||
internet_response = pRequestServersResponse;
|
||||
|
||||
server_list::request_servers([](const bool success, const std::unordered_set<game::netadr_t>& s)
|
||||
{
|
||||
const auto res = current_response.load();
|
||||
const auto res = internet_response.load();
|
||||
if (!res)
|
||||
{
|
||||
return;
|
||||
@ -174,7 +191,7 @@ namespace steam
|
||||
return;
|
||||
}
|
||||
|
||||
queried_servers.access([&s](servers& srvs)
|
||||
internet_servers.access([&s](servers& srvs)
|
||||
{
|
||||
srvs = {};
|
||||
srvs.reserve(s.size());
|
||||
@ -191,7 +208,7 @@ namespace steam
|
||||
|
||||
for (auto& srv : s)
|
||||
{
|
||||
ping_server(srv);
|
||||
ping_server(srv, handle_internet_server_response);
|
||||
}
|
||||
});
|
||||
|
||||
@ -213,7 +230,45 @@ namespace steam
|
||||
void* matchmaking_servers::RequestFavoritesServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters,
|
||||
matchmaking_server_list_response* pRequestServersResponse)
|
||||
{
|
||||
return reinterpret_cast<void*>(4);
|
||||
favorites_response = pRequestServersResponse;
|
||||
|
||||
auto& srvs = server_list::get_favorite_servers();
|
||||
srvs.access([&](std::unordered_set<game::netadr_t> s)
|
||||
{
|
||||
const auto res = favorites_response.load();
|
||||
if (!res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (s.empty())
|
||||
{
|
||||
res->RefreshComplete(favorites_request, eNoServersListedOnMasterServer);
|
||||
return;
|
||||
}
|
||||
|
||||
favorites_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, handle_favorites_server_response);
|
||||
}
|
||||
});
|
||||
|
||||
return favorites_request;
|
||||
}
|
||||
|
||||
void* matchmaking_servers::RequestHistoryServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters,
|
||||
@ -232,19 +287,25 @@ namespace steam
|
||||
{
|
||||
if (internet_request == hServerListRequest)
|
||||
{
|
||||
current_response = nullptr;
|
||||
internet_response = nullptr;
|
||||
}
|
||||
if (favorites_request == hServerListRequest)
|
||||
{
|
||||
favorites_response = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
gameserveritem_t* matchmaking_servers::GetServerDetails(void* hRequest, int iServer)
|
||||
{
|
||||
if (internet_request != hRequest)
|
||||
if (internet_request != hRequest && favorites_request != hRequest)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& servers_list = hRequest == favorites_request ? favorites_servers : internet_servers;
|
||||
|
||||
static thread_local gameserveritem_t server_item{};
|
||||
return queried_servers.access<gameserveritem_t*>([iServer](const servers& s) -> gameserveritem_t* {
|
||||
return servers_list.access<gameserveritem_t*>([iServer](const servers& s) -> gameserveritem_t* {
|
||||
if (iServer < 0 || static_cast<size_t>(iServer) >= s.size())
|
||||
{
|
||||
return nullptr;
|
||||
@ -270,12 +331,13 @@ namespace steam
|
||||
|
||||
int matchmaking_servers::GetServerCount(void* hRequest)
|
||||
{
|
||||
if (internet_request != hRequest)
|
||||
if (internet_request != hRequest && favorites_request != hRequest)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return queried_servers.access<int>([](const servers& s)
|
||||
auto& servers_list = hRequest == favorites_request ? favorites_servers : internet_servers;
|
||||
return servers_list.access<int>([](const servers& s)
|
||||
{
|
||||
return static_cast<int>(s.size());
|
||||
});
|
||||
@ -283,13 +345,14 @@ namespace steam
|
||||
|
||||
void matchmaking_servers::RefreshServer(void* hRequest, const int iServer)
|
||||
{
|
||||
if (internet_request != hRequest)
|
||||
if (internet_request != hRequest && favorites_request != hRequest)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<game::netadr_t> address{};
|
||||
queried_servers.access([&](const servers& s)
|
||||
auto& servers_list = hRequest == favorites_request ? favorites_servers : internet_servers;
|
||||
servers_list.access([&](const servers& s)
|
||||
{
|
||||
if (iServer < 0 || static_cast<size_t>(iServer) >= s.size())
|
||||
{
|
||||
@ -301,7 +364,8 @@ namespace steam
|
||||
|
||||
if (address)
|
||||
{
|
||||
ping_server(*address);
|
||||
auto callback = hRequest == favorites_request ? handle_favorites_server_response : handle_internet_server_response;
|
||||
ping_server(*address, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user