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.
492 lines
13 KiB
492 lines
13 KiB
5 years ago
|
//========= 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;
|
||
|
}
|
||
|
|