engine(masterserver): Add sequence for requesting server info, implement StopRefresh and use it on timeout

This commit is contained in:
nillerusr 2023-02-10 22:45:36 +03:00
parent a08b6ae7bf
commit 12716fd07f
6 changed files with 171 additions and 67 deletions

View File

@ -18,11 +18,14 @@
#include "host.h" #include "host.h"
#include "eiface.h" #include "eiface.h"
#include "server.h" #include "server.h"
#include "utlmap.h"
extern ConVar sv_tags; extern ConVar sv_tags;
extern ConVar sv_lan; extern ConVar sv_lan;
#define S2A_EXTRA_DATA_HAS_GAMETAG_DATA 0x01 // Next bytes are the game tag string #define S2A_EXTRA_DATA_HAS_GAMETAG_DATA 0x01 // Next bytes are the game tag string
#define RETRY_INFO_REQUEST_TIME 0.4 // seconds
#define INFO_REQUEST_TIMEOUT 5.0 // seconds
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: List of master servers and some state info about them // Purpose: List of master servers and some state info about them
@ -62,6 +65,7 @@ public:
void UseDefault ( void ); void UseDefault ( void );
void CheckHeartbeat (void); void CheckHeartbeat (void);
void RespondToHeartbeatChallenge( netadr_t &from, bf_read &msg ); void RespondToHeartbeatChallenge( netadr_t &from, bf_read &msg );
void PingServer( netadr_t &svadr );
void ProcessConnectionlessPacket( netpacket_t *packet ); void ProcessConnectionlessPacket( netpacket_t *packet );
@ -70,23 +74,35 @@ public:
void RunFrame(); void RunFrame();
void RequestServersInfo(); void RequestServersInfo();
void ReplyInfo( const netadr_t &adr );
void ReplyInfo( const netadr_t &adr, uint sequence );
newgameserver_t &ProcessInfo( bf_read &buf ); newgameserver_t &ProcessInfo( bf_read &buf );
// SeversInfo // SeversInfo
void RequestInternetServerList( const char *gamedir, IServerListResponse *response ); void RequestInternetServerList( const char *gamedir, IServerListResponse *response );
void RequestLANServerList( const char *gamedir, IServerListResponse *response ); void RequestLANServerList( const char *gamedir, IServerListResponse *response );
void AddServerAddresses( netadr_t **adr, int count ); void AddServerAddresses( netadr_t **adr, int count );
void StopRefresh();
private: private:
// List of known master servers // List of known master servers
adrlist_t *m_pMasterAddresses; adrlist_t *m_pMasterAddresses;
bool m_bInitialized; bool m_bInitialized;
bool m_bWaitingForReplys;
int m_iServersResponded;
double m_flStartRequestTime;
double m_flRetryRequestTime;
uint m_iInfoSequence;
// If nomaster is true, the server will not send heartbeats to the master server // If nomaster is true, the server will not send heartbeats to the master server
bool m_bNoMasters; bool m_bNoMasters;
CUtlLinkedList<netadr_t> m_serverAddresses; CUtlMap<netadr_t, bool> m_serverAddresses;
CUtlMap<uint, double> m_serversRequestTime;
IServerListResponse *m_serverListResponse; IServerListResponse *m_serverListResponse;
}; };
@ -108,8 +124,13 @@ CMaster::CMaster( void )
m_pMasterAddresses = NULL; m_pMasterAddresses = NULL;
m_bNoMasters = false; m_bNoMasters = false;
m_bInitialized = false; m_bInitialized = false;
m_iServersResponded = 0;
m_serverListResponse = NULL; m_serverListResponse = NULL;
SetDefLessFunc( m_serverAddresses );
SetDefLessFunc( m_serversRequestTime );
m_bWaitingForReplys = false;
m_iInfoSequence = 0;
Init(); Init();
} }
@ -121,9 +142,34 @@ CMaster::~CMaster( void )
void CMaster::RunFrame() void CMaster::RunFrame()
{ {
CheckHeartbeat(); CheckHeartbeat();
if( !m_bWaitingForReplys )
return;
if( m_serverListResponse &&
m_flStartRequestTime < Plat_FloatTime()-INFO_REQUEST_TIMEOUT )
{
m_serverListResponse->RefreshComplete( NServerResponse::nServerFailedToRespond );
m_bWaitingForReplys = false;
} }
void CMaster::ReplyInfo( const netadr_t &adr ) if( m_flRetryRequestTime < Plat_FloatTime() - RETRY_INFO_REQUEST_TIME )
{
m_flRetryRequestTime = Plat_FloatTime();
if( m_iServersResponded < m_serverAddresses.Count() )
RequestServersInfo();
}
}
void CMaster::StopRefresh()
{
m_bWaitingForReplys = false;
m_serverAddresses.RemoveAll();
m_serversRequestTime.RemoveAll();
}
void CMaster::ReplyInfo( const netadr_t &adr, uint sequence )
{ {
static char gamedir[MAX_OSPATH]; static char gamedir[MAX_OSPATH];
Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) ); Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) );
@ -134,6 +180,7 @@ void CMaster::ReplyInfo( const netadr_t &adr )
buf.PutUnsignedInt( LittleDWord( CONNECTIONLESS_HEADER ) ); buf.PutUnsignedInt( LittleDWord( CONNECTIONLESS_HEADER ) );
buf.PutUnsignedChar( S2C_INFOREPLY ); buf.PutUnsignedChar( S2C_INFOREPLY );
buf.PutUnsignedInt(sequence);
buf.PutUnsignedChar( PROTOCOL_VERSION ); // Hardcoded protocol version number buf.PutUnsignedChar( PROTOCOL_VERSION ); // Hardcoded protocol version number
buf.PutString( sv.GetName() ); buf.PutString( sv.GetName() );
buf.PutString( sv.GetMapName() ); buf.PutString( sv.GetMapName() );
@ -217,7 +264,6 @@ void CMaster::ProcessConnectionlessPacket( netpacket_t *packet )
} }
case M2C_QUERY: case M2C_QUERY:
{ {
if( m_serverAddresses.Count() > 0 )
m_serverAddresses.RemoveAll(); m_serverAddresses.RemoveAll();
ip = msg.ReadLong(); ip = msg.ReadLong();
@ -227,33 +273,48 @@ void CMaster::ProcessConnectionlessPacket( netpacket_t *packet )
{ {
netadr_t adr(ip, port); netadr_t adr(ip, port);
m_serverAddresses.AddToHead(adr); m_serverAddresses.Insert(adr, false);
ip = msg.ReadLong(); ip = msg.ReadLong();
port = msg.ReadShort(); port = msg.ReadShort();
} }
m_iServersResponded = 0;
RequestServersInfo(); RequestServersInfo();
m_flRetryRequestTime = m_flStartRequestTime = Plat_FloatTime();
break; break;
} }
case C2S_INFOREQUEST: case C2S_INFOREQUEST:
{ {
ReplyInfo(packet->from); ReplyInfo(packet->from, msg.ReadLong());
break; break;
} }
case S2C_INFOREPLY: case S2C_INFOREPLY:
{ {
uint sequence = msg.ReadLong();
newgameserver_t &s = ProcessInfo( msg ); newgameserver_t &s = ProcessInfo( msg );
Msg("hostname = %s\nplayers: %d/%d\nbots: %d\n", s.m_szServerName, s.m_nPlayers, s.m_nMaxPlayers, s.m_nBotPlayers);
unsigned short index = m_serverAddresses.Find(packet->from);
unsigned short rindex = m_serversRequestTime.Find(sequence);
if( index == m_serverAddresses.InvalidIndex() ||
rindex == m_serversRequestTime.InvalidIndex() )
break;
double requestTime = m_serversRequestTime[rindex];
m_serverAddresses[index] = true;
s.m_nPing = (packet->received-requestTime)*1000.0;
s.m_NetAdr = packet->from; s.m_NetAdr = packet->from;
m_serverListResponse->ServerResponded( s ); m_serverListResponse->ServerResponded( s );
break;
} m_iServersResponded++;
case A2A_PING:
if( m_iServersResponded >= m_serverAddresses.Count() )
{ {
const char p = A2A_ACK; StopRefresh();
NET_SendPacket( NULL, NS_SERVER, packet->from, (unsigned char*)&p, 1); m_serverListResponse->RefreshComplete( NServerResponse::nServerResponded );
}
break; break;
} }
} }
@ -265,17 +326,24 @@ void CMaster::RequestServersInfo()
bf_write msg( string, sizeof(string) ); bf_write msg( string, sizeof(string) );
FOR_EACH_LL( m_serverAddresses, i ) FOR_EACH_MAP_FAST( m_serverAddresses, i )
{ {
const netadr_t adr = m_serverAddresses[i]; bool bResponded = m_serverAddresses.Element(i);
if( bResponded )
continue;
Msg("Request server info %s\n", adr.ToString()); const netadr_t adr = m_serverAddresses.Key(i);
msg.WriteLong( CONNECTIONLESS_HEADER ); msg.WriteLong( CONNECTIONLESS_HEADER );
msg.WriteByte( C2S_INFOREQUEST ); msg.WriteByte( C2S_INFOREQUEST );
msg.WriteLong( m_iInfoSequence );
m_serversRequestTime.Insert(m_iInfoSequence, net_time);
m_iInfoSequence++;
NET_SendPacket( NULL, NS_CLIENT, adr, msg.GetData(), msg.GetNumBytesWritten() ); NET_SendPacket( NULL, NS_CLIENT, adr, msg.GetData(), msg.GetNumBytesWritten() );
} }
m_bWaitingForReplys = true;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -410,7 +478,7 @@ void CMaster::AddServer( netadr_t *adr )
n = ( adrlist_t * ) malloc ( sizeof( adrlist_t ) ); n = ( adrlist_t * ) malloc ( sizeof( adrlist_t ) );
if ( !n ) if ( !n )
Sys_Error( "Error allocating %i bytes for master address.", sizeof( adrlist_t ) ); Sys_Error( "Error allocating %zd bytes for master address.", sizeof( adrlist_t ) );
memset( n, 0, sizeof( adrlist_t ) ); memset( n, 0, sizeof( adrlist_t ) );

