summaryrefslogtreecommitdiff
path: root/tracker/common/Socket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tracker/common/Socket.cpp')
-rw-r--r--tracker/common/Socket.cpp1035
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;
+}