From bd693ac1eb93c0e3c0e8837331afa0148c201381 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 26 Feb 2025 16:45:11 -0600 Subject: [PATCH] prediction progress --- src/sim_ent.c | 24 ++++- src/sim_ent.h | 1 + src/sim_step.c | 3 + src/user.c | 242 ++++++++++++++++++++++++++++++++----------------- 4 files changed, 187 insertions(+), 83 deletions(-) diff --git a/src/sim_ent.c b/src/sim_ent.c index ebd24da7..ae60809d 100644 --- a/src/sim_ent.c +++ b/src/sim_ent.c @@ -225,6 +225,14 @@ void sim_ent_set_id(struct sim_ent *ent, struct sim_ent_id id) /* Insert new id into lookup */ if (!sim_ent_id_eq(id, SIM_ENT_NIL_ID)) { +#if RTC + { + struct sim_ent *existing = sim_ent_from_id(ss, id); + /* Collision should be extremely unlikely under normal circumstances, there's probably a logic error somewhere. */ + ASSERT(!existing->valid); + } +#endif + struct sim_ent_bin *bin = bin_from_id(ss, id); u32 ent_index = index_from_ent(ss, ent); struct sim_ent *last = ent_from_index(ss, bin->last); @@ -576,6 +584,20 @@ void sim_ent_sync(struct sim_ent *local, struct sim_ent *remote) struct sim_ent old = *local; MEMCPY_STRUCT(local, remote); + + + + if (!MEMEQ_STRUCT(&old._xform, &remote->_xform)) { + DEBUGBREAKABLE; + } + + + + + + + + /* Why would 2 ents w/ different uids ever be synced? */ ASSERT(sim_ent_id_eq(local->id, old.id)); @@ -628,7 +650,7 @@ void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_en u64 chunk_size = min_u64(pos + 8, sizeof(*e1)) - pos; u8 *chunk0 = (u8 *)e0 + pos; u8 *chunk1 = (u8 *)e1 + pos; - if (bw_write_bit(bw, MEMEQ(chunk0, chunk1, chunk_size))) { + if (bw_write_bit(bw, !MEMEQ(chunk0, chunk1, chunk_size))) { u64 bits = 0; MEMCPY(&bits, chunk1, chunk_size); bw_write_ubits(bw, bits, 64); diff --git a/src/sim_ent.h b/src/sim_ent.h index 463858c5..2e1062f4 100644 --- a/src/sim_ent.h +++ b/src/sim_ent.h @@ -20,6 +20,7 @@ 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_CLIENT_IS_MASTER, SIM_ENT_PROP_CMD_CONTROL, diff --git a/src/sim_step.c b/src/sim_step.c index 88cf85e1..bbba5535 100644 --- a/src/sim_step.c +++ b/src/sim_step.c @@ -379,7 +379,10 @@ void sim_step(struct sim_step_ctx *ctx) user_input_client->ent_id = client_ent->id; world_client->ent_id = client_ent->id; world->local_client_ent = client_ent->id; + client_ent->owner = client_ent->id; + sim_ent_enable_prop(client_ent, SIM_ENT_PROP_CLIENT_IS_MASTER); } + logf_info("Created client ent with id %F for sim client %F. is_master: %F", FMT_UID(client_ent->id.uid), FMT_HANDLE(client->handle), FMT_UINT(sim_ent_has_prop(client_ent, SIM_ENT_PROP_CLIENT_IS_MASTER))); } /* Update rtt */ diff --git a/src/user.c b/src/user.c index 534fba1d..f369f33a 100644 --- a/src/user.c +++ b/src/user.c @@ -964,15 +964,17 @@ INTERNAL void user_update(void) struct v2 vdc = v2_neg(vcd); f32 opacity_a = 1; - if (v2_dot(velocity, vca) <= 0) { - a = c; - opacity_a = 0; - } else { - opacity_a = v2_dot(vcd, vca) / v2_len_sq(vcd); + f32 opacity_b = 1; + if (v2_len_sq(vcd) != 0) { + if (v2_dot(velocity, vca) <= 0) { + a = c; + opacity_a = 0; + } else { + opacity_a = v2_dot(vcd, vca) / v2_len_sq(vcd); + } + opacity_a = clamp_f32(opacity_a, 0, 1); + opacity_b = clamp_f32(1.f - (v2_dot(vdc, vdb) / v2_len_sq(vdc)), 0, 1); } - opacity_a = clamp_f32(opacity_a, 0, 1); - - f32 opacity_b = clamp_f32(1.f - (v2_dot(vdc, vdb) / v2_len_sq(vdc)), 0, 1); f32 thickness = 0.01f; u32 color_start = RGBA_32_F(1, 0.5, 0, opacity_a); @@ -1851,6 +1853,38 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg) +INTERNAL void generate_user_input_cmds(struct sim_client *user_input_client, u64 tick) +{ + struct sim_snapshot *prev_user_input_ss = sim_snapshot_from_tick(user_input_client, user_input_client->last_tick); + struct sim_snapshot *user_input_ss = sim_snapshot_alloc(user_input_client, prev_user_input_ss, tick); + struct sim_ent *user_input_root = sim_ent_from_id(user_input_ss, SIM_ENT_ROOT_ID); + /* Find / create local control cmd ent */ + struct sim_ent *control_cmd_ent = sim_ent_find_first_match_one(user_input_ss, SIM_ENT_PROP_CMD_CONTROL); + if (!control_cmd_ent->valid) { + control_cmd_ent = sim_ent_alloc_sync_src(user_input_root); + control_cmd_ent->predictor = user_input_ss->local_client_ent; + sim_ent_enable_prop(control_cmd_ent, SIM_ENT_PROP_CMD_CONTROL); + control_cmd_ent->predictor = user_input_client->ent_id; + sim_ent_activate(control_cmd_ent, user_input_ss->tick); + } + /* Update local control cmd ent */ + { + struct sys_lock lock = sys_mutex_lock_e(&G.user_sim_cmd_mutex); + control_cmd_ent->cmd_control = G.user_sim_cmd_control; + control_cmd_ent->cmd_hovered_ent = G.user_hovered_ent; + ++G.user_sim_cmd_gen; + sys_mutex_unlock(&lock); + } +} + + + + + + + + + @@ -1918,8 +1952,11 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) +#if 0 + i64 prev_last_tick_from_master = 0; i64 last_tick_from_master = 0; i64 last_ticks_ahead_from_master = 0; +#endif @@ -2132,9 +2169,14 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) /* Release unneeded user input snapshots */ sim_snapshot_release_ticks_in_range(user_input_client, 0, local_client->first_tick - 1); + + + +#if 0 u64 sim_base_tick = local_client->last_tick; u64 sim_to_tick = sim_base_tick + 1; if (master_client->valid) { + prev_last_tick_from_master = master_client->last_tick; last_tick_from_master = master_client->last_tick; last_ticks_ahead_from_master = master_client->ack - last_tick_from_master; if (math_abs_i64(last_ticks_ahead_from_master) > 50) { @@ -2161,81 +2203,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) - - - /* Create user input */ - { - struct sim_snapshot *prev_user_input_ss = sim_snapshot_from_tick(user_input_client, user_input_client->last_tick); - struct sim_snapshot *user_input_ss = sim_snapshot_alloc(user_input_client, prev_user_input_ss, sim_to_tick); - struct sim_ent *user_input_root = sim_ent_from_id(user_input_ss, SIM_ENT_ROOT_ID); - /* Find / create local control cmd ent */ - struct sim_ent *control_cmd_ent = sim_ent_find_first_match_one(user_input_ss, SIM_ENT_PROP_CMD_CONTROL); - if (!control_cmd_ent->valid) { - control_cmd_ent = sim_ent_alloc_sync_src(user_input_root); - control_cmd_ent->predictor = user_input_ss->local_client_ent; - sim_ent_enable_prop(control_cmd_ent, SIM_ENT_PROP_CMD_CONTROL); - control_cmd_ent->predictor = user_input_client->ent_id; - sim_ent_activate(control_cmd_ent, user_input_ss->tick); - } - /* Update local control cmd ent */ - { - struct sys_lock lock = sys_mutex_lock_e(&G.user_sim_cmd_mutex); - control_cmd_ent->cmd_control = G.user_sim_cmd_control; - control_cmd_ent->cmd_hovered_ent = G.user_hovered_ent; - ++G.user_sim_cmd_gen; - sys_mutex_unlock(&lock); - } - } - - - - - - - - - - - - - - - - /* Step */ -#if 1 - { - struct sim_step_ctx ctx = ZI; - ctx.is_master = is_master; - ctx.sim_dt_ns = step_dt_ns; - ctx.accel = &accel; - - ctx.user_input_client = user_input_client; - ctx.master_client = master_client; - ctx.publish_client = publish_client; - - if (is_master) { - ASSERT(sim_to_tick == sim_base_tick + 1); - struct sim_snapshot *prev_world = sim_snapshot_from_tick(local_client, sim_base_tick); - ctx.world = sim_snapshot_alloc(local_client, prev_world, sim_to_tick); - sim_step(&ctx); - } else { - struct sim_snapshot *master_ss = sim_snapshot_from_tick(master_client, sim_base_tick); - if (master_ss->valid) { - local_client->ent_id = master_ss->local_client_ent; - user_input_client->ent_id = master_ss->local_client_ent; - - struct sim_snapshot *prev_world = sim_snapshot_alloc(local_client, master_ss, master_ss->tick); - - while (prev_world->tick < sim_to_tick) { - ctx.world = sim_snapshot_alloc(local_client, prev_world, prev_world->tick + 1); - sim_step(&ctx); - prev_world = ctx.world; - } - } - } - } -#else { struct sim_step_ctx ctx = ZI; ctx.is_master = is_master; @@ -2277,7 +2245,117 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) + if (is_master) { + /* Step master */ + struct sim_step_ctx ctx = ZI; + ctx.is_master = is_master; + ctx.sim_dt_ns = step_dt_ns; + ctx.accel = &accel; + ctx.user_input_client = user_input_client; + ctx.master_client = master_client; + ctx.publish_client = publish_client; + struct sim_snapshot *prev_world = sim_snapshot_from_tick(local_client, local_client->last_tick); + ctx.world = sim_snapshot_alloc(local_client, prev_world, local_client->last_tick + 1); + generate_user_input_cmds(user_input_client, local_client->last_tick + 1); + sim_step(&ctx); + } else if (master_client->valid) { + /* Step client */ + /* TODO: Eventually determine master tick based on a delay to allow for jitter and also interpolation so we can lower snapshot publish frequency */ + + + u64 master_tick = master_client->last_tick; + struct sim_snapshot *master_ss = sim_snapshot_from_tick(master_client, master_tick); + struct sim_ent *master_client_ent = sim_ent_find_first_match_one(master_ss, SIM_ENT_PROP_CLIENT_IS_MASTER); + + user_input_client->ent_id = master_ss->local_client_ent; + local_client->ent_id = master_ss->local_client_ent; + + /* Check for misprediction */ + /* TODO: Actually check for misprediction rather than triggering mispredict any time a new master snapshot is received */ + u64 mispredicted_tick = master_tick; + + u64 step_base_tick = local_client->last_tick; + u64 step_end_tick = step_base_tick + 1; + if (mispredicted_tick > 0) { + step_base_tick = mispredicted_tick; + if (step_end_tick <= step_base_tick) { + step_end_tick = step_base_tick + 1; + } + } + + + + + + + + + + + + { + u64 cmds_ahead_on_master = master_client->ack - master_client->last_tick; + if (math_abs_i64(cmds_ahead_on_master) > 50) { + /* Cmds are too far from master time, snap step end tick */ + i64 rtt_ns = master_client->rtt_ns; + f64 rtt_tick_ratio = (f64)(rtt_ns + (step_dt_ns - 1)) / (f64)step_dt_ns; + i64 num_predict_ticks = math_round_to_int64(rtt_tick_ratio) + 2; + step_end_tick = step_base_tick + num_predict_ticks; + compute_timescale = 1.1; + } else if (cmds_ahead_on_master > 4) { + /* Slow down time to bring sim time closer to master time */ + compute_timescale = 0.9; + } else if (cmds_ahead_on_master < 2) { + /* Speed up time to give master more inputs to work with */ + compute_timescale = 1.1; + } else { + compute_timescale = 1; + } + } + + + + + + + + + + /* Sync master with local base tick */ + struct sim_snapshot *base_ss = sim_snapshot_from_tick(local_client, step_base_tick); + if (base_ss->valid) { + /* FIXME: Set master client ent id somewhere */ + sim_snapshot_sync_ents(base_ss, master_ss, master_client_ent->id); + } else { + base_ss = sim_snapshot_alloc(local_client, master_ss, step_base_tick); + } + + /* Release any existing ticks that are about to be simulated */ + sim_snapshot_release_ticks_in_range(local_client, step_base_tick + 1, U64_MAX); + + /* Step */ + generate_user_input_cmds(user_input_client, step_end_tick); + { + struct sim_step_ctx ctx = ZI; + ctx.is_master = is_master; + ctx.sim_dt_ns = step_dt_ns; + ctx.accel = &accel; + ctx.user_input_client = user_input_client; + ctx.master_client = master_client; + ctx.publish_client = publish_client; + + u64 step_tick = step_base_tick + 1; + struct sim_snapshot *prev_ss = base_ss; + while (step_tick <= step_end_tick) { + ctx.world = sim_snapshot_alloc(local_client, prev_ss, step_tick); + sim_step(&ctx); + prev_ss = ctx.world; + ++step_tick; + } + } + + }