Monday, November 21, 2011

MTUDP.cpp More Fixes

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 );
 }


No comments: