2010-05-19 17:59:01 -07:00
/* Copyright (C) 2010 Wildfire Games.
2009-04-18 10:00:33 -07:00
* This file is part of 0 A . D .
*
* 0 A . D . is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
* 0 A . D . is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with 0 A . D . If not , see < http : //www.gnu.org/licenses/>.
*/
2009-04-11 10:00:39 -07:00
/**
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* FILE : NetSession . cpp
* PROJECT : 0 A . D .
* DESCRIPTION : Network session class implementation
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
// INCLUDES
# include "precompiled.h"
# include "NetSession.h"
# include "NetLog.h"
// DECLARATIONS
//-----------------------------------------------------------------------------
// Name: CNetHost()
// Desc: Constructor
//-----------------------------------------------------------------------------
CNetHost : : CNetHost ( void )
{
m_Host = NULL ;
}
//-----------------------------------------------------------------------------
// Name: ~CNetHost()
// Desc: Destructor
//-----------------------------------------------------------------------------
CNetHost : : ~ CNetHost ( void )
{
// Release host
if ( m_Host ) enet_host_destroy ( m_Host ) ;
m_Host = NULL ;
}
//-----------------------------------------------------------------------------
// Name: Create()
// Desc: Creates a client host
//-----------------------------------------------------------------------------
bool CNetHost : : Create ( void )
{
// Create ENet host
m_Host = enet_host_create ( NULL , 1 , 0 , 0 ) ;
if ( ! m_Host ) return false ;
return true ;
}
//-----------------------------------------------------------------------------
// Name: Create()
// Desc: Creates a server host
//-----------------------------------------------------------------------------
bool CNetHost : : Create ( uint port , uint maxPeers )
{
ENetAddress addr ;
// Bind to default host
addr . host = ENET_HOST_ANY ;
addr . port = port ;
// Create ENet server
m_Host = enet_host_create ( & addr , maxPeers , 0 , 0 ) ;
if ( ! m_Host ) return false ;
return true ;
}
//-----------------------------------------------------------------------------
// Name: Shutdown()
// Desc: Shuts down network server and releases any resources
//-----------------------------------------------------------------------------
void CNetHost : : Shutdown ( void )
{
// Destroy server
if ( m_Host ) enet_host_destroy ( m_Host ) ;
// Disconnect and release each peer
PeerSessionList : : iterator it = m_PeerSessions . begin ( ) ;
for ( ; it ! = m_PeerSessions . end ( ) ; it + + )
{
if ( ! it - > pSession ) continue ;
Disconnect ( it - > pSession ) ;
delete it - > pSession ;
}
m_PeerSessions . clear ( ) ;
m_Host = NULL ;
}
//-----------------------------------------------------------------------------
// Name: Connect()
// Desc: Connects to the specified remote host
// Note: Only clients use this method for connection to server
//-----------------------------------------------------------------------------
bool CNetHost : : Connect ( const CStr & host , uint port )
{
ENetEvent event ;
ENetAddress addr ;
PeerSession item ;
2010-05-19 17:59:01 -07:00
debug_assert ( m_Host ) ;
2009-04-11 10:00:39 -07:00
// Bind to specified host
addr . port = port ;
if ( enet_address_set_host ( & addr , host . c_str ( ) ) < 0 ) return false ;
// Initiate connection, allocate one channel
ENetPeer * pPeer = enet_host_connect ( m_Host , & addr , 1 ) ;
if ( ! pPeer ) return false ;
// Wait 3 seconds for the connection to succeed
if ( enet_host_service ( m_Host , & event , 5000 ) > 0 & &
event . type = = ENET_EVENT_TYPE_CONNECT )
{
// Connection succeeded
CNetSession * pNewSession = new CNetSession ( this , event . peer ) ;
if ( ! pNewSession ) return false ;
if ( ! SetupSession ( pNewSession ) ) return false ;
2009-11-06 02:59:10 -08:00
NET_LOG3 ( " Successfully connected to server %s:%d succeeded " , host . c_str ( ) , port ) ;
2009-04-11 10:00:39 -07:00
// Successfully handled?
if ( ! HandleConnect ( pNewSession ) )
{
delete pNewSession ;
return false ;
}
// Store the only server session
item . pPeer = event . peer ;
item . pSession = pNewSession ;
m_PeerSessions . push_back ( item ) ;
return true ;
}
2009-11-06 02:59:10 -08:00
NET_LOG3 ( " Connection to server %s:%d failed " , host . c_str ( ) , port ) ;
2009-04-11 10:00:39 -07:00
// 3 seconds are up or a host was disconnected
enet_peer_reset ( pPeer ) ;
return false ;
}
//-----------------------------------------------------------------------------
// Name: Disconnect()
// Desc: Disconnects the specified session from the host
//-----------------------------------------------------------------------------
bool CNetHost : : Disconnect ( CNetSession * pSession )
{
ENetEvent event ;
// Validate parameters
if ( ! pSession ) return false ;
2010-05-19 17:59:01 -07:00
debug_assert ( m_Host ) ;
debug_assert ( pSession - > m_Peer ) ;
2009-04-11 10:00:39 -07:00
// Disconnect peer
enet_peer_disconnect ( pSession - > m_Peer , 0 ) ;
// Allow up to 3 seconds for the disconnect to succeed
while ( enet_host_service ( m_Host , & event , 5000 ) > 0 )
{
switch ( event . type )
{
case ENET_EVENT_TYPE_RECEIVE :
// Drop any received packets
enet_packet_destroy ( event . packet ) ;
break ;
case ENET_EVENT_TYPE_DISCONNECT :
// Disconnect received for peer
if ( ! HandleDisconnect ( pSession ) ) return false ;
break ;
}
}
// Disconnect attempt didn't succeed, force connection down
enet_peer_reset ( pSession - > m_Peer ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Name: ProcessEvents()
// Desc: Wait for events and shuttles packets between the host and its peers
//-----------------------------------------------------------------------------
bool CNetHost : : Poll ( void )
{
ENetEvent event ;
CNetSession * pSession = NULL ;
PeerSession item ;
PeerSessionList : : iterator it ;
2010-05-19 17:59:01 -07:00
debug_assert ( m_Host ) ;
2009-04-11 10:00:39 -07:00
// Poll host for events
while ( enet_host_service ( m_Host , & event , 0 ) > 0 )
{
// Handle occured event
switch ( event . type )
{
case ENET_EVENT_TYPE_CONNECT :
// A new client has connected, handle it
pSession = new CNetSession ( this , event . peer ) ;
if ( ! pSession ) return false ;
// Setup new session
if ( ! SetupSession ( pSession ) ) return false ;
NET_LOG3 ( " A new client connected from %x:%u " , event . peer - > address . host , event . peer - > address . port ) ;
// Successfully handled?
if ( ! HandleConnect ( pSession ) ) return false ;
event . peer - > data = pSession ;
// Add new item to internal list
item . pPeer = event . peer ;
item . pSession = pSession ;
m_PeerSessions . push_back ( item ) ;
break ;
case ENET_EVENT_TYPE_DISCONNECT :
// Client has disconnected, handle it
it = m_PeerSessions . begin ( ) ; ;
for ( ; it ! = m_PeerSessions . end ( ) ; it + + )
{
// Is this our session?
if ( it - > pPeer = = event . peer )
{
2009-08-14 11:42:39 -07:00
NET_LOG2 ( " %p disconnected " , event . peer - > data ) ;
2009-04-11 10:00:39 -07:00
// Successfully handled?
if ( ! HandleDisconnect ( it - > pSession ) ) return false ;
m_PeerSessions . erase ( it ) ;
return true ;
}
}
break ;
case ENET_EVENT_TYPE_RECEIVE :
// A new data packet was received from client, handle message
it = m_PeerSessions . begin ( ) ;
for ( ; it ! = m_PeerSessions . end ( ) ; it + + )
{
// Is this our session?
if ( it - > pPeer = = event . peer )
{
2010-05-19 17:59:01 -07:00
bool ok = false ;
2009-04-11 10:00:39 -07:00
// Create message from raw data
CNetMessage * pNewMessage = CNetMessageFactory : : CreateMessage ( event . packet - > data , event . packet - > dataLength ) ;
2010-05-19 17:59:01 -07:00
if ( pNewMessage )
{
NET_LOG4 ( " Message %s of size %lu was received from %p " , pNewMessage - > ToString ( ) . c_str ( ) , ( unsigned long ) pNewMessage - > GetSerializedLength ( ) , event . peer - > data ) ;
2009-04-11 10:00:39 -07:00
2010-05-19 17:59:01 -07:00
ok = HandleMessageReceive ( pNewMessage , it - > pSession ) ;
2009-04-11 10:00:39 -07:00
2010-05-19 17:59:01 -07:00
delete pNewMessage ;
2009-04-11 10:00:39 -07:00
}
// Done using the packet
enet_packet_destroy ( event . packet ) ;
2010-05-19 17:59:01 -07:00
if ( ! ok )
return false ;
2009-04-11 10:00:39 -07:00
}
}
break ;
}
}
return true ;
}
//-----------------------------------------------------------------------------
// Name: Broadcast()
// Desc: Broadcast the specified message to connected clients
//-----------------------------------------------------------------------------
void CNetHost : : Broadcast ( const CNetMessage * pMessage )
{
// Validate parameters
if ( ! pMessage ) return ;
// Loop through the list of sessions and send the message to each
for ( uint i = 0 ; i < GetSessionCount ( ) ; i + + )
{
CNetSession * pCurrSession = GetSession ( i ) ;
if ( ! pCurrSession ) continue ;
SendMessage ( pCurrSession , pMessage ) ;
}
}
//-----------------------------------------------------------------------------
// Name: SendMessage()
// Desc: Sends the specified message to peer
//-----------------------------------------------------------------------------
bool CNetHost : : SendMessage (
const CNetSession * pSession ,
const CNetMessage * pMessage )
{
// Validate parameters
if ( ! pMessage | | ! pSession ) return false ;
2010-05-19 17:59:01 -07:00
debug_assert ( pSession - > m_Peer ) ;
debug_assert ( m_Host ) ;
2009-04-11 10:00:39 -07:00
size_t size = pMessage - > GetSerializedLength ( ) ;
2010-05-20 10:58:37 -07:00
debug_assert ( size ) ;
2009-04-11 10:00:39 -07:00
// Adjust buffer for message
2010-05-20 10:58:37 -07:00
m_Buffer . resize ( size ) ;
2009-04-11 10:00:39 -07:00
// Save message to internal buffer
2010-05-20 10:58:37 -07:00
pMessage - > Serialize ( & m_Buffer [ 0 ] ) ;
2009-04-11 10:00:39 -07:00
// Create a reliable packet
2010-05-20 10:58:37 -07:00
ENetPacket * pPacket = enet_packet_create ( & m_Buffer [ 0 ] , size , ENET_PACKET_FLAG_RELIABLE ) ;
2009-04-11 10:00:39 -07:00
if ( ! pPacket ) return false ;
// Let ENet send the message to peer
if ( enet_peer_send ( pSession - > m_Peer , ENET_DEFAULT_CHANNEL , pPacket ) < 0 )
{
// ENet failed to send the packet
NET_LOG ( " Failed to send ENet packet to peer " ) ;
return false ;
}
else
{
2009-11-06 02:59:10 -08:00
NET_LOG4 ( " Message %s of size %lu was sent to %p " ,
2010-05-20 10:58:37 -07:00
pMessage - > ToString ( ) . c_str ( ) , ( unsigned long ) size , pSession - > m_Peer - > data ) ;
2009-04-11 10:00:39 -07:00
}
enet_host_flush ( m_Host ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Name: ReceiveMessage()
// Desc: Receives a message from client if incoming packets are available
//-----------------------------------------------------------------------------
CNetMessage * CNetHost : : ReceiveMessage ( const CNetSession * pSession )
{
// Validate parameters
if ( ! pSession ) return NULL ;
2010-05-19 17:59:01 -07:00
debug_assert ( pSession - > m_Peer ) ;
2009-04-11 10:00:39 -07:00
// Let ENet receive a message from peer
ENetPacket * pPacket = enet_peer_receive ( pSession - > m_Peer , ENET_DEFAULT_CHANNEL ) ;
if ( ! pPacket ) return NULL ;
// Create new message
return CNetMessageFactory : : CreateMessage ( pPacket - > data , pPacket - > dataLength ) ;
}
//-----------------------------------------------------------------------------
// Name: SetupSession()
// Desc: Setup new session upon creation
//-----------------------------------------------------------------------------
bool CNetHost : : SetupSession ( CNetSession * UNUSED ( pSession ) )
{
return true ;
}
//-----------------------------------------------------------------------------
// Name: HandleConnect()
// Desc: Allow application to handle client connect
//-----------------------------------------------------------------------------
bool CNetHost : : HandleConnect ( CNetSession * UNUSED ( pSession ) )
{
return true ;
}
//-----------------------------------------------------------------------------
// Name: HandleDisconnect()
// Desc: Allow application to handle client disconnect
//-----------------------------------------------------------------------------
bool CNetHost : : HandleDisconnect ( CNetSession * UNUSED ( pSession ) )
{
return true ;
}
//-----------------------------------------------------------------------------
// Name: HandleMessageReceive()
// Desc: Allow application to handle message recive
//-----------------------------------------------------------------------------
bool CNetHost : : HandleMessageReceive (
CNetMessage * pMessage ,
CNetSession * pSession )
{
// Validate parameters
if ( ! pSession | | ! pMessage ) return false ;
// Update FSM
return pSession - > Update ( pMessage - > GetType ( ) , pMessage ) ;
}
//-----------------------------------------------------------------------------
// Name: GetSessionCount()
// Desc: Returns the number of sessions the host manages
//-----------------------------------------------------------------------------
uint CNetHost : : GetSessionCount ( void ) const
{
return ( uint ) m_PeerSessions . size ( ) ;
}
//-----------------------------------------------------------------------------
// Name: GetSession()
// Desc: Rteurns the session for the index
//-----------------------------------------------------------------------------
CNetSession * CNetHost : : GetSession ( uint index )
{
// Validate parameter
if ( index > = GetSessionCount ( ) ) return NULL ;
return m_PeerSessions [ index ] . pSession ;
} ;
//-----------------------------------------------------------------------------
// Name: CNetSession()
// Desc: Constructor
//-----------------------------------------------------------------------------
CNetSession : : CNetSession ( CNetHost * pHost , ENetPeer * pPeer )
{
m_Host = pHost ;
m_Peer = pPeer ;
m_ID = INVALID_SESSION ;
m_PlayerSlot = NULL ;
}
//-----------------------------------------------------------------------------
// Name: ~CNetSession()
// Desc: Destructor
//-----------------------------------------------------------------------------
CNetSession : : ~ CNetSession ( void )
{
// Release any resources
//if ( m_Host ) enet_host_destroy( m_Host );
//m_Host = NULL;
m_Peer = NULL ;
}
//-----------------------------------------------------------------------------
// Name: SetName()
// Desc: Set a new name for the session
//-----------------------------------------------------------------------------
void CNetSession : : SetName ( const CStr & name )
{
m_Name = name ;
}
//-----------------------------------------------------------------------------
// Name: SetID()
// Desc: Set new ID for this session
//-----------------------------------------------------------------------------
void CNetSession : : SetID ( uint ID )
{
m_ID = ID ;
}
//-----------------------------------------------------------------------------
// Name: SetPlayerSlot()
// Desc: Set the player slot for this session
//-----------------------------------------------------------------------------
void CNetSession : : SetPlayerSlot ( CPlayerSlot * pPlayerSlot )
{
m_PlayerSlot = pPlayerSlot ;
}
//-----------------------------------------------------------------------------
// Name: ScriptingInit()
// Desc:
//-----------------------------------------------------------------------------
void CNetSession : : ScriptingInit ( void )
{
AddProperty ( L " id " , & CNetSession : : m_ID ) ;
AddProperty ( L " name " , & CNetSession : : m_Name ) ;
CJSObject < CNetSession > : : ScriptingInit ( " NetSession " ) ;
}