diff options
Diffstat (limited to 'tracker/common/Socket.cpp')
| -rw-r--r-- | tracker/common/Socket.cpp | 1035 |
1 files changed, 1035 insertions, 0 deletions
diff --git a/tracker/common/Socket.cpp b/tracker/common/Socket.cpp new file mode 100644 index 0000000..93109ba --- /dev/null +++ b/tracker/common/Socket.cpp @@ -0,0 +1,1035 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= +#if !defined( _X360 ) +#define FD_SETSIZE 1024 +#endif + +#include <assert.h> +#include "winlite.h" +#if !defined( _X360 ) +#include "winsock.h" +#else +#include "winsockx.h" +#endif +#include "msgbuffer.h" +#include "socket.h" +#include "inetapi.h" +#include "tier0/vcrmode.h" + +#include <VGUI/IVGui.h> + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +//----------------------------------------------------------------------------- +// Purpose: All socket I/O occurs on a thread +//----------------------------------------------------------------------------- +class CSocketThread +{ +public: + typedef struct threadsocket_s + { + struct threadsocket_s *next; + CSocket *socket; + } threadsocket_t; + + // Construction + CSocketThread( void ); + virtual ~CSocketThread( void ); + + // Sockets add/remove themselves via their constructor + virtual void AddSocketToThread( CSocket *socket ); + virtual void RemoveSocketFromThread( CSocket *socket ); + + // Lock changes to socket list, etc. + virtual void Lock( void ); + // Unlock socket list, etc. + virtual void Unlock( void ); + + // Retrieve handle to shutdown event + virtual HANDLE GetShutdownHandle( void ); + // Get head of socket list + virtual threadsocket_t *GetSocketList( void ); + + // Sample clock for socket thread + virtual float GetClock( void ); + +private: + // Initialize the clock + void InitTimer( void ); + +private: + // Critical section used for synchronizing access to socket list + CRITICAL_SECTION cs; + // List of sockets we are listening on + threadsocket_t *m_pSocketList; + // Thread handle + HANDLE m_hThread; + // Thread id + DWORD m_nThreadId; + // Event to set when we want to tell the thread to shut itself down + HANDLE m_hShutdown; + + // High performance clock frequency + double m_dClockFrequency; + // Current accumulated time + double m_dCurrentTime; + // How many bits to shift raw 64 bit sample count by + int m_nTimeSampleShift; + // Previous 32 bit sample count + unsigned int m_uiPreviousTime; +}; + +// Singleton handler +static CSocketThread *GetSocketThread() +{ + static CSocketThread g_SocketThread; + return &g_SocketThread; +} + +//----------------------------------------------------------------------------- +// Purpose: Main winsock processing thread +// Input : threadobject - +// Output : static DWORD WINAPI +//----------------------------------------------------------------------------- +static DWORD WINAPI SocketThreadFunc( LPVOID threadobject ) +{ + // Get pointer to CSocketThread object + CSocketThread *socketthread = ( CSocketThread * )threadobject; + assert( socketthread ); + if ( !socketthread ) + { + return 0; + } + + // Keep looking for data until shutdown event is triggered + while ( 1 ) + { + // List of sockets + CSocketThread::threadsocket_t *sockets; + // file descriptor set for sockets + fd_set fdset; + // number of sockets with messages ready + int number; + // number of sockets added to fd_set + int count; + + // Check for shutdown event + if ( WAIT_OBJECT_0 == VCRHook_WaitForSingleObject( socketthread->GetShutdownHandle(), 0 ) ) + { + break; + } + + // Clear the set + FD_ZERO(&fdset); + + // No changes to list right now + socketthread->Lock(); + + // Add all active sockets to the fdset + count = 0; + for ( sockets = socketthread->GetSocketList(); sockets; sockets = sockets->next ) + { + FD_SET( static_cast<u_int>( sockets->socket->GetSocketNumber() ), &fdset ); + count = max( count, sockets->socket->GetSocketNumber() ); + } + + // Done + socketthread->Unlock(); + + if ( count ) + { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100000; // 100 millisecond == 100000 usec + + // Block for 100000 usec, or until a message is in the queue + number = select( count + 1, &fdset, NULL, NULL, &tv ); +#if !defined( NO_VCR ) + VCRGenericValue( "", &number, sizeof( number ) ); +#endif + if ( number > 0 ) + { + // Iterate through socket list and see who has data waiting // + // No changes to list right now + socketthread->Lock(); + + // Check FD_SET for incoming network messages + for ( sockets = socketthread->GetSocketList(); sockets; sockets = sockets->next ) + { + bool bSet = FD_ISSET( sockets->socket->GetSocketNumber(), &fdset ); +#if !defined( NO_VCR ) + VCRGenericValue( "", &bSet, sizeof( bSet ) ); +#endif + if ( bSet ) + { + // keep reading as long as there is data on the socket + while (sockets->socket->ReceiveData()) + { + } + } + } + + // Done + socketthread->Unlock(); + } + } + + // no need to sleep here, much better let it sleep in the select + } + + ExitThread( 0 ); + + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Construction +//----------------------------------------------------------------------------- +CSocketThread::CSocketThread( void ) +{ + InitTimer(); + + m_pSocketList = NULL; + + InitializeCriticalSection( &cs ); + + m_hShutdown = CreateEvent( NULL, TRUE, FALSE, NULL ); + assert( m_hShutdown ); + + m_hThread = 0; + m_nThreadId = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSocketThread::~CSocketThread( void ) +{ + Lock(); + if ( m_hThread ) + { + SetEvent( m_hShutdown ); + Sleep( 2 ); + TerminateThread( m_hThread, 0 ); + } + Unlock(); + + // Kill the socket +//!! need to validate this line +// assert( !m_pSocketList ); + + if ( m_hThread ) + { + CloseHandle( m_hThread ); + } + + CloseHandle( m_hShutdown ); + + DeleteCriticalSection( &cs ); +} + +//----------------------------------------------------------------------------- +// Purpose: Initialize socket thread timer +//----------------------------------------------------------------------------- +void CSocketThread::InitTimer( void ) +{ + BOOL success; + LARGE_INTEGER PerformanceFreq; + unsigned int lowpart, highpart; + + // Start clock at zero + m_dCurrentTime = 0.0; + + success = QueryPerformanceFrequency( &PerformanceFreq ); + assert( success ); + + // get 32 out of the 64 time bits such that we have around + // 1 microsecond resolution + lowpart = (unsigned int)PerformanceFreq.LowPart; + highpart = (unsigned int)PerformanceFreq.HighPart; + + m_nTimeSampleShift = 0; + + while ( highpart || ( lowpart > 2000000.0 ) ) + { + m_nTimeSampleShift++; + lowpart >>= 1; + lowpart |= (highpart & 1) << 31; + highpart >>= 1; + } + + m_dClockFrequency = 1.0 / (double)lowpart; + + // Get initial sample + unsigned int temp; + LARGE_INTEGER PerformanceCount; + QueryPerformanceCounter( &PerformanceCount ); + if ( !m_nTimeSampleShift ) + { + temp = (unsigned int)PerformanceCount.LowPart; + } + else + { + // Rotate counter to right by m_nTimeSampleShift places + temp = ((unsigned int)PerformanceCount.LowPart >> m_nTimeSampleShift) | + ((unsigned int)PerformanceCount.HighPart << (32 - m_nTimeSampleShift)); + } + + // Set first time stamp + m_uiPreviousTime = temp; +} + +//----------------------------------------------------------------------------- +// Purpose: Thread local timer function +// Output : float +//----------------------------------------------------------------------------- +float CSocketThread::GetClock( void ) +{ + LARGE_INTEGER PerformanceCount; + unsigned int temp, t2; + double time; + + // Get sample counter + QueryPerformanceCounter( &PerformanceCount ); + + if ( !m_nTimeSampleShift ) + { + temp = (unsigned int)PerformanceCount.LowPart; + } + else + { + // Rotate counter to right by m_nTimeSampleShift places + temp = ((unsigned int)PerformanceCount.LowPart >> m_nTimeSampleShift) | + ((unsigned int)PerformanceCount.HighPart << (32 - m_nTimeSampleShift)); + } + + // check for turnover or backward time + if ( ( temp <= m_uiPreviousTime ) && + ( ( m_uiPreviousTime - temp ) < 0x10000000) ) + { + m_uiPreviousTime = temp; // so we can't get stuck + } + else + { + // gap in performance clocks + t2 = temp - m_uiPreviousTime; + + // Convert to time using frequencey of clock + time = (double)t2 * m_dClockFrequency; + + // Remember old time + m_uiPreviousTime = temp; + + // Increment clock + m_dCurrentTime += time; + } +#if !defined( NO_VCR ) + VCRGenericValue( "", &m_dCurrentTime, sizeof( m_dCurrentTime ) ); +#endif + // Convert to float + return (float)m_dCurrentTime; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns handle of shutdown event +// Output : HANDLE +//----------------------------------------------------------------------------- +HANDLE CSocketThread::GetShutdownHandle( void ) +{ + return m_hShutdown; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns head of socket list +// Output : CSocketThread::threadsocket_t +//----------------------------------------------------------------------------- +CSocketThread::threadsocket_t *CSocketThread::GetSocketList( void ) +{ + return m_pSocketList; +} + +int socketCount = 0; + +//----------------------------------------------------------------------------- +// Purpose: Locks object and adds socket to thread +//----------------------------------------------------------------------------- +void CSocketThread::AddSocketToThread( CSocket *socket ) +{ + // create the thread if it isn't there + if (!m_hThread) + { + m_hThread = VCRHook_CreateThread( NULL, 0, SocketThreadFunc, (void *)this, 0, &m_nThreadId ); + assert( m_hThread ); + } + + socketCount++; + + threadsocket_t *p = new threadsocket_t; + p->socket = socket; + + Lock(); + p->next = m_pSocketList; + m_pSocketList = p; + Unlock(); +} + +//----------------------------------------------------------------------------- +// Purpose: Locks list and removes specified socket from thread +//----------------------------------------------------------------------------- +void CSocketThread::RemoveSocketFromThread( CSocket *socket ) +{ + if (!m_hThread) + return; + + socketCount--; + + Lock(); + if ( m_pSocketList ) + { + threadsocket_t *p, *n; + p = m_pSocketList; + if ( p->socket == socket ) + { + m_pSocketList = m_pSocketList->next; + delete p; + } + else + { + while ( p->next ) + { + n = p->next; + if ( n->socket == socket ) + { + p->next = n->next; + delete n; + break; + } + p = n; + } + } + } + Unlock(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSocketThread::Lock( void ) +{ + VCRHook_EnterCriticalSection( &cs ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSocketThread::Unlock( void ) +{ + LeaveCriticalSection( &cs ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructs a message handler for incoming socket messages +//----------------------------------------------------------------------------- +CMsgHandler::CMsgHandler( HANDLERTYPE type, void *typeinfo /*=NULL*/ ) +{ + m_Type = type; + m_pNext = NULL; + + // Assume no socket + SetSocket( NULL ); + + // Assume no special checking + m_ByteCode = 0; + m_szString[ 0 ] = 0; + + switch ( m_Type ) + { + default: + case MSGHANDLER_ALL: + break; + case MSGHANDLER_BYTECODE: + m_ByteCode = *(unsigned char *)typeinfo; + break; + case MSGHANDLER_STRING: + strcpy( m_szString, (char *)typeinfo ); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CMsgHandler::~CMsgHandler( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Default message handler for received messages +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CMsgHandler::Process( netadr_t *from, CMsgBuffer *msg ) +{ + // Swallow message by default + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Check for special handling +// Input : *from - +// *msg - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CMsgHandler::ProcessMessage( netadr_t *from, CMsgBuffer *msg ) +{ + bool bret = false; + unsigned char ch; + const char *str; + + // Crack bytecode or string code + switch( m_Type ) + { + case MSGHANDLER_BYTECODE: + msg->Push(); + ch = (unsigned char)msg->ReadByte(); + msg->Pop(); + if ( ch == m_ByteCode ) + { + bret = Process( from, msg ); + } + break; + case MSGHANDLER_STRING: + msg->Push(); + str = msg->ReadString(); + msg->Pop(); + if ( str && str[ 0 ] && !stricmp( m_szString, str ) ) + { + bret = Process( from, msg ); + } + break; + default: + case MSGHANDLER_ALL: + bret = Process( from, msg ); + break; + } + + return bret; +} + +//----------------------------------------------------------------------------- +// Purpose: Get next in chain of handlers +//----------------------------------------------------------------------------- +CMsgHandler *CMsgHandler::GetNext( void ) const +{ + return m_pNext; +} + +//----------------------------------------------------------------------------- +// Purpose: Set next in handler chain +// Input : *next - +//----------------------------------------------------------------------------- +void CMsgHandler::SetNext( CMsgHandler *next ) +{ + m_pNext = next; +} + +//----------------------------------------------------------------------------- +// Purpose: Get underlying socket object +// Output : CSocket +//----------------------------------------------------------------------------- +CSocket *CMsgHandler::GetSocket( void ) const +{ + return m_pSocket; +} + +//----------------------------------------------------------------------------- +// Purpose: Set underlying socket object +// Input : *socket - +//----------------------------------------------------------------------------- +void CMsgHandler::SetSocket( CSocket *socket ) +{ + m_pSocket = socket; +} + +//----------------------------------------------------------------------------- +// Purpose: Creates a non-blocking, broadcast capable, UDP socket. If port is +// specified, binds it to listen on that port, otherwise, chooses a random port. +//----------------------------------------------------------------------------- +CSocket::CSocket( const char *socketname, int port /*= -1*/ ) : m_SendBuffer(socketname) +{ + struct sockaddr_in address; + unsigned long _true = 1; + int i = 1; + + m_pSocketName = socketname; + + m_bValid = false; + m_bResolved = false; + m_pMessageHandlers = NULL; + m_nUserData = 0; + m_bBroadcastSend = false; + m_iTotalPackets = 0; + m_iCurrentPackets = 0; + m_iRetries = 0; + + m_pBufferCS = new CRITICAL_SECTION; + InitializeCriticalSection((CRITICAL_SECTION *)m_pBufferCS); + + // ensure the socketthread singleton has been created + GetSocketThread(); + + // Set up the socket + m_Socket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if ( m_Socket == -1 ) + { + //int err = WSAGetLastError(); + // WSANOTINITIALISED + return; + } + + // Set it to non-blocking + if ( ioctlsocket ( m_Socket, FIONBIO, &_true ) == -1 ) + { + closesocket( m_Socket ); + m_Socket = 0; + return; + } + + // Allow broadcast packets + if ( setsockopt( m_Socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i) ) == -1 ) + { + closesocket( m_Socket ); + m_Socket = 0; + return; + } + + // LATER: Support specifying interface name + //if (!net_interface || !net_interface[0] || !stricmp(net_interface, "localhost")) + address.sin_addr.s_addr = INADDR_ANY; + //else + // NET_StringToSockaddr (net_interface, (struct sockaddr *)&address); + + if ( port == -1 ) + { + address.sin_port = 0; + } + else + { + address.sin_port = htons( (short)port ); + } + + address.sin_family = AF_INET; + + // only bind if we're required to be on a certain port + if ( address.sin_port > 0) + { + // Bind the socket to specified port + if ( bind( m_Socket, (struct sockaddr *)&address, sizeof(address) ) == -1 ) + { + closesocket (m_Socket); + m_Socket = 0; + return; + } + } + + // Mark as valid + m_bValid = true; + + // Only add valid sockets to thread + GetSocketThread()->AddSocketToThread( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSocket::~CSocket( void ) +{ + DeleteCriticalSection((CRITICAL_SECTION *)m_pBufferCS); + delete (CRITICAL_SECTION *)m_pBufferCS; + + // Try to remove socket from thread + GetSocketThread()->RemoveSocketFromThread( this ); + + // Ask message handlers to remove selves? + if ( m_bValid ) + { + ::shutdown(m_Socket, 0x01); + ::shutdown(m_Socket, 0x02); + closesocket( m_Socket ); + m_Socket = 0; + } + + // Remove handlers + CMsgHandler *handler = m_pMessageHandlers; + while ( handler ) + { + RemoveMessageHandler( handler ); + delete handler; + handler = m_pMessageHandlers; + } + m_pMessageHandlers = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Add hander to head of chain +// Input : *handler - +//----------------------------------------------------------------------------- +void CSocket::AddMessageHandler( CMsgHandler *handler ) +{ + handler->SetNext( m_pMessageHandlers ); + m_pMessageHandlers = handler; + + // Set the socket pointer + handler->SetSocket( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Removed indicated handler +// Input : *handler - +//----------------------------------------------------------------------------- +void CSocket::RemoveMessageHandler( CMsgHandler *handler ) +{ + if ( !handler ) + { + return; + } + + CMsgHandler *list = m_pMessageHandlers; + if ( list == handler ) + { + m_pMessageHandlers = m_pMessageHandlers->GetNext(); + return; + } + + while ( list ) + { + if ( list->GetNext() == handler ) + { + list->SetNext( handler->GetNext() ); + handler->SetNext( NULL ); + return; + } + list = list->GetNext(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Send message to specified address +// Input : *to - +// Output : int - number of bytes sent +//----------------------------------------------------------------------------- +int CSocket::SendMessage( netadr_t *to, CMsgBuffer *msg /*= NULL*/ ) +{ + m_bBroadcastSend = false; + m_ToAddress = *to; + + if ( !m_bValid ) + { + return 0; + } + + if ( !msg ) + { + msg = GetSendBuffer(); + } + + struct sockaddr addr; + net->NetAdrToSockAddr ( to, &addr ); + + int bytessent = sendto( m_Socket, (const char *)msg->GetData(), msg->GetCurSize(), 0, &addr, sizeof( addr ) ); + if ( bytessent == msg->GetCurSize() ) + { + return bytessent; + } + + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Send broadcast message on specified port +// Input : port - +// Output : int - number of bytes sent +//----------------------------------------------------------------------------- +int CSocket::Broadcast( int port, CMsgBuffer *msg /*= NULL*/ ) +{ + m_bBroadcastSend = true; + memset( &m_ToAddress, 0, sizeof( m_ToAddress ) ); + + if ( !m_bValid ) + { + return 0; + } + + if ( !msg ) + { + msg = GetSendBuffer(); + } + + struct sockaddr addr; + netadr_t to; + + to.port = (unsigned short)htons( (unsigned short)port ); + to.type = NA_BROADCAST; + + net->NetAdrToSockAddr ( &to, &addr ); + + int bytessent = sendto( m_Socket, (const char *)msg->GetData(), msg->GetCurSize(), 0, &addr, sizeof( addr ) ); + if ( bytessent == msg->GetCurSize() ) + { + return bytessent; + } + + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Retrieve internal message buffer +// Output : CMsgBuffer +//----------------------------------------------------------------------------- +CMsgBuffer *CSocket::GetSendBuffer( void ) +{ + return &m_SendBuffer; +} + +//----------------------------------------------------------------------------- +// Purpose: Called once per frame (outside of the socket thread) to allow socket to receive incoming messages +// and route them as appropriate +//----------------------------------------------------------------------------- +void CSocket::Frame( void ) +{ + // No data waiting + if (!m_MsgBuffers.Size()) + return; + + VCRHook_EnterCriticalSection( (CRITICAL_SECTION *)m_pBufferCS ); + + // pass up all the receive buffers + for (int i = 0; i < m_MsgBuffers.Size(); i++) + { + // See if there's a handler for this message + CMsgHandler *handler = m_pMessageHandlers; + netadr_t addr = m_MsgBuffers[i].GetNetAddress(); + while ( handler ) + { + // Swallow message? + if ( handler->ProcessMessage( &addr, &m_MsgBuffers[i] ) ) + break; + + handler = handler->GetNext(); + } + } + + // free the buffer list + m_MsgBuffers.RemoveAll(); + + LeaveCriticalSection((CRITICAL_SECTION *)m_pBufferCS); +} + +//----------------------------------------------------------------------------- +// Purpose: Is socket set up correctly +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSocket::IsValid( void ) const +{ + return m_bValid; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CSocket::GetClock( void ) +{ + return GetSocketThread()->GetClock(); +} + +//----------------------------------------------------------------------------- +// Purpose: Resolves the socket address +// Output : const netadr_t +//----------------------------------------------------------------------------- +const netadr_t *CSocket::GetAddress( void ) +{ + assert( m_bValid ); + + if ( !m_bResolved ) + { + m_bResolved = true; + // Determine resulting socket address + net->GetSocketAddress( m_Socket, &m_Address ); + } + + return &m_Address; +} + +//----------------------------------------------------------------------------- +// Purpose: Let the user store/retrieve a 32 bit value +// Input : userData - +//----------------------------------------------------------------------------- +void CSocket::SetUserData( unsigned int userData ) +{ + m_nUserData = userData; +} + +//----------------------------------------------------------------------------- +// Purpose: Let the user store/retrieve a 32 bit value +// Output : unsigned int +//----------------------------------------------------------------------------- +unsigned int CSocket::GetUserData(void ) const +{ + return m_nUserData; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the underlying socket id number for setting up the fd_set +//----------------------------------------------------------------------------- +int CSocket::GetSocketNumber( void ) const +{ + return m_Socket; +} + +//----------------------------------------------------------------------------- +// Purpose: Called once FD_ISSET is detected +//----------------------------------------------------------------------------- +bool CSocket::ReceiveData( void ) +{ + // Check for data + struct sockaddr from; + int fromlen; + int bytes; + unsigned char buffer[ CMsgBuffer::NET_MAXMESSAGE ]; + + fromlen = sizeof( from ); + bytes = VCRHook_recvfrom( m_Socket, (char *)buffer, CMsgBuffer::NET_MAXMESSAGE, 0, (struct sockaddr *)&from, &fromlen ); + + //int port = ntohs( ((struct sockaddr_in *)&from)->sin_port); + + // Socket error + if ( bytes == -1 ) + { + return false; + } + + // Too much data, ignore it + if ( bytes >= CMsgBuffer::NET_MAXMESSAGE ) + { + return false; + } + + // Packets must have -1 tag + if ( bytes < 4 ) + { + return false; + } + + // Mark the time no matter what since FD_SET said there was data and we should have it now + float recvTime = GetClock(); + + if( *(int *)&buffer[0] == -2 ) // its a split packet :) + { + int curPacket=0,offset=0; + SPLITPACKET *pak =reinterpret_cast<SPLITPACKET *>(&buffer[0]); + + if(m_iTotalPackets==0) // this is the first in the series + { + m_iTotalPackets = (pak->packetID & 0x0f); + m_iSeqNo = pak->sequenceNumber; + m_iRetries=0; + + m_iCurrentPackets=1;// packet numbers start at zero, total is the total number (i.e =2 for packet 0,1) + curPacket= (pak->packetID & 0xf0)>>4; + } + else if (m_iSeqNo == pak->sequenceNumber) + { + m_iCurrentPackets++; + curPacket= (pak->packetID & 0xf0)>>4; + } + else + { + m_iRetries++; + if(m_iRetries>MAX_RETRIES) // make sure we give up eventually on fragments + { + m_iTotalPackets=0; + } + return false; // TODO: add support for multiple fragments at one time? + } + + + if(curPacket==0) + { + offset=4; // strip the "-1" at the front of the first packet + } + + if(curPacket<MAX_PACKETS) // just in case... + { + m_CurPacket[curPacket].Clear(); // new packet, clear the buffer out + m_CurPacket[curPacket].WriteBuf(bytes-offset-sizeof(SPLITPACKET),&buffer[offset+sizeof(SPLITPACKET)]); + } + + if(m_iCurrentPackets==m_iTotalPackets) + { + + VCRHook_EnterCriticalSection((CRITICAL_SECTION *)m_pBufferCS); + + // Get from address + netadr_t addr; + net->SockAddrToNetAdr( &from, &addr ); + + // append to the receive buffer + int idx = m_MsgBuffers.AddToTail(); + CMsgBuffer &msgBuffer = m_MsgBuffers[idx]; + + msgBuffer.Clear(); + + // copy all our fragments together + for(int i=0;i<m_iTotalPackets;i++) + { + // buffer must be big enough for us to use, that is where the data originally came from :) + m_CurPacket[i].ReadBuf(m_CurPacket[i].GetCurSize(),buffer); + msgBuffer.WriteBuf(m_CurPacket[i].GetCurSize(),buffer); + } + msgBuffer.SetTime(recvTime); + msgBuffer.SetNetAddress(addr); + + LeaveCriticalSection((CRITICAL_SECTION *)m_pBufferCS); + + m_iTotalPackets = 0; // we have collected all the fragments for + //this packet, we can start on a new one now + + } + + + } + else if ( *(int *)&buffer[0] == -1 ) // Must have 255,255,255,255 oob tag + { + /* + // Fake packet loss + if ( rand() % 1000 < 200 ) + return; + */ + + VCRHook_EnterCriticalSection((CRITICAL_SECTION *)m_pBufferCS); + + // Get from address + netadr_t addr; + net->SockAddrToNetAdr( &from, &addr ); + + // append to the receive buffer + int idx = m_MsgBuffers.AddToTail(); + CMsgBuffer &msgBuffer = m_MsgBuffers[idx]; + + // Copy payload minus the -1 tag + msgBuffer.Clear(); + msgBuffer.WriteBuf( bytes - 4, &buffer[ 4 ] ); + msgBuffer.SetTime(recvTime); + msgBuffer.SetNetAddress(addr); + + LeaveCriticalSection((CRITICAL_SECTION *)m_pBufferCS); + } + + return true; +} |