Some of these questions apply to general programming/c++, but overall i feel it applies to multiplayer programming.
First off, my serialization class, BitStream. This has to do with function template specialization.
class BitStream
{
std::size_t size;
unsigned char data[2048];
public:
class iterator
{
const BitStream* stream;
std::size_t index;
public:
iterator( const BitStream& bs ) : stream(&bs), index(0) {}
virtual ~iterator() {}
// Set the iterator to the contents of the iterator on the rhs
iterator& operator=( iterator& rhs )
{
stream = rhs.stream;
index = rhs.index;
}
// iterator comparison
bool operator==( const iterator& rhs )
{
if ( rhs.stream == stream )
{
if ( rhs.index == index )
{
return true;
}
}
return false;
}
// iterator inequality
bool operator!=( const iterator& rhs )
{
if ( rhs.stream != stream || rhs.index != index )
{
return true;
}
return false;
}
// increment the iterator
void operator++()
{
++index;
if ( index > stream->Size() )
{
index = stream->Size();
}
}
// Read a numeric value from the stream
template < typename T >
void Read( T& v )
{
char parts[sizeof(T)];
for ( int i=0; i<sizeof(T); i++ )
{
Read(parts);
}
Endian< T > en(parts);
v = en.Value();
}
template < >
void Read< std::string > ( std::string& v )
{
// First clear the string
v.clear();
//
std::size_t length;
// First get the length
Read( length );
// Set the length
v.resize(length);
for ( std::size_t i=0; i<length; i++ )
{
char c;
// Read in the character
Read(c);
// Set the characters as we go!
v = c;
}
}
template < >
void Read< unsigned char >( unsigned char& v )
{
v = stream->data[index];
++index;
}
template < >
void Read< char >( char& v )
{
v = stream->data[index];
++index;
}
friend class BitStream;
};
friend class iterator;
public:
BitStream();
virtual ~BitStream();
template < typename T >
void Write( const T& v)
{
Endian< T > en(v);
// Push the endian modified value
*this << en;
}
template<>
void Write< std::string >( const std::string& s)
{
// first write the length
Write( s.length() );
// now write the string
for ( std::size_t i=0; i<s.length(); i++ )
{
Write( s.at(i) );
}
}
template<>
void Write< char >( const char& c)
{
data[size] = c;
++size;
}
template<>
void Write< unsigned char >( const unsigned char& c)
{
data[size] = c;
++size;
}
std::size_t Size() const { return size; }
iterator Begin() const;
iterator End() const;
};
#endif // BITSTREAM_H
As you can see i specialized the char/unsigned char + string write & read types in the class, originally they were in they were in the cpp file like so... ( the current way works without any problems, the original dosn't )
template<>
void BitStream::Write< unsigned char >( const unsigned char& c )
{
data[size] = c;
++size;
}
template<>
void BitStream::Write< char >( const char& c )
{
data[size] = c;
++size;
}
template<>
void BitStream::Write< std::string >( const std::string& s )
{
// first write the length
Write( s.length() );
// now write the string
for ( std::size_t i=0; i<s.length(); i++ )
{
Write( s.at(i) );
}
}
//
// BitStream iterator
//
template<>
void BitStream::iterator::Read< char >( char& v )
{
v = stream->data[index];
++index;
}
template<>
void BitStream::iterator::Read< unsigned char >( unsigned char& v )
{
v = stream->data[index];
++index;
}
template<>
void BitStream::iterator::Read< std::string >( std::string& v )
{
// First clear the string
v.clear();
//
std::size_t length;
// First get the length
Read( length );
// Set the length
v.resize(length);
for ( std::size_t i=0; i<length; i++ )
{
char c;
// Read in the character
Read(c);
// Set the characters as we go!
v = c;
}
}
With the second code block in a c++ file without the definitions for those specialized functions in the first codeblock it ended up in unresolved externals. I have a feeling this is because i'm a scrooge and have not invested in a copy of the c++ iso standards, and would appreciate being pointed out where wrong. It will end up using std::queue, but for now it is a static 2k block of memory.
Ok, the NEXT question. Something that i realized while compiling, yet forgot while running the loginserver, is that the client was still running the old serilisation & message system. In the old system a string length was an unsigned char, not std::size_t. So when the client returned any messages it would crash the login server for obvious reasons. What this made me realise is that clients with malicious intent would try to exploit certain bugs like this, and now i had to litter my code with tests to make sure that packets or streams from client/server or server/client were valid.
Suggestions? I have some ideas of my own, just thought while asking about the former i could ask about the latter.
Ideas are appreciated!
EDIT: source tags, not code tags.
[Edited by - UNiSOL on March 3, 2007 8:33:12 AM]