1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
19 #include "nel/net/message.h"
21 /*#ifdef MESSAGES_PLAIN_TEXT
22 #pragma message( "CMessage: compiling messages as plain text" )
24 #pragma message( "CMessage: compiling messages as binary" )
30 bool CMessage::_DefaultStringMode
= false;
32 const char *LockedSubMessageError
= "a sub message is forbidden";
41 CMessage::CMessage (const std::string
&name
, bool inputStream
, TStreamFormat streamformat
, uint32 defaultCapacity
) :
42 NLMISC::CMemStream (inputStream
, false, defaultCapacity
),
43 _Type(OneWay
), _SubMessagePosR(0), _LengthR(0), _HeaderSize(0xFFFFFFFF), _TypeSet (false)
45 init( name
, streamformat
);
52 void CMessage::init( const std::string
&name
, TStreamFormat streamformat
)
54 if ( streamformat
== UseDefault
)
56 setStringMode( _DefaultStringMode
);
60 setStringMode( streamformat
== String
);
69 * Constructor with copy from CMemStream
71 CMessage::CMessage (NLMISC::CMemStream
&memstr
) :
72 NLMISC::CMemStream( memstr
),
73 _Type(OneWay
), _SubMessagePosR(0), _LengthR(0), _HeaderSize(0xFFFFFFFF), _TypeSet (false)
75 sint32 pos
= getPos();
76 bool reading
= isReading();
77 if ( reading
) // force input mode to read the type
78 readType(); // sets _TypeSet, _HeaderSize and _LengthR
80 invert(); // calls readType()
82 invert(); // set output mode back if necessary
83 seek( pos
, begin
); // sets the same position as the one in the memstream
90 CMessage::CMessage (const CMessage
&other
)
100 CMessage
&CMessage::operator= (const CMessage
&other
)
102 // nlassertex( (!other.isReading()) || (!other.hasLockedSubMessage()), ("Storing %s", LockedSubMessageError) );
103 nlassertex( (!isReading()) || (!hasLockedSubMessage()), ("Assigning %s", LockedSubMessageError
) );
104 if ( other
.hasLockedSubMessage() )
106 assignFromSubMessage(other
);
111 CMemStream::operator= (other
);
113 _TypeSet
= other
._TypeSet
;
115 _HeaderSize
= other
._HeaderSize
;
116 _SubMessagePosR
= other
._SubMessagePosR
;
117 _LengthR
= other
._LengthR
;
123 void CMessage::swap(CMessage
&other
)
125 nlassert( !hasLockedSubMessage() );
126 CMemStream::swap(other
);
127 _Name
.swap(other
._Name
);
128 std::swap(_SubMessagePosR
, other
._SubMessagePosR
);
129 std::swap(_LengthR
, other
._LengthR
);
130 std::swap(_HeaderSize
, other
._HeaderSize
);
131 std::swap(_TypeSet
, other
._TypeSet
);
132 std::swap(_Type
, other
._Type
);
137 * Similar to operator=, but makes the current message contain *only* the locked sub message in msgin
138 * or the whole msgin if it is not locked
141 * - msgin is an input message (isReading())
142 * - The current message is blank (new or reset with clear())
145 * - If msgin has been locked using lockSubMessage(), the current message contains only the locked
146 * sub message in msgin, otherwise the current message is exactly msgin
147 * - The current message is an input message, it is not locked
149 void CMessage::assignFromSubMessage( const CMessage
& msgin
)
151 nlassert( msgin
.isReading() );
152 nlassert( ! _TypeSet
);
156 if ( msgin
.hasLockedSubMessage() )
158 fill( msgin
.buffer(), msgin
._LengthR
);
160 seek( msgin
.getPos(), IStream::begin
);
170 * Sets the message type as a string and put it in the buffer if we are in writing mode
172 void CMessage::setType (const std::string
&name
, TMessageType type
)
174 // check if we already do a setType ()
175 nlassert (!_TypeSet
);
176 // don't accept empty string
177 nlassert (!name
.empty ());
184 // check if they don't already serial some stuffs
185 nlassert (length () == 0);
187 // if we can send the id instead of the string, "just do it" (c)nike!
188 //NLMISC::CStringIdArray::TStringId id = _SIDA->getId (name);
190 // Force binary mode for header
191 bool msgmode
= _StringMode
;
194 // debug features, we number all packet to be sure that they are all sent and received
195 // \todo remove this debug feature when ok
196 // this value will be fill after in the callback function
197 uint32 zeroValue
= 123;
202 format
.LongFormat
= FormatLong
;
203 format
.StringMode
= msgmode
;
204 format
.MessageType
= _Type
;
205 //nldebug( "OUT format = %hu", (uint16)format );
208 // End of binary header
209 _StringMode
= msgmode
;
211 serial ((std::string
&)name
);
213 _HeaderSize
= getPos ();
221 * Warning: MUST be of the same size than previous name!
222 * Output message only.
224 void CMessage::changeType (const std::string
&name
)
226 sint32 prevPos
= getPos();
227 seek( sizeof(uint32
)+sizeof(uint8
), begin
);
228 serial ((std::string
&)name
);
229 seek( prevPos
, begin
);
234 * Returns the size, in byte of the header that contains the type name of the message or the type number
236 uint32
CMessage::getHeaderSize () const
238 nlassert (_HeaderSize
!= 0xFFFFFFFF);
239 nlassert(!hasLockedSubMessage());
245 * The message was filled with an CMemStream, Now, we'll get the message type on this buffer
247 void CMessage::readType ()
249 nlassert (isReading ());
251 // debug features, we number all packet to be sure that they are all sent and received
252 // \todo remove this debug feature when ok
254 // we remove the message from the message
255 resetSubMessageInternals();
256 const uint HeaderSize
= 4;
257 seek (HeaderSize
, begin
);
259 // serial (zeroValue);
261 // Force binary mode for header
266 //nldebug( "IN format = %hu", (uint16)format );
268 // Set mode for the following of the buffer
269 _StringMode
= format
.StringMode
;
273 setType (name
, TMessageType(format
.MessageType
));
274 _HeaderSize
= getPos();
279 * Get the message name (input message only) and advance the current pos
281 std::string
CMessage::readTypeAtCurrentPos() const
283 nlassert( isReading() );
285 const uint HeaderSize
= 4;
286 seek( HeaderSize
, current
);
288 bool sm
= _StringMode
;
292 nlRead(*this, serial
, format
);
293 bool LongFormat
= format
.LongFormat
;
294 _StringMode
= format
.StringMode
;
295 _Type
= TMessageType(format
.MessageType
);
300 nlRead(*this, serial
, name
);
305 nlerror( "Id not supported" );
313 // Returns true if the message type was already set
314 bool CMessage::typeIsSet () const
319 // Clear the message. With this function, you can reuse a message to create another message
320 void CMessage::clear ()
322 nlassertex( (!isReading()) || (!hasLockedSubMessage()), ("Clearing %s", LockedSubMessageError
) );
324 CMemStream::clear ();
331 * Returns the type name in string if available. Be sure that the message have the name of the message type
333 std::string
CMessage::getName () const
335 if ( hasLockedSubMessage() )
337 CMessage
& notconstMsg
= const_cast<CMessage
&>(*this);
338 sint32 savedPos
= notconstMsg
.getPos();
339 uint32 subPosSaved
= _SubMessagePosR
;
340 uint32 lenthRSaved
= _LengthR
;
341 const_cast<uint32
&>(_SubMessagePosR
) = 0;
342 // const_cast<uint32&>(_LengthR) = _Buffer.size();
343 const_cast<uint32
&>(_LengthR
) = _Buffer
.getBuffer().size();
344 notconstMsg
.seek( subPosSaved
, begin
); // not really const... but removing the const from getName() would need too many const changes elsewhere
345 std::string name
= notconstMsg
.readTypeAtCurrentPos();
346 notconstMsg
.seek( subPosSaved
+savedPos
, begin
);
347 const_cast<uint32
&>(_SubMessagePosR
) = subPosSaved
;
348 const_cast<uint32
&>(_LengthR
) = lenthRSaved
;
358 CMessage::TMessageType
CMessage::getType() const
360 if ( hasLockedSubMessage() )
362 CMessage
& notconstMsg
= const_cast<CMessage
&>(*this);
363 sint32 savedPos
= notconstMsg
.getPos();
364 uint32 subPosSaved
= _SubMessagePosR
;
365 uint32 lenthRSaved
= _LengthR
;
366 const_cast<uint32
&>(_SubMessagePosR
) = 0;
367 // const_cast<uint32&>(_LengthR) = _Buffer.size();
368 const_cast<uint32
&>(_LengthR
) = _Buffer
.getBuffer().size();
369 notconstMsg
.seek( subPosSaved
, begin
); // not really const... but removing the const from getName() would need too many const changes elsewhere
370 notconstMsg
.readTypeAtCurrentPos();
371 notconstMsg
.seek( subPosSaved
+savedPos
, begin
);
372 const_cast<uint32
&>(_SubMessagePosR
) = subPosSaved
;
373 const_cast<uint32
&>(_LengthR
) = lenthRSaved
;
384 /* Returns a readable string to display it to the screen. It's only for debugging purpose!
385 * Don't use it for anything else than to debugging, the string format could change in the future.
386 * \param hexFormat If true, display all bytes in hexadecimal
387 * \param textFormat If true, display all bytes as chars (above 31, otherwise '.')
389 std::string
CMessage::toString( bool hexFormat
, bool textFormat
) const
391 //nlassert (_TypeSet);
392 std::string s
= "('" + _Name
+ "')";
394 s
+= " " + CMemStream::toString( true );
396 s
+= " " + CMemStream::toString( false );
402 * Return an input stream containing the stream beginning in the message at the specified pos
404 NLMISC::CMemStream
CMessage::extractStreamFromPos( sint32 pos
)
406 NLMISC::CMemStream
msg( true );
407 sint32 len
= length() - pos
;
408 memcpy( msg
.bufferToFill( len
), buffer() + pos
, len
);
414 * Encapsulate/decapsulate another message inside the current message
416 void CMessage::serialMessage( CMessage
& msg
)
420 // Init 'msg' with the contents serialised from 'this'
423 if ( ! msg
.isReading() )
425 serialBuffer( msg
.bufferToFill( len
), len
);
428 msg
.seek( 0, CMemStream::end
);
432 // Store into 'this' the contents of 'msg'
433 uint32 len
= msg
.length();
435 serialBuffer( const_cast<uint8
*>(msg
.buffer()), msg
.length() );