From 0df7838ee197d93e1c58c59f3d9e3018267cb647 Mon Sep 17 00:00:00 2001 From: nillerusr Date: Sat, 28 Jan 2023 05:29:26 +0300 Subject: [PATCH] WIP: initial masterserver client implementation --- common/proto_oob.h | 24 +- engine/baseclientstate.cpp | 3 + engine/baseserver.cpp | 3 + engine/host.cpp | 5 +- engine/master.h | 63 +++++ engine/masterserver.cpp | 502 +++++++++++++++++++++++++++++++++++ engine/net_chan.cpp | 5 + engine/net_ws.cpp | 14 +- engine/sv_steamauth.cpp | 2 +- engine/wscript | 3 +- public/engine/iserversinfo.h | 97 +++++++ 11 files changed, 703 insertions(+), 18 deletions(-) create mode 100644 engine/master.h create mode 100644 engine/masterserver.cpp create mode 100644 public/engine/iserversinfo.h diff --git a/common/proto_oob.h b/common/proto_oob.h index dbeeb60f..a6c70152 100644 --- a/common/proto_oob.h +++ b/common/proto_oob.h @@ -16,6 +16,8 @@ // This is used, unless overridden in the registry #define VALVE_MASTER_ADDRESS "207.173.177.10:27011" +#define HB_TIMEOUT 15 + #define PORT_RCON 27015 // defualt RCON port, TCP #define PORT_MASTER 27011 // Default master port, UDP #define PORT_CLIENT 27005 // Default client port, UDP/TCP @@ -29,6 +31,8 @@ #endif // ENABLE_RPT #define PORT_REPLAY 27040 // Default replay port +#define PORT_SERVERSINFO 27069 // Default matchmaking port + // out of band message id bytes // M = master, S = server, C = client, A = any @@ -80,16 +84,17 @@ // A user is requesting the list of master servers, auth servers, and titan dir servers from the Client Master server -#define A2M_GETMASTERSERVERS 'v' // + byte (type of request, TYPE_CLIENT_MASTER or TYPE_SERVER_MASTER) // Master server list response -#define M2A_MASTERSERVERS 'w' // + byte type + 6 byte IP/Port List - -#define A2M_GETACTIVEMODS 'x' // + string Request to master to provide mod statistics ( current usage ). "1" for first mod. +#define S2M_GETCHALLENGE 'w' // + dword challenge +#define S2M_HEARTBEAT 'y' +#define S2M_SHUTDOWN 'z' // Master peering message +#define M2S_CHALLENGE 'x' // + dword challenge +#define M2C_QUERY 'J' // request module from master +#define C2M_CLIENTQUERY '1' // New style server query -#define M2A_ACTIVEMODS 'y' // response: modname\r\nusers\r\nservers - -#define M2M_MSG 'z' // Master peering message +#define C2S_INFOREQUEST 'v' +#define S2C_INFOREPLY 'K' // SERVER TO CLIENT/ANY @@ -106,9 +111,6 @@ #define S2A_INFO_SRC 'I' // + Address, hostname, map, gamedir, gamedescription, active players, maxplayers, protocol #define S2A_INFO_GOLDSRC 'm' // Reserved for use by goldsrc servers -#define S2M_GETFILE 'J' // request module from master -#define M2S_SENDFILE 'K' // send module to server - #define S2C_REDIRECT 'L' // + IP x.x.x.x:port, redirect client to other server/proxy #define C2M_CHECKMD5 'M' // player client asks secure master if Module MD5 is valid @@ -133,8 +135,6 @@ #define A2S_KEY_STRING "Source Engine Query" // required postfix to a A2S_INFO query -#define A2M_GET_SERVERS_BATCH2 '1' // New style server query - #define A2M_GETACTIVEMODS2 '2' // New style mod info query #define C2S_AUTHREQUEST1 '3' // diff --git a/engine/baseclientstate.cpp b/engine/baseclientstate.cpp index bb0d3de3..ed001afd 100644 --- a/engine/baseclientstate.cpp +++ b/engine/baseclientstate.cpp @@ -37,6 +37,7 @@ #include "sv_plugin.h" #include "sys_dll.h" #include "host.h" +#include "master.h" #if defined( REPLAY_ENABLED ) #include "replay_internal.h" #include "replayserver.h" @@ -875,6 +876,8 @@ bool CBaseClientState::ProcessConnectionlessPacket( netpacket_t *packet ) Assert( packet ); + master->ProcessConnectionlessPacket( packet ); + bf_read &msg = packet->message; // handy shortcut int c = msg.ReadByte(); diff --git a/engine/baseserver.cpp b/engine/baseserver.cpp index b0acee7c..d9c61a96 100644 --- a/engine/baseserver.cpp +++ b/engine/baseserver.cpp @@ -56,6 +56,7 @@ #include "sv_ipratelimit.h" #include "cl_steamauth.h" #include "sv_filter.h" +#include "master.h" #if defined( _X360 ) #include "xbox/xbox_win32stubs.h" @@ -663,6 +664,8 @@ bool CBaseServer::ValidInfoChallenge( netadr_t & adr, const char *nugget ) bool CBaseServer::ProcessConnectionlessPacket(netpacket_t * packet) { + master->ProcessConnectionlessPacket( packet ); + bf_read msg = packet->message; // handy shortcut char c = msg.ReadChar(); diff --git a/engine/host.cpp b/engine/host.cpp index 9ebdc928..2e22474f 100644 --- a/engine/host.cpp +++ b/engine/host.cpp @@ -40,6 +40,7 @@ #include "steam/steam_api.h" #include "LoadScreenUpdate.h" #include "datacache/idatacache.h" +#include "master.h" #if !defined SWDS #include "voice.h" @@ -1820,6 +1821,8 @@ void Host_ShutdownServer( void ) if ( !sv.IsActive() ) return; + master->ShutdownConnection(); + Host_AllowQueuedMaterialSystem( false ); // clear structures #if !defined( SWDS ) @@ -4951,7 +4954,7 @@ void Host_Shutdown(void) TRACESHUTDOWN( HLTV_Shutdown() ); TRACESHUTDOWN( g_Log.Shutdown() ); - + TRACESHUTDOWN( g_GameEventManager.Shutdown() ); TRACESHUTDOWN( sv.Shutdown() ); diff --git a/engine/master.h b/engine/master.h new file mode 100644 index 00000000..eddb39e0 --- /dev/null +++ b/engine/master.h @@ -0,0 +1,63 @@ +//======== (C) Copyright 1999, 2000 Valve, L.L.C. All rights reserved. ======== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//============================================================================= +#ifndef MASTER_H +#define MASTER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "engine/iserversinfo.h" + +#define DEFAULT_MASTER_ADDRESS "185.192.97.130:27010" + +//----------------------------------------------------------------------------- +// Purpose: Implements a master server interface. +//----------------------------------------------------------------------------- +class IMaster +{ +public: + // Allow master server to register cvars/commands + virtual void Init( void ) = 0; + // System is shutting down + virtual void Shutdown( void ) = 0; + // Server is shutting down + virtual void ShutdownConnection( void ) = 0; + // Sends the actual heartbeat to the master ( after challenge value is parsed ) + virtual void SendHeartbeat( struct adrlist_s *p ) = 0; + // Add server to global master list + virtual void AddServer( struct netadr_s *adr ) = 0; + // If parsing for server, etc. fails, always have at least one server around to use. + virtual void UseDefault ( void ) = 0; + // See if it's time to send the next heartbeat + virtual void CheckHeartbeat( void ) = 0; + // Master sent back a challenge value, read it and send the actual heartbeat + virtual void RespondToHeartbeatChallenge( netadr_t &from, bf_read &msg ) = 0; + // Console command to set/remove master server + virtual void SetMaster_f( void ) = 0; + // Force a heartbeat to be issued right away + virtual void Heartbeat_f( void ) = 0; + + virtual void ProcessConnectionlessPacket( netpacket_t *packet ) = 0; + + virtual void RunFrame( void ) = 0; +}; + +extern IMaster *master; +extern IServersInfo *g_pServersInfo; + +#endif // MASTER_H diff --git a/engine/masterserver.cpp b/engine/masterserver.cpp new file mode 100644 index 00000000..62d7e614 --- /dev/null +++ b/engine/masterserver.cpp @@ -0,0 +1,502 @@ +//======177== (C) Copyright 1999, 2000 Valve, L.L.C. All rights reserved. ======== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//============================================================================= +#include "quakedef.h" +#include "server.h" +#include "master.h" +#include "proto_oob.h" +#include "host.h" + +extern ConVar sv_lan; + +//----------------------------------------------------------------------------- +// Purpose: List of master servers and some state info about them +//----------------------------------------------------------------------------- +typedef struct adrlist_s +{ + // Next master in chain + struct adrlist_s *next; + // Challenge request sent to master + qboolean heartbeatwaiting; + // Challenge request send time + float heartbeatwaitingtime; + // Last one is Main master + int heartbeatchallenge; + // Time we sent last heartbeat + double last_heartbeat; + // Master server address + netadr_t adr; +} adrlist_t; + +//----------------------------------------------------------------------------- +// Purpose: Implements the master server interface +//----------------------------------------------------------------------------- +class CMaster : public IMaster, public IServersInfo +{ +public: + CMaster( void ); + virtual ~CMaster( void ); + + // Heartbeat functions. + void Init( void ); + void Shutdown( void ); + // Sets up master address + void ShutdownConnection(void); + void SendHeartbeat( struct adrlist_s *p ); + void AddServer( struct netadr_s *adr ); + void UseDefault ( void ); + void CheckHeartbeat (void); + void RespondToHeartbeatChallenge( netadr_t &from, bf_read &msg ); + + void ProcessConnectionlessPacket( netpacket_t *packet ); + + void SetMaster_f( void ); + void Heartbeat_f( void ); + + void RunFrame(); + void RequestServersInfo(); + + // SeversInfo + void RequestInternetServerList( const char *gamedir, IServerListResponse *response ); + void RequestLANServerList( const char *gamedir, IServerListResponse *response ); + void AddServerAddresses( netadr_t **adr, int count ); +private: + // List of known master servers + adrlist_t *m_pMasterAddresses; + + bool m_bInitialized; + + // If nomaster is true, the server will not send heartbeats to the master server + bool m_bNoMasters; + + CUtlLinkedList m_serverAddresses; +}; + +static CMaster s_MasterServer; +IMaster *master = (IMaster *)&s_MasterServer; + +IServersInfo *g_pServersInfo = (IServersInfo*)&s_MasterServer; + +#define HEARTBEAT_SECONDS 140.0 + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CMaster::CMaster( void ) +{ + m_pMasterAddresses = NULL; + m_bNoMasters = false; + m_bInitialized = false; + Init(); +} + +CMaster::~CMaster( void ) +{ +} + +void CMaster::RunFrame() +{ + CheckHeartbeat(); +} + +void CMaster::ProcessConnectionlessPacket( netpacket_t *packet ) +{ + static ALIGN4 char string[2048] ALIGN4_POST; // Buffer for sending heartbeat + + uint ip; uint16 port; + + bf_read msg = packet->message; + char c = msg.ReadChar(); + + if ( c == 0 ) + return; + + switch( c ) + { + case M2S_CHALLENGE: + { + RespondToHeartbeatChallenge( packet->from, msg ); + break; + } + case M2C_QUERY: + { + if( m_serverAddresses.Count() > 0 ) + m_serverAddresses.RemoveAll(); + + ip = msg.ReadLong(); + port = msg.ReadShort(); + + while( ip != 0 && port != 0 ) + { + netadr_t adr(ip, port); + + m_serverAddresses.AddToHead(adr); + + ip = msg.ReadLong(); + port = msg.ReadShort(); + } + + RequestServersInfo(); + break; + } + case C2S_INFOREQUEST: + { + bf_write p(string, sizeof(string)); + p.WriteLong(CONNECTIONLESS_HEADER); + p.WriteByte(S2C_INFOREPLY); + p.WriteString(sv.GetName()); + + NET_SendPacket(NULL, NS_SERVER, packet->from, p.GetData(), p.GetNumBytesWritten()); + + break; + } + case S2C_INFOREPLY: + { + char hostname[1024]; + msg.ReadString(hostname, sizeof(hostname)); + + break; + } + } +} + +void CMaster::RequestServersInfo() +{ + static ALIGN4 char string[256] ALIGN4_POST; // Buffer for sending heartbeat + + bf_write msg( string, sizeof(string) ); + + FOR_EACH_LL( m_serverAddresses, i ) + { + const netadr_t adr = m_serverAddresses[i]; + + msg.WriteLong( CONNECTIONLESS_HEADER ); + msg.WriteByte( C2S_INFOREQUEST ); + + NET_SendPacket( NULL, NS_CLIENT, adr, msg.GetData(), msg.GetNumBytesWritten() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sends a heartbeat to the master server +// Input : *p - x00\x00\x00\x00\x00\x00 +//----------------------------------------------------------------------------- +void CMaster::SendHeartbeat ( adrlist_t *p ) +{ + static ALIGN4 char string[256] ALIGN4_POST; // Buffer for sending heartbeat + char szGD[ MAX_OSPATH ]; + + if ( !p ) + return; + + // Still waiting on challenge response? + if ( p->heartbeatwaiting ) + return; + + // Waited too long + if ( (realtime - p->heartbeatwaitingtime ) >= HB_TIMEOUT ) + return; + + // Send to master + // TODO(nillerusr): send engine version in this packet + Q_FileBase( com_gamedir, szGD, sizeof( szGD ) ); + + bf_write buf( string, sizeof(string) ); + buf.WriteByte( S2M_HEARTBEAT ); + buf.WriteLong( p->heartbeatchallenge ); + buf.WriteShort( PROTOCOL_VERSION ); + buf.WriteString( szGD ); + + NET_SendPacket( NULL, NS_SERVER, p->adr, buf.GetData(), buf.GetNumBytesWritten() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Requests a challenge so we can then send a heartbeat +//----------------------------------------------------------------------------- +void CMaster::CheckHeartbeat (void) +{ + adrlist_t *p; + ALIGN4 char buf[256] ALIGN4_POST; + + if ( m_bNoMasters || // We are ignoring heartbeats + sv_lan.GetInt() || // Lan servers don't heartbeat + (sv.GetMaxClients() <= 1) || // not a multiplayer server. + !sv.IsActive() ) // only heartbeat if a server is running. + return; + + p = m_pMasterAddresses; + while ( p ) + { + // Time for another try? + if ( ( realtime - p->last_heartbeat) < HEARTBEAT_SECONDS) // not time to send yet + { + p = p->next; + continue; + } + + // Should we resend challenge request? + if ( p->heartbeatwaiting && + ( ( realtime - p->heartbeatwaitingtime ) < HB_TIMEOUT ) ) + { + p = p->next; + continue; + } + + int32 challenge = RandomInt( 0, INT_MAX ); + + p->heartbeatwaiting = true; + p->heartbeatwaitingtime = realtime; + + p->last_heartbeat = realtime; // Flag at start so we don't just keep trying for hb's when + p->heartbeatchallenge = challenge; + + bf_write msg("Master Join", buf, sizeof(buf)); + + msg.WriteByte( S2M_GETCHALLENGE ); + msg.WriteLong( challenge ); + + // Send to master asking for a challenge # + NET_SendPacket( NULL, NS_SERVER, p->adr, msg.GetData(), msg.GetNumBytesWritten() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Server is shutting down, unload master servers list, tell masters that we are closing the server +//----------------------------------------------------------------------------- +void CMaster::ShutdownConnection( void ) +{ + adrlist_t *p; + + if ( !host_initialized ) + return; + + if ( m_bNoMasters || // We are ignoring heartbeats + sv_lan.GetInt() || // Lan servers don't heartbeat + (sv.GetMaxClients() <= 1) ) // not a multiplayer server. + return; + + const char packet = S2M_SHUTDOWN; + + p = m_pMasterAddresses; + while ( p ) + { + NET_SendPacket( NULL, NS_SERVER, p->adr, (unsigned char*)&packet, 1); + p->last_heartbeat = -99999.0; + p = p->next; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Add server to the master list +// Input : *adr - +//----------------------------------------------------------------------------- +void CMaster::AddServer( netadr_t *adr ) +{ + adrlist_t *n; + + // See if it's there + n = m_pMasterAddresses; + while ( n ) + { + if ( n->adr.CompareAdr( *adr ) ) + break; + n = n->next; + } + + // Found it in the list. + if ( n ) + return; + + n = ( adrlist_t * ) malloc ( sizeof( adrlist_t ) ); + if ( !n ) + Sys_Error( "Error allocating %i bytes for master address.", sizeof( adrlist_t ) ); + + memset( n, 0, sizeof( adrlist_t ) ); + + n->adr = *adr; + + // Queue up a full heartbeat to all master servers. + n->last_heartbeat = -99999.0; + + // Link it in. + n->next = m_pMasterAddresses; + m_pMasterAddresses = n; +} + +//----------------------------------------------------------------------------- +// Purpose: Add built-in default master if woncomm.lst doesn't parse +//----------------------------------------------------------------------------- +void CMaster::UseDefault ( void ) +{ + netadr_t adr; + + // Convert to netadr_t + if ( NET_StringToAdr ( DEFAULT_MASTER_ADDRESS, &adr ) ) + { + // Add to master list + AddServer( &adr ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMaster::RespondToHeartbeatChallenge( netadr_t &from, bf_read &msg ) +{ + adrlist_t *p; + uint challenge, challenge2; + + // No masters, just ignore. + if ( !m_pMasterAddresses ) + return; + + p = m_pMasterAddresses; + while ( p ) + { + if ( from.CompareAdr( p->adr ) ) + break; + + p = p->next; + } + + // Not a known master server. + if ( !p ) + return; + + challenge = msg.ReadLong(); + challenge2 = msg.ReadLong(); + + if( p->heartbeatchallenge != challenge2 ) + { + Warning("unexpected master server info query packet (wrong challenge!)\n"); + return; + } + + // Kill timer + p->heartbeatwaiting = false; + p->heartbeatchallenge = challenge; + + // Send the actual heartbeat request to this master server. + SendHeartbeat( p ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add/remove master servers +//----------------------------------------------------------------------------- +void CMaster::SetMaster_f (void) +{ + +} + + +//----------------------------------------------------------------------------- +// Purpose: Send a new heartbeat to the master +//----------------------------------------------------------------------------- +void CMaster::Heartbeat_f (void) +{ + adrlist_t *p; + + p = m_pMasterAddresses; + while ( p ) + { + // Queue up a full hearbeat + p->last_heartbeat = -9999.0; + p->heartbeatwaitingtime = -9999.0; + p = p->next; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void SetMaster_f( void ) +{ + master->SetMaster_f(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Heartbeat1_f( void ) +{ + master->Heartbeat_f(); +} + +static ConCommand setmaster("setmaster", SetMaster_f ); +static ConCommand heartbeat("heartbeat", Heartbeat1_f, "Force heartbeat of master servers" ); + +//----------------------------------------------------------------------------- +// Purpose: Adds master server console commands +//----------------------------------------------------------------------------- +void CMaster::Init( void ) +{ + // Already able to initialize at least once? + if ( m_bInitialized ) + return; + + // So we don't do this a send time.sv_mas + m_bInitialized = true; + + UseDefault(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMaster::Shutdown(void) +{ + adrlist_t *p, *n; + + // Free the master list now. + p = m_pMasterAddresses; + while ( p ) + { + n = p->next; + free( p ); + p = n; + } + + m_pMasterAddresses = NULL; +} + +// ServersInfo +void CMaster::RequestInternetServerList(const char *gamedir, IServerListResponse *response) +{ + if( m_bNoMasters ) return; + + ALIGN4 char buf[256] ALIGN4_POST; + bf_write msg(buf, sizeof(buf)); + + msg.WriteByte( C2M_CLIENTQUERY ); + msg.WriteString(gamedir); + + // TODO(nillerusr): add switching between masters? + NET_SendPacket(NULL, NS_CLIENT, m_pMasterAddresses->adr, msg.GetData(), msg.GetNumBytesWritten() ); +} + +void CMaster::RequestLANServerList(const char *gamedir, IServerListResponse *response) +{ + +} + +void CMaster::AddServerAddresses( netadr_t **adr, int count ) +{ + +} + +void Master_Request_f() +{ + g_pServersInfo->RequestInternetServerList("cstrike", NULL); +} + +ConCommand master_request( "master_request", Master_Request_f ); diff --git a/engine/net_chan.cpp b/engine/net_chan.cpp index bccf0073..e722bc02 100644 --- a/engine/net_chan.cpp +++ b/engine/net_chan.cpp @@ -64,10 +64,15 @@ extern int NET_ReceiveStream( int nSock, char * buf, int len, int flags ); // We only need to checksum packets on the PC and only when we're actually sending them over the network. static bool ShouldChecksumPackets() { + // nillerusr: temporary solution for testing + return false; + +#if 0 if ( !IsPC() ) return false; return NET_IsMultiplayer(); +#endif } bool CNetChan::IsLoopback() const diff --git a/engine/net_ws.cpp b/engine/net_ws.cpp index ba3a791e..b2680f1a 100644 --- a/engine/net_ws.cpp +++ b/engine/net_ws.cpp @@ -13,6 +13,7 @@ #include "net_ws_headers.h" #include "net_ws_queued_packet_sender.h" #include "fmtstr.h" +#include "master.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -1644,7 +1645,7 @@ netpacket_t *NET_GetPacket (int sock, byte *scratch ) // Check loopback first if ( !NET_GetLoopPacket( &inpacket ) ) { - if ( !NET_IsMultiplayer() ) + if ( !NET_IsMultiplayer() && sock != NS_CLIENT ) { return NULL; } @@ -2351,7 +2352,7 @@ int NET_SendPacket ( INetChannel *chan, int sock, const netadr_t &to, const uns Msg("UDP -> %s: sz=%i OOB '%c'\n", to.ToString(), length, data[4] ); } - if ( !NET_IsMultiplayer() || to.type == NA_LOOPBACK || ( to.IsLocalhost() && !net_usesocketsforloopback.GetBool() ) ) + if ( (!NET_IsMultiplayer() && sock != NS_CLIENT) || to.type == NA_LOOPBACK || ( to.IsLocalhost() && !net_usesocketsforloopback.GetBool() ) ) { Assert( !pVoicePayload ); @@ -2988,6 +2989,8 @@ void NET_RunFrame( double flRealtime ) #endif // SWDS + master->RunFrame(); + #ifdef _X360 if ( net_logserver.GetInt() ) { @@ -3110,7 +3113,7 @@ void NET_ListenSocket( int sock, bool bListen ) NET_CloseSocket( netsock->hTCP, sock ); } - if ( !NET_IsMultiplayer() || net_notcp ) + if ( (!NET_IsMultiplayer() && sock != NS_CLIENT) || net_notcp ) return; if ( bListen ) @@ -3296,6 +3299,11 @@ void NET_Init( bool bIsDedicated ) ipname.SetValue( ip ); // update the cvar right now, this will get overwritten by "stuffcmds" later } + const int nProtocol = X360SecureNetwork() ? IPPROTO_VDP : IPPROTO_UDP; + + // open client socket for masterserver + OpenSocketInternal( NS_CLIENT, clientport.GetInt(), PORT_SERVER, "client", nProtocol, true ); + if ( bIsDedicated ) { // set dedicated MP mode diff --git a/engine/sv_steamauth.cpp b/engine/sv_steamauth.cpp index 144ae775..caa14cea 100644 --- a/engine/sv_steamauth.cpp +++ b/engine/sv_steamauth.cpp @@ -1005,7 +1005,7 @@ void Heartbeat_f() } } -static ConCommand heartbeat( "heartbeat", Heartbeat_f, "Force heartbeat of master servers", 0 ); +//static ConCommand heartbeat( "heartbeat", Heartbeat_f, "Force heartbeat of master servers", 0 ); //----------------------------------------------------------------------------- diff --git a/engine/wscript b/engine/wscript index 6ef0bc77..81456630 100755 --- a/engine/wscript +++ b/engine/wscript @@ -329,7 +329,8 @@ def build(bld): 'vgui_texturebudgetpanel.cpp', 'vgui_vprofgraphpanel.cpp', 'vgui_vprofpanel.cpp', - 'toolframework.cpp' + 'toolframework.cpp', + 'masterserver.cpp', ] if bld.env.DEST_OS != 'win32': diff --git a/public/engine/iserversinfo.h b/public/engine/iserversinfo.h new file mode 100644 index 00000000..663feb75 --- /dev/null +++ b/public/engine/iserversinfo.h @@ -0,0 +1,97 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to steam managing game server/client match making +// +//============================================================================= + +#ifndef ISERVERSINFO_H +#define ISERVERSINFO_H +#ifdef _WIN32 +#pragma once +#endif + +#define MAX_GAME_DESCRIPTION 8192 +#define MAX_SERVER_NAME 2048 + +enum ServerResponse +{ + ServerResponded = 0, + ServerFailedToRespond, + NoServersListedOnMasterServer, +}; + +class newgameserver_t +{ +public: + newgameserver_t() = default; + + const char* GetName() const; + void SetName( const char *pName ); + + netadr_t m_NetAdr; ///< IP/Query Port/Connection Port for this server + int m_nPing; ///< current ping time in milliseconds + bool m_bHadSuccessfulResponse; ///< server has responded successfully in the past + bool m_bDoNotRefresh; ///< server is marked as not responding and should no longer be refreshed + char m_szGameDir[MAX_PATH]; ///< current game directory + char m_szMap[MAX_PATH]; ///< current map + char m_szGameDescription[MAX_GAME_DESCRIPTION]; ///< game description + + int m_nPlayers; + int m_nMaxPlayers; ///< Maximum players that can join this server + int m_nBotPlayers; ///< Number of bots (i.e simulated players) on this server + bool m_bPassword; ///< true if this server needs a password to join +private: + /// Game server name + char m_szServerName[MAX_SERVER_NAME]; +}; + +class IServerListResponse +{ +public: + // Server has responded ok with updated data + virtual void ServerResponded( newgameserver_t &server ) = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: Callback interface for receiving responses after pinging an individual server +// +class IServerPingResponse +{ +public: + // Server has responded successfully and has updated data + virtual void ServerResponded( newgameserver_t &server ) = 0; +}; +//----------------------------------------------------------------------------- +// Purpose: Callback interface for receiving responses after requesting details on +// who is playing on a particular server. +// +class IServerPlayersResponse +{ +public: + // Got data on a new player on the server -- you'll get this callback once per player + // on the server which you have requested player data on. + virtual void AddPlayerToList( const char *pchName, int nScore, float flTimePlayed ) = 0; + + // The server failed to respond to the request for player details + virtual void PlayersFailedToRespond() = 0; + + // The server has finished responding to the player details request + virtual void PlayersRefreshComplete() = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: Functions for match making services for clients to get to game lists and details +//----------------------------------------------------------------------------- +class IServersInfo +{ +public: + virtual void RequestInternetServerList( const char *gamedir, IServerListResponse *response ) = 0; + virtual void RequestLANServerList( const char *gamedir, IServerListResponse *response ) = 0; + + //virtual HServerQuery PingServer( uint32 unIP, uint16 usPort, ISteamMatchmakingPingResponse *pRequestServersResponse ) = 0; + //virtual HServerQuery PlayerDetails( uint32 unIP, uint16 usPort, ISteamMatchmakingPlayersResponse *pRequestServersResponse ) = 0; + +}; +#define SERVERLIST_INTERFACE_VERSION "ServerList001" + +#endif // ISERVERSINFO_H