View File

@ -89,6 +89,7 @@ class IServersInfo
public: public:
virtual void RequestInternetServerList( const char *gamedir, IServerListResponse *response ) = 0; virtual void RequestInternetServerList( const char *gamedir, IServerListResponse *response ) = 0;
virtual void RequestLANServerList( const char *gamedir, IServerListResponse *response ) = 0; virtual void RequestLANServerList( const char *gamedir, IServerListResponse *response ) = 0;
virtual void StopRefresh() = 0;
//virtual HServerQuery PingServer( uint32 unIP, uint16 usPort, ISteamMatchmakingPingResponse *pRequestServersResponse ) = 0; //virtual HServerQuery PingServer( uint32 unIP, uint16 usPort, ISteamMatchmakingPingResponse *pRequestServersResponse ) = 0;
//virtual HServerQuery PlayerDetails( uint32 unIP, uint16 usPort, ISteamMatchmakingPlayersResponse *pRequestServersResponse ) = 0; //virtual HServerQuery PlayerDetails( uint32 unIP, uint16 usPort, ISteamMatchmakingPlayersResponse *pRequestServersResponse ) = 0;

View File

@ -52,12 +52,6 @@
#pragma once #pragma once
#pragma warning(push) #pragma warning(push)
#pragma warning(disable:4251) #pragma warning(disable:4251)
extern "C"
{
void __declspec(dllimport) __stdcall Sleep( unsigned long );
}
#endif #endif
#ifdef COMPILER_MSVC64 #ifdef COMPILER_MSVC64
@ -200,6 +194,8 @@ PLATFORM_INTERFACE bool ReleaseThreadHandle( ThreadHandle_t );
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
PLATFORM_INTERFACE void ThreadSleep(unsigned duration = 0);
PLATFORM_INTERFACE void ThreadNanoSleep(unsigned ns);
PLATFORM_INTERFACE ThreadId_t ThreadGetCurrentId(); PLATFORM_INTERFACE ThreadId_t ThreadGetCurrentId();
PLATFORM_INTERFACE ThreadHandle_t ThreadGetCurrentHandle(); PLATFORM_INTERFACE ThreadHandle_t ThreadGetCurrentHandle();
PLATFORM_INTERFACE int ThreadGetPriority( ThreadHandle_t hThread = NULL ); PLATFORM_INTERFACE int ThreadGetPriority( ThreadHandle_t hThread = NULL );
@ -233,10 +229,10 @@ inline void ThreadPause()
{ {
#if defined( COMPILER_PS3 ) #if defined( COMPILER_PS3 )
__db16cyc(); __db16cyc();
#elif defined( COMPILER_GCC ) && (defined( __i386__ ) || defined( __x86_64__ )) #elif defined(__arm__) || defined(__aarch64__)
__asm __volatile( "pause" );
#elif defined( POSIX )
sched_yield(); sched_yield();
#elif defined( COMPILER_GCC )
__asm __volatile( "pause" );
#elif defined ( COMPILER_MSVC64 ) #elif defined ( COMPILER_MSVC64 )
_mm_pause(); _mm_pause();
#elif defined( COMPILER_MSVC32 ) #elif defined( COMPILER_MSVC32 )
@ -251,36 +247,6 @@ inline void ThreadPause()
#endif #endif
} }
inline void ThreadSleep(unsigned nMilliseconds = 0)
{
if( nMilliseconds == 0 )
{
ThreadPause();
return;
}
#ifdef _WIN32
#ifdef _WIN32_PC
static bool bInitialized = false;
if ( !bInitialized )
{
bInitialized = true;
// Set the timer resolution to 1 ms (default is 10.0, 15.6, 2.5, 1.0 or
// some other value depending on hardware and software) so that we can
// use Sleep( 1 ) to avoid wasting CPU time without missing our frame
// rate.
timeBeginPeriod( 1 );
}
#endif
Sleep( nMilliseconds );
#elif PS3
sys_timer_usleep( nMilliseconds * 1000 );
#elif defined(POSIX)
usleep( nMilliseconds * 1000 );
#endif
}
PLATFORM_INTERFACE bool ThreadJoin( ThreadHandle_t, unsigned timeout = TT_INFINITE ); PLATFORM_INTERFACE bool ThreadJoin( ThreadHandle_t, unsigned timeout = TT_INFINITE );
PLATFORM_INTERFACE void ThreadSetDebugName( ThreadHandle_t hThread, const char *pszName ); PLATFORM_INTERFACE void ThreadSetDebugName( ThreadHandle_t hThread, const char *pszName );

