Sign in to follow this  
Followers 0
mouseboyx

30 fps lock slow ping updates

So this is probably a really old thing with the halo client, seems to happen in both FV/CE.  When the frame rate is locked to 30fps, the client/server doesn't update the players ping as often as when the frame rate is vsync or novsync.  And the player's ping can read 0 for a long period of time upon joining until it is finally updated.

 

I'm wondering if there could be another method to determine a player's latency, other than reading from get_var(PlayerIndex,"$ping")?

Share this post


Link to post
Share on other sites

Tiddy-bits:

I think there is more than just the ping check that is tied to frame rate. I noticed the animation speed of vehicle aiming changed on SAPP servers that use "custom sleep"
I am not sure it is actually wise to have everything run at the faster rate, even if the ping is more accurate. It would be good to have a better understanding of Halo's networking to see what exactly is going on.

Java and Sunstriker7 like this

Share this post


Link to post
Share on other sites
On 12/21/2022 at 10:14 PM, Vaporeon said:

I am not sure it is actually wise to have everything run at the faster rate, even if the ping is more accurate. It would be good to have a better understanding of Halo's networking to see what exactly is going on.

 

I can't speak to SAPP's modifications, but the vanilla Halo Dedicated Servers periodically (every 3 seconds) broadcast a super_ping_update to the connected machines, which conveys the pings of the players on the server. When a client receives this message, it replies with a client_to_server_pong. When the server receives the pong, it samples the round-trip latency for that player by determining the amount of time that elapsed between issuing the last super_ping_update and processing the client's client_to_server_pong.

This has a few immediate consequences on vanilla servers:

  • Clients modified to not reply to a super_ping_update will show up as 0 ping, regardless of the amount of time that passes.
  • Clients with just over 3000 milliseconds of round-trip latency will appear to have low ping.
  • Depending on when a client joins, their ping will be 0 until they reply to a super_ping_update and the server receives their pong, which could be 3 seconds and then some.
  • The ping and pong are not sent on the reliable channel; Halo's ping is optimistic and does not reflect packet loss, which explains why for some players, servers that read out under 30 ping for them will have over 130 in practice.

Relevant code and technical data is spoilered below.

 

Spoiler
<message_delta_definition name='client_to_server_pong' definition_type='52' max_iterations='1'>
    <header/>
    <body>
        <field name='player_number' struct='message_client_to_server_pong' offset='0' baseline_offset='0' properties='integer_small'/>
    </body>
</message_delta_definition>
<message_delta_definition name='super_ping_update' definition_type='53' max_iterations='16'>
    <header/>
    <body>
        <field name='player_number' struct='client_ping_update' offset='0' baseline_offset='0' properties='integer_small'/>
        <field name='player_ping' struct='client_ping_update' offset='4' baseline_offset='4' properties='integer_large'/>
    </body>
</message_delta_definition>

 

Server code that issues the super_ping_update (taken from the 1.10 Halo CE dedicated server):


bool __cdecl network_game_server_idle(network_game_server *server)

{
  undefined *client_connection_00;
  bool cVar3;
  bool bVar1;
  uint now;
  uint next_super_ping;
  undefined *client_connection;
  undefined auStack28 [28];
  network_game_server_state state;
  
  cVar3 = true;
                    /* (server->flags >> 1) & 1 is the is_valid bit */
  if ((server->flags >> 1 & 1) != 0) {
    client_connection = (undefined *)0x0;
    cVar3 = network_game_server_handle_public_endpoint(server->connection,&client_connection,0);
    client_connection_00 = client_connection;
    if (cVar3 == true) {
      if (client_connection != (undefined *)0x0) {
        cVar3 = network_game_server_add_new_client(server,client_connection);
        if (cVar3 == true) {
                    /* dead code but MSVC couldn't eliminate it because it has side-effects in a
                       global buffer that does not get read */
          FUN_004c7bd0(client_connection_00,auStack28);
        }
        else {
          cVar3 = network_game_server_reject_client(server->connection,client_connection_00);
        }
      }
      if (cVar3 != false) {
        now = get_time_milliseconds();
        next_super_ping = server->last_super_ping_time + 3000;
        if (next_super_ping < now) {
          network_game_server_broadcast_super_ping_update(server);
          server->last_super_ping_time = now;
        }
        cVar3 = network_game_server_handle_client_machines(server,next_super_ping);
        if (cVar3 != false) {
          state = server->state;
          if (state == network_game_server_state_pregame) {
            bVar1 = network_game_server_idle_pregame(server);
            return bVar1;
          }
          if (state == network_game_server_state_ingame) {
            bVar1 = network_game_server_idle_ingame(server);
            return bVar1;
          }
          if (state == network_game_server_state_postgame) {
            bVar1 = network_game_server_idle_postgame(server);
            return bVar1;
          }
          return false;
        }
      }
    }
  }
  return cVar3;
}

 

