[BACK] Return to callback_net_base.cpp CVS log [TXT][DIR] Up to Nevrax / code / nel / src / net

File: Nevrax / code / nel / src / net / callback_net_base.cpp (download)
Revision 1.33, Wed Jun 12 10:16:34 2002 UTC (6 weeks, 4 days ago) by lecroart
Branch: MAIN
CVS Tags: HEAD
Changes since 1.32: +4 -7 lines
ADDED: changed NULL into InvalidSockId

/** \file callback_net_base.cpp
 * Network engine, layer 3, base
 *
 * $Id: callback_net_base.cpp,v 1.33 2002/06/12 10:16:34 lecroart Exp $
 */

/* Copyright, 2001 Nevrax Ltd.
 *
 * This file is part of NEVRAX NEL.
 * NEVRAX NEL 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, or (at your option)
 * any later version.

 * NEVRAX NEL 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 NEVRAX NEL; see the file COPYING. If not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

#include "stdnet.h"

#include "nel/misc/string_id_array.h"
#include "nel/misc/hierarchical_timer.h"

#include "nel/net/buf_sock.h"
#include "nel/net/callback_net_base.h"

#ifdef USE_MESSAGE_RECORDER
#pragma message ( "NeL Net layer 3: message recorder enabled" )
#include "nel/net/message_recorder.h"
#else
#pragma message ( "NeL Net layer 3: message recorder disabled" )
#endif


using namespace std;
using namespace NLMISC;

namespace NLNET {


/*
 *
 */
