From 3d836d694dbc6be28666d3d131120b7fa5249460 Mon Sep 17 00:00:00 2001 From: otavepto Date: Thu, 29 Feb 2024 01:16:01 +0200 Subject: [PATCH] added 2 new options which enable the new behavior for Steam Matchmaking Servers --- CHANGELOG.md | 6 + dll/dll/settings.h | 4 + dll/dll/steam_matchmaking_servers.h | 2 + dll/dll/steam_user.h | 7 +- dll/settings_parser.cpp | 11 + dll/steam_matchmaking_servers.cpp | 201 +++++++++++++++--- post_build/README.release.md | 11 + ...erver_details_via_source_query.EXAMPLE.txt | 1 + ...making_server_list_actual_type.EXAMPLE.txt | 2 + 9 files changed, 218 insertions(+), 27 deletions(-) create mode 100644 post_build/steam_settings.EXAMPLE/matchmaking_server_details_via_source_query.EXAMPLE.txt create mode 100644 post_build/steam_settings.EXAMPLE/matchmaking_server_list_actual_type.EXAMPLE.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index f9a68e0f..043b9fc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +* revert the changes to `steam_matchmaking_servers` and only enable them via the 2 new config files: + - `matchmaking_server_list_actual_type.txt`: this makes steam matchmaking use the actual type of the requestd server list, otherwise it's always LAN + - `matchmaking_server_details_via_source_query.txt`: this makes steam matchmaking use actual source server query to grab the server info + + thanks a lot to **[LuKeSt0rm]** for the help and testing + * added a new flag `-reldir` for the `generate_emu_config` script which allows it to generate temp files/folders, and expect input files, relative to the current workig directory, suggested by **[ImportTaste]** # 2024/2/24 diff --git a/dll/dll/settings.h b/dll/dll/settings.h index 8de20d66..40714e7b 100644 --- a/dll/dll/settings.h +++ b/dll/dll/settings.h @@ -269,6 +269,10 @@ public: //gameserver source query bool disable_source_query = false; + //matchmaking server list & server details + bool matchmaking_server_list_always_lan_type = true; + bool matchmaking_server_details_via_source_query = false; + //overlay bool disable_overlay = false; bool disable_overlay_achievement_notification = false; diff --git a/dll/dll/steam_matchmaking_servers.h b/dll/dll/steam_matchmaking_servers.h index 20da051e..91248893 100644 --- a/dll/dll/steam_matchmaking_servers.h +++ b/dll/dll/steam_matchmaking_servers.h @@ -234,4 +234,6 @@ public: void RunCallbacks(); void Callback(Common_Message *msg); void server_details(Gameserver *g, gameserveritem_t *server); + void server_details_players(Gameserver *g, Steam_Matchmaking_Servers_Direct_IP_Request *r); + void server_details_rules(Gameserver *g, Steam_Matchmaking_Servers_Direct_IP_Request *r); }; diff --git a/dll/dll/steam_user.h b/dll/dll/steam_user.h index 4d77ab5f..4e4bc1ad 100644 --- a/dll/dll/steam_user.h +++ b/dll/dll/steam_user.h @@ -385,7 +385,12 @@ void AdvertiseGame( CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPort server->set_port(usPortServer); server->set_query_port(usPortServer); server->set_appid(settings->get_local_game_id().ToUint64()); - server->set_type(eLANServer); + + if (settings->matchmaking_server_list_always_lan_type) + server->set_type(eLANServer); + else + server->set_type(eFriendsServer); + Common_Message msg; msg.set_allocated_gameserver(server); msg.set_source_id(settings->get_local_steam_id().ConvertToUint64()); diff --git a/dll/settings_parser.cpp b/dll/settings_parser.cpp index 0fea9167..cc0a6cf4 100644 --- a/dll/settings_parser.cpp +++ b/dll/settings_parser.cpp @@ -1212,6 +1212,8 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s bool use_gc_token = false; bool enable_new_app_ticket = false; int build_id = 10; + bool matchmaking_server_list_always_lan_type = true; + bool matchmaking_server_details_via_source_query = false; bool warn_forced_setting = false; @@ -1270,6 +1272,10 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s enable_new_app_ticket = true; } else if (p == "gc_token.txt") { use_gc_token = true; + } else if (p == "matchmaking_server_list_actual_type.txt") { + matchmaking_server_list_always_lan_type = false; + } else if (p == "matchmaking_server_details_via_source_query.txt") { + matchmaking_server_details_via_source_query = true; } } } @@ -1331,6 +1337,11 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s settings_client->use_gc_token = use_gc_token; settings_server->use_gc_token = use_gc_token; + settings_client->matchmaking_server_list_always_lan_type = matchmaking_server_list_always_lan_type; + settings_server->matchmaking_server_list_always_lan_type = matchmaking_server_list_always_lan_type; + settings_client->matchmaking_server_details_via_source_query = matchmaking_server_details_via_source_query; + settings_server->matchmaking_server_details_via_source_query = matchmaking_server_details_via_source_query; + if (local_save) { settings_client->local_save = save_path; settings_server->local_save = save_path; diff --git a/dll/steam_matchmaking_servers.cpp b/dll/steam_matchmaking_servers.cpp index 50554a55..cd386d76 100644 --- a/dll/steam_matchmaking_servers.cpp +++ b/dll/steam_matchmaking_servers.cpp @@ -53,7 +53,7 @@ HServerListRequest Steam_Matchmaking_Servers::RequestServerList(AppId_t iApp, IS requests.push_back(request); PRINT_DEBUG("Steam_Matchmaking_Servers::request id: %p\n", id); - if (type == eLANServer) return id; + if (type == eLANServer || settings->matchmaking_server_list_always_lan_type) return id; if (type == eFriendsServer) { for (auto &g : gameservers_friends) { @@ -145,7 +145,7 @@ HServerListRequest Steam_Matchmaking_Servers::RequestServerList(AppId_t iApp, IS HServerListRequest Steam_Matchmaking_Servers::RequestInternetServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) { PRINT_DEBUG("Steam_Matchmaking_Servers::RequestInternetServerList\n"); - return RequestServerList(iApp, pRequestServersResponse, eLANServer); + return RequestServerList(iApp, pRequestServersResponse, eInternetServer); } HServerListRequest Steam_Matchmaking_Servers::RequestLANServerList( AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse ) @@ -157,27 +157,29 @@ HServerListRequest Steam_Matchmaking_Servers::RequestLANServerList( AppId_t iApp HServerListRequest Steam_Matchmaking_Servers::RequestFriendsServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) { PRINT_DEBUG("Steam_Matchmaking_Servers::RequestFriendsServerList\n"); - return RequestServerList(iApp, pRequestServersResponse, eLANServer); + return RequestServerList(iApp, pRequestServersResponse, eFriendsServer); } HServerListRequest Steam_Matchmaking_Servers::RequestFavoritesServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) { PRINT_DEBUG("Steam_Matchmaking_Servers::RequestFavoritesServerList\n"); - return RequestServerList(iApp, pRequestServersResponse, eLANServer); + return RequestServerList(iApp, pRequestServersResponse, eFavoritesServer); } HServerListRequest Steam_Matchmaking_Servers::RequestHistoryServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) { PRINT_DEBUG("Steam_Matchmaking_Servers::RequestHistoryServerList\n"); - return RequestServerList(iApp, pRequestServersResponse, eLANServer); + return RequestServerList(iApp, pRequestServersResponse, eHistoryServer); } HServerListRequest Steam_Matchmaking_Servers::RequestSpectatorServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) { PRINT_DEBUG("Steam_Matchmaking_Servers::RequestSpectatorServerList\n"); - return RequestServerList(iApp, pRequestServersResponse, eLANServer); + return RequestServerList(iApp, pRequestServersResponse, eSpectatorServer); } +// old server list request + void Steam_Matchmaking_Servers::RequestOldServerList(AppId_t iApp, ISteamMatchmakingServerListResponse001 *pRequestServersResponse, EMatchMakingType type) { PRINT_DEBUG("Steam_Matchmaking_Servers::RequestOldServerList %u\n", iApp); @@ -338,13 +340,78 @@ void Steam_Matchmaking_Servers::ReleaseRequest( HServerListRequest hServerListRe void Steam_Matchmaking_Servers::server_details(Gameserver *g, gameserveritem_t *server) { + int latency = 10; + + if (settings->matchmaking_server_details_via_source_query && !(g->ip() < 0) && !(g->query_port() < 0)) { + unsigned char ip[4]{}; + char newip[24]; + ip[0] = g->ip() & 0xFF; + ip[1] = (g->ip() >> 8) & 0xFF; + ip[2] = (g->ip() >> 16) & 0xFF; + ip[3] = (g->ip() >> 24) & 0xFF; + snprintf(newip, sizeof(newip), "%d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0]); + + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details connecting to ssq server on %s:%u\n", newip, g->query_port()); + SSQ_SERVER *ssq = ssq_server_new(newip, g->query_port()); + if (ssq != NULL && ssq_server_eok(ssq)) { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details ssq server connection ok\n"); + ssq_server_timeout(ssq, (SSQ_TIMEOUT_SELECTOR)(SSQ_TIMEOUT_RECV | SSQ_TIMEOUT_SEND), 1200); + + std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now(); + A2S_INFO *ssq_a2s_info = ssq_info(ssq); + std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now(); + latency = (int)std::chrono::duration_cast(t2 - t1).count(); + if (latency < 10) latency = 10; // TODO I don't know if low latency is problematic or not + + if (ssq_server_eok(ssq)) { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details ssq server info ok\n"); + if (ssq_info_has_steamid(ssq_a2s_info)) g->set_id(ssq_a2s_info->steamid); + g->set_game_description(ssq_a2s_info->game); + g->set_mod_dir(ssq_a2s_info->folder); + if (ssq_a2s_info->server_type == A2S_SERVER_TYPE_DEDICATED) g->set_dedicated_server(true); + else if (ssq_a2s_info->server_type == A2S_SERVER_TYPE_STV_RELAY) g->set_dedicated_server(true); + else g->set_dedicated_server(false); + g->set_max_player_count(ssq_a2s_info->max_players); + g->set_bot_player_count(ssq_a2s_info->bots); + g->set_server_name(ssq_a2s_info->name); + g->set_map_name(ssq_a2s_info->map); + if (ssq_a2s_info->visibility) g->set_password_protected(true); + else g->set_password_protected(false); + if (ssq_info_has_stv(ssq_a2s_info)) { + g->set_spectator_port(ssq_a2s_info->stv_port); + g->set_spectator_server_name(ssq_a2s_info->stv_name); + } + //g->set_tags(ssq_a2s_info->keywords); + //g->set_gamedata(); + //g->set_region(); + g->set_product(ssq_a2s_info->game); + if (ssq_a2s_info->vac) g->set_secure(true); + else g->set_secure(false); + g->set_num_players(ssq_a2s_info->players); + g->set_version(std::stoull(ssq_a2s_info->version, NULL, 0)); + if (ssq_info_has_port(ssq_a2s_info)) g->set_port(ssq_a2s_info->port); + if (ssq_info_has_gameid(ssq_a2s_info)) g->set_appid(ssq_a2s_info->gameid); + else g->set_appid(ssq_a2s_info->id); + g->set_offline(false); + } else { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details ssq server info failed: %s\n", ssq_server_emsg(ssq)); + } + + if (ssq_a2s_info != NULL) ssq_info_free(ssq_a2s_info); + } else { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details ssq server connection failed: %s\n", (ssq ? ssq_server_emsg(ssq) : "NULL instance")); + } + + if (ssq != NULL) ssq_server_free(ssq); + } + uint16 query_port = g->query_port(); if (g->query_port() == 0xFFFF) { query_port = g->port(); } server->m_NetAdr.Init(g->ip(), query_port, g->port()); - server->m_nPing = 10; //TODO + server->m_nPing = latency; server->m_bHadSuccessfulResponse = true; server->m_bDoNotRefresh = false; @@ -374,6 +441,104 @@ void Steam_Matchmaking_Servers::server_details(Gameserver *g, gameserveritem_t * PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details " "%" PRIu64 "\n", g->id()); } +void Steam_Matchmaking_Servers::server_details_players(Gameserver *g, Steam_Matchmaking_Servers_Direct_IP_Request *r) +{ + if (settings->matchmaking_server_details_via_source_query && !(g->ip() < 0) && !(g->query_port() < 0)) { + unsigned char ip[4]{}; + char newip[24]; + ip[0] = g->ip() & 0xFF; + ip[1] = (g->ip() >> 8) & 0xFF; + ip[2] = (g->ip() >> 16) & 0xFF; + ip[3] = (g->ip() >> 24) & 0xFF; + snprintf(newip, sizeof(newip), "%d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0]); + + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_players connecting to ssq server on %s:%u\n", newip, g->query_port()); + SSQ_SERVER *ssq = ssq_server_new(newip, g->query_port()); + if (ssq != NULL && ssq_server_eok(ssq)) { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_players ssq server connection ok\n"); + ssq_server_timeout(ssq, (SSQ_TIMEOUT_SELECTOR)(SSQ_TIMEOUT_RECV | SSQ_TIMEOUT_SEND), 1200); + + uint8_t ssq_a2s_player_count = 0; + A2S_PLAYER *ssq_a2s_player = ssq_player(ssq, &ssq_a2s_player_count); + + if (ssq_server_eok(ssq)) { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_players ssq server players ok\n"); + for (int i = 0; i < ssq_a2s_player_count; i++) { + r->players_response->AddPlayerToList(ssq_a2s_player[i].name, ssq_a2s_player[i].score, ssq_a2s_player[i].duration); + } + } else { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_players ssq server players failed: %s\n", ssq_server_emsg(ssq)); + } + + if (ssq_a2s_player != NULL) ssq_player_free(ssq_a2s_player, ssq_a2s_player_count); + } else { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_players ssq server connection failed: %s\n", (ssq ? ssq_server_emsg(ssq) : "NULL instance")); + } + + if (ssq != NULL) ssq_server_free(ssq); + } else if (!settings->matchmaking_server_details_via_source_query) { // original behavior + uint32_t number_players = g->num_players(); + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_players players: %u\n", number_players); + const auto &players = get_steam_client()->steam_gameserver->get_players(); + auto &player = players->cbegin(); + for (int i = 0; i < number_players && player != players->end(); ++i, ++player) { + float playtime = static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - player->second.join_time).count()); + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_players PLAYER [%u] '%s' %u %f\n", i, player->second.name.c_str(), player->second.score, playtime); + r->players_response->AddPlayerToList(player->second.name.c_str(), player->second.score, playtime); + } + } + + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_players " "%" PRIu64 "\n", g->id()); +} + +void Steam_Matchmaking_Servers::server_details_rules(Gameserver *g, Steam_Matchmaking_Servers_Direct_IP_Request *r) +{ + if (settings->matchmaking_server_details_via_source_query && !(g->ip() < 0) && !(g->query_port() < 0)) { + unsigned char ip[4]{}; + char newip[24]; + ip[0] = g->ip() & 0xFF; + ip[1] = (g->ip() >> 8) & 0xFF; + ip[2] = (g->ip() >> 16) & 0xFF; + ip[3] = (g->ip() >> 24) & 0xFF; + snprintf(newip, sizeof(newip), "%d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0]); + + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_rules connecting to ssq server on %s:%u\n", newip, g->query_port()); + SSQ_SERVER *ssq = ssq_server_new(newip, g->query_port()); + if (ssq != NULL && ssq_server_eok(ssq)) { + ssq_server_timeout(ssq, (SSQ_TIMEOUT_SELECTOR)(SSQ_TIMEOUT_RECV | SSQ_TIMEOUT_SEND), 1200); + + uint16_t ssq_a2s_rules_count = 0; + A2S_RULES *ssq_a2s_rules = ssq_rules(ssq, &ssq_a2s_rules_count); + + if (ssq_server_eok(ssq)) { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_rules ssq server rules ok\n"); + for (int i = 0; i < ssq_a2s_rules_count; i++) { + r->rules_response->RulesResponded(ssq_a2s_rules[i].name, ssq_a2s_rules[i].value); + } + } else { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_rules ssq server rules failed: %s\n", ssq_server_emsg(ssq)); + } + + if (ssq_a2s_rules != NULL) ssq_rules_free(ssq_a2s_rules, ssq_a2s_rules_count); + } else { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_rules ssq server connection failed: %s\n", (ssq ? ssq_server_emsg(ssq) : "NULL instance")); + } + + if (ssq != NULL) ssq_server_free(ssq); + } else if (!settings->matchmaking_server_details_via_source_query) { // original behavior + int number_rules = (int)g->values().size(); + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_rules rules: %i\n", number_rules); + auto rule = g->values().begin(); + for (int i = 0; i < number_rules; ++i) { + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_rules RULE '%s' '%s'\n", rule->first.c_str(), rule->second.c_str()); + r->rules_response->RulesResponded(rule->first.c_str(), rule->second.c_str()); + ++rule; + } + } + + PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details_rules " "%" PRIu64 "\n", g->id()); +} + // Get details on a given server in the list, you can get the valid range of index // values by calling GetServerCount(). You will also receive index values in // ISteamMatchmakingServerListResponse::ServerResponded() callbacks @@ -637,35 +802,19 @@ void Steam_Matchmaking_Servers::RunCallbacks() if (query_port == r.port && g.server.ip() == r.ip) { if (r.rules_response) { - int number_rules = (int)g.server.values().size(); - PRINT_DEBUG("Steam_Matchmaking_Servers rules: %i\n", number_rules); - auto rule = g.server.values().begin(); - for (int i = 0; i < number_rules; ++i) { - PRINT_DEBUG("Steam_Matchmaking_Servers RULE '%s' '%s'\n", rule->first.c_str(), rule->second.c_str()); - r.rules_response->RulesResponded(rule->first.c_str(), rule->second.c_str()); - ++rule; - } - + server_details_rules(&(g.server), &r); r.rules_response->RulesRefreshComplete(); r.rules_response = NULL; } if (r.players_response) { - uint32_t number_players = g.server.num_players(); - PRINT_DEBUG("Steam_Matchmaking_Servers players: %u\n", number_players); - const auto &players = get_steam_client()->steam_gameserver->get_players(); - auto &player = players->cbegin(); - for (int i = 0; i < number_players && player != players->end(); ++i, ++player) { - float playtime = static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - player->second.join_time).count()); - PRINT_DEBUG("Steam_Matchmaking_Servers PLAYER [%u] '%s' %u %f\n", i, player->second.name.c_str(), player->second.score, playtime); - r.players_response->AddPlayerToList(player->second.name.c_str(), player->second.score, playtime); - } + server_details_players(&(g.server), &r); r.players_response->PlayersRefreshComplete(); r.players_response = NULL; } if (r.ping_response) { - gameserveritem_t server; + gameserveritem_t server{}; server_details(&(g.server), &server); r.ping_response->ServerResponded(server); r.ping_response = NULL; diff --git a/post_build/README.release.md b/post_build/README.release.md index 1ff5b46e..f90c72d3 100644 --- a/post_build/README.release.md +++ b/post_build/README.release.md @@ -500,6 +500,17 @@ Check the exaxmple file in the `steam_settings` folder --- +## Enable non-LAN behavior in `steam_matchmaking_servers`: + +By default, match making servers (which handles browsing for matches) will always return LAN servers list whenever the game inquires about the available servers with a specific type (Internet, Friends, LAN, etc...). +You can make the emu return the proper/actual servers list for the given type by creating a file called `matchmaking_server_list_actual_type.txt` inside the `steam_settings` folder. +**This is currently broken**. + +Also, match making servers will return the info of the server from the incoming local packets, you can make the emu retrieve the actual server info by performing a source server query, this is enabled by creating a file called `matchmaking_server_details_via_source_query.txt` inside the `steam_settings` folder. +**This is currently broken**. + +--- + ## More configurations: Due to the various changes and additions, it became tedious to document everything, so it is recommended to check each example file in the `steam_settings` folder. diff --git a/post_build/steam_settings.EXAMPLE/matchmaking_server_details_via_source_query.EXAMPLE.txt b/post_build/steam_settings.EXAMPLE/matchmaking_server_details_via_source_query.EXAMPLE.txt new file mode 100644 index 00000000..38ce4974 --- /dev/null +++ b/post_build/steam_settings.EXAMPLE/matchmaking_server_details_via_source_query.EXAMPLE.txt @@ -0,0 +1 @@ +Rename this file to matchmaking_server_details_via_source_query.txt to grab the server details for match making using an actual server query. \ No newline at end of file diff --git a/post_build/steam_settings.EXAMPLE/matchmaking_server_list_actual_type.EXAMPLE.txt b/post_build/steam_settings.EXAMPLE/matchmaking_server_list_actual_type.EXAMPLE.txt new file mode 100644 index 00000000..429dfe17 --- /dev/null +++ b/post_build/steam_settings.EXAMPLE/matchmaking_server_list_actual_type.EXAMPLE.txt @@ -0,0 +1,2 @@ +Rename this file to matchmaking_server_list_actual_type.txt to use the proper type of the server list (internet, friends, etc...) when requested by the game. +Otherwise, the emu will always return LAN server list. \ No newline at end of file