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