prediction progress
This commit is contained in:
parent
6955da8fd1
commit
bd693ac1eb
@ -225,6 +225,14 @@ void sim_ent_set_id(struct sim_ent *ent, struct sim_ent_id id)
|
|||||||
|
|
||||||
/* Insert new id into lookup */
|
/* Insert new id into lookup */
|
||||||
if (!sim_ent_id_eq(id, SIM_ENT_NIL_ID)) {
|
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);
|
struct sim_ent_bin *bin = bin_from_id(ss, id);
|
||||||
u32 ent_index = index_from_ent(ss, ent);
|
u32 ent_index = index_from_ent(ss, ent);
|
||||||
struct sim_ent *last = ent_from_index(ss, bin->last);
|
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;
|
struct sim_ent old = *local;
|
||||||
MEMCPY_STRUCT(local, remote);
|
MEMCPY_STRUCT(local, remote);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!MEMEQ_STRUCT(&old._xform, &remote->_xform)) {
|
||||||
|
DEBUGBREAKABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Why would 2 ents w/ different uids ever be synced? */
|
/* Why would 2 ents w/ different uids ever be synced? */
|
||||||
ASSERT(sim_ent_id_eq(local->id, old.id));
|
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;
|
u64 chunk_size = min_u64(pos + 8, sizeof(*e1)) - pos;
|
||||||
u8 *chunk0 = (u8 *)e0 + pos;
|
u8 *chunk0 = (u8 *)e0 + pos;
|
||||||
u8 *chunk1 = (u8 *)e1 + 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;
|
u64 bits = 0;
|
||||||
MEMCPY(&bits, chunk1, chunk_size);
|
MEMCPY(&bits, chunk1, chunk_size);
|
||||||
bw_write_ubits(bw, bits, 64);
|
bw_write_ubits(bw, bits, 64);
|
||||||
|
|||||||
@ -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_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_CLIENT_IS_MASTER,
|
||||||
|
|
||||||
SIM_ENT_PROP_CMD_CONTROL,
|
SIM_ENT_PROP_CMD_CONTROL,
|
||||||
|
|
||||||
|
|||||||
@ -379,7 +379,10 @@ void sim_step(struct sim_step_ctx *ctx)
|
|||||||
user_input_client->ent_id = client_ent->id;
|
user_input_client->ent_id = client_ent->id;
|
||||||
world_client->ent_id = client_ent->id;
|
world_client->ent_id = client_ent->id;
|
||||||
world->local_client_ent = 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 */
|
/* Update rtt */
|
||||||
|
|||||||
242
src/user.c
242
src/user.c
@ -964,15 +964,17 @@ INTERNAL void user_update(void)
|
|||||||
struct v2 vdc = v2_neg(vcd);
|
struct v2 vdc = v2_neg(vcd);
|
||||||
|
|
||||||
f32 opacity_a = 1;
|
f32 opacity_a = 1;
|
||||||
if (v2_dot(velocity, vca) <= 0) {
|
f32 opacity_b = 1;
|
||||||
a = c;
|
if (v2_len_sq(vcd) != 0) {
|
||||||
opacity_a = 0;
|
if (v2_dot(velocity, vca) <= 0) {
|
||||||
} else {
|
a = c;
|
||||||
opacity_a = v2_dot(vcd, vca) / v2_len_sq(vcd);
|
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;
|
f32 thickness = 0.01f;
|
||||||
u32 color_start = RGBA_32_F(1, 0.5, 0, opacity_a);
|
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_tick_from_master = 0;
|
||||||
i64 last_ticks_ahead_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 */
|
/* Release unneeded user input snapshots */
|
||||||
sim_snapshot_release_ticks_in_range(user_input_client, 0, local_client->first_tick - 1);
|
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_base_tick = local_client->last_tick;
|
||||||
u64 sim_to_tick = sim_base_tick + 1;
|
u64 sim_to_tick = sim_base_tick + 1;
|
||||||
if (master_client->valid) {
|
if (master_client->valid) {
|
||||||
|
prev_last_tick_from_master = master_client->last_tick;
|
||||||
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;
|
last_ticks_ahead_from_master = master_client->ack - last_tick_from_master;
|
||||||
if (math_abs_i64(last_ticks_ahead_from_master) > 50) {
|
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 */
|
/* 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;
|
struct sim_step_ctx ctx = ZI;
|
||||||
ctx.is_master = is_master;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user