2010-05-20 02:59:01 +02:00
/* Copyright (C) 2010 Wildfire Games.
2009-04-18 19:00:33 +02: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 19:00:39 +02:00
# include "precompiled.h"
2010-06-08 00:19:05 +02:00
# include "NetServer.h"
2010-06-30 23:41:04 +02:00
# include "NetClient.h"
# include "NetMessage.h"
2009-04-11 19:00:39 +02:00
# include "NetSession.h"
2010-07-03 15:15:01 +02:00
# include "NetStats.h"
2010-06-30 23:41:04 +02:00
# include "NetTurnManager.h"
2010-06-08 00:19:05 +02:00
# include "ps/CLogger.h"
2010-06-30 23:41:04 +02:00
# include "scriptinterface/ScriptInterface.h"
2010-05-20 02:59:01 +02:00
# include "simulation2/Simulation2.h"
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
# include <enet/enet.h>
# define DEFAULT_SERVER_NAME L"Unnamed Server"
# define DEFAULT_WELCOME_MESSAGE L"Welcome"
# define MAX_CLIENTS 8
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServer * g_NetServer = NULL ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
static CStr DebugName ( CNetServerSession * session )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
if ( session = = NULL )
return " [unknown host] " ;
if ( session - > GetGUID ( ) . empty ( ) )
return " [unauthed host] " ;
return " [ " + session - > GetGUID ( ) . substr ( 0 , 8 ) + " ...] " ;
}
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServer : : CNetServer ( ) :
2010-07-03 15:15:01 +02:00
m_ScriptInterface ( new ScriptInterface ( " Engine " ) ) , m_NextHostID ( 1 ) , m_Host ( NULL ) , m_Stats ( NULL )
2010-06-30 23:41:04 +02:00
{
m_State = SERVER_STATE_UNCONNECTED ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
m_ServerTurnManager = NULL ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
m_Port = PS_DEFAULT_PORT ;
m_ServerName = DEFAULT_SERVER_NAME ;
m_WelcomeMessage = DEFAULT_WELCOME_MESSAGE ;
2009-04-11 19:00:39 +02:00
}
CNetServer : : ~ CNetServer ( )
{
2010-07-03 15:15:01 +02:00
delete m_Stats ;
2010-07-02 23:28:48 +02:00
for ( size_t i = 0 ; i < m_Sessions . size ( ) ; + + i )
{
m_Sessions [ i ] - > Disconnect ( ) ;
delete m_Sessions [ i ] ;
}
if ( m_Host )
{
enet_host_destroy ( m_Host ) ;
}
2010-06-30 23:41:04 +02:00
m_GameAttributes = CScriptValRooted ( ) ; // clear root before deleting its context
delete m_ScriptInterface ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
bool CNetServer : : SetupConnection ( )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
debug_assert ( m_State = = SERVER_STATE_UNCONNECTED ) ;
debug_assert ( ! m_Host ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// Bind to default host
ENetAddress addr ;
addr . host = ENET_HOST_ANY ;
addr . port = m_Port ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// Create ENet server
m_Host = enet_host_create ( & addr , MAX_CLIENTS , 0 , 0 ) ;
if ( ! m_Host )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
LOGERROR ( L " Net server: enet_host_create failed " ) ;
2009-04-11 19:00:39 +02:00
return false ;
}
2010-07-03 15:15:01 +02:00
m_Stats = new CNetStatsTable ( m_Host ) ;
g_ProfileViewer . AddRootTable ( m_Stats ) ;
2010-06-30 23:41:04 +02:00
m_State = SERVER_STATE_PREGAME ;
2009-04-11 19:00:39 +02:00
return true ;
}
2010-06-30 23:41:04 +02:00
bool CNetServer : : SendMessage ( ENetPeer * peer , const CNetMessage * message )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
debug_assert ( m_Host ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServerSession * session = static_cast < CNetServerSession * > ( peer - > data ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
return CNetHost : : SendMessage ( message , peer , DebugName ( session ) . c_str ( ) ) ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
bool CNetServer : : Broadcast ( const CNetMessage * message )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
debug_assert ( m_Host ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
bool ok = true ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// Send to all sessions that are active and has finished authentication
for ( size_t i = 0 ; i < m_Sessions . size ( ) ; + + i )
{
if ( m_Sessions [ i ] - > GetCurrState ( ) = = NSS_PREGAME | | m_Sessions [ i ] - > GetCurrState ( ) = = NSS_INGAME )
{
if ( ! m_Sessions [ i ] - > SendMessage ( message ) )
ok = false ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// TODO: this does lots of repeated message serialisation if we have lots
// of remote peers; could do it more efficiently if that's a real problem
}
}
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
return ok ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
void CNetServer : : Poll ( )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
debug_assert ( m_Host ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
for ( size_t i = 0 ; i < m_LocalMessageQueue . size ( ) ; + + i )
{
CNetMessage * msg = m_LocalMessageQueue [ i ] . second ;
CNetServerSession * session = m_LocalMessageQueue [ i ] . first ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
LOGMESSAGE ( L " Net server: Received local message %hs of size %lu from %hs " , msg - > ToString ( ) . c_str ( ) , ( unsigned long ) msg - > GetSerializedLength ( ) , DebugName ( session ) . c_str ( ) ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
bool ok = HandleMessageReceive ( msg , session ) ;
debug_assert ( ok ) ; // TODO
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
delete msg ;
}
m_LocalMessageQueue . clear ( ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// Poll host for events
ENetEvent event ;
while ( enet_host_service ( m_Host , & event , 0 ) > 0 )
{
switch ( event . type )
{
case ENET_EVENT_TYPE_CONNECT :
{
// If this is a new client to our server, save the peer reference
if ( std : : find ( m_Peers . begin ( ) , m_Peers . end ( ) , event . peer ) = = m_Peers . end ( ) )
m_Peers . push_back ( event . peer ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// Report the client address
char hostname [ 256 ] = " (error) " ;
enet_address_get_host_ip ( & event . peer - > address , hostname , ARRAY_SIZE ( hostname ) ) ;
LOGMESSAGE ( L " Net server: Received connection from %hs:%u " , hostname , event . peer - > address . port ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// Set up a session object for this peer
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServerSession * session = new CNetServerSessionRemote ( * this , event . peer ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
SetupSession ( session ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
if ( ! HandleConnect ( session ) )
{
delete session ;
break ;
}
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
debug_assert ( event . peer - > data = = NULL ) ;
event . peer - > data = session ;
break ;
}
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
case ENET_EVENT_TYPE_DISCONNECT :
{
// Delete from our peer list
m_Peers . erase ( remove ( m_Peers . begin ( ) , m_Peers . end ( ) , event . peer ) , m_Peers . end ( ) ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// If there is an active session with this peer, then reset and delete it
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServerSession * session = static_cast < CNetServerSession * > ( event . peer - > data ) ;
if ( session )
{
LOGMESSAGE ( L " Net server: %hs disconnected " , DebugName ( session ) . c_str ( ) ) ;
2009-04-11 19:00:39 +02:00
2010-07-02 23:28:48 +02:00
session - > Update ( ( uint ) NMT_CONNECTION_LOST , NULL ) ;
2010-06-30 23:41:04 +02:00
HandleDisconnect ( session ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
delete session ;
event . peer - > data = NULL ;
}
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
break ;
}
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
case ENET_EVENT_TYPE_RECEIVE :
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
// If there is an active session with this peer, then process the message
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServerSession * session = static_cast < CNetServerSession * > ( event . peer - > data ) ;
if ( session )
{
// Create message from raw data
CNetMessage * msg = CNetMessageFactory : : CreateMessage ( event . packet - > data , event . packet - > dataLength , GetScriptInterface ( ) ) ;
if ( msg )
{
LOGMESSAGE ( L " Net server: Received message %hs of size %lu from %hs " , msg - > ToString ( ) . c_str ( ) , ( unsigned long ) msg - > GetSerializedLength ( ) , DebugName ( session ) . c_str ( ) ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
bool ok = HandleMessageReceive ( msg , session ) ;
debug_assert ( ok ) ; // TODO
delete msg ;
}
}
// Done using the packet
enet_packet_destroy ( event . packet ) ;
2009-04-11 19:00:39 +02:00
break ;
}
}
}
}
2010-06-30 23:41:04 +02:00
void CNetServer : : AddLocalClientSession ( CNetClientSessionLocal & clientSession )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
LOGMESSAGE ( L " Net server: Received local connection " ) ;
CNetServerSessionLocal * session = new CNetServerSessionLocal ( * this , clientSession ) ;
clientSession . SetServerSession ( session ) ;
SetupSession ( session ) ;
HandleConnect ( session ) ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
void CNetServer : : SendLocalMessage ( CNetClientSessionLocal & clientSession , const CNetMessage * message )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
CNetMessage * clonedMessage = CNetMessageFactory : : CloneMessage ( message , GetScriptInterface ( ) ) ;
if ( ! clonedMessage )
return ;
m_LocalMessageQueue . push_back ( std : : make_pair ( clientSession . GetServerSession ( ) , clonedMessage ) ) ;
}
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
bool CNetServer : : HandleMessageReceive ( const CNetMessage * message , CNetServerSession * session )
{
// Update FSM
bool ok = session - > Update ( message - > GetType ( ) , ( void * ) message ) ;
if ( ! ok )
LOGERROR ( L " Net server: Error running FSM update (type=%d state=%d) " , ( int ) message - > GetType ( ) , ( int ) session - > GetCurrState ( ) ) ;
return ok ;
}
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
void CNetServer : : SetupSession ( CNetServerSession * session )
{
void * context = session ;
// Set up transitions for session
2010-07-02 23:28:48 +02:00
session - > AddTransition ( NSS_HANDSHAKE , ( uint ) NMT_CONNECTION_LOST , NSS_UNCONNECTED , ( void * ) & OnDisconnect , context ) ;
2010-06-30 23:41:04 +02:00
session - > AddTransition ( NSS_HANDSHAKE , ( uint ) NMT_CLIENT_HANDSHAKE , NSS_AUTHENTICATE , ( void * ) & OnClientHandshake , context ) ;
2010-07-02 23:28:48 +02:00
2010-06-30 23:41:04 +02:00
session - > AddTransition ( NSS_AUTHENTICATE , ( uint ) NMT_AUTHENTICATE , NSS_PREGAME , ( void * ) & OnAuthenticate , context ) ;
2010-07-02 23:28:48 +02:00
session - > AddTransition ( NSS_PREGAME , ( uint ) NMT_CONNECTION_LOST , NSS_UNCONNECTED , ( void * ) & OnDisconnect , context ) ;
2010-06-30 23:41:04 +02:00
session - > AddTransition ( NSS_PREGAME , ( uint ) NMT_CHAT , NSS_PREGAME , ( void * ) & OnChat , context ) ;
session - > AddTransition ( NSS_PREGAME , ( uint ) NMT_LOADED_GAME , NSS_INGAME , ( void * ) & OnLoadedGame , context ) ;
2010-07-02 23:28:48 +02:00
session - > AddTransition ( NSS_INGAME , ( uint ) NMT_CONNECTION_LOST , NSS_UNCONNECTED , ( void * ) & OnDisconnect , context ) ;
2010-06-30 23:41:04 +02:00
session - > AddTransition ( NSS_INGAME , ( uint ) NMT_CHAT , NSS_INGAME , ( void * ) & OnChat , context ) ;
session - > AddTransition ( NSS_INGAME , ( uint ) NMT_SIMULATION_COMMAND , NSS_INGAME , ( void * ) & OnInGame , context ) ;
session - > AddTransition ( NSS_INGAME , ( uint ) NMT_SYNC_CHECK , NSS_INGAME , ( void * ) & OnInGame , context ) ;
session - > AddTransition ( NSS_INGAME , ( uint ) NMT_END_COMMAND_BATCH , NSS_INGAME , ( void * ) & OnInGame , context ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// Set first state
session - > SetFirstState ( NSS_HANDSHAKE ) ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
bool CNetServer : : HandleConnect ( CNetServerSession * session )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
m_Sessions . push_back ( session ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// Player joined the game, start authentication
CSrvHandshakeMessage handshake ;
handshake . m_Magic = PS_PROTOCOL_MAGIC ;
handshake . m_ProtocolVersion = PS_PROTOCOL_VERSION ;
handshake . m_SoftwareVersion = PS_PROTOCOL_VERSION ;
return session - > SendMessage ( & handshake ) ;
}
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
bool CNetServer : : HandleDisconnect ( CNetServerSession * session )
{
m_Sessions . erase ( remove ( m_Sessions . begin ( ) , m_Sessions . end ( ) , session ) , m_Sessions . end ( ) ) ;
2009-04-11 19:00:39 +02:00
return true ;
}
2010-06-30 23:41:04 +02:00
void CNetServer : : OnUserJoin ( CNetServerSession * session )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
AddPlayer ( session - > GetGUID ( ) , session - > GetUserName ( ) ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CGameSetupMessage gameSetupMessage ( GetScriptInterface ( ) ) ;
gameSetupMessage . m_Data = m_GameAttributes ;
session - > SendMessage ( & gameSetupMessage ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CPlayerAssignmentMessage assignMessage ;
ConstructPlayerAssignmentMessage ( assignMessage ) ;
session - > SendMessage ( & assignMessage ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
OnAddPlayer ( ) ;
2009-04-11 19:00:39 +02:00
}
2010-07-02 23:28:48 +02:00
void CNetServer : : OnUserLeave ( CNetServerSession * session )
{
RemovePlayer ( session - > GetGUID ( ) ) ;
OnRemovePlayer ( ) ;
}
2010-06-30 23:41:04 +02:00
void CNetServer : : AddPlayer ( const CStr & guid , const CStrW & name )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
// Find the first free player ID
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
std : : set < i32 > usedIDs ;
for ( PlayerAssignmentMap : : iterator it = m_PlayerAssignments . begin ( ) ; it ! = m_PlayerAssignments . end ( ) ; + + it )
usedIDs . insert ( it - > second . m_PlayerID ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
i32 playerID ;
for ( playerID = 1 ; usedIDs . find ( playerID ) ! = usedIDs . end ( ) ; + + playerID )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
// (do nothing)
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
PlayerAssignment assignment ;
assignment . m_Name = name ;
assignment . m_PlayerID = playerID ;
m_PlayerAssignments [ guid ] = assignment ;
// Send the new assignments to all currently active players
// (which does not include the one that's just joining)
SendPlayerAssignments ( ) ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
void CNetServer : : RemovePlayer ( const CStr & guid )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
m_PlayerAssignments . erase ( guid ) ;
2009-04-11 19:00:39 +02:00
2010-07-02 23:28:48 +02:00
SendPlayerAssignments ( ) ;
2010-06-30 23:41:04 +02:00
OnRemovePlayer ( ) ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
void CNetServer : : AssignPlayer ( int playerID , const CStr & guid )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
// Remove anyone who's already assigned to this player
for ( PlayerAssignmentMap : : iterator it = m_PlayerAssignments . begin ( ) ; it ! = m_PlayerAssignments . end ( ) ; + + it )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
if ( it - > second . m_PlayerID = = playerID )
it - > second . m_PlayerID = - 1 ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
// Update this host's assignment if it exists
if ( m_PlayerAssignments . find ( guid ) ! = m_PlayerAssignments . end ( ) )
m_PlayerAssignments [ guid ] . m_PlayerID = playerID ;
SendPlayerAssignments ( ) ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
void CNetServer : : ConstructPlayerAssignmentMessage ( CPlayerAssignmentMessage & message )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
for ( PlayerAssignmentMap : : iterator it = m_PlayerAssignments . begin ( ) ; it ! = m_PlayerAssignments . end ( ) ; + + it )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
CPlayerAssignmentMessage : : S_m_Hosts h ;
h . m_GUID = it - > first ;
h . m_Name = it - > second . m_Name ;
h . m_PlayerID = it - > second . m_PlayerID ;
message . m_Hosts . push_back ( h ) ;
2009-04-11 19:00:39 +02:00
}
}
2010-06-30 23:41:04 +02:00
void CNetServer : : SendPlayerAssignments ( )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
CPlayerAssignmentMessage message ;
ConstructPlayerAssignmentMessage ( message ) ;
Broadcast ( & message ) ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
ScriptInterface & CNetServer : : GetScriptInterface ( )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
return * m_ScriptInterface ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
bool CNetServer : : OnClientHandshake ( void * context , CFsmEvent * event )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
debug_assert ( event - > GetType ( ) = = ( uint ) NMT_CLIENT_HANDSHAKE ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServerSession * session = ( CNetServerSession * ) context ;
CNetServer & server = session - > GetServer ( ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CCliHandshakeMessage * message = ( CCliHandshakeMessage * ) event - > GetParamRef ( ) ;
if ( message - > m_ProtocolVersion ! = PS_PROTOCOL_VERSION )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
// TODO: probably should report some error message (either locally or to the client)
session - > Disconnect ( ) ;
}
else
{
CSrvHandshakeResponseMessage handshakeResponse ;
handshakeResponse . m_UseProtocolVersion = PS_PROTOCOL_VERSION ;
handshakeResponse . m_Message = server . m_WelcomeMessage ;
handshakeResponse . m_Flags = 0 ;
session - > SendMessage ( & handshakeResponse ) ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
return true ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
bool CNetServer : : OnAuthenticate ( void * context , CFsmEvent * event )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
debug_assert ( event - > GetType ( ) = = ( uint ) NMT_AUTHENTICATE ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServerSession * session = ( CNetServerSession * ) context ;
CNetServer & server = session - > GetServer ( ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CAuthenticateMessage * message = ( CAuthenticateMessage * ) event - > GetParamRef ( ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// TODO: check server password etc?
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
u32 newHostID = server . m_NextHostID + + ;
2009-04-11 19:00:39 +02:00
2010-07-02 23:28:48 +02:00
CStrW username = server . DeduplicatePlayerName ( SanitisePlayerName ( message - > m_Name ) ) ;
session - > SetUserName ( username ) ;
2010-06-30 23:41:04 +02:00
session - > SetGUID ( message - > m_GUID ) ;
session - > SetHostID ( newHostID ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CAuthenticateResultMessage authenticateResult ;
authenticateResult . m_Code = ARC_OK ;
authenticateResult . m_HostID = newHostID ;
authenticateResult . m_Message = L " Logged in " ;
session - > SendMessage ( & authenticateResult ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
server . OnUserJoin ( session ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
return true ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
bool CNetServer : : OnInGame ( void * context , CFsmEvent * event )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
// TODO: should split each of these cases into a separate method
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServerSession * session = ( CNetServerSession * ) context ;
CNetServer & server = session - > GetServer ( ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetMessage * message = ( CNetMessage * ) event - > GetParamRef ( ) ;
if ( message - > GetType ( ) = = ( uint ) NMT_SIMULATION_COMMAND )
{
CSimulationMessage * simMessage = static_cast < CSimulationMessage * > ( message ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// Send it back to all clients immediately
server . Broadcast ( simMessage ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// TODO: we should do some validation of ownership (clients can't send commands on behalf of opposing players)
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// TODO: we shouldn't send the message back to the client that first sent it
}
else if ( message - > GetType ( ) = = ( uint ) NMT_SYNC_CHECK )
{
CSyncCheckMessage * syncMessage = static_cast < CSyncCheckMessage * > ( message ) ;
server . m_ServerTurnManager - > NotifyFinishedClientUpdate ( session - > GetHostID ( ) , syncMessage - > m_Turn , syncMessage - > m_Hash ) ;
}
else if ( message - > GetType ( ) = = ( uint ) NMT_END_COMMAND_BATCH )
{
CEndCommandBatchMessage * endMessage = static_cast < CEndCommandBatchMessage * > ( message ) ;
server . m_ServerTurnManager - > NotifyFinishedClientCommands ( session - > GetHostID ( ) , endMessage - > m_Turn ) ;
}
return true ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
bool CNetServer : : OnChat ( void * context , CFsmEvent * event )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
debug_assert ( event - > GetType ( ) = = ( uint ) NMT_CHAT ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServerSession * session = ( CNetServerSession * ) context ;
CNetServer & server = session - > GetServer ( ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CChatMessage * message = ( CChatMessage * ) event - > GetParamRef ( ) ;
2010-07-02 23:28:48 +02:00
2010-06-30 23:41:04 +02:00
message - > m_Sender = session - > GetUserName ( ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
server . Broadcast ( message ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
return true ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
bool CNetServer : : OnLoadedGame ( void * context , CFsmEvent * event )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
debug_assert ( event - > GetType ( ) = = ( uint ) NMT_LOADED_GAME ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CNetServerSession * session = ( CNetServerSession * ) context ;
CNetServer & server = session - > GetServer ( ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
server . CheckGameLoadStatus ( session ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
return true ;
2009-04-11 19:00:39 +02:00
}
2010-07-02 23:28:48 +02:00
bool CNetServer : : OnDisconnect ( void * context , CFsmEvent * event )
{
debug_assert ( event - > GetType ( ) = = ( uint ) NMT_CONNECTION_LOST ) ;
CNetServerSession * session = ( CNetServerSession * ) context ;
CNetServer & server = session - > GetServer ( ) ;
// If the user had authenticated, we need to handle their leaving
if ( session - > GetCurrState ( ) = = NSS_PREGAME | | session - > GetCurrState ( ) = = NSS_INGAME )
server . OnUserLeave ( session ) ;
return true ;
}
2010-06-30 23:41:04 +02:00
void CNetServer : : CheckGameLoadStatus ( CNetServerSession * changedSession )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
for ( size_t i = 0 ; i < m_Sessions . size ( ) ; + + i )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
if ( m_Sessions [ i ] ! = changedSession & & m_Sessions [ i ] - > GetCurrState ( ) ! = NSS_INGAME )
return ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
CLoadedGameMessage loaded ;
Broadcast ( & loaded ) ;
m_State = SERVER_STATE_INGAME ;
2009-04-11 19:00:39 +02:00
}
2010-06-30 23:41:04 +02:00
void CNetServer : : StartGame ( )
2009-04-11 19:00:39 +02:00
{
2010-06-30 23:41:04 +02:00
m_ServerTurnManager = new CNetServerTurnManager ( * this ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
for ( size_t i = 0 ; i < m_Sessions . size ( ) ; + + i )
m_ServerTurnManager - > InitialiseClient ( m_Sessions [ i ] - > GetHostID ( ) ) ; // TODO: only for non-observers
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
m_State = SERVER_STATE_LOADING ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
// Send the final setup state to all clients
UpdateGameAttributes ( m_GameAttributes ) ;
SendPlayerAssignments ( ) ;
2009-04-11 19:00:39 +02:00
2010-06-30 23:41:04 +02:00
CGameStartMessage gameStart ;
Broadcast ( & gameStart ) ;
}
void CNetServer : : UpdateGameAttributes ( const CScriptValRooted & attrs )
{
m_GameAttributes = attrs ;
if ( ! m_Host )
return ;
CGameSetupMessage gameSetupMessage ( GetScriptInterface ( ) ) ;
gameSetupMessage . m_Data = m_GameAttributes ;
Broadcast ( & gameSetupMessage ) ;
2009-04-11 19:00:39 +02:00
}
2010-07-02 23:28:48 +02:00
CStrW CNetServer : : SanitisePlayerName ( const CStrW & original )
{
const size_t MAX_LENGTH = 32 ;
CStrW name = original ;
name . Replace ( L " [ " , L " { " ) ; // remove GUI tags
name . Replace ( L " ] " , L " } " ) ; // remove for symmetry
// Restrict the length
if ( name . length ( ) > MAX_LENGTH )
name = name . Left ( MAX_LENGTH ) ;
// Don't allow surrounding whitespace
name . Trim ( PS_TRIM_BOTH ) ;
// Don't allow empty name
if ( name . empty ( ) )
name = L " Anonymous " ;
return name ;
}
CStrW CNetServer : : DeduplicatePlayerName ( const CStrW & original )
{
CStrW name = original ;
size_t id = 2 ;
while ( true )
{
bool unique = true ;
for ( size_t i = 0 ; i < m_Sessions . size ( ) ; + + i )
{
if ( m_Sessions [ i ] - > GetUserName ( ) = = name )
{
unique = false ;
break ;
}
}
if ( unique )
return name ;
name = original + L " ( " + CStrW ( id + + ) + L " ) " ;
}
}