prediction progress

This commit is contained in:
jacob 2025-02-26 16:45:11 -06:00
parent 6955da8fd1
commit bd693ac1eb
4 changed files with 187 additions and 83 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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 */

View File

@ -964,6 +964,8 @@ INTERNAL void user_update(void)
struct v2 vdc = v2_neg(vcd);
f32 opacity_a = 1;
f32 opacity_b = 1;
if (v2_len_sq(vcd) != 0) {
if (v2_dot(velocity, vca) <= 0) {
a = c;
opacity_a = 0;
@ -971,8 +973,8 @@ INTERNAL void user_update(void)
opacity_a = v2_dot(vcd, vca) / v2_len_sq(vcd);
}
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);
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;
}
}
}