From 12716fd07f089fc25b4da5eb3be24c9f9a067365 Mon Sep 17 00:00:00 2001 From: nillerusr Date: Fri, 10 Feb 2023 22:45:36 +0300 Subject: [PATCH] engine(masterserver): Add sequence for requesting server info, implement StopRefresh and use it on timeout --- engine/masterserver.cpp | 104 +++++++++++++++++++++++++++++------ public/engine/iserversinfo.h | 1 + public/tier0/threadtools.h | 44 ++------------- public/tier1/memhelpers.h | 16 ++++-- tier0/threadtools.cpp | 53 ++++++++++++++++++ vstdlib/jobthread.cpp | 20 +++++-- 6 files changed, 171 insertions(+), 67 deletions(-) diff --git a/engine/masterserver.cpp b/engine/masterserver.cpp index 43262e73..e7580773 100644 --- a/engine/masterserver.cpp +++ b/engine/masterserver.cpp @@ -18,11 +18,14 @@ #include "host.h" #include "eiface.h" #include "server.h" +#include "utlmap.h" extern ConVar sv_tags; extern ConVar sv_lan; #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 @@ -62,6 +65,7 @@ public: void UseDefault ( void ); void CheckHeartbeat (void); void RespondToHeartbeatChallenge( netadr_t &from, bf_read &msg ); + void PingServer( netadr_t &svadr ); void ProcessConnectionlessPacket( netpacket_t *packet ); @@ -70,23 +74,35 @@ public: void RunFrame(); void RequestServersInfo(); - void ReplyInfo( const netadr_t &adr ); + + void ReplyInfo( const netadr_t &adr, uint sequence ); newgameserver_t &ProcessInfo( bf_read &buf ); // SeversInfo void RequestInternetServerList( const char *gamedir, IServerListResponse *response ); void RequestLANServerList( const char *gamedir, IServerListResponse *response ); void AddServerAddresses( netadr_t **adr, int count ); + void StopRefresh(); + private: // List of known master servers adrlist_t *m_pMasterAddresses; 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 bool m_bNoMasters; - CUtlLinkedList m_serverAddresses; + CUtlMap m_serverAddresses; + CUtlMap m_serversRequestTime; IServerListResponse *m_serverListResponse; }; @@ -108,8 +124,13 @@ CMaster::CMaster( void ) m_pMasterAddresses = NULL; m_bNoMasters = false; m_bInitialized = false; + m_iServersResponded = 0; m_serverListResponse = NULL; + SetDefLessFunc( m_serverAddresses ); + SetDefLessFunc( m_serversRequestTime ); + m_bWaitingForReplys = false; + m_iInfoSequence = 0; Init(); } @@ -121,9 +142,34 @@ CMaster::~CMaster( void ) void CMaster::RunFrame() { CheckHeartbeat(); + + if( !m_bWaitingForReplys ) + return; + + if( m_serverListResponse && + m_flStartRequestTime < Plat_FloatTime()-INFO_REQUEST_TIMEOUT ) + { + m_serverListResponse->RefreshComplete( NServerResponse::nServerFailedToRespond ); + m_bWaitingForReplys = false; + } + + if( m_flRetryRequestTime < Plat_FloatTime() - RETRY_INFO_REQUEST_TIME ) + { + m_flRetryRequestTime = Plat_FloatTime(); + + if( m_iServersResponded < m_serverAddresses.Count() ) + RequestServersInfo(); + } } -void CMaster::ReplyInfo( const netadr_t &adr ) +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]; Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) ); @@ -134,6 +180,7 @@ void CMaster::ReplyInfo( const netadr_t &adr ) buf.PutUnsignedInt( LittleDWord( CONNECTIONLESS_HEADER ) ); buf.PutUnsignedChar( S2C_INFOREPLY ); + buf.PutUnsignedInt(sequence); buf.PutUnsignedChar( PROTOCOL_VERSION ); // Hardcoded protocol version number buf.PutString( sv.GetName() ); buf.PutString( sv.GetMapName() ); @@ -217,8 +264,7 @@ void CMaster::ProcessConnectionlessPacket( netpacket_t *packet ) } case M2C_QUERY: { - if( m_serverAddresses.Count() > 0 ) - m_serverAddresses.RemoveAll(); + m_serverAddresses.RemoveAll(); ip = msg.ReadLong(); port = msg.ReadShort(); @@ -227,33 +273,48 @@ void CMaster::ProcessConnectionlessPacket( netpacket_t *packet ) { netadr_t adr(ip, port); - m_serverAddresses.AddToHead(adr); + m_serverAddresses.Insert(adr, false); ip = msg.ReadLong(); port = msg.ReadShort(); } + m_iServersResponded = 0; RequestServersInfo(); + m_flRetryRequestTime = m_flStartRequestTime = Plat_FloatTime(); break; } case C2S_INFOREQUEST: { - ReplyInfo(packet->from); + ReplyInfo(packet->from, msg.ReadLong()); break; } case S2C_INFOREPLY: { + uint sequence = msg.ReadLong(); 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; m_serverListResponse->ServerResponded( s ); - break; - } - case A2A_PING: - { - const char p = A2A_ACK; - NET_SendPacket( NULL, NS_SERVER, packet->from, (unsigned char*)&p, 1); + + m_iServersResponded++; + + if( m_iServersResponded >= m_serverAddresses.Count() ) + { + StopRefresh(); + m_serverListResponse->RefreshComplete( NServerResponse::nServerResponded ); + } break; } } @@ -265,17 +326,24 @@ void CMaster::RequestServersInfo() 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.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() ); } + + m_bWaitingForReplys = true; } //----------------------------------------------------------------------------- @@ -410,7 +478,7 @@ void CMaster::AddServer( netadr_t *adr ) n = ( adrlist_t * ) malloc ( sizeof( adrlist_t ) ); 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 ) ); diff --git a/public/engine/iserversinfo.h b/public/engine/iserversinfo.h index e2fd07de..d892cf2c 100644 --- a/public/engine/iserversinfo.h +++ b/public/engine/iserversinfo.h @@ -89,6 +89,7 @@ class IServersInfo public: virtual void RequestInternetServerList( 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 PlayerDetails( uint32 unIP, uint16 usPort, ISteamMatchmakingPlayersResponse *pRequestServersResponse ) = 0; diff --git a/public/tier0/threadtools.h b/public/tier0/threadtools.h index b7216193..2b18dda7 100644 --- a/public/tier0/threadtools.h +++ b/public/tier0/threadtools.h @@ -52,12 +52,6 @@ #pragma once #pragma warning(push) #pragma warning(disable:4251) - -extern "C" -{ - void __declspec(dllimport) __stdcall Sleep( unsigned long ); -} - #endif #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 ThreadHandle_t ThreadGetCurrentHandle(); PLATFORM_INTERFACE int ThreadGetPriority( ThreadHandle_t hThread = NULL ); @@ -233,10 +229,10 @@ inline void ThreadPause() { #if defined( COMPILER_PS3 ) __db16cyc(); -#elif defined( COMPILER_GCC ) && (defined( __i386__ ) || defined( __x86_64__ )) - __asm __volatile( "pause" ); -#elif defined( POSIX ) +#elif defined(__arm__) || defined(__aarch64__) sched_yield(); +#elif defined( COMPILER_GCC ) + __asm __volatile( "pause" ); #elif defined ( COMPILER_MSVC64 ) _mm_pause(); #elif defined( COMPILER_MSVC32 ) @@ -251,36 +247,6 @@ inline void ThreadPause() #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 void ThreadSetDebugName( ThreadHandle_t hThread, const char *pszName ); diff --git a/public/tier1/memhelpers.h b/public/tier1/memhelpers.h index 35cd513e..898cafb2 100644 --- a/public/tier1/memhelpers.h +++ b/public/tier1/memhelpers.h @@ -11,15 +11,21 @@ namespace memutils template inline void copy( T *dest, const T *src, size_t n ) { - for(; n; n--) - *(dest++) = *(src++); + do + { + --n; + *(dest+n) = *(src+n); + } while( n ); } template - inline void set( T *dest, const T& value, size_t n ) + inline void set( T *dest, T value, size_t n ) { - for(; n; n--) - *(dest++) = value; + do + { + --n; + *(dest+n) = value; + } while( n ); } } diff --git a/tier0/threadtools.cpp b/tier0/threadtools.cpp index 06c8296f..b0dbb645 100644 --- a/tier0/threadtools.cpp +++ b/tier0/threadtools.cpp @@ -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 ThreadId_t ThreadGetCurrentId() { diff --git a/vstdlib/jobthread.cpp b/vstdlib/jobthread.cpp index 634d5358..922b770f 100644 --- a/vstdlib/jobthread.cpp +++ b/vstdlib/jobthread.cpp @@ -214,11 +214,7 @@ public: //----------------------------------------------------- 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 ); - inline void Yield( unsigned timeout ) - { - Assert( ThreadInMainThread() ); - ThreadSleep( timeout ); - } + void Yield( unsigned timeout ); //----------------------------------------------------- // 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); } +//--------------------------------------------------------- + +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 //---------------------------------------------------------