diff --git a/dll/network.cpp b/dll/network.cpp
index e7c5b2d..fa18d3c 100644
--- a/dll/network.cpp
+++ b/dll/network.cpp
@@ -16,6 +16,7 @@
. */
#include "network.h"
+#include "dll.h"
#define MAX_BROADCASTS 16
static int number_broadcasts = -1;
@@ -218,25 +219,26 @@ static int set_socket_nonblocking(sock_t sock)
static void kill_socket(sock_t sock)
{
-#if defined(STEAM_WIN32)
- closesocket(sock);
-#else
- close(sock);
-#endif
+ if (is_socket_valid(sock))
+ {
+ #if defined(STEAM_WIN32)
+ closesocket(sock);
+ #else
+ close(sock);
+ #endif
+ }
}
static void kill_tcp_socket(struct TCP_Socket &socket)
-{
- if (is_socket_valid(socket.sock)) {
- kill_socket(socket.sock);
- }
+{
+ kill_socket(socket.sock);
socket = TCP_Socket();
}
-static bool initialed;
static void run_at_startup()
{
+ static bool initialed = false;
if (initialed) {
return;
}
@@ -891,6 +893,22 @@ void Networking::Run()
char data[2048];
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));
@@ -1252,3 +1270,74 @@ bool Networking::isAlive()
{
return alive;
}
+
+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;
+}
diff --git a/dll/network.h b/dll/network.h
index 5bb3e62..f153e5d 100644
--- a/dll/network.h
+++ b/dll/network.h
@@ -87,8 +87,9 @@ struct Connection {
class Networking {
bool enabled = false;
bool alive;
+ 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 connections;
@@ -136,6 +137,11 @@ public:
void shutDown();
bool isAlive();
+
+ void startQuery(IP_PORT ip_port);
+ void shutDownQuery();
+ bool isQueryAlive();
+
};
#endif
diff --git a/dll/settings.h b/dll/settings.h
index f813cd9..e034bf8 100644
--- a/dll/settings.h
+++ b/dll/settings.h
@@ -145,6 +145,9 @@ public:
//networking
bool disable_networking = false;
+ //gameserver source query
+ bool disable_source_query = false;
+
//overlay
bool disable_overlay = false;
};
diff --git a/dll/steam_gameserver.cpp b/dll/steam_gameserver.cpp
index 584f369..c89a43f 100644
--- a/dll/steam_gameserver.cpp
+++ b/dll/steam_gameserver.cpp
@@ -16,6 +16,7 @@
. */
#include "steam_gameserver.h"
+#include "source_query.h"
#define SEND_SERVER_RATE 5.0
@@ -33,6 +34,11 @@ Steam_GameServer::~Steam_GameServer()
delete ticket_manager;
}
+std::vector>* 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.
@@ -53,7 +59,11 @@ bool Steam_GameServer::InitGameServer( uint32 unIP, uint16 usGamePort, uint16 us
server_data.set_ip(unIP);
server_data.set_port(usGamePort);
server_data.set_query_port(usQueryPort);
- server_data.set_offline(false);
+ 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;
@@ -71,7 +81,7 @@ void Steam_GameServer::SetProduct( const char *pszProduct )
{
PRINT_DEBUG("SetProduct\n");
std::lock_guard lock(global_mutex);
- server_data.set_product(pszProduct);
+ server_data.set_product(pszProduct);// Set product to game name if this is empty
}
@@ -344,7 +354,22 @@ bool Steam_GameServer::SendUserConnectAndAuthenticate( uint32 unIPClient, const
PRINT_DEBUG("SendUserConnectAndAuthenticate %u %u\n", unIPClient, cubAuthBlobSize);
std::lock_guard lock(global_mutex);
- return ticket_manager->SendUserConnectAndAuthenticate(unIPClient, pvAuthBlob, cubAuthBlobSize, pSteamIDUser);
+ bool res = ticket_manager->SendUserConnectAndAuthenticate(unIPClient, pvAuthBlob, cubAuthBlobSize, pSteamIDUser);
+
+ if (res)
+ {
+ std::pair infos;
+ if( pSteamIDUser != nullptr)
+ infos.first = *pSteamIDUser;
+
+ infos.second.join_time = std::chrono::steady_clock::now();
+ infos.second.score = 0;
+ infos.second.name = "Player";
+
+ players.emplace_back(std::move(infos));
+ }
+
+ return res;
}
@@ -357,7 +382,16 @@ CSteamID Steam_GameServer::CreateUnauthenticatedUserConnection()
PRINT_DEBUG("CreateUnauthenticatedUserConnection\n");
std::lock_guard lock(global_mutex);
- return ticket_manager->fakeUser();
+ CSteamID bot_id = ticket_manager->fakeUser();
+ std::pair infos;
+ infos.first = bot_id;
+ infos.second.join_time = std::chrono::steady_clock::now();
+ infos.second.score = 0;
+ infos.second.name = "Bot";
+
+ players.emplace_back(std::move(infos));
+
+ return bot_id;
}
@@ -369,6 +403,16 @@ void Steam_GameServer::SendUserDisconnect( CSteamID steamIDUser )
PRINT_DEBUG("SendUserDisconnect\n");
std::lock_guard lock(global_mutex);
+ auto player_it = std::find_if(players.begin(), players.end(), [&steamIDUser](std::pair& player)
+ {
+ return player.first == steamIDUser;
+ });
+
+ if (player_it != players.end())
+ {
+ players.erase(player_it);
+ }
+
ticket_manager->endAuth(steamIDUser);
}
@@ -381,7 +425,21 @@ void Steam_GameServer::SendUserDisconnect( CSteamID steamIDUser )
bool Steam_GameServer::BUpdateUserData( CSteamID steamIDUser, const char *pchPlayerName, uint32 uScore )
{
PRINT_DEBUG("BUpdateUserData\n");
- return true;
+
+ auto player_it = std::find_if(players.begin(), players.end(), [&steamIDUser](std::pair& 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.
@@ -584,6 +642,18 @@ bool Steam_GameServer::HandleIncomingPacket( const void *pData, int cbData, uint
{
PRINT_DEBUG("HandleIncomingPacket %i %X %i\n", cbData, srcIP, srcPort);
std::lock_guard 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;
}
@@ -596,6 +666,7 @@ int Steam_GameServer::GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *p
{
PRINT_DEBUG("GetNextOutgoingPacket\n");
std::lock_guard 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();
@@ -696,6 +767,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();
}
}
}
diff --git a/dll/steam_gameserver.h b/dll/steam_gameserver.h
index e66f3b9..de75dd0 100644
--- a/dll/steam_gameserver.h
+++ b/dll/steam_gameserver.h
@@ -15,6 +15,9 @@
License along with the Goldberg Emulator; if not, see
. */
+#ifndef __INCLUDED_GAMESERVER__
+#define __INCLUDED_GAMESERVER__
+
#include "base.h"
//-----------------------------------------------------------------------------
@@ -22,12 +25,18 @@
//-----------------------------------------------------------------------------
struct Gameserver_Outgoing_Packet {
- std::string data;
+ std::vector 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 ISteamGameServer005,
public ISteamGameServer008,
@@ -47,6 +56,7 @@ public ISteamGameServer
bool logged_in = false;
bool call_servers_disconnected = false;
Gameserver server_data;
+ std::vector> players;
uint32 flags;
bool policy_response_called;
@@ -59,6 +69,9 @@ public:
Steam_GameServer(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks);
~Steam_GameServer();
+
+ std::vector>* get_players();
+
//
// Basic server data. These properties, if set, must be set before before calling LogOn. They
// may not be changed after logged in.
@@ -330,3 +343,5 @@ public:
//
void RunCallbacks();
};
+
+#endif
\ No newline at end of file
diff --git a/dll/steam_matchmaking.h b/dll/steam_matchmaking.h
index 695d49c..8dee0e2 100644
--- a/dll/steam_matchmaking.h
+++ b/dll/steam_matchmaking.h
@@ -15,6 +15,9 @@
License along with the Goldberg Emulator; if not, see
. */
+#ifndef __INCLUDED_STEAM_MATCHMAKING__
+#define __INCLUDED_STEAM_MATCHMAKING__
+
#include "base.h"
#define SEND_LOBBY_RATE 5.0
@@ -1479,3 +1482,5 @@ void Callback(Common_Message *msg)
};
+
+#endif
\ No newline at end of file
diff --git a/dll/steam_utils.h b/dll/steam_utils.h
index ce76e8c..3c89d62 100644
--- a/dll/steam_utils.h
+++ b/dll/steam_utils.h
@@ -15,6 +15,9 @@
License along with the Goldberg Emulator; if not, see
. */
+#ifndef __INCLUDED_STEAM_UTILS__
+#define __INCLUDED_STEAM_UTILS__
+
#include "base.h"
#include "local_storage.h"
#include "../overlay_experimental/steam_overlay.h"
@@ -406,3 +409,5 @@ ESteamIPv6ConnectivityState GetIPv6ConnectivityState( ESteamIPv6ConnectivityProt
}
};
+
+#endif
\ No newline at end of file