WARNING - I should remove this post, but instead will give a warning. Do not use the MTUDP.cpp library as is. It has many bugs and makes a fatal mistake. The user ID is the IP address. This breaks if there is a fire wall in the way, and there is always a firewall. I have nearly rewritten the library with very little of the original code left over.
--------------------------------------------------------------------
Ok, turns out the previous post was not a complete fix for the Ack code.
As usual the blog mangles the C code a bit, but you should be able to copy this, else email me for a complete copy.
unsigned short NetLibHost::ProcessIncomingACKs( char *pBuffer, unsigned short len, DWORD receiveTime )
{
UNREFERENCED_PARAMETER(len);
// Get the number of ACKs in this message, not counting the base ACK.
unsigned char numAcks, mask, *ptr;
DWORD basePacketID, ackID;
ptr = (unsigned char *)pBuffer;
// The story: We want to ack each received packet.
// But if we have received a series of packets we only
// have to ack the highest numbered one and we assume all the lesser packets are ack'd too.
// But then there can be some higher number packets we have received with some gaps.
// So we send the highest received packet so far with no unreceived packets less than it, then
// have bytes with the bits representing yes/no acks for higher numbered packets.'
// So say we have received 3,4,5,6,7,9,10,11,13
// The base would be 7 since we have all pacjets up to that,
// Then the next byte is the bits 0x00 | 0x40 | 0x20 | 0x10 | 0x00 | 0x40
// for packets 8,9,10,11,12,13 respectively.
// Get the base packet ID, which indicates all the ordered packets received so far.
memcpy( &basePacketID, ptr, sizeof( DWORD ) );
ptr += sizeof( DWORD );
// Get the number of additional ACKs.
// TODO - Keene - Runs off the end if Ack record was truncated to fit!
// Solution: don't ever make packets messages that are too large, e.g. 3k
numAcks = *ptr;
ptr++;
// Zero the byte so if there is a one off error in bits, it is not a false ack.
*ptr = 0x00;
ackID = d_outQueue.GetLowestID();
#if defined( _DEBUG_VERBOSE )
OUTPUTREPORT3( "< Ack low=%04d base=%04d end=%04d\n", ackID, basePacketID, basePacketID + numAcks );
#endif
// Can get stuck in loop here if corrupt data.
int debugCount = 0;
while( ackID <= basePacketID )
{
debugCount++;
// The packet has been ack's so update average ping time.
ACKPacket( ackID, receiveTime );
ackID++;
}
mask = 0x80;
// TODO - Keene - Runs off the end if ack record was truncated to fit!
// Solution: don't ever make packets messages that are too large, e.g. 3k
while( ackID < basePacketID + numAcks )
{
if( mask == 0x00 )
{
mask = 0x80;
ptr++;
// Zero the byte so if there is a one off error in bits, it is not a false ack.
*ptr = 0x00;
}
if( ( *ptr & mask ) != 0 )
{
ACKPacket( ackID, receiveTime );
}
mask >>= 1;
ackID++;
}
return (unsigned short)(ptr - (unsigned char *)pBuffer);
}
unsigned short NetLibHost::ProcessIncomingReliable( char *pBuffer, unsigned short maxLen, DWORD receiveTime )
{
maxLen;
// Process any messages in the packet.
DWORD packetID;
char *readPtr;
unsigned short length;
readPtr = pBuffer;
memcpy( &packetID, readPtr, sizeof( DWORD ) );
readPtr += sizeof( DWORD );
memcpy( &length, readPtr, sizeof( unsigned short ) );
readPtr += sizeof( unsigned short );
#if defined( _DEBUG_VERBOSE )
OUTPUTREPORT2( "< %04d (%d) R\n", packetID, length );
#endif
// If this message is a packet, queue the data
// to be dealt with by the application later.
d_inQueue.AddPacket( packetID, (char *)readPtr, length, receiveTime );
readPtr += length;
// Should we build an ACK message?
if( d_inQueue.GetCount() == 0 )
{
return (unsigned short)( readPtr - pBuffer );
}
// Build the new ACK message.
DWORD lowest, highest, ackID;
unsigned char mask, *ptr;
lowest = d_inQueue.GetCurrentID();
highest = d_inQueue.GetHighestID();
// Cap the highest so as not to overflow the ACK buffer
// (or spend too much time building ACK messages).
// (Was bug here because ACK_MAXPERMSG was 256 which does not fit in a byte.)
if( highest > lowest + ACK_MAXPERMSG )
{
highest = lowest + ACK_MAXPERMSG;
}
#if defined( _DEBUG_VERBOSE )
OUTPUTREPORT2( " > %04d ack to %04d ", lowest, highest );
#endif
// The story: We want to ack each received packet.
// But if we have received a series of packets we only
// have to ack the highest numbered one and we assume all the lesser packets are ack'd too.
// But then there can be some higher number packets we have received with some gaps.
// So we send the highest received packet so far with no unreceived packets less than it, then
// have bytes with the bits representing yes/no acks for higher numbered packets.'
// So say we have received 3,4,5,6,7,9,10,11,13
// The base would be 7 since we have all pacjets up to that,
// Then the next byte is the bits 0x00 | 0x40 | 0x20 | 0x10 | 0x00 | 0x40
// for packets 8,9,10,11,12,13 respectively.
ptr = (unsigned char *)d_ackBuffer;
// Send the base packet ID, which is the ID of the last ordered packet received.
memcpy( ptr, &lowest, sizeof( DWORD ) );
ptr += sizeof( DWORD );
// Add the number of additional ACKs.
*ptr = (unsigned char)(highest - lowest);
ptr++;
// Zero the byte so if there is a one off error in bits, it is not a false ack.
*ptr = 0x00;
ackID = lowest + 1;
mask = 0x80;
while( ackID <= highest )
{
if( mask == 0x00 )
{
mask = 0x80;
ptr++;
// Zero the byte so if there is a one off error in bits, it is not a false ack.
*ptr = 0x00;
}
// Is there a packet with id 'ackID' ?
if( d_inQueue.UnorderedPacketIsQueued( ackID ) == true )
{
*ptr |= mask; // There is
}
else
{
*ptr &= ~mask; // There isn't
}
mask >>= 1;
ackID++;
}
#if defined( _DEBUG_VERBOSE )
OUTPUTREPORT0( "\n" );
#endif
// Record the ammount of the ackBuffer used.
d_ackLength = (unsigned short)(ptr - (unsigned char *)d_ackBuffer);
assert(d_ackLength <= ACK_BUFFERLENGTH);
// return the number of bytes read from buffer
return (unsigned short)( readPtr - pBuffer );
}