void cbnbMessageRecvAssociations (CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
{
        // receive a new message association

        CStringIdArray::TStringId size;
        msgin.serial (size);

        nldebug ("LNETL3NB_ASSOC: The other side gave me %d association strings", size);

        for (CStringIdArray::TStringId i = 0; i < size; i++)
        {
                std::string name;
                CStringIdArray::TStringId id;

                msgin.serial (name);
                msgin.serial (id);

                // if id == -1, it means that there are no callback associated to this message
                // it should not happen, it mean that one side send a message that the other side
                // can't manage in his callbackarray.
                // to resolve the problem, add the callback in the callbackarray in the other side
                // and put NULL if you don't want to manage this message
                nlassert (id != -1);

                nldebug ("LNETL3NB_ASSOC:  association '%s' -> %d", name.c_str (), id);
                netbase.getSIDA().addString (name, id);
        }
}


/*
 * the other side want to know some of my association, send them!
 */
void cbnbMessageAskAssociations (CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
{
        CMessage msgout (netbase.getSIDA(), "RA");
        CStringIdArray::TStringId size;
        msgin.serial (size);

        nldebug ("LNETL3NB_ASSOC: The other side want %d string associations", size);

        msgout.serial (size);

        for (sint i = 0; i < size; i++)
        {
                string name;
                msgin.serial (name);
                nldebug ("LNETL3NB_ASSOC:  sending association '%s' -> %d", name.c_str (), netbase._OutputSIDA.getId(name));

                // if this assert occurs, it means that the other side ask an unknown message
                // or that there are different types of client (with differents callbackarray) and one of the client doesn't have this callback
                nlassert(netbase._OutputSIDA.getId(name) != -1);

                msgout.serial (name);

                CStringIdArray::TStringId id = netbase._OutputSIDA.getId (name);
                msgout.serial (id);
        }
        // send the message to the other side
        netbase.send (msgout, from);
}

static TCallbackItem cbnbMessageAssociationArray[] =
{
        { "AA", cbnbMessageAskAssociations },
        { "RA", cbnbMessageRecvAssociations },
};


/*
 * Disconnection callback
 */
void cbnbNewDisconnection (TSockId from, void *data)
{
        nlassert (data != NULL);
        CCallbackNetBase *base = (CCallbackNetBase *)data;

        nldebug("LNETL3NB: cbnbNewDisconnection()");

#ifdef USE_MESSAGE_RECORDER
        // Record or replay disconnection
        base->noticeDisconnection( from );
#endif
        
        // Call the client callback if necessary
        if (base->_DisconnectionCallback != NULL)
                base->_DisconnectionCallback (from, base->_DisconnectionCbArg);
}


/*
 * Constructor
 */
CCallbackNetBase::CCallbackNetBase(  TRecordingState rec, const string& recfilename, bool recordall ) :
        _FirstUpdate (true), _DisconnectionCallback(NULL), _DisconnectionCbArg(NULL)
#ifdef USE_MESSAGE_RECORDER
        , _MR_RecordingState(rec), _MR_UpdateCounter(0)
#endif
{
        _ThreadId = getThreadId ();
        _NewDisconnectionCallback = cbnbNewDisconnection;

        _BytesSent = 0;
        _BytesReceived = 0;

        createDebug(); // for addNegativeFilter to work even in release and releasedebug modes

        // add the callback needed to associate messages with id
        addCallbackArray (cbnbMessageAssociationArray, sizeof (cbnbMessageAssociationArray) / sizeof (cbnbMessageAssociationArray[0]));

#ifdef USE_MESSAGE_RECORDER
        switch ( _MR_RecordingState )
        {
        case Record :
                _MR_Recorder.startRecord( recfilename, recordall );
                break;
        case Replay :
                _MR_Recorder.startReplay( recfilename );
                break;
        default:;
                // No recording
        }
#endif
}


/*
 *      Append callback array with the specified array
 */
void CCallbackNetBase::addCallbackArray (const TCallbackItem *callbackarray, CStringIdArray::TStringId arraysize)
{
        checkThreadId ();

        // be sure that the 2 array have the same size
        nlassert (_CallbackArray.size () == (uint)_OutputSIDA.size ());

        if (arraysize == 1 && callbackarray[0].Callback == NULL && string("") == callbackarray[0].Key)
        {
                // it's an empty array, ignore it
                return;
        }

        // resize the array
        sint oldsize = _CallbackArray.size();

        _CallbackArray.resize (oldsize + arraysize);
        _OutputSIDA.resize (oldsize + arraysize);

//TOO MUCH MESSAGE      nldebug ("L3NB_CB: Adding %d callback to the array", arraysize);

        for (sint i = 0; i < arraysize; i++)
        {
                CStringIdArray::TStringId ni = oldsize + i;
//TOO MUCH MESSAGE              nldebug ("L3NB_CB: Adding callback to message '%s', id '%d'", callbackarray[i].Key, ni);
                // copy callback value
                
                _CallbackArray[ni] = callbackarray[i];
                // add the string to the string id array
                _OutputSIDA.addString (callbackarray[i].Key, ni);

        }
//      nldebug ("LNETL3NB_CB: Added %d callback Now, there's %d callback associated with message type", arraysize, _CallbackArray.size ());
}


/*
 * processOneMessage()
 */
void CCallbackNetBase::processOneMessage ()
{
        checkThreadId ();

        H_AUTO (CCallbackNetBase_processOneMessage);

        CMessage msgin (_OutputSIDA, "", true);
        TSockId tsid;
        receive (msgin, &tsid);

        _BytesReceived += msgin.length ();

        nldebug ("LNETL3NB: Received a message %s from %s", msgin.toString().c_str(), tsid->asString().c_str());
        
        // now, we have to call the good callback
        NLMISC::CStringIdArray::TStringId pos = -1;
        if (msgin.TypeHasAnId)
        {
                pos = msgin.getId ();
        }
        else
        {
                std::string name = msgin.getName ();
                sint16 i;
                for (i = 0; i < (sint16) _CallbackArray.size (); i++)
                {
                        if (name == _CallbackArray[i].Key)
                        {
                                pos = i;
                                break;
                        }
                }
        }

        TMsgCallback        cb = NULL;
        if (pos < 0 || pos >= (sint16) _CallbackArray.size ())
        {
                if (_DefaultCallback == NULL)
                {
                        nlwarning ("LNETL3NB_CB: Callback %s not found in _CallbackArray", msgin.toString().c_str());
                }
                else
                {
                        cb = _DefaultCallback;
                }
        }
        else
        {
                cb = _CallbackArray[pos].Callback;
        }

        TSockId realid = getSockId (tsid);

        if (!realid->AuthorizedCallback.empty() && msgin.getName() != realid->AuthorizedCallback)
        {
                nlwarning ("LNETL3NB_CB: %s try to call the callback %s but only %s is authorized. Disconnect him!", tsid->asString().c_str(), msgin.toString().c_str(), tsid->AuthorizedCallback.c_str());
                disconnect (tsid);
        }
        else if (cb == NULL)
        {
                nlwarning ("LNETL3NB_CB: Callback %s is NULL, can't call it", msgin.toString().c_str());
        }
        else
        {
                nldebug ("LNETL3NB_CB: Calling callback (%s)%s", msgin.getName().c_str(), (cb==_DefaultCallback)?" DEFAULT_CB":"");
                cb(msgin, realid, *this);
        }
        
/*
        if (pos < 0 || pos >= (sint16) _CallbackArray.size ())
        {
                if (_DefaultCallback == NULL)
                        nlwarning ("LNETL3NB_CB: Callback %s not found in _CallbackArray", msgin.toString().c_str());
                else
                {
                        // ...
                }
        }
        else
        {
                TSockId realid = getSockId (tsid);

                if (!realid->AuthorizedCallback.empty() && msgin.getName() != realid->AuthorizedCallback)
                {
                        nlwarning ("LNETL3NB_CB: %s try to call the callback %s but only %s is authorized. Disconnect him!", tsid->asString().c_str(), msgin.toString().c_str(), tsid->AuthorizedCallback.c_str());
                        disconnect (tsid);
                }
                else if (_CallbackArray[pos].Callback == NULL)
                {
                        nlwarning ("LNETL3NB_CB: Callback %s is NULL, can't call it", msgin.toString().c_str());
                }
                else
                {
                        nldebug ("LNETL3NB_CB: Calling callback (%s)", _CallbackArray[pos].Key);
                        _CallbackArray[pos].Callback (msgin, realid, *this);
                }
        }
*/
}



/*
 * baseUpdate
 * Recorded : YES
 * Replayed : YES
 */
void CCallbackNetBase::baseUpdate (sint32 timeout)
{
        checkThreadId ();

        H_AUTO (CCallbackNetBase_baseUpdate);

        nlassert( timeout >= -1 );
        TTime t0 = CTime::getLocalTime();

        //
        // The first time, we init time counters
        //
        if (_FirstUpdate)
        {
//              nldebug("LNETL3NB: First update()");
                _FirstUpdate = false;
                _LastUpdateTime = t0;
                _LastMovedStringArray = t0;
        }

        //
        // Every 1 seconds if we have new unknown association, we ask them to the other side
        //
        if (t0 - _LastUpdateTime >  1000)
        {
//              nldebug("LNETL3NB: baseUpdate()");
                _LastUpdateTime = t0;

                const set<string> &sa = _InputSIDA.getNeedToAskedStringArray ();
                if (!sa.empty ())
                {
                        CMessage msgout (_InputSIDA, "AA");
                        //nlassert (sa.size () < 65536); // no size limit anymore
                        CStringIdArray::TStringId size = sa.size ();
                        nldebug ("LNETL3NB_ASSOC: I need %d string association, ask them to the other side", size);
                        msgout.serial (size);
                        for (set<string>::iterator it = sa.begin(); it != sa.end(); it++)
                        {
                                nldebug ("LNETL3NB_ASSOC:  what is the id of '%s'?", (*it).c_str ());
                                string str(*it);
                                msgout.serial (str);
                        }
                        // send the message to the other side
                        send (msgout, 0);
                        _InputSIDA.moveNeedToAskToAskedStringArray();
                        _LastMovedStringArray = t0;
                }
        }

        //
        // Every 60 seconds if we have not answered association, we ask again to get them!
        //
        if (!_InputSIDA.getAskedStringArray().empty() && t0 - _LastMovedStringArray > 60000)
        {
                // we didn't have an answer for the association, resend them
                const set<string> sa = _InputSIDA.getAskedStringArray ();
                CMessage msgout (_InputSIDA, "AA");
                //nlassert (sa.size () < 65536); // no size limit anymore
                CStringIdArray::TStringId size = sa.size ();
                nldebug ("LNETL3NB_ASSOC: client didn't answer my asked association, retry! I need %d string association, ask them to the other side", size);
                msgout.serial (size);
                for (set<string>::iterator it = sa.begin(); it != sa.end(); it++)
                {
                        nldebug ("LNETL3NB_ASSOC:  what is the id of '%s'?", (*it).c_str ());
                        string str(*it);
                        msgout.serial (str);
                }
                // sends the message to the other side
                send (msgout, 0);
                _LastMovedStringArray = t0;
        }

        /*
         * timeout -1    =>  read one message in the queue
         * timeout 0     =>  read all messages in the queue
         * timeout other =>  read all messages in the queue until timeout expired (at least all one time)
         */

        bool exit = false;

        while (!exit)
        {
                // process all messages in the queue
                while (dataAvailable ())
                {
                        processOneMessage ();
                        if (timeout == -1)
                        {
                                exit = true;
                                break;
                        }
                }

                // need to exit?
                if (timeout == 0 || (sint32)(CTime::getLocalTime() - t0) > timeout)
                {
                        exit = true;
                }
                else
                {
                        // enable multithreading on windows :-/
                        H_AUTO (CCallbackNetBase_baseUpdate_nlSleep);
                        nlSleep (10);
                }
        }

#ifdef USE_MESSAGE_RECORDER
        _MR_UpdateCounter++;
#endif

}


const   CInetAddress& CCallbackNetBase::hostAddress (TSockId hostid)
{
        // should never be called
        nlstop;
        static CInetAddress tmp;
        return tmp;
}

void    CCallbackNetBase::setOtherSideAssociations (const char **associationarray, NLMISC::CStringIdArray::TStringId arraysize)
{
        checkThreadId ();

        nldebug ("LNETL3NB_ASSOC: setOtherSideAssociations() sets %d association strings", arraysize);

        for (sint i = 0; i < arraysize; i++)
        {
                nldebug ("LNETL3NB_ASSOC:  association '%s' -> %d", associationarray[i], i);
                getSIDA().addString (associationarray[i], i);
        }
}

void    CCallbackNetBase::displayAllMyAssociations ()
{
        checkThreadId ();

        _OutputSIDA.display ();
}

void    CCallbackNetBase::authorizeOnly (const char *callbackName, TSockId hostid)
{
        checkThreadId ();

        nldebug ("LNETL3NB: authorizeOnly (%s, %s)", callbackName, hostid->asString().c_str());

        hostid = getSockId (hostid);
        
        nlassert (hostid != InvalidSockId);

        hostid->AuthorizedCallback = (callbackName == NULL)?"":callbackName;
}


#ifdef USE_MESSAGE_RECORDER

/*
 * Replay dataAvailable() in replay mode
 */
bool CCallbackNetBase::replayDataAvailable()
{
        nlassert( _MR_RecordingState == Replay );

        if ( _MR_Recorder.ReceivedMessages.empty() )
        {
                // Fill the queue of received messages related to the present update
                _MR_Recorder.replayNextDataAvailable( _MR_UpdateCounter );
        }

        return replaySystemCallbacks();
}


/*
 * Record or replay disconnection
 */
void CCallbackNetBase::noticeDisconnection( TSockId hostid )
{
        nlassert (hostid != InvalidSockId);        // invalid hostid
        if ( _MR_RecordingState != Replay )
        {
                if ( _MR_RecordingState == Record )
                {
                        // Record disconnection
                        CMessage emptymsg;
                        _MR_Recorder.recordNext( _MR_UpdateCounter, Disconnecting, hostid, emptymsg );
                }
        }
        else
        {
                // Replay disconnection
                hostid->disconnect( false );
        }
}

#endif // USE_MESSAGE_RECORDER



/*
 * checkThreadId
 */
void CCallbackNetBase::checkThreadId () const
{
/*      some people use this class in different thread but with a mutex to be sure to have
        no concurent access
        if (getThreadId () != _ThreadId)
        {
                nlerror ("You try to access to the same CCallbackClient or CCallbackServer with 2 differents thread (%d and %d)", _ThreadId, getThreadId());
        }
*/
}


} // NLNET