You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
491 lines
13 KiB
491 lines
13 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//===========================================================================// |
|
|
|
#include "networksystem.h" |
|
#include "filesystem.h" |
|
#include "UDP_Socket.h" |
|
#include "sm_protocol.h" |
|
#include "NetChannel.h" |
|
#include "UDP_Process.h" |
|
#include <winsock.h> |
|
#include "networkclient.h" |
|
#include "networkserver.h" |
|
#include "networksystem/inetworkmessage.h" |
|
#include "mathlib/mathlib.h" |
|
#include "tier2/tier2.h" |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton instance |
|
//----------------------------------------------------------------------------- |
|
static CNetworkSystem g_NetworkSystem; |
|
CNetworkSystem *g_pNetworkSystemImp = &g_NetworkSystem; |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CNetworkSystem, INetworkSystem, |
|
NETWORKSYSTEM_INTERFACE_VERSION, g_NetworkSystem ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
CNetworkSystem::CNetworkSystem() |
|
{ |
|
m_bWinsockInitialized = false; |
|
m_bNetworkEventCreated = false; |
|
m_bInMidPacket = false; |
|
m_pServer = NULL; |
|
m_pClient = NULL; |
|
m_nGroupBits = 1; |
|
m_nTypeBits = LargestPowerOfTwoLessThanOrEqual( net_num_messages ); |
|
} |
|
|
|
CNetworkSystem::~CNetworkSystem() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Initialization, shutdown |
|
//----------------------------------------------------------------------------- |
|
InitReturnVal_t CNetworkSystem::Init() |
|
{ |
|
InitReturnVal_t nRetVal = BaseClass::Init(); |
|
if ( nRetVal != INIT_OK ) |
|
return nRetVal; |
|
|
|
// initialize winsock 2.0 |
|
WSAData wsaData; |
|
|
|
if ( WSAStartup( MAKEWORD(2,0), &wsaData ) != 0 ) |
|
{ |
|
Warning( "Error! Failed to load network socket library.\n"); |
|
return INIT_OK; |
|
} |
|
else |
|
{ |
|
m_bWinsockInitialized = true; |
|
} |
|
|
|
LPHOSTENT lp = gethostbyname("localhost"); |
|
if ( !lp ) |
|
{ |
|
Warning( "Error! Failed to query local host info\n"); |
|
return INIT_OK; |
|
} |
|
m_LocalHostName = lp->h_name; |
|
|
|
lp = gethostbyname( m_LocalHostName ); |
|
if ( !lp ) |
|
{ |
|
Warning( "Error! Failed to query local host info\n"); |
|
return INIT_OK; |
|
} |
|
|
|
sockaddr ip; |
|
m_LocalAddressString = inet_ntoa( *((in_addr*)(lp->h_addr_list[0])) ); |
|
StringToSockaddr( m_LocalAddressString, &ip ); |
|
m_LocalAddress.SetFromSockadr( &ip ); |
|
return INIT_OK; |
|
} |
|
|
|
void CNetworkSystem::Shutdown() |
|
{ |
|
if ( m_bWinsockInitialized ) |
|
{ |
|
WSACleanup(); |
|
} |
|
CleanupNetworkMessages(); |
|
BaseClass::Shutdown(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Connect, disconnect |
|
//----------------------------------------------------------------------------- |
|
bool CNetworkSystem::Connect( CreateInterfaceFn factory ) |
|
{ |
|
if ( !BaseClass::Connect( factory ) ) |
|
return false; |
|
|
|
if ( !g_pFullFileSystem ) |
|
{ |
|
Warning( "The network system requires the filesystem to run!\n" ); |
|
return false; |
|
} |
|
|
|
return INIT_OK; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the current time |
|
//----------------------------------------------------------------------------- |
|
float CNetworkSystem::GetTime( void ) |
|
{ |
|
return Plat_FloatTime(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Installs network message factories to be used with all connections |
|
//----------------------------------------------------------------------------- |
|
bool CNetworkSystem::RegisterMessage( INetworkMessage *pMessage ) |
|
{ |
|
if ( m_pServer || m_pClient ) |
|
{ |
|
Warning( "Cannot register messages after connection has started.\n" ); |
|
return false; |
|
} |
|
|
|
if ( pMessage->GetGroup() == 0 ) |
|
{ |
|
Warning( "Network message group 0 is reserved by the network system.\n" ); |
|
return false; |
|
} |
|
|
|
// Look for already registered message |
|
if ( m_NetworkMessages.Find( pMessage ) >= 0 ) |
|
return false; |
|
|
|
// Allocate more space in messages |
|
int nGroupBits = LargestPowerOfTwoLessThanOrEqual( pMessage->GetGroup() ); |
|
int nTypeBits = LargestPowerOfTwoLessThanOrEqual( pMessage->GetType() ); |
|
m_nGroupBits = max( nGroupBits, m_nGroupBits ); |
|
m_nTypeBits = max( nTypeBits, m_nTypeBits ); |
|
|
|
m_NetworkMessages.AddToTail( pMessage ); |
|
return true; |
|
} |
|
|
|
void CNetworkSystem::CleanupNetworkMessages( ) |
|
{ |
|
int nCount = m_NetworkMessages.Count(); |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
m_NetworkMessages[i]->Release(); |
|
} |
|
|
|
m_NetworkMessages.RemoveAll(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Finds a network message given a particular message type |
|
//----------------------------------------------------------------------------- |
|
INetworkMessage* CNetworkSystem::FindNetworkMessage( int group, int type ) |
|
{ |
|
int nCount = m_NetworkMessages.Count(); |
|
for (int i=0; i < nCount; i++ ) |
|
{ |
|
if ( ( m_NetworkMessages[i]->GetGroup() == group ) && ( m_NetworkMessages[i]->GetType() == type ) ) |
|
return m_NetworkMessages[i]; |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CNetworkSystem::StringToSockaddr( const char *s, struct sockaddr *sadr ) |
|
{ |
|
struct hostent *h; |
|
char *colon; |
|
char copy[128]; |
|
|
|
Q_memset (sadr, 0, sizeof(*sadr)); |
|
((struct sockaddr_in *)sadr)->sin_family = AF_INET; |
|
((struct sockaddr_in *)sadr)->sin_port = 0; |
|
|
|
Q_strncpy (copy, s, sizeof( copy ) ); |
|
// strip off a trailing :port if present |
|
for (colon = copy ; *colon ; colon++) |
|
if (*colon == ':') |
|
{ |
|
*colon = 0; |
|
((struct sockaddr_in *)sadr)->sin_port = htons((short)atoi(colon+1)); |
|
} |
|
|
|
if (copy[0] >= '0' && copy[0] <= '9' && Q_strstr( copy, "." ) ) |
|
{ |
|
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy); |
|
} |
|
else |
|
{ |
|
// if ( net_nodns ) |
|
// return false; // DNS names disabled |
|
|
|
if ( (h = gethostbyname(copy)) == NULL ) |
|
return false; |
|
|
|
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0]; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the local address |
|
//----------------------------------------------------------------------------- |
|
const char* CNetworkSystem::GetLocalHostName( void ) const |
|
{ |
|
return m_LocalHostName; |
|
} |
|
|
|
const char* CNetworkSystem::GetLocalAddress( void ) const |
|
{ |
|
return m_LocalAddressString; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Start, shutdown a server |
|
//----------------------------------------------------------------------------- |
|
bool CNetworkSystem::StartServer( unsigned short nServerListenPort ) |
|
{ |
|
if ( !m_bWinsockInitialized ) |
|
return false; |
|
|
|
Assert( !m_pServer ); |
|
m_pServer = new CNetworkServer; |
|
return m_pServer->Init( nServerListenPort ); |
|
} |
|
|
|
void CNetworkSystem::ShutdownServer( ) |
|
{ |
|
if ( m_pServer ) |
|
{ |
|
m_pServer->Shutdown(); |
|
delete m_pServer; |
|
m_pServer = NULL; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Server update |
|
//----------------------------------------------------------------------------- |
|
void CNetworkSystem::ServerReceiveMessages() |
|
{ |
|
if ( m_pServer ) |
|
{ |
|
m_pServer->ReadPackets(); |
|
} |
|
} |
|
|
|
void CNetworkSystem::ServerSendMessages() |
|
{ |
|
if ( m_pServer ) |
|
{ |
|
m_pServer->SendUpdates(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Start, shutdown a client |
|
//----------------------------------------------------------------------------- |
|
bool CNetworkSystem::StartClient( unsigned short nClientListenPort ) |
|
{ |
|
if ( !m_bWinsockInitialized ) |
|
return false; |
|
|
|
Assert( !m_pClient ); |
|
m_pClient = new CNetworkClient; |
|
return m_pClient->Init( nClientListenPort ); |
|
} |
|
|
|
void CNetworkSystem::ShutdownClient( ) |
|
{ |
|
if ( m_pClient ) |
|
{ |
|
m_pClient->Shutdown(); |
|
delete m_pClient; |
|
m_pClient = NULL; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Server update |
|
//----------------------------------------------------------------------------- |
|
void CNetworkSystem::ClientReceiveMessages() |
|
{ |
|
if ( m_pClient ) |
|
{ |
|
m_pClient->ReadPackets(); |
|
} |
|
} |
|
|
|
void CNetworkSystem::ClientSendMessages() |
|
{ |
|
if ( m_pClient ) |
|
{ |
|
m_pClient->SendUpdate(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Server update |
|
//----------------------------------------------------------------------------- |
|
INetChannel* CNetworkSystem::ConnectClientToServer( const char *pServer, int nServerListenPort ) |
|
{ |
|
if ( m_pClient ) |
|
{ |
|
if ( m_pClient->Connect( pServer, nServerListenPort ) ) |
|
return m_pClient->GetNetChannel(); |
|
} |
|
return NULL; |
|
} |
|
|
|
void CNetworkSystem::DisconnectClientFromServer( INetChannel* pChannel ) |
|
{ |
|
if ( m_pClient && ( m_pClient->GetNetChannel() == pChannel ) ) |
|
{ |
|
m_pClient->Disconnect(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Queues up a network packet |
|
//----------------------------------------------------------------------------- |
|
void CNetworkSystem::EnqueueConnectionlessNetworkPacket( CNetPacket *pPacket, IConnectionlessPacketHandler *pHandler ) |
|
{ |
|
int i = m_PacketQueue.AddToTail( ); |
|
|
|
PacketInfo_t& info = m_PacketQueue[i]; |
|
info.m_pPacket = pPacket; |
|
info.m_pHandler = pHandler; |
|
info.m_pNetChannel = NULL; |
|
} |
|
|
|
void CNetworkSystem::EnqueueNetworkPacket( CNetPacket *pPacket, CNetChannel *pNetChannel ) |
|
{ |
|
int i = m_PacketQueue.AddToTail( ); |
|
|
|
PacketInfo_t& info = m_PacketQueue[i]; |
|
info.m_pPacket = pPacket; |
|
info.m_pHandler = NULL; |
|
info.m_pNetChannel = pNetChannel; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Network event iteration helpers |
|
//----------------------------------------------------------------------------- |
|
bool CNetworkSystem::StartProcessingNewPacket() |
|
{ |
|
PacketInfo_t& info = m_PacketQueue[ m_nProcessingPacket ]; |
|
if ( info.m_pHandler ) |
|
{ |
|
UDP_ProcessConnectionlessPacket( info.m_pPacket, info.m_pHandler ); |
|
return false; |
|
} |
|
|
|
if ( !info.m_pNetChannel ) |
|
{ |
|
// Not an error that may happen during connect or disconnect |
|
Warning( "Sequenced packet without connection from %s\n" , info.m_pPacket->m_From.ToString() ); |
|
return false; |
|
} |
|
|
|
return info.m_pNetChannel->StartProcessingPacket( info.m_pPacket ); |
|
} |
|
|
|
bool CNetworkSystem::AdvanceProcessingNetworkPacket( ) |
|
{ |
|
m_PacketQueue[ m_nProcessingPacket ].m_pPacket->Release(); |
|
m_PacketQueue[ m_nProcessingPacket ].m_pPacket = NULL; |
|
|
|
++m_nProcessingPacket; |
|
bool bOverflowed = ( m_nProcessingPacket >= m_PacketQueue.Count() ); |
|
if ( bOverflowed ) |
|
{ |
|
m_PacketQueue.RemoveAll(); |
|
m_nProcessingPacket = 0; |
|
} |
|
return !bOverflowed; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Network event iteration |
|
//----------------------------------------------------------------------------- |
|
NetworkEvent_t *CNetworkSystem::FirstNetworkEvent( ) |
|
{ |
|
Assert( !m_bInMidPacket && !m_nProcessingPacket ); |
|
m_nProcessingPacket = 0; |
|
m_bInMidPacket = false; |
|
return NextNetworkEvent(); |
|
} |
|
|
|
NetworkEvent_t *CNetworkSystem::NextNetworkEvent( ) |
|
{ |
|
int nPacketCount = m_PacketQueue.Count(); |
|
if ( m_nProcessingPacket >= nPacketCount ) |
|
return NULL; |
|
|
|
while( true ) |
|
{ |
|
// Continue processing the packet we're currently on |
|
if ( m_bInMidPacket ) |
|
{ |
|
PacketInfo_t& info = m_PacketQueue[ m_nProcessingPacket ]; |
|
while( info.m_pNetChannel->ProcessPacket( info.m_pPacket ) ) |
|
{ |
|
Assert( m_bNetworkEventCreated ); |
|
m_bNetworkEventCreated = false; |
|
return (NetworkEvent_t*)m_EventMessageBuffer; |
|
} |
|
info.m_pNetChannel->EndProcessingPacket( info.m_pPacket ); |
|
m_bInMidPacket = false; |
|
|
|
if ( !AdvanceProcessingNetworkPacket() ) |
|
return NULL; |
|
} |
|
|
|
// Keep reading packets until we find one that either generates an event, |
|
// one that encounters a packet we need to process, or we exhaust the event queue |
|
while( !StartProcessingNewPacket() ) |
|
{ |
|
bool bOverflowed = !AdvanceProcessingNetworkPacket(); |
|
|
|
if ( m_bNetworkEventCreated ) |
|
{ |
|
m_bNetworkEventCreated = false; |
|
return (NetworkEvent_t*)m_EventMessageBuffer; |
|
} |
|
|
|
if ( bOverflowed ) |
|
return NULL; |
|
} |
|
|
|
m_bInMidPacket = true; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Network event creation |
|
//----------------------------------------------------------------------------- |
|
NetworkEvent_t* CNetworkSystem::CreateNetworkEvent( int nSizeInBytes ) |
|
{ |
|
Assert( nSizeInBytes <= sizeof( m_EventMessageBuffer ) ); |
|
|
|
// If this assertion fails, it means two or more network events were created |
|
// before the main network event loop had a chance to inform external code |
|
Assert( !m_bNetworkEventCreated ); |
|
m_bNetworkEventCreated = true; |
|
return ( NetworkEvent_t* )m_EventMessageBuffer; |
|
} |
|
|
|
bool CNetworkSystem::IsNetworkEventCreated() |
|
{ |
|
return m_bNetworkEventCreated; |
|
} |
|
|
|
|