source-engine/tracker/common/Socket.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

1036 lines
26 KiB
C++

//========= 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;
}