Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / net / callback_net_base.cpp
blob7a117f6403743f879212d072a25abd9a04a93a3d
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdnet.h"
19 #include "nel/misc/hierarchical_timer.h"
21 #include "nel/net/buf_sock.h"
22 #include "nel/net/callback_net_base.h"
23 #include "nel/net/net_log.h"
27 #ifdef USE_MESSAGE_RECORDER
28 #ifdef NL_OS_WINDOWS
29 #pragma message ( "NeL Net layer 3: message recorder enabled" )
30 #endif // NL_OS_WINDOWS
31 #include "nel/net/message_recorder.h"
32 #else
33 #ifdef NL_OS_WINDOWS
34 #pragma message ( "NeL Net layer 3: message recorder disabled" )
35 #endif // NL_OS_WINDOWS
36 #endif
39 using namespace std;
40 using namespace NLMISC;
42 namespace NLNET {
46 * Disconnection callback
48 void cbnbNewDisconnection (TSockId from, void *data)
50 nlassert (data != NULL);
51 CCallbackNetBase *base = (CCallbackNetBase *)data;
53 LNETL3_DEBUG("LNETL3NB: cbnbNewDisconnection()");
55 #ifdef USE_MESSAGE_RECORDER
56 // Record or replay disconnection
57 base->noticeDisconnection( from );
58 #endif
60 // Call the client callback if necessary
61 if (base->_DisconnectionCallback != NULL)
62 base->_DisconnectionCallback (from, base->_DisconnectionCbArg);
67 * Constructor
69 CCallbackNetBase::CCallbackNetBase( TRecordingState /* rec */, const string& /* recfilename */, bool /* recordall */ )
70 : _BytesSent(0),
71 _BytesReceived(0),
72 _NewDisconnectionCallback(cbnbNewDisconnection),
73 _DefaultCallback(NULL),
74 _PreDispatchCallback(NULL),
75 _FirstUpdate (true),
76 _UserData(NULL),
77 _DisconnectionCallback(NULL),
78 _DisconnectionCbArg(NULL)
79 #ifdef USE_MESSAGE_RECORDER
80 , _MR_RecordingState(rec), _MR_UpdateCounter(0)
81 #endif
83 createDebug(); // for addNegativeFilter to work even in release and releasedebug modes
85 #ifdef USE_MESSAGE_RECORDER
86 switch ( _MR_RecordingState )
88 case Record :
89 _MR_Recorder.startRecord( recfilename, recordall );
90 break;
91 case Replay :
92 _MR_Recorder.startReplay( recfilename );
93 break;
94 default:;
95 // No recording
97 #endif
100 /** Set the user data */
101 void CCallbackNetBase::setUserData(void *userData)
103 _UserData = userData;
106 /** Get the user data */
107 void *CCallbackNetBase::getUserData()
109 return _UserData;
113 * Append callback array with the specified array
115 void CCallbackNetBase::addCallbackArray (const TCallbackItem *callbackarray, sint arraysize)
117 if (arraysize == 1 && callbackarray[0].Callback == NULL && strlen(callbackarray[0].Key) == 0)
119 // it's an empty array, ignore it
120 return;
123 // resize the array
124 sint oldsize = (sint)_CallbackArray.size();
126 _CallbackArray.resize (oldsize + arraysize);
128 //TOO MUCH MESSAGE nldebug ("L3NB_CB: Adding %d callback to the array", arraysize);
130 for (sint i = 0; i < arraysize; i++)
132 sint ni = oldsize + i;
133 //TOO MUCH MESSAGE nldebug ("L3NB_CB: Adding callback to message '%s', id '%d'", callbackarray[i].Key, ni);
134 // copy callback value
136 _CallbackArray[ni] = callbackarray[i];
141 // LNETL3_DEBUG ("LNETL3NB_CB: Added %d callback Now, there're %d callback associated with message type", arraysize, _CallbackArray.size ());
146 * processOneMessage()
148 void CCallbackNetBase::processOneMessage ()
150 // slow down the layer H_AUTO (CCallbackNetBase_processOneMessage);
152 CMessage msgin ("", true);
153 TSockId tsid;
156 receive (msgin, &tsid);
158 catch (const Exception &e)
160 nlwarning(e.what());
161 return;
164 _BytesReceived += msgin.length ();
166 // now, we have to call the good callback
167 sint pos = -1;
168 std::string name = msgin.getName ();
169 sint i;
170 for (i = 0; i < (sint)_CallbackArray.size (); i++)
172 if (name == _CallbackArray[i].Key)
174 pos = i;
175 break;
179 TMsgCallback cb = NULL;
180 if (pos < 0 || pos >= (sint16) _CallbackArray.size ())
182 if (_DefaultCallback == NULL)
184 nlwarning ("LNETL3NB_CB: Callback %s not found in _CallbackArray", msgin.toString().c_str());
186 else
188 cb = _DefaultCallback;
191 else
193 cb = _CallbackArray[pos].Callback;
196 TSockId realid = getSockId (tsid);
198 if (!realid->AuthorizedCallback.empty() && msgin.getName() != realid->AuthorizedCallback)
200 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());
201 disconnect (tsid);
203 else if (cb == NULL)
205 nlwarning ("LNETL3NB_CB: Callback %s is NULL, can't call it", msgin.toString().c_str());
207 else
209 LNETL3_DEBUG ("LNETL3NB_CB: Calling callback (%s)%s", msgin.getName().c_str(), (cb==_DefaultCallback)?" DEFAULT_CB":"");
211 if (_PreDispatchCallback != NULL)
213 // call the pre dispatch callback
214 _PreDispatchCallback(msgin, realid, *this);
216 cb(msgin, realid, *this);
220 if (pos < 0 || pos >= (sint16) _CallbackArray.size ())
222 if (_DefaultCallback == NULL)
223 nlwarning ("LNETL3NB_CB: Callback %s not found in _CallbackArray", msgin.toString().c_str());
224 else
226 // ...
229 else
231 TSockId realid = getSockId (tsid);
233 if (!realid->AuthorizedCallback.empty() && msgin.getName() != realid->AuthorizedCallback)
235 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());
236 disconnect (tsid);
238 else if (_CallbackArray[pos].Callback == NULL)
240 nlwarning ("LNETL3NB_CB: Callback %s is NULL, can't call it", msgin.toString().c_str());
242 else
244 LNETL3_DEBUG ("LNETL3NB_CB: Calling callback (%s)", _CallbackArray[pos].Key);
245 _CallbackArray[pos].Callback (msgin, realid, *this);
253 * baseUpdate
254 * Recorded : YES
255 * Replayed : YES
257 void CCallbackNetBase::baseUpdate (sint32 timeout)
259 H_AUTO(L3UpdateCallbackNetBase);
260 #ifdef NL_DEBUG
261 nlassert( timeout >= -1 );
262 #endif
264 TTime t0 = CTime::getLocalTime();
267 // The first time, we init time counters
269 if (_FirstUpdate)
271 // LNETL3_DEBUG("LNETL3NB: First update()");
272 _FirstUpdate = false;
273 _LastUpdateTime = t0;
274 _LastMovedStringArray = t0;
278 * timeout -1 => read one message in the queue or nothing if no message in queue
279 * timeout 0 => read all messages in the queue
280 * timeout other => read all messages in the queue until timeout expired (at least all one time)
283 bool exit = false;
285 while (!exit)
287 // process all messages in the queue
288 while (dataAvailable ())
290 processOneMessage ();
291 if (timeout == -1)
293 exit = true;
294 break;
298 // need to exit?
299 if (timeout == 0 || (sint32)(CTime::getLocalTime() - t0) > timeout)
301 exit = true;
303 else
305 // enable multithreading on windows :-/
306 // slow down the layer H_AUTO (CCallbackNetBase_baseUpdate_nlSleep);
307 nlSleep (10);
311 #ifdef USE_MESSAGE_RECORDER
312 _MR_UpdateCounter++;
313 #endif
319 * baseUpdate
320 * Recorded : YES
321 * Replayed : YES
323 void CCallbackNetBase::baseUpdate2 (sint32 timeout, sint32 mintime)
325 H_AUTO(L3UpdateCallbackNetBase2);
326 #ifdef NL_DEBUG
327 nlassert( timeout >= -1 );
328 #endif
330 TTime t0 = CTime::getLocalTime();
333 // The first time, we init time counters
335 if (_FirstUpdate)
337 // LNETL3_DEBUG("LNETL3NB: First update()");
338 _FirstUpdate = false;
339 _LastUpdateTime = t0;
340 _LastMovedStringArray = t0;
344 * Controlling the blocking time of this update loop:
346 * "GREEDY" MODE (legacy default mode)
347 * timeout -1 => While data are available, read all messages in the queue,
348 * mintime t>=0 return only when the queue is empty (and mintime is reached/exceeded).
350 * "CONSTRAINED" MODE (used by layer 5 with mintime=0)
351 * timeout t>0 => Read messages in the queue, exit when the timeout is reached/exceeded,
352 * mintime t>=0 or when there are no more data (and mintime is reached/exceeded).
354 * "ONE-SHOT"/"HARE AND TORTOISE" MODE
355 * timeout 0 => Read up to one message in the queue (nothing if empty), then return at
356 * mintime t>=0 once, or, if mintime not reached, sleep (yield cpu) and start again.
358 * Backward compatibility: baseUpdate(): To have the same behaviour with baseUpdate2():
359 * Warning! The semantics timeout t>0 timeout 0, mintime t>0
360 * of the timeout argument timeout -1 timeout 0, mintime 0
361 * has changed timeout 0 timeout -1, mintime 0
363 * About "Reached/exceeded"
364 * This loop does not control the time of the user-code in the callback. Thus if some data
365 * are available, the callback may take more time than specified in timeout. Then the loop
366 * will end when the timeout is "exceeded" instead of "reached". When yielding cpu, some
367 * more time than specified may be spent as well.
369 * Flooding Detection Option (TODO)
370 * _FloodingByteLimit => If setFloodingDetection() has been called, and the size of the
371 * queue exceeds the flooding limit, the connection will be dropped and
372 * the loop will return immediately.
374 * Message Frame Option (TODO)
375 * At the beginning of the loop, the number of pending messages would be read, and then
376 * only these messages would be processed in this loop, no more. As a result, any messages
377 * received in the meantime would be postponed until the next call.
378 * However, to do this we need to add a fast method getMsgNb() in NLMISC::CBufFifo
379 * (see more information in the class header of CBufFifo).
381 * Implementation notes:
382 * As CTime::getLocalTime() may be slow on certain platforms, we test it only
383 * if timeout > 0 or mintime > 0.
384 * As CTime::getLocalTime() is not an accurate time measure (ms unit, resolution on several
385 * ms on certain platforms), we compare with timeout & mintime using "greater or equal".
387 * Testing:
388 * See nel\tools\nel_unit_test\net_ut\layer3_test.cpp
391 // // TODO: Flooding Detection Option (would work best with the Message Frame Option, otherwise
392 // // we won't detect here a flooding that would occur during the loop.
393 // if ( _FloodingDetectionEnabled )
394 // {
395 // if ( getDataAvailableFlagV() )
396 // {
397 // uint64 nbBytesToHandle = getReceiveQueueSize(); // see above about a possible getMsgNb()
398 // if ( nbBytesToHandle > _FloodingByteLimit )
399 // {
400 // // TODO: disconnect
401 // }
402 // }
403 // }
405 // Outer loop
406 while ( true )
408 // Inner loop
409 while ( dataAvailable () )
411 processOneMessage();
413 // ONE-SHOT MODE/"HARE AND TORTOISE" (or CONSTRAINED with no more time): break
414 if ( timeout == 0 )
415 break;
416 // CONTRAINED MODE: break if timeout reached even if more data are available
417 if ( (timeout > 0) && ((sint32)(CTime::getLocalTime() - t0) >= timeout) )
418 break;
419 // GREEDY MODE (timeout -1): loop until no more data are available
422 // If mintime provided, loop until mintime reached, otherwise exit
423 if ( mintime == 0 )
424 break;
425 if (((sint32)(CTime::getLocalTime() - t0) >= mintime))
426 break;
427 nlSleep( 0 ); // yield cpu
430 #ifdef USE_MESSAGE_RECORDER
431 _MR_UpdateCounter++;
432 #endif
437 const CInetAddress& CCallbackNetBase::hostAddress (TSockId /* hostid */)
439 // should never be called
440 nlstop;
441 static CInetAddress tmp;
442 return tmp;
445 void CCallbackNetBase::authorizeOnly (const char *callbackName, TSockId hostid)
447 LNETL3_DEBUG ("LNETL3NB: authorizeOnly (%s, %s)", callbackName, hostid->asString().c_str());
449 hostid = getSockId (hostid);
451 nlassert (hostid != InvalidSockId);
453 hostid->AuthorizedCallback = (callbackName == NULL)?"":callbackName;
457 #ifdef USE_MESSAGE_RECORDER
460 * Replay dataAvailable() in replay mode
462 bool CCallbackNetBase::replayDataAvailable()
464 nlassert( _MR_RecordingState == Replay );
466 if ( _MR_Recorder.ReceivedMessages.empty() )
468 // Fill the queue of received messages related to the present update
469 _MR_Recorder.replayNextDataAvailable( _MR_UpdateCounter );
472 return replaySystemCallbacks();
477 * Record or replay disconnection
479 void CCallbackNetBase::noticeDisconnection( TSockId hostid )
481 nlassert (hostid != InvalidSockId); // invalid hostid
482 if ( _MR_RecordingState != Replay )
484 if ( _MR_RecordingState == Record )
486 // Record disconnection
487 CMessage emptymsg;
488 _MR_Recorder.recordNext( _MR_UpdateCounter, Disconnecting, hostid, emptymsg );
491 else
493 // Replay disconnection
494 hostid->disconnect( false );
498 #endif // USE_MESSAGE_RECORDER
500 } // NLNET