//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: The main manager of the networking code in the game // // $Revision: $ // $NoKeywords: $ //===========================================================================// #include "networkmanager.h" #include "legion.h" #include "networksystem/inetworkmessage.h" #include "tier1/bitbuf.h" #include "inetworkmessagelistener.h" #include "tier0/icommandline.h" #include "tier2/tier2.h" //----------------------------------------------------------------------------- // Singleton accessor //----------------------------------------------------------------------------- static CNetworkManager s_NetworkManager; extern CNetworkManager *g_pNetworkManager = &s_NetworkManager; //----------------------------------------------------------------------------- // Static members. // NOTE: Do *not* set this to 0; it could cause us to lose some registered // menus since that list is set up during construction //----------------------------------------------------------------------------- INetworkMessageFactory *CNetworkManager::m_pFirstFactory; //----------------------------------------------------------------------------- // Call to register methods to register network messages //----------------------------------------------------------------------------- INetworkMessageFactory *CNetworkManager::RegisterNetworkMessage( INetworkMessageFactory *pNetworkMessageFactory ) { // NOTE: This method is expected to be called during global constructor // time, so it must not require any global constructors to be called to work INetworkMessageFactory *pPrevFactory = m_pFirstFactory; m_pFirstFactory = pNetworkMessageFactory; return pPrevFactory; } //----------------------------------------------------------------------------- // Init, shutdown //----------------------------------------------------------------------------- bool CNetworkManager::Init() { m_bIsClient = m_bIsServer = false; m_pClientToServerConnection = NULL; // Make a listener array for each message type // Register all network messages INetworkMessageFactory *pFactory; for ( pFactory = m_pFirstFactory; pFactory; pFactory = pFactory->GetNextFactory() ) { INetworkMessage *pNetworkMessage = pFactory->CreateNetworkMessage(); g_pNetworkSystem->RegisterMessage( pNetworkMessage ); } return true; } void CNetworkManager::Shutdown() { ShutdownClient(); ShutdownServer(); } //----------------------------------------------------------------------------- // Start a server up //----------------------------------------------------------------------------- bool CNetworkManager::StartServer( unsigned short nServerListenPort ) { ShutdownServer( ); if ( nServerListenPort == LEGION_DEFAULT_SERVER_PORT ) { nServerListenPort = CommandLine()->ParmValue( "-serverport", NETWORKSYSTEM_DEFAULT_SERVER_PORT ); } m_bIsServer = g_pNetworkSystem->StartServer( nServerListenPort ); return m_bIsServer; } void CNetworkManager::ShutdownServer( ) { if ( m_bIsServer ) { g_pNetworkSystem->ShutdownServer( ); m_bIsServer = false; } } //----------------------------------------------------------------------------- // Start a client up //----------------------------------------------------------------------------- bool CNetworkManager::StartClient( unsigned short nClientListenPort ) { ShutdownClient( ); if ( nClientListenPort == LEGION_DEFAULT_CLIENT_PORT ) { nClientListenPort = CommandLine()->ParmValue( "-clientport", NETWORKSYSTEM_DEFAULT_CLIENT_PORT ); } m_bIsClient = g_pNetworkSystem->StartClient( nClientListenPort ); return m_bIsClient; } void CNetworkManager::ShutdownClient( ) { if ( m_bIsClient ) { DisconnectClientFromServer(); g_pNetworkSystem->ShutdownClient( ); m_bIsClient = false; } } //----------------------------------------------------------------------------- // Connects/disconnects the client to a server //----------------------------------------------------------------------------- bool CNetworkManager::ConnectClientToServer( const char *pServerName, unsigned short nServerListenPort ) { DisconnectClientFromServer(); if ( !IsClient() ) return false; if ( nServerListenPort == LEGION_DEFAULT_SERVER_PORT ) { nServerListenPort = CommandLine()->ParmValue( "-serverport", NETWORKSYSTEM_DEFAULT_SERVER_PORT ); } m_pClientToServerConnection = g_pNetworkSystem->ConnectClientToServer( pServerName, nServerListenPort ); return ( m_pClientToServerConnection != NULL ); } void CNetworkManager::DisconnectClientFromServer( ) { if ( m_pClientToServerConnection ) { g_pNetworkSystem->DisconnectClientFromServer( m_pClientToServerConnection ); m_pClientToServerConnection = NULL; } } //----------------------------------------------------------------------------- // Start up a game where the local player is playing //----------------------------------------------------------------------------- bool CNetworkManager::HostGame() { if ( !StartServer() ) return false; if ( !StartClient() ) { ShutdownServer(); return false; } if ( !ConnectClientToServer( "localhost" ) ) { ShutdownClient(); ShutdownServer(); return false; } return true; } void CNetworkManager::StopHostingGame() { ShutdownClient(); ShutdownServer(); } //----------------------------------------------------------------------------- // Are we a client?/Are we a server? (we can be both) //----------------------------------------------------------------------------- bool CNetworkManager::IsClient() { return m_bIsClient; } bool CNetworkManager::IsServer() { return m_bIsServer; } //----------------------------------------------------------------------------- // If we are a client, are we connected to the server? //----------------------------------------------------------------------------- bool CNetworkManager::IsClientConnected() { return m_bIsClient && ( m_pClientToServerConnection != NULL ) && ( m_pClientToServerConnection->GetConnectionState() == CONNECTION_STATE_CONNECTED ); } //----------------------------------------------------------------------------- // Post a network message to the server //----------------------------------------------------------------------------- void CNetworkManager::PostClientToServerMessage( INetworkMessage *pMessage ) { if ( IsClientConnected() ) { m_pClientToServerConnection->AddNetMsg( pMessage ); } } //----------------------------------------------------------------------------- // Broadcast a network message to all clients //----------------------------------------------------------------------------- void CNetworkManager::BroadcastServerToClientMessage( INetworkMessage *pMessage ) { if ( IsServer() ) { int nCount = m_ServerToClientConnection.Count(); for ( int i = 0; i < nCount; ++i ) { m_ServerToClientConnection[i]->AddNetMsg( pMessage ); } } } //----------------------------------------------------------------------------- // Add, remove network message listeners //----------------------------------------------------------------------------- void CNetworkManager::AddListener( NetworkMessageRoute_t route, int nGroup, int nType, INetworkMessageListener *pListener ) { CUtlVector< CUtlVector< CUtlVector < INetworkMessageListener* > > > &listeners = m_Listeners[ route ]; if ( listeners.Count() <= nGroup ) { listeners.AddMultipleToTail( nGroup - listeners.Count() + 1 ); } if ( listeners[nGroup].Count() <= nType ) { listeners[nGroup].AddMultipleToTail( nType - listeners[nGroup].Count() + 1 ); } Assert( listeners[nGroup][nType].Find( pListener ) < 0 ); listeners[nGroup][nType].AddToTail( pListener ); } void CNetworkManager::RemoveListener( NetworkMessageRoute_t route, int nGroup, int nType, INetworkMessageListener *pListener ) { CUtlVector< CUtlVector< CUtlVector < INetworkMessageListener* > > > &listeners = m_Listeners[ route ]; if ( listeners.Count() > nGroup ) { if( listeners[nGroup].Count() > nType ) { // Maintain order of listeners (not sure if it matters) listeners[nGroup][nType].FindAndRemove( pListener ); } } } //----------------------------------------------------------------------------- // Notifies listeners about a network message //----------------------------------------------------------------------------- void CNetworkManager::NotifyListeners( NetworkMessageRoute_t route, INetworkMessage *pMessage ) { CUtlVector< CUtlVector< CUtlVector < INetworkMessageListener* > > > &listeners = m_Listeners[ route ]; // based on id, search for installed listeners int nGroup = pMessage->GetGroup(); if ( listeners.Count() > nGroup ) { int nType = pMessage->GetType(); if ( listeners[nGroup].Count() > nType ) { CUtlVector< INetworkMessageListener* > &list = listeners[nGroup][nType]; int nCount = list.Count(); for ( int i = 0; i < nCount; ++i ) { list[i]->OnNetworkMessage( route, pMessage ); } } } } //----------------------------------------------------------------------------- // Process messages received by the client //----------------------------------------------------------------------------- void CNetworkManager::ProcessClientMessages() { NetworkEvent_t *pEvent = g_pNetworkSystem->FirstNetworkEvent(); for ( ; pEvent; pEvent = g_pNetworkSystem->NextNetworkEvent( ) ) { switch ( pEvent->m_nType ) { /* case NETWORK_EVENT_CONNECTED: { NetworkConnectionEvent_t* pConnectEvent = static_cast( pEvent ); m_pClientToServerConnection = pConnectEvent->m_pChannel; } break; case NETWORK_EVENT_DISCONNECTED: { NetworkDisconnectionEvent_t* pDisconnectEvent = static_cast( pEvent ); Assert( m_pClientToServerConnection == pDisconnectEvent->m_pChannel ); if ( m_pClientToServerConnection == pDisconnectEvent->m_pChannel ) { m_pClientToServerConnection = NULL; } } break; */ case NETWORK_EVENT_MESSAGE_RECEIVED: { NetworkMessageReceivedEvent_t* pPacketEvent = static_cast( pEvent ); NotifyListeners( NETWORK_MESSAGE_SERVER_TO_CLIENT, pPacketEvent->m_pNetworkMessage ); } break; } } } //----------------------------------------------------------------------------- // Process messages received by the server //----------------------------------------------------------------------------- void CNetworkManager::ProcessServerMessages() { NetworkEvent_t *pEvent = g_pNetworkSystem->FirstNetworkEvent(); for ( ; pEvent; pEvent = g_pNetworkSystem->NextNetworkEvent( ) ) { switch ( pEvent->m_nType ) { case NETWORK_EVENT_CONNECTED: { NetworkConnectionEvent_t* pConnectEvent = static_cast( pEvent ); m_ServerToClientConnection.AddToTail( pConnectEvent->m_pChannel ); } break; case NETWORK_EVENT_DISCONNECTED: { NetworkDisconnectionEvent_t* pDisconnectEvent = static_cast( pEvent ); m_ServerToClientConnection.FindAndRemove( pDisconnectEvent->m_pChannel ); } break; case NETWORK_EVENT_MESSAGE_RECEIVED: { NetworkMessageReceivedEvent_t* pPacketEvent = static_cast( pEvent ); NotifyListeners( NETWORK_MESSAGE_CLIENT_TO_SERVER, pPacketEvent->m_pNetworkMessage ); } break; } } } //----------------------------------------------------------------------------- // Per-frame update //----------------------------------------------------------------------------- void CNetworkManager::Update( ) { if ( IsClient() ) { g_pNetworkSystem->ClientSendMessages(); } if ( IsServer() ) { g_pNetworkSystem->ServerReceiveMessages(); ProcessServerMessages(); g_pNetworkSystem->ServerSendMessages(); } if ( IsClient() ) { g_pNetworkSystem->ClientReceiveMessages(); ProcessClientMessages(); } }