diff --git a/src/host.c b/src/host.c index eea5ef05..c3a48d0e 100644 --- a/src/host.c +++ b/src/host.c @@ -34,6 +34,7 @@ enum host_packet_kind { HOST_PACKET_KIND_TRY_CONNECT, HOST_PACKET_KIND_CONNECT_SUCCESS, HOST_PACKET_KIND_DISCONNECT, + HOST_PACKET_KIND_HEARTBEAT, HOST_PACKET_KIND_MSG_CHUNK }; @@ -75,7 +76,10 @@ struct host_channel { struct host_msg_assembler *least_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 their_acked_seq; @@ -622,10 +626,10 @@ void host_queue_write(struct host *host, struct host_channel_id channel_id, stru * 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); - return channel->rtt_ns; + return channel->last_heartbeat_rtt_ns; } /* ========================== * @@ -738,6 +742,24 @@ void host_update(struct host *host) } 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: { if (channel->valid && channel->connected) { @@ -817,6 +839,15 @@ void host_update(struct host *host) cmd->kind = HOST_CMD_KIND_TRY_CONNECT; 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 */ { u64 acked_seq = channel->their_acked_seq; @@ -888,7 +919,7 @@ void host_update(struct host *host) 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); + 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_CONNECT_SUCCESS, 8); bw_write_ubits(&bw, packet_flags, 8); @@ -901,7 +932,7 @@ void host_update(struct host *host) 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); + 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_DISCONNECT, 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); } 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: { b32 is_reliable = cmd->write_reliable; diff --git a/src/host.h b/src/host.h index f5d1a950..0033ee44 100644 --- a/src/host.h +++ b/src/host.h @@ -18,6 +18,7 @@ enum host_cmd_kind { HOST_CMD_KIND_TRY_CONNECT, HOST_CMD_KIND_CONNECT_SUCCESS, HOST_CMD_KIND_DISCONNECT, + HOST_CMD_KIND_HEARTBEAT, HOST_CMD_KIND_WRITE }; @@ -39,9 +40,13 @@ struct host_cmd { enum host_cmd_kind kind; struct host_channel_id channel_id; + u16 heartbeat_id; + u16 heartbeat_ack_id; + b32 write_reliable; struct string write_msg; + struct host_cmd *next; }; @@ -128,7 +133,7 @@ void host_queue_write(struct host *host, struct host_channel_id channel_id, stru * 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 diff --git a/src/sim_ent.h b/src/sim_ent.h index 94b5bffa..463858c5 100644 --- a/src/sim_ent.h +++ b/src/sim_ent.h @@ -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_CLIENT, - SIM_ENT_PROP_LOCAL_CLIENT, SIM_ENT_PROP_CMD_CONTROL, @@ -153,6 +152,9 @@ struct sim_ent { b32 client_dbg_drag_start; b32 client_dbg_drag_stop; + /* Client round-trip-time to server */ + i64 client_rtt_ns; + /* ====================================================================== */ /* Collider */ diff --git a/src/sim_step.c b/src/sim_step.c index 0c0719f0..78496167 100644 --- a/src/sim_step.c +++ b/src/sim_step.c @@ -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 */ if (client_ent->valid) { struct sim_snapshot *src_ss = sim_snapshot_from_tick(client, world->tick); diff --git a/src/user.c b/src/user.c index ca43a741..e3ca6520 100644 --- a/src/user.c +++ b/src/user.c @@ -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))); 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; 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) { struct sim_client *client = &store->clients[i]; 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 */ if (client->double_ack > 0) { //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 */ { struct sim_snapshot *local_world = sim_snapshot_from_tick(local_client, local_client->last_tick); - /* TODO: Double buffer */ 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);