Too many specialized structures.
For each type of packet, only an enumeration is really necessary to tell what kind of packet it is. After that, treat the data in the packet as a stream.
Append to it as you want.
In this case you have made a structure as a replacement for a function.
For scalability, your packets really must be just a buffer of bytes or bits that can grow as data is added.
The actual act of adding data goes to various locations.
Assuming you have a network manager, some of the packets that are specifically related to networking could be created by the manager itself.
A disconnect packet could be issued when the game decides to call a function on the network manager. No structures involved. A function creates a packet, fills it with the disconnect data (if any), and it is sent.
Rather than creating specialized structures, create specialized functions.
But remember, specializing is rare. Packets needed for standard network communication can be wrapped inside specialized functions, but packets related to players should be quite dynamic.
When it comes to players, one suggestion would be to have your scene manager decide when to update players etc., and ask for their packets by calling a virtual (optionally) function that accepts a packet and adds its data to it.
The benefit of using the scene manager for this is because it allows you to make one big packet that has the data for many players instead of sending a bunch of smaller ones.
Each player appends data to the packet only as necessary. If its position has not changed, don't send positional coordinates.
This is why structures can't really work.
That is a global overview. As for the actual act of specifically adding data to a packet:
Making a packet class is fine (I noticed you are also using structures with constructors and methods; the frequently used standard is to use structures for plain-old-data only and if any constructors or methods are needed use a class).
It can be initialized with its type etc.
Then add methods to it such as appendInt32() etc.
All parts of your engine use these to construct the packet data.
This is an example of a function on the player class that adds its data to a packet that was given to it by the scene manager:
- Code: Select all
void playerClass::fillPacket( sf::Packet &packet ) {
// Set a bunch of flags to tell the receiver what is present in the packet.
unsigned long packetFlags = 0;
if ( m_pos != m_lastSentPos ) {
packetFlags |= POS_BIT_FLAG;
}
if ( m_heath != m_lastSentHealth ) {
packetFlags |= HP_BIT_FLAG;
}
// Packet starts with the flags so the receiver knows what to read.
packet.addUint32( packetFlags );
if ( m_pos != m_lastSentPos ) {
packet.addPos( m_pos );
m_lastSentPos = m_pos;
}
if ( m_heath != m_lastSentHealth ) {
packet.addUint32( m_heath );
m_lastSentHealth = m_heath;
}
}
Again, just think of it as creating a file with the goal of keeping the size to its absolute minimum.
Also note that I made a specialized addPos() function. Don't add too many of these. The reason for adding it is because you may have a way to reduce the size of the data if you know that it is positional. Just as with normals. The function would compress the position data for you.
Where compression is not possible, just stick to addUint32() and friends.
L. Spiro
It is amazing how often people try to be unique, and yet they are always trying to make others be like them.
- L. Spiro 2011