Restore networking, source query, avatar, friends changes/additions from: 75e6d7c8ab, 4399c0b12b

This commit is contained in:
otavepto 2024-02-27 21:17:53 +02:00 committed by otavepto
parent 001e74aafa
commit c38601fdcd
21 changed files with 1063 additions and 65 deletions

View File

@ -91,8 +91,9 @@ struct Connection {
class Networking {
bool enabled = false;
bool query_alive;
std::chrono::high_resolution_clock::time_point last_run;
sock_t udp_socket, tcp_socket;
sock_t query_socket, udp_socket, tcp_socket;
uint16 udp_port, tcp_port;
uint32 own_ip;
std::vector<struct Connection> connections;
@ -139,6 +140,9 @@ public:
uint32 getIP(CSteamID id);
uint32 getOwnIP();
void startQuery(IP_PORT ip_port);
void shutDownQuery();
bool isQueryAlive();
};
#endif

View File

@ -248,6 +248,7 @@ public:
//images
std::map<int, struct Image_Data> images;
int add_image(std::string data, uint32 width, uint32 height);
bool disable_account_avatar = false;
//installed app ids, Steam_Apps::BIsAppInstalled()
std::set<AppId_t> installed_app_ids;
@ -265,6 +266,9 @@ public:
//networking
bool disable_networking = false;
//gameserver source query
bool disable_source_query = false;
//overlay
bool disable_overlay = false;
bool disable_overlay_achievement_notification = false;

28
dll/dll/source_query.h Normal file
View File

@ -0,0 +1,28 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Source_Query
{
Source_Query () = delete;
~Source_Query() = delete;
public:
static std::vector<uint8_t> handle_source_query(const void* buffer, size_t len, Gameserver const& gs);
};

View File

@ -102,12 +102,95 @@ struct Avatar_Numbers add_friend_avatars(CSteamID id)
return avatar_ids->second;
}
//TODO: get real image data from self/other peers
struct Avatar_Numbers avatar_numbers;
std::string small_avatar(32 * 32 * 4, 0);
std::string medium_avatar(64 * 64 * 4, 0);
std::string large_avatar(184 * 184 * 4, 0);
if (!(settings->disable_account_avatar) && (id == settings->get_local_steam_id())) {
std::string file_path;
unsigned long long file_size;
for (int i = 0; i < 3; i++) {
std::string file_name;
if (i == 0) file_name = "account_avatar.png";
if (i == 1) file_name = "account_avatar.jpg";
if (i == 2) file_name = "account_avatar.jpeg";
file_path = Local_Storage::get_game_settings_path() + file_name;
file_size = file_size_(file_path);
if (file_size) break;
}
if (!file_size) {
for (int i = 0; i < 3; i++) {
std::string file_name;
if (i == 0) file_name = "account_avatar.png";
if (i == 1) file_name = "account_avatar.jpg";
if (i == 2) file_name = "account_avatar.jpeg";
if (settings->local_save.length() > 0) {
file_path = settings->local_save + "/settings/" + file_name;
} else {
file_path = Local_Storage::get_user_appdata_path() + "/settings/" + file_name;
}
file_size = file_size_(file_path);
if (file_size) break;
}
}
// no else statement here for default or else this breaks default images for friends
if (file_size) {
small_avatar = Local_Storage::load_image_resized(file_path, "", 32);
medium_avatar = Local_Storage::load_image_resized(file_path, "", 64);
large_avatar = Local_Storage::load_image_resized(file_path, "", 184);
}
} else if (!(settings->disable_account_avatar)) {
Friend *f = find_friend(id);
if (f && (large_avatar.compare(f->avatar()) != 0)) {
large_avatar = f->avatar();
medium_avatar = Local_Storage::load_image_resized("", f->avatar(), 64);
small_avatar = Local_Storage::load_image_resized("", f->avatar(), 32);
} else {
std::string file_path;
unsigned long long file_size;
for (int i = 0; i < 3; i++) {
std::string file_name;
if (i == 0) file_name = "account_avatar_default.png";
if (i == 1) file_name = "account_avatar_default.jpg";
if (i == 2) file_name = "account_avatar_default.jpeg";
file_path = Local_Storage::get_game_settings_path() + file_name;
file_size = file_size_(file_path);
if (file_size) break;
}
if (!file_size) {
for (int i = 0; i < 3; i++) {
std::string file_name;
if (i == 0) file_name = "account_avatar_default.png";
if (i == 1) file_name = "account_avatar_default.jpg";
if (i == 2) file_name = "account_avatar_default.jpeg";
if (settings->local_save.length() > 0) {
file_path = settings->local_save + "/settings/" + file_name;
} else {
file_path = Local_Storage::get_user_appdata_path() + "/settings/" + file_name;
}
file_size = file_size_(file_path);
if (file_size) break;
}
}
if (file_size) {
small_avatar = Local_Storage::load_image_resized(file_path, "", 32);
medium_avatar = Local_Storage::load_image_resized(file_path, "", 64);
large_avatar = Local_Storage::load_image_resized(file_path, "", 184);
}
}
}
avatar_numbers.smallest = settings->add_image(small_avatar, 32, 32);
avatar_numbers.medium = settings->add_image(medium_avatar, 64, 64);
avatar_numbers.large = settings->add_image(large_avatar, 184, 184);
@ -1114,6 +1197,9 @@ void Callback(Common_Message *msg)
f->set_name(settings->get_local_name());
f->set_appid(settings->get_local_game_id().AppID());
f->set_lobby_id(settings->get_lobby().ConvertToUint64());
int avatar_number = GetLargeFriendAvatar(settings->get_local_steam_id());
if (settings->images[avatar_number].data.length() > 0) f->set_avatar(settings->images[avatar_number].data);
else f->set_avatar("");
msg_.set_allocated_friend_(f);
network->sendTo(&msg_, true);
}
@ -1127,6 +1213,7 @@ void Callback(Common_Message *msg)
friends.push_back(msg->friend_());
overlay->FriendConnect(msg->friend_());
persona_change((uint64)msg->friend_().id(), k_EPersonaChangeName);
GetLargeFriendAvatar((uint64)msg->friend_().id());
}
} else {
std::map<std::string, std::string> map1(f->rich_presence().begin(), f->rich_presence().end());

View File

@ -23,12 +23,18 @@
//-----------------------------------------------------------------------------
struct Gameserver_Outgoing_Packet {
std::string data;
std::vector<uint8_t> data;
uint32 ip;
uint16 port;
};
struct Gameserver_Player_Info_t {
std::chrono::steady_clock::time_point join_time;
std::string name;
uint32 score;
};
class Steam_GameServer :
public ISteamGameServer004,
public ISteamGameServer005,
@ -51,6 +57,7 @@ public ISteamGameServer
bool logged_in = false;
bool call_servers_disconnected = false;
Gameserver server_data;
std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>> players;
uint32 flags;
bool policy_response_called;
@ -63,6 +70,9 @@ public:
Steam_GameServer(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks);
~Steam_GameServer();
std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>>* get_players();
//
// Basic server data. These properties, if set, must be set before before calling LogOn. They
// may not be changed after logged in.

View File

@ -343,6 +343,16 @@ static Lobby_Member *get_lobby_member(Lobby *lobby, CSteamID user_id)
int GetFavoriteGameCount()
{
PRINT_DEBUG("Steam_MatchMaking::GetFavoriteGameCount\n");
std::string file_path = Local_Storage::get_user_appdata_path() + "/7/" + Local_Storage::remote_storage_folder + "/serverbrowser_favorites.txt";
unsigned long long file_size = file_size_(file_path);
if (file_size) {
std::string list;
list.resize(file_size);
Local_Storage::get_file_data(file_path, (char *)list.data(), file_size, 0);
auto list_lines = std::count(list.begin(), list.end(), '\n');
list_lines += (!list.empty() && list.back() != '\n');
return list_lines;
}
return 0;
}
@ -363,8 +373,72 @@ bool GetFavoriteGame( int iGame, AppId_t *pnAppID, uint32 *pnIP, uint16 *pnConnP
int AddFavoriteGame( AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQueryPort, uint32 unFlags, uint32 rTime32LastPlayedOnServer )
{
PRINT_DEBUG("Steam_MatchMaking::AddFavoriteGame %u %u %hu %hu %u %u\n", nAppID, nIP, nConnPort, nQueryPort, unFlags, rTime32LastPlayedOnServer);
//TODO: what should this return?
return 0;
std::string file_path;
unsigned long long file_size;
if (unFlags == 1) {
file_path = Local_Storage::get_user_appdata_path() + "/7/" + Local_Storage::remote_storage_folder + "/serverbrowser_favorites.txt";
file_size = file_size_(file_path);
}
else if (unFlags == 2) {
file_path = Local_Storage::get_user_appdata_path() + "/7/" + Local_Storage::remote_storage_folder + "/serverbrowser_history.txt";
file_size = file_size_(file_path);
}
else {
return 0;
}
unsigned char ip[4];
ip[0] = nIP & 0xFF;
ip[1] = (nIP >> 8) & 0xFF;
ip[2] = (nIP >> 16) & 0xFF;
ip[3] = (nIP >> 24) & 0xFF;
char newip[24];
snprintf(newip, sizeof(newip), "%d.%d.%d.%d:%d\n", ip[3], ip[2], ip[1], ip[0], nConnPort);
std::string newip_string;
newip_string.append(newip);
if (file_size) {
std::string list;
list.resize(file_size);
Local_Storage::get_file_data(file_path, (char *)list.data(), file_size, 0);
auto list_lines = std::count(list.begin(), list.end(), '\n');
list_lines += (!list.empty() && list.back() != '\n');
std::size_t find_ip = list.find(newip_string);
if (find_ip == std::string::npos) {
list.append(newip_string);
list.append("\n");
std::size_t file_directory = file_path.rfind("/");
std::string directory_path;
std::string file_name;
if (file_directory != std::string::npos) {
directory_path = file_path.substr(0, file_directory);
file_name = file_path.substr(file_directory);
}
Local_Storage::store_file_data(directory_path, file_name, (char *)list.data(), list.size());
return ++list_lines;
}
return list_lines;
}
else {
newip_string.append("\n");
std::size_t file_directory = file_path.rfind("/");
std::string directory_path;
std::string file_name;
if (file_directory != std::string::npos) {
directory_path = file_path.substr(0, file_directory);
file_name = file_path.substr(file_directory);
}
Local_Storage::store_file_data(directory_path, file_name, (char *)newip_string.data(), newip_string.size());
return 1;
}
}
@ -372,6 +446,54 @@ int AddFavoriteGame( AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQuery
bool RemoveFavoriteGame( AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQueryPort, uint32 unFlags )
{
PRINT_DEBUG("Steam_MatchMaking::RemoveFavoriteGame\n");
std::string file_path;
unsigned long long file_size;
if (unFlags == 1) {
file_path = Local_Storage::get_user_appdata_path() + "/7/" + Local_Storage::remote_storage_folder + "/serverbrowser_favorites.txt";
file_size = file_size_(file_path);
}
else if (unFlags == 2) {
file_path = Local_Storage::get_user_appdata_path() + "/7/" + Local_Storage::remote_storage_folder + "/serverbrowser_history.txt";
file_size = file_size_(file_path);
}
else {
return false;
}
if (file_size) {
std::string list;
list.resize(file_size);
Local_Storage::get_file_data(file_path, (char *)list.data(), file_size, 0);
unsigned char ip[4];
ip[0] = nIP & 0xFF;
ip[1] = (nIP >> 8) & 0xFF;
ip[2] = (nIP >> 16) & 0xFF;
ip[3] = (nIP >> 24) & 0xFF;
char newip[24];
snprintf((char *)newip, sizeof(newip), "%d.%d.%d.%d:%d\n", ip[3], ip[2], ip[1], ip[0], nConnPort);
std::string newip_string;
newip_string.append(newip);
std::size_t list_ip = list.find(newip_string);
if (list_ip != std::string::npos) {
list.erase(list_ip, newip_string.length());
std::size_t file_directory = file_path.rfind("/");
std::string directory_path;
std::string file_name;
if (file_directory != std::string::npos) {
directory_path = file_path.substr(0, file_directory);
file_name = file_path.substr(file_directory);
}
Local_Storage::store_file_data(directory_path, file_name, (char *)list.data(), list.size());
return true;
}
}
return false;
}

View File

@ -16,6 +16,7 @@
<http://www.gnu.org/licenses/>. */
#include "base.h"
#include <ssq/a2s.h>
#define SERVER_TIMEOUT 10.0
#define DIRECT_IP_DELAY 0.05
@ -31,9 +32,17 @@ struct Steam_Matchmaking_Servers_Direct_IP_Request {
ISteamMatchmakingPingResponse *ping_response = NULL;
};
struct Steam_Matchmaking_Servers_Gameserver_Friends {
uint64 source_id;
uint32 ip;
uint16 port;
std::chrono::high_resolution_clock::time_point last_recv;
};
struct Steam_Matchmaking_Servers_Gameserver {
Gameserver server;
std::chrono::high_resolution_clock::time_point last_recv;
EMatchMakingType type;
};
struct Steam_Matchmaking_Request {
@ -43,6 +52,7 @@ struct Steam_Matchmaking_Request {
ISteamMatchmakingServerListResponse001 *old_callbacks;
bool completed, cancelled, released;
std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers_filtered;
EMatchMakingType type;
};
class Steam_Matchmaking_Servers : public ISteamMatchmakingServers,
@ -52,8 +62,10 @@ public ISteamMatchmakingServers001
class Networking *network;
std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers;
std::vector <struct Steam_Matchmaking_Servers_Gameserver_Friends> gameservers_friends;
std::vector <struct Steam_Matchmaking_Request> requests;
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests;
HServerListRequest RequestServerList(AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse, EMatchMakingType type);
void RequestOldServerList(AppId_t iApp, ISteamMatchmakingServerListResponse001 *pRequestServersResponse, EMatchMakingType type);
public:
Steam_Matchmaking_Servers(class Settings *settings, class Networking *network);
@ -222,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);
};

View File

@ -385,6 +385,7 @@ 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(eFriendsServer);
Common_Message msg;
msg.set_allocated_gameserver(server);
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());

View File

@ -169,6 +169,7 @@ message Gameserver {
uint32 appid = 35;
bool offline = 48;
uint32 type = 49;
}
message Friend {
@ -177,6 +178,7 @@ message Friend {
map<string, bytes> rich_presence = 3;
uint32 appid = 4;
uint64 lobby_id = 5;
bytes avatar = 6;
}
message Auth_Ticket {

View File

@ -942,6 +942,22 @@ void Networking::Run()
char data[MAX_UDP_SIZE];
int len;
if (query_alive && is_socket_valid(query_socket)) {
PRINT_DEBUG("RECV QUERY\n");
Steam_Client* client = get_steam_client();
sockaddr_in addr;
addr.sin_family = AF_INET;
while ((len = receive_packet(query_socket, &ip_port, data, sizeof(data))) >= 0) {
client->steam_gameserver->HandleIncomingPacket(data, len, htonl(ip_port.ip), htons(ip_port.port));
len = client->steam_gameserver->GetNextOutgoingPacket(data, sizeof(data), &ip_port.ip, &ip_port.port);
addr.sin_addr.s_addr = htonl(ip_port.ip);
addr.sin_port = htons(ip_port.port);
sendto(query_socket, data, len, 0, (sockaddr*)&addr, sizeof(addr));
}
}
PRINT_DEBUG("RECV UDP\n");
while((len = receive_packet(udp_socket, &ip_port, data, sizeof(data))) >= 0) {
PRINT_DEBUG("recv %i %hhu.%hhu.%hhu.%hhu:%hu\n", len, ((unsigned char *)&ip_port.ip)[0], ((unsigned char *)&ip_port.ip)[1], ((unsigned char *)&ip_port.ip)[2], ((unsigned char *)&ip_port.ip)[3], htons(ip_port.port));
@ -1300,3 +1316,74 @@ uint32 Networking::getOwnIP()
{
return own_ip;
}
void Networking::startQuery(IP_PORT ip_port)
{
if (ip_port.port <= 1024)
return;
if (!query_alive)
{
if (ip_port.port == MASTERSERVERUPDATERPORT_USEGAMESOCKETSHARE)
{
PRINT_DEBUG("Source Query in Shared Mode\n");
return;
}
int retry = 0;
constexpr auto max_retry = 10;
while (retry++ < max_retry)
{
query_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (is_socket_valid(query_socket))
break;
if (retry > max_retry)
{
reset_last_error();
return;
}
}
retry = 0;
sockaddr_in addr;
addr.sin_addr.s_addr = htonl(ip_port.ip);
addr.sin_port = htons(ip_port.port);
addr.sin_family = AF_INET;
while (retry++ < max_retry)
{
int res = bind(query_socket, (sockaddr*)&addr, sizeof(sockaddr_in));
if (res == 0)
{
set_socket_nonblocking(query_socket);
break;
}
if (retry >= max_retry)
{
kill_socket(query_socket);
query_socket = -1;
reset_last_error();
return;
}
}
char str_ip[16];
inet_ntop(AF_INET, &(addr.sin_addr), str_ip, 16);
PRINT_DEBUG("Started query server on %s:%d\n", str_ip, htons(addr.sin_port));
}
query_alive = true;
}
void Networking::shutDownQuery()
{
query_alive = false;
kill_socket(query_socket);
}
bool Networking::isQueryAlive()
{
return query_alive;
}

View File

@ -1205,6 +1205,8 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
bool disable_overlay_warning_bad_appid = false;
bool disable_overlay_warning_any = false;
bool disable_lobby_creation = false;
bool disable_source_query = false;
bool disable_account_avatar = false;
bool achievement_bypass = false;
bool is_beta_branch = false;
bool use_gc_token = false;
@ -1246,6 +1248,10 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
disable_overlay_warning_any = true;
} else if (p == "disable_lobby_creation.txt") {
disable_lobby_creation = true;
} else if (p == "disable_source_query.txt") {
disable_source_query = true;
} else if (p == "disable_account_avatar.txt") {
disable_account_avatar = true;
} else if (p == "achievements_bypass.txt") {
achievement_bypass = true;
} else if (p == "is_beta_branch.txt") {
@ -1302,6 +1308,10 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
settings_client->disable_lobby_creation = disable_lobby_creation;
settings_server->disable_lobby_creation = disable_lobby_creation;
settings_client->disable_source_query = disable_source_query;
settings_server->disable_source_query = disable_source_query;
settings_client->disable_account_avatar = disable_account_avatar;
settings_server->disable_account_avatar = disable_account_avatar;
settings_client->build_id = build_id;
settings_server->build_id = build_id;
settings_client->supported_languages = supported_languages;

267
dll/source_query.cpp Normal file
View File

@ -0,0 +1,267 @@
/* Copyright (C) 2019 Mr Goldberg, , Nemirtingas
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "dll/source_query.h"
#include "dll/dll.h"
using lock_t = std::lock_guard<std::mutex>;
enum class source_query_magic : uint32_t
{
simple = 0xFFFFFFFFul,
multi = 0xFFFFFFFEul, // <--- TODO ?
};
enum class source_query_header : uint8_t
{
A2S_INFO = 'T',
A2S_PLAYER = 'U',
A2S_RULES = 'V',
};
enum class source_response_header : uint8_t
{
A2S_CHALLENGE = 'A',
A2S_INFO = 'I',
A2S_PLAYER = 'D',
A2S_RULES = 'E',
};
enum class source_server_type : uint8_t
{
dedicated = 'd',
non_dedicated = 'i',
source_tc = 'p',
};
enum class source_server_env : uint8_t
{
linux = 'l',
windows = 'w',
old_mac = 'm',
mac = 'o',
};
enum class source_server_visibility : uint8_t
{
_public = 0,
_private = 1,
};
enum class source_server_vac : uint8_t
{
unsecured = 0,
secured = 1,
};
enum source_server_extra_flag : uint8_t
{
none = 0x00,
gameid = 0x01,
steamid = 0x10,
keywords = 0x20,
spectator = 0x40,
port = 0x80,
};
#if defined(STEAM_WIN32)
static constexpr source_server_env my_server_env = source_server_env::windows;
#else
static constexpr source_server_env my_server_env = source_server_env::linux;
#endif
#pragma pack(push)
#pragma pack(1)
constexpr char a2s_info_payload[] = "Source Engine Query";
constexpr size_t a2s_info_payload_size = sizeof(a2s_info_payload);
struct source_query_data
{
source_query_magic magic;
source_query_header header;
union
{
char a2s_info_payload[a2s_info_payload_size];
uint32_t challenge;
};
};
static constexpr size_t source_query_header_size = sizeof(source_query_magic) + sizeof(source_query_header);
static constexpr size_t a2s_query_info_size = source_query_header_size + sizeof(source_query_data::a2s_info_payload);
static constexpr size_t a2s_query_challenge_size = source_query_header_size + sizeof(source_query_data::challenge);
#pragma pack(pop)
void serialize_response(std::vector<uint8_t>& buffer, const void* _data, size_t len)
{
const uint8_t* data = reinterpret_cast<const uint8_t*>(_data);
buffer.insert(buffer.end(), data, data + len);
}
template<typename T>
void serialize_response(std::vector<uint8_t>& buffer, T const& v)
{
uint8_t const* data = reinterpret_cast<uint8_t const*>(&v);
serialize_response(buffer, data, sizeof(T));
}
template<>
void serialize_response<std::string>(std::vector<uint8_t>& buffer, std::string const& v)
{
uint8_t const* str = reinterpret_cast<uint8_t const*>(v.c_str());
serialize_response(buffer, str, v.length()+1);
}
template<typename T, int N>
void serialize_response(std::vector <uint8_t>& buffer, char(&str)[N])
{
serialize_response(buffer, reinterpret_cast<uint8_t const*>(str), N);
}
void get_challenge(std::vector<uint8_t> &challenge_buff)
{
// TODO: generate the challenge id
serialize_response(challenge_buff, source_query_magic::simple);
serialize_response(challenge_buff, source_response_header::A2S_CHALLENGE);
serialize_response(challenge_buff, static_cast<uint32_t>(0x00112233ul));
}
std::vector<uint8_t> Source_Query::handle_source_query(const void* buffer, size_t len, Gameserver const& gs)
{
std::vector<uint8_t> output_buffer;
if (len < source_query_header_size) // its not at least 5 bytes long (0xFF 0xFF 0xFF 0xFF 0x??)
return output_buffer;
source_query_data const& query = *reinterpret_cast<source_query_data const*>(buffer);
// || gs.max_player_count() == 0
if (gs.offline() || query.magic != source_query_magic::simple)
return output_buffer;
switch (query.header)
{
case source_query_header::A2S_INFO:
if (len >= a2s_query_info_size && !strncmp(query.a2s_info_payload, a2s_info_payload, a2s_info_payload_size))
{
std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>> const& players = *get_steam_client()->steam_gameserver->get_players();
serialize_response(output_buffer, source_query_magic::simple);
serialize_response(output_buffer, source_response_header::A2S_INFO);
serialize_response(output_buffer, static_cast<uint8_t>(2));
serialize_response(output_buffer, gs.server_name());
serialize_response(output_buffer, gs.map_name());
serialize_response(output_buffer, gs.mod_dir());
serialize_response(output_buffer, gs.product());
serialize_response(output_buffer, static_cast<uint16_t>(gs.appid()));
serialize_response(output_buffer, static_cast<uint8_t>(players.size()));
serialize_response(output_buffer, static_cast<uint8_t>(gs.max_player_count()));
serialize_response(output_buffer, static_cast<uint8_t>(gs.bot_player_count()));
serialize_response(output_buffer, (gs.dedicated_server() ? source_server_type::dedicated : source_server_type::non_dedicated));;
serialize_response(output_buffer, my_server_env);
serialize_response(output_buffer, (gs.password_protected() ? source_server_visibility::_private : source_server_visibility::_public));
serialize_response(output_buffer, (gs.secure() ? source_server_vac::secured : source_server_vac::unsecured));
serialize_response(output_buffer, std::to_string(gs.version()));
uint8_t flags = source_server_extra_flag::none;
if (gs.port() != 0)
flags |= source_server_extra_flag::port;
if (gs.spectator_port() != 0)
flags |= source_server_extra_flag::spectator;
if(CGameID(gs.appid()).IsValid())
flags |= source_server_extra_flag::gameid;
if (flags != source_server_extra_flag::none)
serialize_response(output_buffer, flags);
if (flags & source_server_extra_flag::port)
serialize_response(output_buffer, static_cast<uint16_t>(gs.port()));
// add steamid
if (flags & source_server_extra_flag::spectator)
{
serialize_response(output_buffer, static_cast<uint16_t>(gs.spectator_port()));
serialize_response(output_buffer, gs.spectator_server_name());
}
// keywords
if (flags & source_server_extra_flag::gameid)
serialize_response(output_buffer, CGameID(gs.appid()).ToUint64());
}
break;
case source_query_header::A2S_PLAYER:
if (len >= a2s_query_challenge_size)
{
if (query.challenge == 0xFFFFFFFFul)
{
get_challenge(output_buffer);
}
else if (query.challenge == 0x00112233ul)
{
std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>> const& players = *get_steam_client()->steam_gameserver->get_players();
serialize_response(output_buffer, source_query_magic::simple);
serialize_response(output_buffer, source_response_header::A2S_PLAYER);
serialize_response(output_buffer, static_cast<uint8_t>(players.size())); // num_players
for (int i = 0; i < players.size(); ++i)
{
serialize_response(output_buffer, static_cast<uint8_t>(i)); // player index
serialize_response(output_buffer, players[i].second.name); // player name
serialize_response(output_buffer, players[i].second.score); // player score
serialize_response(output_buffer, static_cast<float>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - players[i].second.join_time).count()));
}
}
}
break;
case source_query_header::A2S_RULES:
if (len >= a2s_query_challenge_size)
{
if (query.challenge == 0xFFFFFFFFul)
{
get_challenge(output_buffer);
}
else if (query.challenge == 0x00112233ul)
{
auto values = gs.values();
serialize_response(output_buffer, source_query_magic::simple);
serialize_response(output_buffer, source_response_header::A2S_RULES);
serialize_response(output_buffer, static_cast<uint16_t>(values.size()));
for (auto const& i : values)
{
serialize_response(output_buffer, i.first);
serialize_response(output_buffer, i.second);
}
}
}
break;
}
return output_buffer;
}

View File

@ -16,6 +16,7 @@
<http://www.gnu.org/licenses/>. */
#include "dll/steam_gameserver.h"
#include "dll/source_query.h"
#define SEND_SERVER_RATE 5.0
@ -33,6 +34,11 @@ Steam_GameServer::~Steam_GameServer()
delete auth_manager;
}
std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>>* Steam_GameServer::get_players()
{
return &players;
}
//
// Basic server data. These properties, if set, must be set before before calling LogOn. They
// may not be changed after logged in.
@ -61,6 +67,10 @@ bool Steam_GameServer::InitGameServer( uint32 unIP, uint16 usGamePort, uint16 us
server_data.set_port(usGamePort);
server_data.set_query_port(usQueryPort);
server_data.set_offline(false);
if (!settings->disable_source_query)
network->startQuery({ unIP, usQueryPort });
if (!settings->get_local_game_id().AppID()) settings->set_game_id(CGameID(nGameAppId));
//TODO: flags should be k_unServerFlag
flags = unFlags;
@ -78,6 +88,8 @@ void Steam_GameServer::SetProduct( const char *pszProduct )
{
PRINT_DEBUG("Steam_GameServer::SetProduct\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
// pszGameDescription should be used instead of pszProduct for accurate information
// Example: 'Counter-Strike: Source' instead of 'cstrike'
server_data.set_product(pszProduct);
}
@ -89,6 +101,7 @@ void Steam_GameServer::SetGameDescription( const char *pszGameDescription )
PRINT_DEBUG("Steam_GameServer::SetGameDescription\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
server_data.set_game_description(pszGameDescription);
//server_data.set_product(pszGameDescription);
}
@ -183,8 +196,13 @@ bool Steam_GameServer::BSecure()
{
PRINT_DEBUG("Steam_GameServer::BSecure\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!policy_response_called) return false;
return !!(flags & k_unServerFlagSecure);
if (!policy_response_called) {
server_data.set_secure(0);
return false;
}
const bool res = !!(flags & k_unServerFlagSecure);
server_data.set_secure(res);
return res;
}
CSteamID Steam_GameServer::GetSteamID()
@ -351,7 +369,18 @@ bool Steam_GameServer::SendUserConnectAndAuthenticate( uint32 unIPClient, const
PRINT_DEBUG("Steam_GameServer::SendUserConnectAndAuthenticate %u %u\n", unIPClient, cubAuthBlobSize);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return auth_manager->SendUserConnectAndAuthenticate(unIPClient, pvAuthBlob, cubAuthBlobSize, pSteamIDUser);
bool res = auth_manager->SendUserConnectAndAuthenticate(unIPClient, pvAuthBlob, cubAuthBlobSize, pSteamIDUser);
if (res) {
std::pair<CSteamID, Gameserver_Player_Info_t> infos;
infos.first = *pSteamIDUser;
infos.second.join_time = std::chrono::steady_clock::now();
infos.second.score = 0;
infos.second.name = "unnamed";
players.emplace_back(std::move(infos));
}
return res;
}
void Steam_GameServer::SendUserConnectAndAuthenticate( CSteamID steamIDUser, uint32 unIPClient, void *pvAuthBlob, uint32 cubAuthBlobSize )
@ -368,7 +397,15 @@ CSteamID Steam_GameServer::CreateUnauthenticatedUserConnection()
PRINT_DEBUG("Steam_GameServer::CreateUnauthenticatedUserConnection\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return auth_manager->fakeUser();
CSteamID bot_id = auth_manager->fakeUser();
std::pair<CSteamID, Gameserver_Player_Info_t> infos;
infos.first = bot_id;
infos.second.join_time = std::chrono::steady_clock::now();
infos.second.score = 0;
infos.second.name = "unnamed";
players.emplace_back(std::move(infos));
return bot_id;
}
@ -380,6 +417,16 @@ void Steam_GameServer::SendUserDisconnect( CSteamID steamIDUser )
PRINT_DEBUG("Steam_GameServer::SendUserDisconnect\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto player_it = std::find_if(players.begin(), players.end(), [&steamIDUser](std::pair<CSteamID, Gameserver_Player_Info_t>& player)
{
return player.first == steamIDUser;
});
if (player_it != players.end())
{
players.erase(player_it);
}
auth_manager->endAuth(steamIDUser);
}
@ -392,7 +439,22 @@ void Steam_GameServer::SendUserDisconnect( CSteamID steamIDUser )
bool Steam_GameServer::BUpdateUserData( CSteamID steamIDUser, const char *pchPlayerName, uint32 uScore )
{
PRINT_DEBUG("Steam_GameServer::BUpdateUserData %llu %s %u\n", steamIDUser.ConvertToUint64(), pchPlayerName, uScore);
return true;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto player_it = std::find_if(players.begin(), players.end(), [&steamIDUser](std::pair<CSteamID, Gameserver_Player_Info_t>& player)
{
return player.first == steamIDUser;
});
if (player_it != players.end())
{
if (pchPlayerName != nullptr)
player_it->second.name = pchPlayerName;
player_it->second.score = uScore;
return true;
}
return false;
}
// You shouldn't need to call this as it is called internally by SteamGameServer_Init() and can only be called once.
@ -505,6 +567,13 @@ EBeginAuthSessionResult Steam_GameServer::BeginAuthSession( const void *pAuthTic
PRINT_DEBUG("Steam_GameServer::BeginAuthSession %i %llu\n", cbAuthTicket, steamID.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
std::pair<CSteamID, Gameserver_Player_Info_t> infos;
infos.first = steamID;
infos.second.join_time = std::chrono::steady_clock::now();
infos.second.score = 0;
infos.second.name = "unnamed";
players.emplace_back(std::move(infos));
return auth_manager->beginAuth(pAuthTicket, cbAuthTicket, steamID );
}
@ -515,6 +584,16 @@ void Steam_GameServer::EndAuthSession( CSteamID steamID )
PRINT_DEBUG("Steam_GameServer::EndAuthSession %llu\n", steamID.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto player_it = std::find_if(players.begin(), players.end(), [&steamID](std::pair<CSteamID, Gameserver_Player_Info_t>& player)
{
return player.first == steamID;
});
if (player_it != players.end())
{
players.erase(player_it);
}
auth_manager->endAuth(steamID);
}
@ -612,6 +691,18 @@ bool Steam_GameServer::HandleIncomingPacket( const void *pData, int cbData, uint
{
PRINT_DEBUG("Steam_GameServer::HandleIncomingPacket %i %X %i\n", cbData, srcIP, srcPort);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (settings->disable_source_query) return true;
Gameserver_Outgoing_Packet packet;
packet.data = std::move(Source_Query::handle_source_query(pData, cbData, server_data));
if (packet.data.empty())
return false;
packet.ip = srcIP;
packet.port = srcPort;
outgoing_packets.emplace_back(std::move(packet));
return true;
}
@ -624,6 +715,7 @@ int Steam_GameServer::GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *p
{
PRINT_DEBUG("Steam_GameServer::GetNextOutgoingPacket\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (settings->disable_source_query) return 0;
if (outgoing_packets.size() == 0) return 0;
if (outgoing_packets.back().data.size() < cbMaxOut) cbMaxOut = outgoing_packets.back().data.size();
@ -747,6 +839,10 @@ void Steam_GameServer::RunCallbacks()
msg.set_allocated_gameserver(new Gameserver(server_data));
msg.mutable_gameserver()->set_offline(true);
network->sendToAllIndividuals(&msg, true);
// Shutdown Source Query
network->shutDownQuery();
// And empty the queue if needed
outgoing_packets.clear();
}
}
}

View File

@ -33,7 +33,111 @@ Steam_Matchmaking_Servers::Steam_Matchmaking_Servers(class Settings *settings, c
this->network->setCallback(CALLBACK_ID_GAMESERVER, (uint64) 0, &network_callback, this);
}
static int server_list_request = 0;
static int server_list_request;
HServerListRequest Steam_Matchmaking_Servers::RequestServerList(AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse, EMatchMakingType type)
{
PRINT_DEBUG("Steam_Matchmaking_Servers::RequestServerList %u %p, %i\n", iApp, pRequestServersResponse, (int)type);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
++server_list_request;
HServerListRequest id = (char *)0 + server_list_request; // (char *)0 silences the compiler warning
struct Steam_Matchmaking_Request request;
request.appid = iApp;
request.callbacks = pRequestServersResponse;
request.old_callbacks = NULL;
request.cancelled = false;
request.completed = false;
request.type = type;
request.id = id;
requests.push_back(request);
PRINT_DEBUG("Steam_Matchmaking_Servers::request id: %p\n", id);
if (type == eLANServer) return id;
if (type == eFriendsServer) {
for (auto &g : gameservers_friends) {
if (g.source_id != settings->get_local_steam_id().ConvertToUint64()) {
Gameserver server;
server.set_ip(g.ip);
server.set_port(g.port);
server.set_query_port(g.port);
server.set_appid(iApp);
struct Steam_Matchmaking_Servers_Gameserver g2;
g2.last_recv = std::chrono::high_resolution_clock::now();
g2.server = server;
g2.type = type;
gameservers.push_back(g2);
PRINT_DEBUG(" eFriendsServer SERVER ADDED\n");
}
}
return id;
}
std::string file_path;
unsigned long long file_size;
if (type == eInternetServer || type == eSpectatorServer) {
file_path = Local_Storage::get_user_appdata_path() + "/7/" + Local_Storage::remote_storage_folder + "/serverbrowser.txt";
file_size = file_size_(file_path);
} else if (type == eFavoritesServer) {
file_path = Local_Storage::get_user_appdata_path() + "/7/" + Local_Storage::remote_storage_folder + "/serverbrowser_favorites.txt";
file_size = file_size_(file_path);
} else if (type == eHistoryServer) {
file_path = Local_Storage::get_user_appdata_path() + "/7/" + Local_Storage::remote_storage_folder + "/serverbrowser_history.txt";
file_size = file_size_(file_path);
}
PRINT_DEBUG("Steam_Matchmaking_Servers::Server list file '%s' [%llu bytes]\n", file_path.c_str(), file_size);
std::string list;
if (file_size) {
list.resize(file_size);
Local_Storage::get_file_data(file_path, (char *)list.data(), file_size, 0);
} else {
return id;
}
std::istringstream list_ss (list);
std::string list_ip;
while (std::getline(list_ss, list_ip)) {
if (list_ip.length() < 0) continue;
unsigned int byte4, byte3, byte2, byte1, byte0;
uint32 ip_int;
uint16 port_int;
char newip[24];
if (sscanf(list_ip.c_str(), "%u.%u.%u.%u:%u", &byte3, &byte2, &byte1, &byte0, &byte4) == 5) {
ip_int = (byte3 << 24) + (byte2 << 16) + (byte1 << 8) + byte0;
port_int = byte4;
unsigned char ip_tmp[4];
ip_tmp[0] = ip_int & 0xFF;
ip_tmp[1] = (ip_int >> 8) & 0xFF;
ip_tmp[2] = (ip_int >> 16) & 0xFF;
ip_tmp[3] = (ip_int >> 24) & 0xFF;
snprintf(newip, sizeof(newip), "%d.%d.%d.%d", ip_tmp[3], ip_tmp[2], ip_tmp[1], ip_tmp[0]);
} else {
continue;
}
Gameserver server;
server.set_ip(ip_int);
server.set_port(port_int);
server.set_query_port(port_int);
server.set_appid(iApp);
struct Steam_Matchmaking_Servers_Gameserver g;
g.last_recv = std::chrono::high_resolution_clock::now();
g.server = server;
g.type = type;
gameservers.push_back(g);
PRINT_DEBUG(" SERVER ADDED\n");
list_ip = "";
}
return id;
}
// Request a new list of servers of a particular type. These calls each correspond to one of the EMatchMakingType values.
// Each call allocates a new asynchronous request object.
@ -41,55 +145,37 @@ static int server_list_request = 0;
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");
//TODO
return RequestLANServerList(iApp, pRequestServersResponse);
return RequestServerList(iApp, pRequestServersResponse, eInternetServer);
}
HServerListRequest Steam_Matchmaking_Servers::RequestLANServerList( AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse )
{
PRINT_DEBUG("Steam_Matchmaking_Servers::RequestLANServerList %u\n", iApp);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
++server_list_request;
HServerListRequest id = (char *)0 + server_list_request; // (char *)0 silences the compiler warning
struct Steam_Matchmaking_Request request{};
request.appid = iApp;
request.callbacks = pRequestServersResponse;
request.old_callbacks = NULL;
request.cancelled = false;
request.completed = false;
request.id = id;
requests.push_back(request);
PRINT_DEBUG("Steam_Matchmaking_Servers::RequestLANServerList request id: %p\n", id);
return id;
PRINT_DEBUG("Steam_Matchmaking_Servers::RequestLANServerList\n");
return RequestServerList(iApp, pRequestServersResponse, eLANServer);
}
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");
//TODO
return RequestLANServerList(iApp, pRequestServersResponse);
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");
//TODO
return RequestLANServerList(iApp, pRequestServersResponse);
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");
//TODO
return RequestLANServerList(iApp, pRequestServersResponse);
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");
//TODO
return RequestLANServerList(iApp, pRequestServersResponse);
return RequestServerList(iApp, pRequestServersResponse, eSpectatorServer);
}
void Steam_Matchmaking_Servers::RequestOldServerList(AppId_t iApp, ISteamMatchmakingServerListResponse001 *pRequestServersResponse, EMatchMakingType type)
@ -111,6 +197,7 @@ void Steam_Matchmaking_Servers::RequestOldServerList(AppId_t iApp, ISteamMatchma
request.old_callbacks = pRequestServersResponse;
request.cancelled = false;
request.completed = false;
request.type = type;
requests.push_back(request);
requests[requests.size() - 1].id = (void *)type;
}
@ -251,15 +338,85 @@ void Steam_Matchmaking_Servers::ReleaseRequest( HServerListRequest hServerListRe
void Steam_Matchmaking_Servers::server_details(Gameserver *g, gameserveritem_t *server)
{
long long latency = 10;
if (!(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(" 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(" 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 = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
if (ssq_server_eok(ssq)) {
PRINT_DEBUG(" 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(" 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(" 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;
strncpy(server->m_szGameDir, g->mod_dir().c_str(), k_cbMaxGameServerGameDir - 1);
strncpy(server->m_szMap, g->map_name().c_str(), k_cbMaxGameServerMapName - 1);
strncpy(server->m_szGameDescription, g->game_description().c_str(), k_cbMaxGameServerGameDescription - 1);
server->m_szGameDir[k_cbMaxGameServerGameDir - 1] = 0;
server->m_szMap[k_cbMaxGameServerMapName - 1] = 0;
server->m_szGameDescription[k_cbMaxGameServerGameDescription - 1] = 0;
server->m_nAppID = g->appid();
server->m_nPlayers = g->num_players();
@ -271,20 +428,91 @@ void Steam_Matchmaking_Servers::server_details(Gameserver *g, gameserveritem_t *
server->m_nServerVersion = g->version();
server->SetName(g->server_name().c_str());
server->m_steamID = CSteamID((uint64)g->id());
memset(server->m_szGameDir, 0, sizeof(server->m_szGameDir));
g->mod_dir().copy(server->m_szGameDir, sizeof(server->m_szGameDir) - 1);
memset(server->m_szMap, 0, sizeof(server->m_szMap));
g->map_name().copy(server->m_szMap, sizeof(server->m_szMap) - 1);
memset(server->m_szGameDescription, 0, sizeof(server->m_szGameDescription));
g->game_description().copy(server->m_szGameDescription, sizeof(server->m_szGameDescription) - 1);
PRINT_DEBUG(" Steam_Matchmaking_Servers::server_details " "%" PRIu64 "\n", g->id());
memset(server->m_szGameTags, 0, sizeof(server->m_szGameTags));
g->tags().copy(server->m_szGameTags, sizeof(server->m_szGameTags) - 1);
// strncpy(server->m_szGameTags, g->tags().c_str(), k_cbMaxGameServerTags - 1);
// server->m_szGameTags[k_cbMaxGameServerTags - 1] = 0;
}
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 (!(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(" 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(" 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(" 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(" 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(" server_details_players() ssq server connection failed: %s\n", (ssq ? ssq_server_emsg(ssq) : "NULL instance"));
}
if (ssq != NULL) ssq_server_free(ssq);
}
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 (!(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(" 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(" 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(" 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(" server_details_rules() ssq server connection failed: %s\n", (ssq ? ssq_server_emsg(ssq) : "NULL instance"));
}
if (ssq != NULL) ssq_server_free(ssq);
}
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
@ -482,7 +710,7 @@ void Steam_Matchmaking_Servers::RunCallbacks()
r.gameservers_filtered.clear();
for (auto &g : gameservers) {
PRINT_DEBUG("Steam_Matchmaking_Servers::game_server_check %u %u\n", g.server.appid(), r.appid);
if (g.server.appid() == r.appid) {
if ((g.server.appid() == r.appid) && (g.type == r.type)) {
PRINT_DEBUG("Steam_Matchmaking_Servers::REQUESTS server found\n");
r.gameservers_filtered.push_back(g);
}
@ -540,9 +768,9 @@ void Steam_Matchmaking_Servers::RunCallbacks()
}
for (auto &r : direct_ip_requests_temp) {
PRINT_DEBUG("Steam_Matchmaking_Servers dip request: %u:%hu\n", r.ip, r.port);
PRINT_DEBUG("Steam_Matchmaking_Servers::dip request: %u:%hu\n", r.ip, r.port);
for (auto &g : gameservers) {
PRINT_DEBUG("Steam_Matchmaking_Servers server: %u:%u\n", g.server.ip(), g.server.query_port());
PRINT_DEBUG("Steam_Matchmaking_Servers::server: %u:%u\n", g.server.ip(), g.server.query_port());
uint16 query_port = g.server.query_port();
if (query_port == 0xFFFF) {
query_port = g.server.port();
@ -550,31 +778,27 @@ 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) {
server_details_players(&(g.server), &r);
r.players_response->PlayersRefreshComplete();
r.players_response = NULL;
}
if (r.ping_response) {
gameserveritem_t server;
server_details(&(g.server), &server);
r.ping_response->ServerResponded(server);
r.ping_response = NULL;
}
//TODO: players response
}
}
if (r.rules_response) r.rules_response->RulesRefreshComplete();
//TODO: player response
if (r.players_response) r.players_response->PlayersRefreshComplete();
if (r.ping_response) r.ping_response->ServerFailedToRespond();
}
@ -582,12 +806,13 @@ void Steam_Matchmaking_Servers::RunCallbacks()
void Steam_Matchmaking_Servers::Callback(Common_Message *msg)
{
if (msg->has_gameserver()) {
PRINT_DEBUG("Steam_Matchmaking_Servers got SERVER " "%" PRIu64 ", offline:%u\n", msg->gameserver().id(), msg->gameserver().offline());
if (msg->has_gameserver() && msg->gameserver().type() != eFriendsServer) {
PRINT_DEBUG("Steam_Matchmaking_Servers::got SERVER " "%" PRIu64 ", offline:%u\n", msg->gameserver().id(), msg->gameserver().offline());
if (msg->gameserver().offline()) {
for (auto &g : gameservers) {
if (g.server.id() == msg->gameserver().id()) {
g.last_recv = std::chrono::high_resolution_clock::time_point();
g.type = eLANServer;
}
}
} else {
@ -597,6 +822,7 @@ void Steam_Matchmaking_Servers::Callback(Common_Message *msg)
g.last_recv = std::chrono::high_resolution_clock::now();
g.server = msg->gameserver();
g.server.set_ip(msg->source_ip());
g.type = eLANServer;
already = true;
}
}
@ -606,9 +832,31 @@ void Steam_Matchmaking_Servers::Callback(Common_Message *msg)
g.last_recv = std::chrono::high_resolution_clock::now();
g.server = msg->gameserver();
g.server.set_ip(msg->source_ip());
g.type = eLANServer;
gameservers.push_back(g);
PRINT_DEBUG("Steam_Matchmaking_Servers SERVER ADDED\n");
PRINT_DEBUG("Steam_Matchmaking_Servers::SERVER ADDED\n");
}
}
}
if (msg->has_gameserver() && msg->gameserver().type() == eFriendsServer) {
bool addserver = true;
for (auto &g : gameservers_friends) {
if (g.source_id == msg->source_id()) {
g.ip = msg->gameserver().ip();
g.port = msg->gameserver().port();
g.last_recv = std::chrono::high_resolution_clock::now();
addserver = false;
}
}
if (addserver) {
struct Steam_Matchmaking_Servers_Gameserver_Friends gameserver_friend;
gameserver_friend.source_id = msg->source_id();
gameserver_friend.ip = msg->gameserver().ip();
gameserver_friend.port = msg->gameserver().port();
gameserver_friend.last_recv = std::chrono::high_resolution_clock::now();
gameservers_friends.push_back(gameserver_friend);
}
}
}

View File

@ -243,6 +243,15 @@ To allow external downloads which will be stored in this `steam_settings\http` f
---
## Avatar:
Copy a `PNG` or `JPG` image to your `Goldberg SteamEmu Settings\settings` folder and name it `account_avatar`
You can also set a default profile picture for users who are missing one by copying a similar file called `account_avatar_default`
You can find example in `steam_settings.EXAMPLE`
---
## Support for CPY steam_api(64).dll cracks:
See the build in the experimental folder.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1 @@
Rename this to: disable_account_avatar.txt to disable avatar functionality.

View File

@ -0,0 +1 @@
Rename this to: disable_source_query.txt to not send server details for the server browser. Only works for game servers.

View File

@ -485,10 +485,12 @@ def get_dlc(raw_infos):
return (set(), set(), set())
EXTRA_FEATURES: list[tuple[str, str]] = [
("disable_account_avatar.txt", "disable avatar functionality."),
("disable_networking.txt", "disable all networking functionality."),
("disable_overlay.txt", "disable the overlay."),
("disable_overlay_achievement_notification.txt", "disable the achievement notifications."),
("disable_overlay_friend_notification.txt", "disable the friend invite and message notifications."),
("disable_source_query.txt", "Do not send server details for the server browser. Only works for game servers."),
]
def disable_all_extra_features(emu_settings_dir : str) -> None:

View File

@ -130,6 +130,11 @@ The Main_Page file would contain the data returned by the steamHTTP api when it
An example that was made for payday 2 can be found in steam_settings.EXAMPLE
To allow external downloads which will be stored in this steam_settings\http folder copy the disable_lan_only file to the steam_settings folder with .EXAMPLE removed from the file name.
Avatar:
Copy a PNG or JPG image to your Goldberg SteamEmu Settings\settings folder and name it account_avatar
You can also set a default profile picture for users who are missing one by copying a similar file called account_avatar_default
You can find example in steam_settings.EXAMPLE
Support for CPY steam_api(64).dll cracks: See the build in the experimental folder.