--------------------------------------------------------------------
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:
Post a Comment