host channel heartbeat for rtt

This commit is contained in:
jacob 2025-02-25 19:39:47 -06:00
parent a364f311d1
commit 6442998795
5 changed files with 69 additions and 9 deletions

View File

@ -34,6 +34,7 @@ enum host_packet_kind {
HOST_PACKET_KIND_TRY_CONNECT, HOST_PACKET_KIND_TRY_CONNECT,
HOST_PACKET_KIND_CONNECT_SUCCESS, HOST_PACKET_KIND_CONNECT_SUCCESS,
HOST_PACKET_KIND_DISCONNECT, HOST_PACKET_KIND_DISCONNECT,
HOST_PACKET_KIND_HEARTBEAT,
HOST_PACKET_KIND_MSG_CHUNK HOST_PACKET_KIND_MSG_CHUNK
}; };
@ -75,7 +76,10 @@ struct host_channel {
struct host_msg_assembler *least_recent_msg_assembler; struct host_msg_assembler *least_recent_msg_assembler;
struct host_msg_assembler *most_recent_msg_assembler; struct host_msg_assembler *most_recent_msg_assembler;
i64 rtt_ns; u16 last_heartbeat_received_id;
u16 last_heartbeat_acked_id;
i64 last_heartbeat_acked_ns;
i64 last_heartbeat_rtt_ns;
u64 last_sent_msg_id; u64 last_sent_msg_id;
u64 their_acked_seq; u64 their_acked_seq;
@ -622,10 +626,10 @@ void host_queue_write(struct host *host, struct host_channel_id channel_id, stru
* Info * Info
* ========================== */ * ========================== */
i64 host_get_channel_rtt_ns(struct host *host, struct host_channel_id channel_id) i64 host_get_channel_last_rtt_ns(struct host *host, struct host_channel_id channel_id)
{ {
struct host_channel *channel = host_single_channel_from_id(host, channel_id); struct host_channel *channel = host_single_channel_from_id(host, channel_id);
return channel->rtt_ns; return channel->last_heartbeat_rtt_ns;
} }
/* ========================== * /* ========================== *
@ -738,6 +742,24 @@ void host_update(struct host *host)
} break; } break;
case HOST_PACKET_KIND_HEARTBEAT:
{
if (channel->valid) {
u16 heartbeat_id = br_read_ubits(&br, 16);
u16 acked_heartbeat_id = br_read_ubits(&br, 16);
if (heartbeat_id > channel->last_heartbeat_received_id) {
channel->last_heartbeat_received_id = heartbeat_id;
}
if (acked_heartbeat_id == channel->last_heartbeat_acked_id + 1) {
channel->last_heartbeat_acked_id = acked_heartbeat_id;
if (channel->last_heartbeat_acked_ns > 0) {
channel->last_heartbeat_rtt_ns = now_ns - channel->last_heartbeat_acked_ns;
}
channel->last_heartbeat_acked_ns = now_ns;
}
}
} break;
case HOST_PACKET_KIND_MSG_CHUNK: case HOST_PACKET_KIND_MSG_CHUNK:
{ {
if (channel->valid && channel->connected) { if (channel->valid && channel->connected) {
@ -817,6 +839,15 @@ void host_update(struct host *host)
cmd->kind = HOST_CMD_KIND_TRY_CONNECT; cmd->kind = HOST_CMD_KIND_TRY_CONNECT;
cmd->channel_id = channel->id; cmd->channel_id = channel->id;
} }
/* Send heartbeat */
/* TODO: Send this less frequently (once per second or half of timeout or something) */
{
struct host_cmd *cmd = host_cmd_alloc_and_append(host);
cmd->kind = HOST_CMD_KIND_HEARTBEAT;
cmd->heartbeat_id = channel->last_heartbeat_acked_id + 1;
cmd->heartbeat_ack_id = channel->last_heartbeat_received_id;
cmd->channel_id = channel->id;
}
/* Release acked reliable packets */ /* Release acked reliable packets */
{ {
u64 acked_seq = channel->their_acked_seq; u64 acked_seq = channel->their_acked_seq;
@ -888,7 +919,7 @@ void host_update(struct host *host)
u8 packet_flags = 0; u8 packet_flags = 0;
struct host_snd_packet *packet = host_channel_snd_packet_alloc(channel, false); struct host_snd_packet *packet = host_channel_snd_packet_alloc(channel, false);
struct bitbuff bb = bitbuff_from_string(STRING_FROM_ARRAY(packet->data)); struct bitbuff bb = bitbuff_from_string(STRING_FROM_ARRAY(packet->data));
struct bitbuff_writer bw = bw_from_bitbuff(&bb); struct bitbuff_writer bw = bw_from_bitbuff(&bb);
bw_write_ubits(&bw, PACKET_MAGIC, 32); /* TODO: implicitly encode magic into crc32 */ bw_write_ubits(&bw, PACKET_MAGIC, 32); /* TODO: implicitly encode magic into crc32 */
bw_write_ibits(&bw, HOST_PACKET_KIND_CONNECT_SUCCESS, 8); bw_write_ibits(&bw, HOST_PACKET_KIND_CONNECT_SUCCESS, 8);
bw_write_ubits(&bw, packet_flags, 8); bw_write_ubits(&bw, packet_flags, 8);
@ -901,7 +932,7 @@ void host_update(struct host *host)
u8 packet_flags = 0; u8 packet_flags = 0;
struct host_snd_packet *packet = host_channel_snd_packet_alloc(channel, false); struct host_snd_packet *packet = host_channel_snd_packet_alloc(channel, false);
struct bitbuff bb = bitbuff_from_string(STRING_FROM_ARRAY(packet->data)); struct bitbuff bb = bitbuff_from_string(STRING_FROM_ARRAY(packet->data));
struct bitbuff_writer bw = bw_from_bitbuff(&bb); struct bitbuff_writer bw = bw_from_bitbuff(&bb);
bw_write_ubits(&bw, PACKET_MAGIC, 32); /* TODO: implicitly encode magic into crc32 */ bw_write_ubits(&bw, PACKET_MAGIC, 32); /* TODO: implicitly encode magic into crc32 */
bw_write_ibits(&bw, HOST_PACKET_KIND_DISCONNECT, 8); bw_write_ibits(&bw, HOST_PACKET_KIND_DISCONNECT, 8);
bw_write_ubits(&bw, packet_flags, 8); bw_write_ubits(&bw, packet_flags, 8);
@ -909,6 +940,21 @@ void host_update(struct host *host)
packet->data_len = bw_num_bytes_written(&bw); packet->data_len = bw_num_bytes_written(&bw);
} break; } break;
case HOST_CMD_KIND_HEARTBEAT:
{
u8 packet_flags = 0;
struct host_snd_packet *packet = host_channel_snd_packet_alloc(channel, false);
struct bitbuff bb = bitbuff_from_string(STRING_FROM_ARRAY(packet->data));
struct bitbuff_writer bw = bw_from_bitbuff(&bb);
bw_write_ubits(&bw, PACKET_MAGIC, 32); /* TODO: implicitly encode magic into crc32 */
bw_write_ibits(&bw, HOST_PACKET_KIND_HEARTBEAT, 8);
bw_write_ubits(&bw, packet_flags, 8);
bw_write_uv(&bw, channel->our_acked_seq);
bw_write_ubits(&bw, cmd->heartbeat_id, 16);
bw_write_ubits(&bw, cmd->heartbeat_ack_id, 16);
packet->data_len = bw_num_bytes_written(&bw);
} break;
case HOST_CMD_KIND_WRITE: case HOST_CMD_KIND_WRITE:
{ {
b32 is_reliable = cmd->write_reliable; b32 is_reliable = cmd->write_reliable;

View File

@ -18,6 +18,7 @@ enum host_cmd_kind {
HOST_CMD_KIND_TRY_CONNECT, HOST_CMD_KIND_TRY_CONNECT,
HOST_CMD_KIND_CONNECT_SUCCESS, HOST_CMD_KIND_CONNECT_SUCCESS,
HOST_CMD_KIND_DISCONNECT, HOST_CMD_KIND_DISCONNECT,
HOST_CMD_KIND_HEARTBEAT,
HOST_CMD_KIND_WRITE HOST_CMD_KIND_WRITE
}; };
@ -39,9 +40,13 @@ struct host_cmd {
enum host_cmd_kind kind; enum host_cmd_kind kind;
struct host_channel_id channel_id; struct host_channel_id channel_id;
u16 heartbeat_id;
u16 heartbeat_ack_id;
b32 write_reliable; b32 write_reliable;
struct string write_msg; struct string write_msg;
struct host_cmd *next; struct host_cmd *next;
}; };
@ -128,7 +133,7 @@ void host_queue_write(struct host *host, struct host_channel_id channel_id, stru
* Info * Info
* ========================== */ * ========================== */
i64 host_get_channel_rtt_ns(struct host *host, struct host_channel_id channel_id); i64 host_get_channel_last_rtt_ns(struct host *host, struct host_channel_id channel_id);
/* ========================== * /* ========================== *
* Update * Update

View File

@ -20,7 +20,6 @@ enum sim_ent_prop {
SIM_ENT_PROP_SYNC_DST, /* This entity is not locally created, and should sync with incoming net src ents */ SIM_ENT_PROP_SYNC_DST, /* This entity is not locally created, and should sync with incoming net src ents */
SIM_ENT_PROP_CLIENT, SIM_ENT_PROP_CLIENT,
SIM_ENT_PROP_LOCAL_CLIENT,
SIM_ENT_PROP_CMD_CONTROL, SIM_ENT_PROP_CMD_CONTROL,
@ -153,6 +152,9 @@ struct sim_ent {
b32 client_dbg_drag_start; b32 client_dbg_drag_start;
b32 client_dbg_drag_stop; b32 client_dbg_drag_stop;
/* Client round-trip-time to server */
i64 client_rtt_ns;
/* ====================================================================== */ /* ====================================================================== */
/* Collider */ /* Collider */

View File

@ -382,6 +382,11 @@ void sim_step(struct sim_step_ctx *ctx)
} }
} }
/* Update rtt */
if (is_master && client_ent->valid) {
client_ent->client_rtt_ns = client->rtt_ns;
}
/* Sync ents from client */ /* Sync ents from client */
if (client_ent->valid) { if (client_ent->valid) {
struct sim_snapshot *src_ss = sim_snapshot_from_tick(client, world->tick); struct sim_snapshot *src_ss = sim_snapshot_from_tick(client, world->tick);

View File

@ -1643,6 +1643,9 @@ INTERNAL void user_update(void)
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Network write: %F mbit/s"), FMT_FLOAT_P((f64)G.net_bytes_sent.last_second * 8 / 1000 / 1000, 3))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Network write: %F mbit/s"), FMT_FLOAT_P((f64)G.net_bytes_sent.last_second * 8 / 1000 / 1000, 3)));
pos.y += spacing; pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Ping: %F ms"), FMT_FLOAT_P(SECONDS_FROM_NS(local_client_ent->client_rtt_ns) * 1000, 3)));
pos.y += spacing;
pos.y += spacing; pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory usage: %F MiB"), FMT_FLOAT_P((f64)gstat_get(GSTAT_MEMORY_COMMITTED) / 1024 / 1024, 3))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory usage: %F MiB"), FMT_FLOAT_P((f64)gstat_get(GSTAT_MEMORY_COMMITTED) / 1024 / 1024, 3)));
@ -2067,7 +2070,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
for (u64 i = 0; i < store->num_clients_reserved; ++i) { for (u64 i = 0; i < store->num_clients_reserved; ++i) {
struct sim_client *client = &store->clients[i]; struct sim_client *client = &store->clients[i];
if (client->valid && client != local_client && client != publish_client && client != user_input_client) { if (client->valid && client != local_client && client != publish_client && client != user_input_client) {
client->rtt_ns = host_get_channel_rtt_ns(host, client->channel_id); client->rtt_ns = host_get_channel_last_rtt_ns(host, client->channel_id);
/* Release unneeded received snapshots */ /* Release unneeded received snapshots */
if (client->double_ack > 0) { if (client->double_ack > 0) {
//u64 keep_tick = max_u64(min_u64(client->double_ack, step_tick), 1); //u64 keep_tick = max_u64(min_u64(client->double_ack, step_tick), 1);
@ -2260,7 +2263,6 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
/* Copy local snapshot to user client */ /* Copy local snapshot to user client */
{ {
struct sim_snapshot *local_world = sim_snapshot_from_tick(local_client, local_client->last_tick); struct sim_snapshot *local_world = sim_snapshot_from_tick(local_client, local_client->last_tick);
/* TODO: Double buffer */ /* TODO: Double buffer */
struct sys_lock lock = sys_mutex_lock_e(&G.local_to_user_client_mutex); struct sys_lock lock = sys_mutex_lock_e(&G.local_to_user_client_mutex);
struct sim_snapshot *copy_ss = sim_snapshot_alloc(G.local_to_user_client, local_world, local_world->tick); struct sim_snapshot *copy_ss = sim_snapshot_alloc(G.local_to_user_client, local_world, local_world->tick);