Client code that handles the super_ping_update (taken from sapien, but from my memory, the FV/CE clients were similar although not exactly the same):

void network_game_client_handle_mdp_super_ping_update(mdp_message_dependent_header *header)

{
  bool decoded;
  game_connection_type gVar1;
  player_datum_t *ply;
  player_datum_t *player;
  int data_size_in_bits;
  network_game_client *client;
  undefined4 client_connection;
  network_game_server *server;
  long last_ping_time;
  long now;
  char network_player_list_index;
  client_ping_update iteration_body;
  datum_iterator it;
  undefined encoded_pong_buffer [64];
  DWORD local_4;
  network_message_protocol protocol;
  bool write_to_stream;
  bool flush;
  undefined *reply_data;
  
  local_4 = reference_security_cookie;
  decoded = mdp_decode_stateless_iterated(&iteration_body,header);
  if (decoded == true) {
    ply = (player_datum_t *)
          datum_array_get_element2(players_datum_array,(uint)(byte)iteration_body.player_number);
    if (ply != (player_datum_t *)0x0) {
      ply->ping = iteration_body.player_ping;
    }
    network_player_list_index = -1;
    datum_iterator_initialize(&it,players_datum_array);
    player = (player_datum_t *)iterator_next(&it);
    while (player != (player_datum_t *)0x0) {
      if (player->local_player_index != -1) {
        network_player_list_index = (player->network_player).player_list_index;
        if ((network_player_list_index != -1) &&
           (gVar1 = game_connection_type(), gVar1 == game_connection_network_server)) {
          server = get_network_game_server();
          last_ping_time = network_game_server_get_last_super_ping_time(server);
          now = get_clock_milliseconds();
          player->ping = now - last_ping_time;
          @[email protected](local_4);
          return;
        }
        break;
      }
      player = (player_datum_t *)iterator_next(&it);
    }
    data_size_in_bits =
         mdp_encode_stateless
                   (definition_type_client_to_server_pong,(void *)0x0,&network_player_list_index,
                    encoded_pong_buffer,0x200);
    if (data_size_in_bits < 1) {
      log_error(error_message_priority_2,"Could not encode ping message");
    }
    else {
      flush = false;
      write_to_stream = false; // if this were true, the reply would be on the reliable channel
      reply_data = encoded_pong_buffer;
      protocol = network_message_protocol_message_delta;
      client = get_network_game_client();
      client_connection = network_game_client_get_connection(client);
      decoded = network_connection_write_message
                          (client_connection,protocol,reply_data,data_size_in_bits,write_to_stream,
                           flush);
      if (decoded == false) {
        network_logf("failed to send a ping message to the server");
        @[email protected](local_4);
        return;
      }
    }
  }
  @[email protected](local_4);
  return;
}

Server code that handles the pong (taken from sapien):


bool __cdecl
network_game_server_handle_client_to_server_pong
          (network_game_server *server,mdp_message_dependent_header *header)

{
  bool decoded;
  player_datum_t *player;
  long last_ping_time;
  long now;
  message_client_to_server_pong message;
  
                    /* 
                       struct message_client_to_server_pong
                       {
                          int8_t player_number;
                       };
                        */
  if (header->decoding_information->mode != message_delta_mode_stateless) {
    mdp_discard_iteration_body(header);
    return true;
  }
  decoded = mdp_decode_stateless_iterated(&message,header);
  if ((decoded == true) &&
     (player = (player_datum_t *)datum_array_get_element2(players_datum_array,(uint)(byte)message),
     player != (player_datum_t *)0x0)) {
    last_ping_time = network_game_server_get_last_super_ping_time(server);
    now = get_clock_milliseconds();
    player->ping = now - last_ping_time;
  }
  return true;
}

 

 

Edited by PiRate
forgot to complete the function signature for: mdp_discard_iteration_body; changed "mode" to "protocol" in network_game_client_handle_mdp_super_ping_update
Sunstriker7, Enclusion, Java and 1 other like this

Share this post


Link to post
Share on other sites

This is because it cause the game to ignore the extra updates from SAPP, including desyncing updates from server caused by server owners setting custom_sleep below 8.  Locking frame-rate just forces the client to only accept updates at the normal rate of 33ms.

 

Spoiler

Example:

 

 

 

Takka likes this

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0
  • Recently Browsing   0 members

    No registered users viewing this page.