Purple Martians
Technical Code Descriptions

Netgame - Ping
Overview Client initiates by sending 'ping' Server receives 'ping' and sends 'pong' Client receives 'pong' and sends 'pang' Server receives 'pang' Rolling averages put in player struct
Overview Netgame needs to know packet transit times times between client and server, to monitor and adjust timing offset. Clients measure and keep track of the round trip time it takes to send a packet to the server and get a reply. The server measures and keeps track of ping times for each active client. Client initiates by sending 'ping' The client starts the process by sending a 'ping' packet containing the client's local timestamp from when the packet was sent.
void mwNetgame::client_send_ping_packet(void)
{
   char data[PACKET_BUFFER_SIZE] = {0}; int pos;
   mPacketBuffer.PacketName(data, pos, "ping");
   mPacketBuffer.PacketPutDouble(data, pos, al_get_time());
   ClientSend(data, pos);
}
The ping is sent whenever the 1Hz timer fires.
      // ----------------------------------------------------------
      // do things based on the 1 Hz sec_timer event
      // ----------------------------------------------------------
      if (mNetgame.ima_client) mNetgame.client_send_ping_packet();
Server receives 'ping' and sends 'pong' When the server receives a 'ping' packet, it immediately replies with a 'pong' packet that contains: - the timestamp from the 'ping' packet it is replying to, and - the server's local timestamp from when it sends the 'pong' packet
void mwNetgame::server_proc_ping_packet(char *data, int who)
{
   int p = server_get_player_num_from_who(who);
   if (p != -1)
   {
      int pos = 4;
      double t0 = mPacketBuffer.PacketGetDouble(data, pos);
      double t1 = al_get_time();
      mPacketBuffer.PacketName(data, pos, "pong");
      mPacketBuffer.PacketPutDouble(data, pos, t0);
      mPacketBuffer.PacketPutDouble(data, pos, t1);
      ServerSendTo(data, pos, who);
   }
}
Client receives 'pong' and sends 'pang' When the client receives a 'pong' packet, it calculates the round trip transit time from the client to the server and back. Then adds the value to the rolling average algorithm. Then immediately replies with a 'pang' packet that contains the timestamp from the 'pong' packet it is replying to.
void mwNetgame::client_proc_pong_packet(char *data)
{
   int pos = 4;
   double t0 = mPacketBuffer.PacketGetDouble(data, pos);
   double t1 = mPacketBuffer.PacketGetDouble(data, pos);

   mPacketBuffer.RA[mPlayer.active_local_player].add_data(al_get_time() - t0);

   mPacketBuffer.PacketName(data, pos, "pang");
   mPacketBuffer.PacketPutDouble(data, pos, t1);
   ClientSend(data, pos);
}
Server receives 'pang' When the server receives a 'pang' packet, it calculates the round trip transit time from the server to the client and back. Then adds the value to the rolling average algorithm.
void mwNetgame::server_proc_pang_packet(char *data, int who)
{
   int p = server_get_player_num_from_who(who);
   if (p != -1)
   {
      int pos = 4;
      double t0 = mPacketBuffer.PacketGetDouble(data, pos);
      mPacketBuffer.RA[p].add_data(al_get_time() - t0);
   }
}
Rolling averages put in player struct Because ping times fluctuate from frame to frame, we don't want to use the raw value. Instead we run it through a rolling average algorithm and use that instead. Once per frame the current and average ping times are copied from the rolling average objects into player structure variables. The client has its one local ping time, and the server has a ping time for each active client.
for (int p=0; p < NUM_PLAYERS; p++)
{
   mPlayer.loc[p].ping = mPacketBuffer.RA[p].last_input;
   mPlayer.loc[p].ping_avg = mPacketBuffer.RA[p].avg;
}
In client_timer_adjust(), 'ping_avg' is used to adjust the timing offset.