1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/net/message_recorder.h"
23 #include "nel/net/inet_address.h"
25 using namespace NLMISC
;
32 /// TNetworkEvent -> string
33 string
EventToString( TNetworkEvent e
)
37 case Sending
: return "SEND";
38 case Receiving
: return "RECV";
39 case Connecting
: return "CONN";
40 case ConnFailing
: return "CNFL";
41 case Accepting
: return "ACCP";
42 case Disconnecting
: return "DISC";
43 default: nlstop
; return "-ERR-";
47 /// string -> TNetworkEvent
48 TNetworkEvent
StringToEvent( string
& s
)
52 else if ( s
== "SEND" )
54 else if ( s
== "DISC" )
56 else if ( s
== "ACCP" )
58 else if ( s
== "CONN" )
60 else if ( s
== "CNFL" )
73 CMessageRecorder::CMessageRecorder() : _RecordAll(true)
75 #ifndef MESSAGES_PLAIN_TEXT
76 nlerror( "The message recorder works only with plain text messages. Please #define MESSAGES_PLAIN_TEXT" );
84 CMessageRecorder::~CMessageRecorder()
86 if ( !_Filename
.empty() )
88 nldebug( "MR:%s: End of recording", _Filename
.c_str() );
98 bool CMessageRecorder::startRecord( const std::string
& filename
, bool recordall
)
100 _Filename
= filename
;
101 _File
.open( _Filename
.c_str(), ios_base::out
);
103 _RecordAll
= recordall
;
106 nlwarning( "MR: Record: Cannot open file %s", _Filename
.c_str() );
111 nldebug( "MR: Start recording into %s", _Filename
.c_str() );
118 * Same as stringFromVector() but assumes the vector contains only printable characters
120 /*string stringFromTextVector( const vector<uint8>& v )
125 s.resize( v.size() );
126 memcpy( &*s.begin(), &*v.begin(), v.size() );
135 void CMessageRecorder::recordNext( sint64 updatecounter
, TNetworkEvent event
, TSockId sockid
, CMessage
& message
)
137 nlassert( _File
.is_open() );
139 if ( (_RecordAll
) || (event
!= Sending
) )
142 TMessageRecord
rec ( event
, sockid
, message
, updatecounter
/*CTime::getLocalTime()*/ );
143 CMemStream
stream ( false, true );
144 rec
.serial( stream
);
145 char c
= '\0'; // end of cstring
146 stream
.serial( c
); // added to the stream for _File << (char*)stream.buffer()
149 nldebug( "MR:%s: Recording [%s]", _Filename
.c_str(), stream
.buffer() );
150 int len
= (int)(stream
.length()-2); // not the null character (and its separator) at the end of the buffer
152 _File
<< len
; // if we put the expression directly, it makes an access violation ! Weird.
154 _File
<< (char*)stream
.buffer() << endl
;
162 void CMessageRecorder::stopRecord()
172 bool CMessageRecorder::startReplay( const std::string
& filename
)
174 _Filename
= filename
;
175 _File
.open( _Filename
.c_str(), ios_base::in
);
178 nlerror( "MR: Replay: Cannot open file %s", _Filename
.c_str() );
183 nldebug( "MR: Start replaying from %s", _Filename
.c_str() );
190 * Get next record (throw EStreamOverflow)
192 bool CMessageRecorder::loadNext( TMessageRecord
& record
)
194 // WARNING!!! This features doesn't work anymore becaues bufferAsVector() is not available with new CMemStream
198 nlassert( _File
.is_open() );
201 CMemStream
stream ( true, true );
204 _File
>> c
; // skip "* ";
206 _File
.ignore(); // skip delimiter
207 if ( ! _File
.fail() )
209 _File
.get( (char*)stream
.bufferToFill( len
+1 ), len
+1, '\0' );
210 //stream.bufferAsVector().resize( len ); // cut end of cstring
211 nldebug( "MR:%s: Reading [%s]", _Filename
.c_str(), stream
.buffer() );
213 // Serial from stream
214 record
.serial( stream
); // may throw EStreamOverflow if _File.fail()
217 return ! _File
.fail(); // retest
222 * Get the next record (from the preloaded records, or from the file)
224 bool CMessageRecorder::getNext( TMessageRecord
& record
, sint64 updatecounter
)
226 if ( ! _PreloadedRecords
.empty() )
228 if ( _PreloadedRecords
.front().UpdateCounter
== updatecounter
)
230 // The requested record is in the preload
231 record
= _PreloadedRecords
.front();
232 _PreloadedRecords
.pop_front();
237 // The requested record is not in the file
238 nlassert( updatecounter
< _PreloadedRecords
.front().UpdateCounter
); // not >
244 if ( loadNext( record
) )
246 if ( record
.UpdateCounter
== updatecounter
)
248 // The requested record has been loaded
253 // The next loaded record is a new one
254 nlassert( updatecounter
< record
.UpdateCounter
); // not >
255 _PreloadedRecords
.push_back( record
); // when we read one too far
268 * Push the received blocks for this counter into the receive queue
270 void CMessageRecorder::replayNextDataAvailable( sint64 updatecounter
)
272 TMessageRecord
rec( true ); // input message
274 while ( getNext( rec
, updatecounter
) )
281 ReceivedMessages
.push( rec
);
289 _ConnectionAttempts
.push_back( rec
);
300 * Returns true and the event type if the counter of the next data is updatecounter
302 TNetworkEvent
CMessageRecorder::checkNextOne( sint64 updatecounter
)
304 TMessageRecord record
;
305 if ( getNext( record
, updatecounter
) )
307 nldebug( "MR: Check next one: %s at update %" NL_I64
"u", EventToString(record
.Event
).c_str(), updatecounter
);
318 * Get the first stored connection attempt corresponding to addr
320 TNetworkEvent
CMessageRecorder::replayConnectionAttempt( const CInetAddress
& addr
)
323 deque
<TMessageRecord
>::iterator ipr
;
325 if ( ! _ConnectionAttempts
.empty() )
327 // Search in the already processed connection attempts
328 for ( ipr
=_ConnectionAttempts
.begin(); ipr
!=_ConnectionAttempts
.end(); ++ipr
)
330 CInetAddress stored_addr
;
331 (*ipr
).Message
.serial( stored_addr
);
332 if ( stored_addr
== addr
)
335 event
= (*ipr
).Event
;
336 nldebug( "MR: Connection attempt found at update %" NL_I64
"u", (*ipr
).UpdateCounter
);
337 _ConnectionAttempts
.erase( ipr
);
343 // Seek in the preloaded records
344 for ( ipr
=_PreloadedRecords
.begin(); ipr
!=_PreloadedRecords
.end(); ++ipr
)
346 event
= (*ipr
).Event
;
347 if ( (event
== Connecting
) || (event
== ConnFailing
) )
349 CInetAddress stored_addr
;
350 (*ipr
).Message
.serial( stored_addr
);
351 if ( stored_addr
== addr
)
354 nldebug( "MR: Connection attempt found at update %" NL_I64
"u", (*ipr
).UpdateCounter
);
355 _PreloadedRecords
.erase( ipr
);
360 if ( ipr
==_PreloadedRecords
.end() )
362 // If not found, load next records until found !
363 TMessageRecord
rec( true );
364 while ( loadNext( rec
) )
366 if ( ( rec
.Event
== Connecting
) || ( rec
.Event
== ConnFailing
) )
368 CInetAddress stored_addr
;
369 rec
.Message
.serial( stored_addr
);
370 if ( stored_addr
== addr
)
373 nldebug( "MR: Connection attempt found at update %" NL_I64
"u", rec
.UpdateCounter
);
378 _PreloadedRecords
.push_back( rec
);
383 _PreloadedRecords
.push_back( rec
);
387 nldebug( "MR: Connection attempt not found" );
398 void CMessageRecorder::stopReplay()