Purple Martians
Technical Code Descriptions

Netgame - Packet Buffer
Overview Check for new packets Where 'add_to_rx_buffer()' is called Processing packets in the buffer
Overview Packet buffer was created to solve one problem; the old method I had for reading packets only checked for new packets once per frame. This worked fine for almost everything, but there were a few cases where I wanted to process packets faster than once every 25 ms. Thus packet buffer was born. It runs much faster (as fast as the OS will let it!) and marks each packet received with a timestamp. This gives a much finer time of arrival than just knowing what frame it was received on. With just frame numbers, the smallest time I could measure was 25ms, which is an eternity in computer and network time. If packet buffer does not immediately process a packet, it stores it in a buffer with its timestamp. Then when the main game loop runs, the server or client will process all packets waiting in the buffer. Check for new packets This function is called to check for new packets. Both the client and the server use it.
// receives all waiting packets and puts them in the rx buffer
void mwPacketBuffer::add_to_rx_buffer(void)
{
   char data[1024] = {0};
   char address[256];

   if (mNetgame.ima_server)
      while (int len = net_receive(mNetgame.Channel, data, 1024, address))
      {
         int p = mNetgame.server_check_address(address);
         if (p == -1) // unknown address
         {
            if (PacketRead(data, "cjon")) mNetgame.server_proc_cjon_packet(data, address); // setup channel and replay with sjon
            if (PacketRead(data, "cjrc")) mNetgame.server_proc_cjrc_packet(data, address); // setup channel and replay with sjrc
         }
         else
         {
            add_to_rx_buffer_single(data, p);
            mPlayer.loc[p].rx_current_bytes_for_this_frame += len;
            mPlayer.loc[p].rx_current_packets_for_this_frame++;
            mPlayer.loc[0].rx_current_bytes_for_this_frame += len;
            mPlayer.loc[0].rx_current_packets_for_this_frame++;
         }
      }

   if (mNetgame.ima_client)
      while (mNetgame.ClientReceive(data))
         add_to_rx_buffer_single(data, mPlayer.active_local_player);
}


void mwPacketBuffer::add_to_rx_buffer_single(char *data, int p)
{
   // process these immediately and do not add to buffer
   if (PacketRead(data, "ping")) { mNetgame.server_proc_ping_packet(data, p);  return;  }
   if (PacketRead(data, "pang")) { mNetgame.server_proc_pang_packet(data, p);  return;  }
   if (PacketRead(data, "pong")) { mNetgame.client_proc_pong_packet(data);     return;  }

   int type = 0;
   if (PacketRead(data, "cdat")) type = PM_NETGAME_PACKET_TYPE_CDAT;
   if (PacketRead(data, "stak")) type = PM_NETGAME_PACKET_TYPE_STAK;
   if (PacketRead(data, "rctl")) type = PM_NETGAME_PACKET_TYPE_RCTL;
   if (PacketRead(data, "sfak")) type = PM_NETGAME_PACKET_TYPE_SFAK;
   if (PacketRead(data, "stdf")) type = PM_NETGAME_PACKET_TYPE_STDF;
   if (PacketRead(data, "snfo")) type = PM_NETGAME_PACKET_TYPE_SNFO;
   if (PacketRead(data, "sfil")) type = PM_NETGAME_PACKET_TYPE_SFIL;
   if (PacketRead(data, "srrf")) type = PM_NETGAME_PACKET_TYPE_SRRF;
   if (PacketRead(data, "crfl")) type = PM_NETGAME_PACKET_TYPE_CRFL;

   if (type)
   {
      int indx = find_empty_rx_packet_buffer();
      if (indx != -1)
      {
         //printf("%d stored rx packet:%d size:%d type:%d\n", mLoop.frame_num, indx, mNetgame.packetsize, type);
         rx_buf[indx].active = 1;
         rx_buf[indx].type = type;
         rx_buf[indx].timestamp = al_get_time();
         rx_buf[indx].p = p;
         memcpy(rx_buf[indx].data, data, 1024);
      }
   }
   else printf("%d received unknown packet type!\n", mLoop.frame_num);
}
Where 'add_to_rx_buffer()' is called I have experimented with calling it in separate thread, but ultimately decided not to go that way. It is called outside the game loop, so it is not constrained to running at 40 fps. I also call it multiple times at various places within the game_loop. This is done so that if the game_loop takes a significant amount of time to complete, I won't have to wait until game_loop is done to check for new packets.
void mwPacketBuffer::check_for_packets(void)
{
   if ((mNetgame.ima_server) || (mNetgame.ima_client)) add_to_rx_buffer();
}
Processing packets in the buffer Both the server and the client process all packets waiting in the buffer by calling 'proc_rx_buffer()' once per frame.
void mwPacketBuffer::proc_rx_buffer(void)
{
   for (int i=0; i<200; i++)
      if (rx_buf[i].active)
      {
         rx_buf[i].packetpos = 4;
         if (mNetgame.ima_server)
         {
            if (rx_buf[i].type == PM_NETGAME_PACKET_TYPE_CDAT) mNetgame.server_proc_cdat_packet(i);
            if (rx_buf[i].type == PM_NETGAME_PACKET_TYPE_STAK) mNetgame.server_proc_stak_packet(i);
            if (rx_buf[i].type == PM_NETGAME_PACKET_TYPE_RCTL) mNetgame.server_proc_rctl_packet(i);
            if (rx_buf[i].type == PM_NETGAME_PACKET_TYPE_SFAK) mNetgame.server_proc_sfak_packet(i);
            if (rx_buf[i].type == PM_NETGAME_PACKET_TYPE_CRFL) mNetgame.server_proc_crfl_packet(i);
         }
         if (mNetgame.ima_client)
         {
            if (rx_buf[i].type == PM_NETGAME_PACKET_TYPE_STDF) mNetgame.client_proc_stdf_packet(i);
            if (rx_buf[i].type == PM_NETGAME_PACKET_TYPE_SNFO) mNetgame.client_proc_snfo_packet(i);
            if (rx_buf[i].type == PM_NETGAME_PACKET_TYPE_SFIL) mNetgame.client_proc_sfil_packet(i);
            if (rx_buf[i].type == PM_NETGAME_PACKET_TYPE_SRRF) mNetgame.client_proc_srrf_packet(i);
         }
         rx_buf[i].active = 0;
      }
}