View File

@ -11,15 +11,21 @@ namespace memutils
template<typename T> template<typename T>
inline void copy( T *dest, const T *src, size_t n ) inline void copy( T *dest, const T *src, size_t n )
{ {
for(; n; n--) do
*(dest++) = *(src++); {
--n;
*(dest+n) = *(src+n);
} while( n );
} }
template<typename T> template<typename T>
inline void set( T *dest, const T& value, size_t n ) inline void set( T *dest, T value, size_t n )
{ {
for(; n; n--) do
*(dest++) = value; {
--n;
*(dest+n) = value;
} while( n );
} }
} }

View File

@ -485,6 +485,59 @@ bool ReleaseThreadHandle( ThreadHandle_t hThread )
// //
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ThreadSleep(unsigned nMilliseconds)
{
#ifdef _WIN32
#ifdef _WIN32_PC
static bool bInitialized = false;
if ( !bInitialized )
{
bInitialized = true;
// Set the timer resolution to 1 ms (default is 10.0, 15.6, 2.5, 1.0 or
// some other value depending on hardware and software) so that we can
// use Sleep( 1 ) to avoid wasting CPU time without missing our frame
// rate.
timeBeginPeriod( 1 );
}
#endif
Sleep( nMilliseconds );
#elif PS3
if( nMilliseconds == 0 )
{
// sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
// sys_timer_usleep( 60 );
sys_ppu_thread_yield();
}
else
{
sys_timer_usleep( nMilliseconds * 1000 );
}
#elif defined(POSIX)
usleep( nMilliseconds * 1000 );
#endif
}
//-----------------------------------------------------------------------------
void ThreadNanoSleep(unsigned ns)
{
#ifdef _WIN32
// ceil
Sleep( ( ns + 999 ) / 1000 );
#elif PS3
sys_timer_usleep( ns );
#elif defined(POSIX)
struct timespec tm;
tm.tv_sec = 0;
tm.tv_nsec = ns;
nanosleep( &tm, NULL );
#endif
}
//-----------------------------------------------------------------------------
#ifndef ThreadGetCurrentId #ifndef ThreadGetCurrentId
ThreadId_t ThreadGetCurrentId() ThreadId_t ThreadGetCurrentId()
{ {

View File

@ -214,11 +214,7 @@ public:
//----------------------------------------------------- //-----------------------------------------------------
virtual int YieldWait( CThreadEvent **pEvents, int nEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE ); virtual int YieldWait( CThreadEvent **pEvents, int nEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE );
virtual int YieldWait( CJob **, int nJobs, bool bWaitAll = true, unsigned timeout = TT_INFINITE ); virtual int YieldWait( CJob **, int nJobs, bool bWaitAll = true, unsigned timeout = TT_INFINITE );
inline void Yield( unsigned timeout ) void Yield( unsigned timeout );
{
Assert( ThreadInMainThread() );
ThreadSleep( timeout );
}
//----------------------------------------------------- //-----------------------------------------------------
// Add a native job to the queue (master thread) // Add a native job to the queue (master thread)
@ -660,6 +656,20 @@ int CThreadPool::YieldWait( CJob **ppJobs, int nJobs, bool bWaitAll, unsigned ti
return YieldWait( handles.Base(), handles.Count(), bWaitAll, timeout); return YieldWait( handles.Base(), handles.Count(), bWaitAll, timeout);
} }
//---------------------------------------------------------
void CThreadPool::Yield( unsigned timeout )
{
// @MULTICORE (toml 10/24/2006): not implemented
Assert( ThreadInMainThread() );
if ( !ThreadInMainThread() )
{
ThreadSleep( timeout );
return;
}
ThreadSleep( timeout );
}
//--------------------------------------------------------- //---------------------------------------------------------
// Add a job to the queue // Add a job to the queue
//--------------------------------------------------------- //---------------------------------------------------------