Purple Martians
Technical Code Descriptions

Netgame - Control and Monitoring
Overview dsync and dsync_avg ping and ping_avg Client player position correction Server late cdats Client base resets
Overview These variables are used to control and monitor the status of netgame. dsync and dsync_avg
Description The timing offset between the client and the server. See Client Timing Sync Calculated by clients when processing 'stdf' packets. A rolling average of dsync called dsync_avg is also calculated and used in client_timer_adjust(). Clients also send dsync to the server with 'stak' packets, so the server also has a dsync value for each client. Variables mPlayer.loc[p].dsync mPlayer.loc[p].dsync_avg Code See Client Timing Sync The client uses dsync_avg to adjust it's timing offset with reference to the server. The server uses dsync for display over. ping and ping_avg
Description The round trip ping response times between client and the server. Clients calculate and store their own ping time, as well as a rolling average. The server calculates and stores ping times for each client. Variables mPlayer.loc[p].ping mPlayer.loc[p].ping_avg Code The method of calculating ping is described here: ping The client uses the ping average to set client_chase_offset here: Determine the setpoint Client player position correction
Description On the client, when a new state is applied, measure the amount each player's position is corrected. This measures the amount of warping of players' positions when corrected by the server. There are 2 classes, the local player, and all remote players. The local client player usually has less correction, because it plays back locally stored moves after applying states. These are not used to set anything, rather they are used to debug and get some measure of how well netgame is performing. They can be viewed in client and server debug overlays, and in server remote control graphs. Variables float mPlayer.loc[p].old_x; // old players position, used to calc corrections float mPlayer.loc[p].old_y; float mPlayer.loc[p].client_loc_plr_cor; float mPlayer.loc[p].client_rmt_plr_cor; float mPlayer.loc[p].client_loc_plr_cor_max; float mPlayer.loc[p].client_rmt_plr_cor_max; float mPlayer.loc[p].client_loc_plr_cor_avg; float mPlayer.loc[p].client_rmt_plr_cor_avg; Code
void mwNetgame::client_apply_dif(void)
{
...
   // make a copy of players' pos
   for (int pp=0; pp < NUM_PLAYERS; pp++)
      if (mPlayer.syn[pp].active)
      {
         mPlayer.loc[pp].old_x = mPlayer.syn[pp].x;
         mPlayer.loc[pp].old_y = mPlayer.syn[pp].y;
      }

   // apply dif to base
   apply_state_dif(base, client_state_dif, STATE_SIZE);

   // copy to game vars and set new frame number
   state_to_game_vars(base);
   mLoop.frame_num = client_state_dif_dst;

   // if we rewound time, play it back
   if (ff>0) mLoop.loop_frame(ff);

   // calc players' corrections
   mPlayer.loc[p].client_rmt_plr_cor = 0; // reset max remote
   for (int pp=0; pp < NUM_PLAYERS; pp++)
      if (mPlayer.syn[pp].active)
      {
         float cor = sqrt(pow((mPlayer.loc[pp].old_x - mPlayer.syn[pp].x), 2) + pow((mPlayer.loc[pp].old_y - mPlayer.syn[pp].y), 2));  // hypotenuse distance
         if (p == pp) mPlayer.loc[p].client_loc_plr_cor = cor; // save local cor
         else if (cor > mPlayer.loc[p].client_rmt_plr_cor) mPlayer.loc[p].client_rmt_plr_cor = cor; // save max remote cor
      }

   // add data to tally
   mTally_client_loc_plr_cor_last_sec[p].add_data(mPlayer.loc[p].client_loc_plr_cor);
   mTally_client_rmt_plr_cor_last_sec[p].add_data(mPlayer.loc[p].client_rmt_plr_cor);
}
Server late cdats
Description On the server, when a cdat packet is received too late to be applied, it is discarded and counted. Variables mPlayer.syn[p].late_cdats; Code
void mwNetgame::server_proc_cdat_packet(int i)
{
...
   // determine the cutoff frame for late cdats
   int of = mStateHistory[0].oldest_state_frame_num;

   if (cdat_frame_num < of)
   {
      mPlayer.syn[p].late_cdats++;
      mTally_late_cdats_last_sec[p].add_data(1); // add to tally
      mLog.addf(LOG_NET_cdat, p, "rx cdat p:%d fn:[%d] sync:[%d] late - droppped\n", p, cdat_frame_num, mPlayer.loc[p].server_game_move_sync);
   }
   else
   {
      mGameMoves.add_game_move(cdat_frame_num, 5, p, cm); // add to game_move array
      mLog.addf(LOG_NET_cdat, p, "rx cdat p:%d fn:[%d] sync:[%d] gmep:[%d] - entered\n", p, cdat_frame_num, mPlayer.loc[p].server_game_move_sync, mGameMoves.entry_pos);
      if (cdat_frame_num < server_dirty_frame) server_dirty_frame = cdat_frame_num;
   }
}
Client base resets
Description When the server does not have a valid last_ack_state for a client and has to send a dif state based on zero, client_base_resets is incremented. Dif states based on zero are much larger than regular difs. They typically take 7-8 packets to send, vs 1-2 packets for regular state difs. Variables mPlayer.loc[p].client_base_resets; Code
void mwNetgame::client_apply_dif(void)
{
   if (client_state_dif_src == 0) mPlayer.loc[p].client_base_resets++; // server sent a dif based on zero
   else
....



void mwNetgame::server_send_dif(int frame_num)
{
	 // get client's most recent base state (the last one acknowledged to the server)
	 // if not found, leaves base as is (zero)
	 mStateHistory[p].get_last_ack_state(base, base_frame_num);
	 if (base_frame_num == 0) mPlayer.loc[p].client_base_resets++;
...