Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / net / message_recorder.cpp
blob438293ba49da9425472a6b50c0a71943e230bf28
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
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/>.
20 #include "stdnet.h"
22 #include "nel/net/message_recorder.h"
23 #include "nel/net/inet_address.h"
25 using namespace NLMISC;
26 using namespace std;
29 namespace NLNET {
32 /// TNetworkEvent -> string
33 string EventToString( TNetworkEvent e )
35 switch ( 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 )
50 if ( s == "RECV" )
51 return Receiving;
52 else if ( s == "SEND" )
53 return Sending;
54 else if ( s == "DISC" )
55 return Disconnecting;
56 else if ( s == "ACCP" )
57 return Accepting;
58 else if ( s == "CONN" )
59 return Connecting;
60 else if ( s == "CNFL" )
61 return ConnFailing;
62 else
64 nlstop;
65 return Error;
71 * Constructor
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" );
77 #endif
82 * Destructor
84 CMessageRecorder::~CMessageRecorder()
86 if ( !_Filename.empty() )
88 nldebug( "MR:%s: End of recording", _Filename.c_str() );
90 stopRecord();
91 stopReplay();
96 * Start recording
98 bool CMessageRecorder::startRecord( const std::string& filename, bool recordall )
100 _Filename = filename;
101 _File.open( _Filename.c_str(), ios_base::out );
102 _File << endl;
103 _RecordAll = recordall;
104 if ( _File.fail() )
106 nlwarning( "MR: Record: Cannot open file %s", _Filename.c_str() );
107 return false;
109 else
111 nldebug( "MR: Start recording into %s", _Filename.c_str() );
112 return true;
118 * Same as stringFromVector() but assumes the vector contains only printable characters
120 /*string stringFromTextVector( const vector<uint8>& v )
122 string s;
124 // Copy contents
125 s.resize( v.size() );
126 memcpy( &*s.begin(), &*v.begin(), v.size() );
128 return s;
133 * Add a record
135 void CMessageRecorder::recordNext( sint64 updatecounter, TNetworkEvent event, TSockId sockid, CMessage& message )
137 nlassert( _File.is_open() );
139 if ( (_RecordAll) || (event != Sending) )
141 // Serial to stream
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()
148 // Dump to file
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
151 _File << "* ";
152 _File << len; // if we put the expression directly, it makes an access violation ! Weird.
153 _File << " ";
154 _File << (char*)stream.buffer() << endl;
160 * Stop recording
162 void CMessageRecorder::stopRecord()
164 _File.close();
165 _Filename.clear();
170 * Start playback
172 bool CMessageRecorder::startReplay( const std::string& filename )
174 _Filename = filename;
175 _File.open( _Filename.c_str(), ios_base::in );
176 if ( _File.fail() )
178 nlerror( "MR: Replay: Cannot open file %s", _Filename.c_str() );
179 return false;
181 else
183 nldebug( "MR: Start replaying from %s", _Filename.c_str() );
184 return true;
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
195 nlstop;
196 return false;
198 nlassert( _File.is_open() );
200 // Dump from file
201 CMemStream stream ( true, true );
202 uint32 len;
203 char c;
204 _File >> c; // skip "* ";
205 _File >> (int&)len;
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();
233 return true;
235 else
237 // The requested record is not in the file
238 nlassert( updatecounter < _PreloadedRecords.front().UpdateCounter ); // not >
239 return false;
242 else
244 if ( loadNext( record ) )
246 if ( record.UpdateCounter == updatecounter )
248 // The requested record has been loaded
249 return true;
251 else
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
256 return false;
259 else
261 return false;
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 ) )
276 switch ( rec.Event )
278 case Receiving :
279 case Accepting :
280 case Disconnecting :
281 ReceivedMessages.push( rec );
282 break;
284 case Sending :
285 break;
287 case Connecting :
288 case ConnFailing :
289 _ConnectionAttempts.push_back( rec );
290 break;
292 default :
293 nlstop;
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 );
308 return record.Event;
310 else
312 return Error;
318 * Get the first stored connection attempt corresponding to addr
320 TNetworkEvent CMessageRecorder::replayConnectionAttempt( const CInetAddress& addr )
322 TNetworkEvent event;
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 )
334 // Found
335 event = (*ipr).Event;
336 nldebug( "MR: Connection attempt found at update %" NL_I64 "u", (*ipr).UpdateCounter );
337 _ConnectionAttempts.erase( ipr );
338 return event;
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 )
353 // Found
354 nldebug( "MR: Connection attempt found at update %" NL_I64 "u", (*ipr).UpdateCounter );
355 _PreloadedRecords.erase( ipr );
356 return event;
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 )
372 // Found
373 nldebug( "MR: Connection attempt found at update %" NL_I64 "u", rec.UpdateCounter );
374 return rec.Event;
376 else
378 _PreloadedRecords.push_back( rec );
381 else
383 _PreloadedRecords.push_back( rec );
386 // Not found
387 nldebug( "MR: Connection attempt not found" );
388 return Error;
390 nlstop;
391 return Error;
396 * Stop playback
398 void CMessageRecorder::stopReplay()
400 _File.close();
401 _Filename.clear();
405 } // NLNET