khanat-opennel-code/code/nel/src/net/callback_server.cpp
2010-05-06 02:08:41 +02:00

382 lines
9.2 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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 <http://www.gnu.org/licenses/>.
#include "stdnet.h"
#include "nel/net/callback_server.h"
#include "nel/net/net_log.h"
#ifdef USE_MESSAGE_RECORDER
#include "nel/net/dummy_tcp_sock.h"
#endif
using namespace std;
using namespace NLMISC;
namespace NLNET {
/*
* Connection callback (the disconnection callback is in callback_net_base.cpp
*/
void cbsNewConnection (TSockId from, void *data)
{
nlassert (data != NULL);
CCallbackServer *server = (CCallbackServer *)data;
LNETL3_DEBUG("LNETL3S: newConnection()");
#ifdef USE_MESSAGE_RECORDER
// Record connection
server->noticeConnection( from );
#endif
// send all my association to the new client
// association are disactivated so we don t need to send them
// server->sendAllMyAssociations (from);
// call the client callback if necessary
if (server->_ConnectionCallback != NULL)
server->_ConnectionCallback (from, server->_ConnectionCbArg);
}
/*
* Constructor
*/
CCallbackServer::CCallbackServer( TRecordingState rec, const string& recfilename, bool recordall, bool initPipeForDataAvailable ) :
CCallbackNetBase( rec, recfilename, recordall ),
CBufServer( DEFAULT_STRATEGY, DEFAULT_MAX_THREADS, DEFAULT_MAX_SOCKETS_PER_THREADS, true, rec==Replay, initPipeForDataAvailable ),
_ConnectionCallback(NULL),
_ConnectionCbArg(NULL)
{
#ifndef USE_MESSAGE_RECORDER
nlassertex( rec==Off, ("LNETL3S: The message recorder is disabled at compilation time ; switch the recording state Off") );
#endif
CBufServer::setDisconnectionCallback (_NewDisconnectionCallback, this);
CBufServer::setConnectionCallback (cbsNewConnection, this);
_IsAServer = true;
_DefaultCallback = NULL;
}
/*
* Send a message to the specified host (pushing to its send queue)
* Recorded : YES
* Replayed : MAYBE
*/
void CCallbackServer::send (const CMessage &buffer, TSockId hostid, bool /* log */)
{
nlassert (connected ());
nlassert (buffer.length() != 0);
nlassert (buffer.typeIsSet());
if (hostid == InvalidSockId)
{
// broadcast
sint nb = nbConnections ();
_BytesSent += buffer.length () * nb;
}
else
{
_BytesSent += buffer.length ();
}
// if (log)
{
// LNETL3_DEBUG ("LNETL3S: Server: send(%s, %s)", buffer.toString().c_str(), hostid->asString().c_str());
}
#ifdef USE_MESSAGE_RECORDER
if ( _MR_RecordingState != Replay )
{
#endif
// Send
CBufServer::send (buffer, hostid);
#ifdef USE_MESSAGE_RECORDER
if ( _MR_RecordingState == Record )
{
// Record sent message
_MR_Recorder.recordNext( _MR_UpdateCounter, Sending, hostid, const_cast<CMessage&>(buffer) );
}
}
#endif
}
/*
* Updates the network (call this method evenly)
* Recorded : YES (in baseUpdate())
* Replayed : YES (in baseUpdate())
*/
void CCallbackServer::update2 ( sint32 timeout, sint32 mintime )
{
H_AUTO(L3UpdateServer);
nlassert (connected ());
// LNETL3_DEBUG ("LNETL3S: Client: update()");
baseUpdate2 ( timeout, mintime ); // first receive
#ifdef USE_MESSAGE_RECORDER
if ( _MR_RecordingState != Replay )
{
#endif
// L1-2 Update (nothing to do in replay mode)
CBufServer::update (); // then send
#ifdef USE_MESSAGE_RECORDER
}
#endif
}
/*
* Updates the network (call this method evenly) (legacy)
* Recorded : YES (in baseUpdate())
* Replayed : YES (in baseUpdate())
*/
void CCallbackServer::update ( sint32 timeout )
{
H_AUTO(L3UpdateServer);
nlassert (connected ());
// LNETL3_DEBUG ("LNETL3S: Client: update()");
baseUpdate ( timeout ); // first receive
#ifdef USE_MESSAGE_RECORDER
if ( _MR_RecordingState != Replay )
{
#endif
// L1-2 Update (nothing to do in replay mode)
CBufServer::update (); // then send
#ifdef USE_MESSAGE_RECORDER
}
#endif
}
/*
* Read the next message in the receive queue
* Recorded : YES
* Replayed : YES
*/
void CCallbackServer::receive (CMessage &buffer, TSockId *hostid)
{
nlassert (connected ());
#ifdef USE_MESSAGE_RECORDER
if ( _MR_RecordingState != Replay )
{
#endif
// Receive
CBufServer::receive (buffer, hostid);
#ifdef USE_MESSAGE_RECORDER
if ( _MR_RecordingState == Record )
{
// Record received message
_MR_Recorder.recordNext( _MR_UpdateCounter, Receiving, *hostid, const_cast<CMessage&>(buffer) );
}
}
else
{
// Retrieve received message loaded by dataAvailable()
buffer = _MR_Recorder.ReceivedMessages.front().Message;
*hostid = _MR_Recorder.ReceivedMessages.front().SockId;
_MR_Recorder.ReceivedMessages.pop();
}
#endif
buffer.readType ();
}
/*
* Disconnect a connection
* Set hostid to InvalidSockId to disconnect all connections.
* If hostid is not null and the socket is not connected, the method does nothing.
* Before disconnecting, any pending data is actually sent.
* Recorded : YES in noticeDisconnection called in the disconnection callback
* Replayed : YES in noticeDisconnection called in the disconnection callback
*/
void CCallbackServer::disconnect( TSockId hostid )
{
#ifdef USE_MESSAGE_RECORDER
if ( _MR_RecordingState != Replay )
{
#endif
// Disconnect
CBufServer::disconnect( hostid );
#ifdef USE_MESSAGE_RECORDER
}
// else, no need to manually replay the disconnection, such as in CCallbackClient,
// it will be replayed during the next update()
#endif
}
/*
*
*/
TSockId CCallbackServer::getSockId (TSockId hostid)
{
nlassert (hostid != InvalidSockId); // invalid hostid
nlassert (connected ());
nlassert (hostid != NULL);
return hostid;
}
/*
* Returns true if there are messages to read
* Recorded : NO
* Replayed : YES
*/
bool CCallbackServer::dataAvailable ()
{
#ifdef USE_MESSAGE_RECORDER
if ( _MR_RecordingState != Replay )
{
#endif
// Real dataAvailable()
return CBufServer::dataAvailable ();
#ifdef USE_MESSAGE_RECORDER
}
else
{
// Simulated dataAvailable()
return CCallbackNetBase::replayDataAvailable();
}
#endif
}
//-------------------------
#ifdef USE_MESSAGE_RECORDER
/*
* Replay connection and disconnection callbacks, client version
*/
bool CCallbackServer::replaySystemCallbacks()
{
do
{
if ( _MR_Recorder.ReceivedMessages.empty() )
{
return false;
}
else
{
// Translate the stored sockid to the replayed sockid
TSockId sockid;
std::map<TSockId,TSockId>::iterator isi = _MR_SockIds.find( _MR_Recorder.ReceivedMessages.front().SockId );
if ( isi != _MR_SockIds.end() )
{
// The sockid is found in the map if the connection already exists
sockid = (*isi).second;
_MR_Recorder.ReceivedMessages.front().SockId = sockid;
}
// Test event type
switch( _MR_Recorder.ReceivedMessages.front().Event )
{
case Receiving:
return true;
case Disconnecting:
LNETL3_DEBUG( "LNETL3S: Disconnection event for %p", sockid );
sockid->Sock->disconnect();
sockid->setConnectedState( false );
// Call callback if needed
if ( disconnectionCallback() != NULL )
{
disconnectionCallback()( sockid, argOfDisconnectionCallback() );
}
break;
case Accepting:
{
// Replay connection:
// Get remote address
CInetAddress addr;
_MR_Recorder.ReceivedMessages.front().Message.serial( addr );
// Create a new connection
sockid = new CBufSock( new CDummyTcpSock() );
sockid->Sock->connect( addr );
_MR_Connections.push_back( sockid );
// Bind it to the "old" sockid
_MR_SockIds.insert( make_pair( _MR_Recorder.ReceivedMessages.front().SockId, sockid ) );
LNETL3_DEBUG( "LNETL3S: Connection event for %p", sockid );
sockid->setConnectedState( true );
// Call callback if needed
if ( connectionCallback() != NULL )
{
connectionCallback()( sockid, argOfConnectionCallback() );
}
break;
}
default:
nlerror( "LNETL3S: Invalid system event type in client receive queue" );
}
// Extract system event
_MR_Recorder.ReceivedMessages.pop();
}
}
while ( true );
}
/*
* Record or replay connection
*/
void CCallbackServer::noticeConnection( TSockId hostid )
{
nlassert (hostid != InvalidSockId); // invalid hostid
if ( _MR_RecordingState != Replay )
{
if ( _MR_RecordingState == Record )
{
// Record connection
CMessage addrmsg;
addrmsg.serial( const_cast<CInetAddress&>(hostAddress(hostid)) );
_MR_Recorder.recordNext( _MR_UpdateCounter, Accepting, hostid, addrmsg );
}
}
}
#endif // USE_MESSAGE_RECORDER
} // NLNET