// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdnet.h" #include "nel/misc/types_nl.h" #include "nel/net/callback_net_base.h" #include "nel/net/callback_client.h" #include "nel/net/net_log.h" #ifdef USE_MESSAGE_RECORDER #include "nel/net/message_recorder.h" #endif namespace NLNET { /* * Constructor */ CCallbackClient::CCallbackClient( TRecordingState rec, const std::string& recfilename, bool recordall, bool initPipeForDataAvailable ) : CCallbackNetBase( rec, recfilename, recordall ), CBufClient( true, rec==Replay, initPipeForDataAvailable ) { LockDeletion = false; CBufClient::setDisconnectionCallback (_NewDisconnectionCallback, this); _IsAServer = false; _DefaultCallback = NULL; } CCallbackClient::~CCallbackClient() { nlassert(!LockDeletion); } /* * Send a message to the remote host (pushing to its send queue) * Recorded : YES * Replayed : MAYBE */ void CCallbackClient::send (const CMessage &buffer, TSockId hostid, bool /* log */) { nlassert (hostid == InvalidSockId); // should always be InvalidSockId on client nlassert (connected ()); nlassert (buffer.length() != 0); nlassert (buffer.typeIsSet()); _BytesSent += buffer.length (); // if (log) { // nldebug ("LNETL3C: Client: send(%s)", buffer.toString().c_str()); // nldebug ("send message number %u", SendNextValue); } #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState != Replay ) { #endif // Send CBufClient::send (buffer); #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState == Record ) { // Record sent message _MR_Recorder.recordNext( _MR_UpdateCounter, Sending, hostid, const_cast(buffer) ); } } #endif } /* * Force to send all data pending in the send queue. * Recorded : NO * Replayed : NO */ bool CCallbackClient::flush (TSockId hostid, uint *nbBytesRemaining) { nlassert (hostid == InvalidSockId); // should always be InvalidSockId on client #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState != Replay ) { #endif // Flush sending (nothing to do in replay mode) return CBufClient::flush( nbBytesRemaining ); #ifdef USE_MESSAGE_RECORDER } else { return true; } #endif } /* * Updates the network (call this method evenly) * Recorded : YES (in baseUpdate()) * Replayed : YES (in baseUpdate()) */ void CCallbackClient::update2 ( sint32 timeout, sint32 mintime ) { LockDeletion = true; // nldebug ("L3: Client: update()"); H_AUTO(L3UpdateClient2); baseUpdate2 (timeout, mintime); // first receive #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState != Replay ) { #endif // L1-2 Update (nothing to do in replay mode) CBufClient::update (); // then send #ifdef USE_MESSAGE_RECORDER } #endif LockDeletion = false; } /* * Updates the network (call this method evenly) (legacy) * Recorded : YES (in baseUpdate()) * Replayed : YES (in baseUpdate()) */ void CCallbackClient::update ( sint32 timeout ) { LockDeletion = true; // nldebug ("L3: Client: update()"); H_AUTO(L3UpdateClient); baseUpdate (timeout); // first receive #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState != Replay ) { #endif // L1-2 Update (nothing to do in replay mode) CBufClient::update (); // then send #ifdef USE_MESSAGE_RECORDER } #endif LockDeletion = false; } /* * Returns true if there are messages to read * Recorded : NO * Replayed : YES */ bool CCallbackClient::dataAvailable () { #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState != Replay ) { #endif // Real dataAvailable() return CBufClient::dataAvailable (); #ifdef USE_MESSAGE_RECORDER } else { // Simulated dataAvailable() return CCallbackNetBase::replayDataAvailable(); } #endif } /* * Read the next message in the receive queue * Recorded : YES * Replayed : YES */ void CCallbackClient::receive (CMessage &buffer, TSockId *hostid) { // nlassert (connected ()); *hostid = InvalidSockId; #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState != Replay ) { #endif // Receive CBufClient::receive (buffer); // debug features, we number all packet to be sure that they are all sent and received // \todo remove this debug feature when ok #ifdef NL_BIG_ENDIAN uint32 val = NLMISC_BSWAP32(*(uint32*)buffer.buffer ()); #else uint32 val = *(uint32*)buffer.buffer (); #endif #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState == Record ) { // Record received message _MR_Recorder.recordNext( _MR_UpdateCounter, Receiving, *hostid, const_cast(buffer) ); } } else { // Retrieve received message loaded by dataAvailable() buffer = _MR_Recorder.ReceivedMessages.front().Message; _MR_Recorder.ReceivedMessages.pop(); } #endif buffer.readType (); } /* * */ TSockId CCallbackClient::getSockId (TSockId hostid) { nlassert (hostid == InvalidSockId); return id (); } /* * Connect to the specified host * Recorded : YES * Replayed : YES */ void CCallbackClient::connect( const CInetAddress& addr ) { #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState != Replay ) { try { #endif // Connect CBufClient::connect( addr ); #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState == Record ) { // Record connection CMessage addrmsg; addrmsg.serial( const_cast(addr) ); _MR_Recorder.recordNext( _MR_UpdateCounter, Connecting, _BufSock, addrmsg ); } } catch (const ESocketConnectionFailed&) { if ( _MR_RecordingState == Record ) { // Record connection CMessage addrmsg; addrmsg.serial( const_cast(addr) ); _MR_Recorder.recordNext( _MR_UpdateCounter, ConnFailing, _BufSock, addrmsg ); } throw; } } else { // Check the connection : failure or not TNetworkEvent event = _MR_Recorder.replayConnectionAttempt( addr ); switch ( event ) { case Connecting : // Set the remote address nlassert( ! _BufSock->Sock->connected() ); _BufSock->connect( addr, _NoDelay, true ); _PrevBytesDownloaded = 0; _PrevBytesUploaded = 0; /*_PrevBytesReceived = 0; _PrevBytesSent = 0;*/ break; case ConnFailing : throw ESocketConnectionFailed( addr ); //break; default : nlwarning( "LNETL3C: No connection event in replay data, at update #%"NL_I64"u", _MR_UpdateCounter ); } } #endif } /* * Disconnect a connection * Recorded : YES * Replayed : YES */ void CCallbackClient::disconnect( TSockId hostid ) { nlassert (hostid == InvalidSockId); // should always be InvalidSockId on client // Disconnect only if connected (same as physically connected for the client) if ( _BufSock->connectedState() ) { #ifdef USE_MESSAGE_RECORDER if ( _MR_RecordingState != Replay ) { #endif // Disconnect CBufClient::disconnect (); #ifdef USE_MESSAGE_RECORDER } else { // Read (skip) disconnection in the file if ( ! (_MR_Recorder.checkNextOne( _MR_UpdateCounter ) == Disconnecting) ) { nlwarning( "LNETL3C: No disconnection event in the replay data, at update #%"NL_I64"u", _MR_UpdateCounter ); } } // Record or replay disconnection (because disconnect() in the client does not push a disc. event) noticeDisconnection( _BufSock ); #endif } } #ifdef USE_MESSAGE_RECORDER /* * replay connection and disconnection callbacks, client version */ bool CCallbackClient::replaySystemCallbacks() { do { if ( _MR_Recorder.ReceivedMessages.empty() ) { return false; } else { switch( _MR_Recorder.ReceivedMessages.front().Event ) { case Receiving: return true; case Disconnecting: LNETL3_DEBUG( "LNETL3C: Disconnection event" ); _BufSock->setConnectedState( false ); // Call callback if needed if ( disconnectionCallback() != NULL ) { disconnectionCallback()( id(), argOfDisconnectionCallback() ); } break; default: nlerror( "LNETL3C: Invalid system event type in client receive queue" ); } // Extract system event _MR_Recorder.ReceivedMessages.pop(); } } while ( true ); } #endif // USE_MESSAGE_RECORDER } // NLNET