prediction progress

This commit is contained in:
jacob 2025-02-21 18:07:17 -06:00
parent 27f49512d6
commit 4d8819d3ee
9 changed files with 635 additions and 455 deletions

View File

@ -62,7 +62,7 @@
#define COLLIDER_DEBUG_DETAILED_DRAW_MENKOWSKI 0 #define COLLIDER_DEBUG_DETAILED_DRAW_MENKOWSKI 0
/* If enabled, bitbuffs will insert/verify magic numbers & length for each read & write */ /* If enabled, bitbuffs will insert/verify magic numbers & length for each read & write */
#define BITBUFF_DEBUG 0 #define BITBUFF_DEBUG RTC
#define BITBUFF_TEST RTC #define BITBUFF_TEST RTC
/* ========================== * /* ========================== *

View File

@ -125,7 +125,7 @@ void sim_client_store_release(struct sim_client_store *store)
* Client alloc * Client alloc
* ========================== */ * ========================== */
struct sim_client *sim_client_alloc(struct sim_client_store *store, enum sim_client_kind kind) struct sim_client *sim_client_alloc(struct sim_client_store *store)
{ {
struct sim_client_handle handle = ZI; struct sim_client_handle handle = ZI;
struct sim_client *client = sim_client_from_handle(store, store->first_free_client); struct sim_client *client = sim_client_from_handle(store, store->first_free_client);
@ -144,7 +144,6 @@ struct sim_client *sim_client_alloc(struct sim_client_store *store, enum sim_cli
*client = *sim_client_nil(); *client = *sim_client_nil();
client->store = store; client->store = store;
client->valid = true; client->valid = true;
client->kind = kind;
client->handle = handle; client->handle = handle;
client->snapshots_arena = arena_alloc(GIGABYTE(8)); client->snapshots_arena = arena_alloc(GIGABYTE(8));
@ -302,12 +301,8 @@ struct sim_snapshot *sim_snapshot_alloc(struct sim_client *client, struct sim_sn
++client->num_ticks; ++client->num_ticks;
/* Copy src info */ /* Copy src info */
ss->is_master = src->is_master; ss->sim_dt_ns = src->sim_dt_ns;
ss->real_dt_ns = src->real_dt_ns; ss->sim_time_ns = src->sim_time_ns;
ss->real_time_ns = src->real_time_ns;
ss->world_timescale = src->world_timescale;
ss->world_dt_ns = src->world_dt_ns;
ss->world_time_ns = src->world_time_ns;
ss->continuity_gen = src->continuity_gen; ss->continuity_gen = src->continuity_gen;
ss->local_client = src->local_client; ss->local_client = src->local_client;
ss->phys_iteration = src->phys_iteration; ss->phys_iteration = src->phys_iteration;
@ -528,11 +523,8 @@ struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_client *client, str
if (should_blend) { if (should_blend) {
/* Blend time */ /* Blend time */
ss->real_dt_ns = math_lerp_i64(ss0->real_dt_ns, ss1->real_dt_ns, blend); ss->sim_dt_ns = math_lerp_i64(ss0->sim_dt_ns, ss1->sim_dt_ns, blend);
ss->real_time_ns = math_lerp_i64(ss0->real_time_ns, ss1->real_time_ns, blend); ss->sim_time_ns = math_lerp_i64(ss0->sim_time_ns, ss1->sim_time_ns, blend);
ss->world_timescale = math_lerp_f64(ss0->world_timescale, ss1->world_timescale, blend);
ss->world_dt_ns = math_lerp_i64(ss0->world_dt_ns, ss1->world_dt_ns, blend);
ss->world_time_ns = math_lerp_i64(ss0->world_time_ns, ss1->world_time_ns, blend);
/* Blend entities */ /* Blend entities */
{ {
@ -559,15 +551,8 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver,
{ {
__prof; __prof;
/* TODO: Don't encode this */ bw_write_iv(bw, ss1->sim_dt_ns);
bw_write_bit(bw, ss1->is_master); bw_write_iv(bw, ss1->sim_time_ns);
bw_write_iv(bw, ss1->real_dt_ns);
bw_write_iv(bw, ss1->real_time_ns);
bw_write_f64(bw, ss1->world_timescale);
bw_write_iv(bw, ss1->world_dt_ns);
bw_write_iv(bw, ss1->world_time_ns);
bw_write_uv(bw, ss1->continuity_gen); bw_write_uv(bw, ss1->continuity_gen);
bw_write_uv(bw, ss1->phys_iteration); bw_write_uv(bw, ss1->phys_iteration);
@ -575,12 +560,6 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver,
bw_write_uv(bw, receiver->handle.gen); bw_write_uv(bw, receiver->handle.gen);
bw_write_uv(bw, receiver->handle.idx); bw_write_uv(bw, receiver->handle.idx);
bw_write_f32(bw, ss1->control.move.x);
bw_write_f32(bw, ss1->control.move.y);
bw_write_f32(bw, ss1->control.focus.x);
bw_write_f32(bw, ss1->control.focus.y);
bw_write_ubits(bw, ss1->control.flags, 32);
/* Ents */ /* Ents */
if (ss1->num_ents_allocated == ss0->num_ents_allocated) { if (ss1->num_ents_allocated == ss0->num_ents_allocated) {
bw_write_bit(bw, 0); bw_write_bit(bw, 0);
@ -612,15 +591,8 @@ void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss)
{ {
__prof; __prof;
/* TODO: Don't encode this */ ss->sim_dt_ns = br_read_iv(br);
ss->is_master = br_read_bit(br); ss->sim_time_ns = br_read_iv(br);
ss->real_dt_ns = br_read_iv(br);
ss->real_time_ns = br_read_iv(br);
ss->world_timescale = br_read_f64(br);
ss->world_dt_ns = br_read_iv(br);
ss->world_time_ns = br_read_iv(br);
ss->continuity_gen = br_read_uv(br); ss->continuity_gen = br_read_uv(br);
ss->phys_iteration = br_read_uv(br); ss->phys_iteration = br_read_uv(br);
@ -628,12 +600,6 @@ void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss)
ss->local_client.gen = br_read_uv(br); ss->local_client.gen = br_read_uv(br);
ss->local_client.idx = br_read_uv(br); ss->local_client.idx = br_read_uv(br);
ss->control.move.x = br_read_f32(br);
ss->control.move.y = br_read_f32(br);
ss->control.focus.x = br_read_f32(br);
ss->control.focus.y = br_read_f32(br);
ss->control.flags = br_read_ubits(br, 32);
/* Ents */ /* Ents */
if (br_read_bit(br)) { if (br_read_bit(br)) {
ss->num_ents_allocated = br_read_uv(br); ss->num_ents_allocated = br_read_uv(br);

View File

@ -60,14 +60,6 @@ void sim_client_store_release(struct sim_client_store *store);
struct sim_snapshot; struct sim_snapshot;
enum sim_client_kind {
SIM_CLIENT_KIND_INVALID,
SIM_CLIENT_KIND_USER,
SIM_CLIENT_KIND_LOCAL_SIM,
SIM_CLIENT_KIND_SLAVE_SIM,
SIM_CLIENT_KIND_MASTER_SIM,
};
struct sim_snapshot_lookup_bucket { struct sim_snapshot_lookup_bucket {
struct sim_snapshot *first; struct sim_snapshot *first;
struct sim_snapshot *last; struct sim_snapshot *last;
@ -75,7 +67,6 @@ struct sim_snapshot_lookup_bucket {
struct sim_client { struct sim_client {
b32 valid; b32 valid;
enum sim_client_kind kind;
struct sim_client_handle handle; struct sim_client_handle handle;
struct sim_client_store *store; struct sim_client_store *store;
@ -91,8 +82,10 @@ struct sim_client {
/* This is the last confirmed tick of ours that we know this client has received */ /* This is the last confirmed tick of ours that we know this client has received */
u64 ack; u64 ack;
/* This is the last confirmed client tick that the client knows we have received */ /* This is the last confirmed ack of ours that we know this client has received (this
u64 reverse_ack; * can be used to determine which client ticks will no longer be delta encoded from and
* can therefore be released) */
u64 double_ack;
/* Snapshots sorted by tick (low to high) */ /* Snapshots sorted by tick (low to high) */
u64 first_tick; u64 first_tick;
@ -111,7 +104,12 @@ INLINE struct sim_client *sim_client_nil(void)
return *_g_sim_client_nil; return *_g_sim_client_nil;
} }
struct sim_client *sim_client_alloc(struct sim_client_store *store, enum sim_client_kind kind); INLINE b32 sim_client_handle_eq(struct sim_client_handle a, struct sim_client_handle b)
{
return a.gen == b.gen && a.idx == b.idx;
}
struct sim_client *sim_client_alloc(struct sim_client_store *store);
void sim_client_release(struct sim_client *client); void sim_client_release(struct sim_client *client);
struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, struct host_channel_id channel_id); struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, struct host_channel_id channel_id);
@ -123,16 +121,14 @@ struct sim_client *sim_client_from_handle(struct sim_client_store *store, struct
* ========================== */ * ========================== */
enum sim_control_flag { enum sim_control_flag {
SIM_CONTROL_FLAG_NONE = 0, SIM_CONTROL_FLAG_FIRE = 1 << 0,
SIM_CONTROL_FLAG_FIRING = 1 << 0,
/* Testing */ /* Testing */
SIM_CONTROL_FLAG_CLEAR_ALL = 1 << 1, SIM_CONTROL_FLAG_DRAG = 1 << 1,
SIM_CONTROL_FLAG_SPAWN_TEST = 1 << 2, SIM_CONTROL_FLAG_CLEAR_ALL = 1 << 2,
SIM_CONTROL_FLAG_PAUSE = 1 << 3, SIM_CONTROL_FLAG_SPAWN_TEST = 1 << 3,
SIM_CONTROL_FLAG_STEP = 1 << 4, SIM_CONTROL_FLAG_PAUSE = 1 << 4,
SIM_CONTROL_FLAG_STEP = 1 << 5
SIM_CONTROL_FLAG_DRAGGING = 1 << 5,
}; };
struct sim_control { struct sim_control {
@ -153,32 +149,13 @@ struct sim_snapshot {
struct arena arena; struct arena arena;
struct sim_client_handle producer_client; /* Program time of local snapshot publish to user thread (if relevant) */
b32 producer_client_is_local;
/* ====================================================================== */
/* SIM_SNAPSHOT_FLAG_CONTROL */
struct sim_control control;
/* ====================================================================== */
/* SIM_SNAPSHOT_FLAG_STATE */
/* Was this snapshot created by the master sim */
b32 is_master;
/* Time of local snapshot publish to user thread */
i64 publish_dt_ns; i64 publish_dt_ns;
i64 publish_time_ns; i64 publish_time_ns;
/* Real time (guaranteed to increase by real_dt_ns each sim step) */ /* Sim time (guaranteed to increase by sim_dt_ns each step) */
i64 real_dt_ns; i64 sim_dt_ns;
i64 real_time_ns; i64 sim_time_ns;
/* World time (affected by timescale) */
f64 world_timescale;
i64 world_dt_ns;
i64 world_time_ns;
/* If != previous tick's continuity then don't lerp */ /* If != previous tick's continuity then don't lerp */
u64 continuity_gen; u64 continuity_gen;
@ -186,8 +163,8 @@ struct sim_snapshot {
/* The last physics iteration (used for tracking contact lifetime) */ /* The last physics iteration (used for tracking contact lifetime) */
u64 phys_iteration; u64 phys_iteration;
/* The receiver's client handle on the sender's machine (use this to find local client ent in snapshot) */ /* TODO: Replace with networked id that resolves to local client ent? */
struct sim_client_handle local_client; struct sim_client_handle local_client; /* The receiver's client handle on the master sim (used for finding local client ent in snapshot) */
/* Entities */ /* Entities */
struct arena ents_arena; struct arena ents_arena;
@ -197,16 +174,6 @@ struct sim_snapshot {
u64 num_ents_reserved; u64 num_ents_reserved;
}; };
struct sim_snapshot_list_node {
struct sim_snapshot *ss;
struct sim_snapshot_list_node *next;
};
struct sim_snapshot_list {
struct sim_snapshot_list_node *first;
struct sim_snapshot_list_node *last;
};
INLINE struct sim_snapshot *sim_snapshot_nil(void) INLINE struct sim_snapshot *sim_snapshot_nil(void)
{ {
extern READONLY struct sim_snapshot **_g_sim_snapshot_nil; extern READONLY struct sim_snapshot **_g_sim_snapshot_nil;

View File

@ -373,6 +373,10 @@ void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_en
{ {
struct sim_snapshot *ss = e1->ss; struct sim_snapshot *ss = e1->ss;
/* FIXME: Things like xforms need to be retreived manually rather than memcopied.
* This will also be true for things like ent handles once netids are implemented. */
/* TODO: Granular delta encoding */ /* TODO: Granular delta encoding */
u64 pos = 0; u64 pos = 0;

View File

@ -20,9 +20,12 @@ enum sim_ent_prop {
SIM_ENT_PROP_RELEASE_THIS_TICK, SIM_ENT_PROP_RELEASE_THIS_TICK,
SIM_ENT_PROP_RELEASE_NEXT_TICK, SIM_ENT_PROP_RELEASE_NEXT_TICK,
SIM_ENT_PROP_REMOTE,
SIM_ENT_PROP_CLIENT, SIM_ENT_PROP_CLIENT,
SIM_ENT_PROP_LOCAL_CLIENT, SIM_ENT_PROP_LOCAL_CLIENT,
SIM_ENT_PROP_CMD_CONTROL,
SIM_ENT_PROP_PHYSICAL_DYNAMIC, SIM_ENT_PROP_PHYSICAL_DYNAMIC,
SIM_ENT_PROP_PHYSICAL_KINEMATIC, SIM_ENT_PROP_PHYSICAL_KINEMATIC,
@ -85,10 +88,19 @@ struct sim_ent {
struct sim_ent_handle last; struct sim_ent_handle last;
struct sim_ent_handle next_free; struct sim_ent_handle next_free;
/* ====================================================================== */
/* Remote */
/* SIM_ENT_PROP_REMOTE */
struct sim_client_handle remote_client;
struct sim_ent_handle remote_ent;
/* ====================================================================== */ /* ====================================================================== */
/* Position */ /* Position */
/* Access with xform getters/setters */ /* Access with xform getters/setters */
/* TODO: Prefix with '_' to signal against direct access */
struct xform local_xform; /* Transform in relation to parent ent (or the world if ent has no parent) */ struct xform local_xform; /* Transform in relation to parent ent (or the world if ent has no parent) */
struct xform cached_global_xform; /* Calculated from ent tree */ struct xform cached_global_xform; /* Calculated from ent tree */
b32 cached_global_xform_dirty; b32 cached_global_xform_dirty;
@ -105,6 +117,35 @@ struct sim_ent {
i32 layer; i32 layer;
i32 final_layer; /* Calculated each tick from ent tree */ i32 final_layer; /* Calculated each tick from ent tree */
/* ====================================================================== */
/* Cmd */
/* SIM_ENT_PROP_CMD_CONTROL */
/* FIXME: Lerp */
struct sim_ent_handle cmd_client;
struct sim_control cmd_control;
/* ====================================================================== */
/* Client */
/* SIM_ENT_PROP_CLIENT */
/* FIXME: Lerp */
struct sim_client_handle client_handle; /* The client handle on the master sim's machine */
struct sim_control client_control;
struct v2 client_cursor_pos;
struct sim_ent_handle client_control_ent;
struct sim_ent_handle client_camera_ent;
struct sim_ent_handle client_dbg_drag_joint_ent;
b32 client_dbg_drag_start;
b32 client_dbg_drag_stop;
/* ====================================================================== */ /* ====================================================================== */
/* Collider */ /* Collider */
@ -134,25 +175,6 @@ struct sim_ent {
/* SIM_ENT_PROP_MOUSE_JOINT */ /* SIM_ENT_PROP_MOUSE_JOINT */
struct phys_mouse_joint mouse_joint_data; struct phys_mouse_joint mouse_joint_data;
/* ====================================================================== */
/* Client */
/* SIM_ENT_PROP_CLIENT */
/* FIXME: Lerp */
struct sim_client_handle client_handle;
struct sim_control client_control;
struct v2 client_cursor_pos;
struct sim_ent_handle client_control_ent;
struct sim_ent_handle client_camera_ent;
struct sim_ent_handle client_dbg_drag_joint_ent;
b32 client_dbg_drag_start;
b32 client_dbg_drag_stop;
/* ====================================================================== */ /* ====================================================================== */
/* Control */ /* Control */
@ -231,7 +253,7 @@ struct sim_ent {
/* SIM_ENT_PROP_TRIGGERED_THIS_TICK */ /* SIM_ENT_PROP_TRIGGERED_THIS_TICK */
f32 trigger_delay; /* Minimum time between triggers */ f32 trigger_delay; /* Minimum time between triggers */
f32 last_triggered; i64 last_triggered_ns;
/* ====================================================================== */ /* ====================================================================== */
/* Bullet */ /* Bullet */

View File

@ -124,6 +124,16 @@ void sim_lookup_remove(struct sim_lookup *l, struct sim_lookup_entry *entry)
l->first_free_entry = entry; l->first_free_entry = entry;
} }
struct sim_lookup_key sim_lookup_key_from_client_and_ent_handles(struct sim_client_handle client_handle, struct sim_ent_handle ent_handle)
{
struct sim_lookup_key key = ZI;
struct string b0 = STRING_FROM_STRUCT(&client_handle);
struct string b1 = STRING_FROM_STRUCT(&ent_handle);
key.hash = hash_fnv64(HASH_FNV64_BASIS, b0);
key.hash = hash_fnv64(key.hash, b1);
return key;
}
struct sim_lookup_key sim_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1) struct sim_lookup_key sim_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1)
{ {
struct sim_lookup_key key = ZI; struct sim_lookup_key key = ZI;
@ -149,6 +159,7 @@ struct sim_accel sim_accel_alloc(void)
{ {
struct sim_accel accel = ZI; struct sim_accel accel = ZI;
accel.space = space_alloc(1, 256); accel.space = space_alloc(1, 256);
accel.remote_lookup = sim_lookup_alloc(4096);
accel.client_lookup = sim_lookup_alloc(4096); accel.client_lookup = sim_lookup_alloc(4096);
accel.contact_lookup = sim_lookup_alloc(4096); accel.contact_lookup = sim_lookup_alloc(4096);
#if COLLIDER_DEBUG #if COLLIDER_DEBUG
@ -164,6 +175,7 @@ void sim_accel_release(struct sim_accel *accel)
#endif #endif
sim_lookup_release(&accel->contact_lookup); sim_lookup_release(&accel->contact_lookup);
sim_lookup_release(&accel->client_lookup); sim_lookup_release(&accel->client_lookup);
sim_lookup_release(&accel->remote_lookup);
space_release(accel->space); space_release(accel->space);
} }
@ -172,6 +184,7 @@ void sim_accel_rebuild(struct sim_snapshot *ss, struct sim_accel *accel)
/* FIXME: Rebuild collision debug lookup */ /* FIXME: Rebuild collision debug lookup */
space_reset(accel->space); space_reset(accel->space);
sim_lookup_reset(&accel->remote_lookup);
sim_lookup_reset(&accel->client_lookup); sim_lookup_reset(&accel->client_lookup);
sim_lookup_reset(&accel->contact_lookup); sim_lookup_reset(&accel->contact_lookup);
@ -188,6 +201,10 @@ void sim_accel_rebuild(struct sim_snapshot *ss, struct sim_accel *accel)
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) { for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *ent = &ss->ents[sim_ent_index]; struct sim_ent *ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(ent)) continue; if (!sim_ent_is_valid_and_active(ent)) continue;
if (sim_ent_has_prop(ent, SIM_ENT_PROP_REMOTE)) {
struct sim_lookup_key key = sim_lookup_key_from_client_and_ent_handles(ent->remote_client, ent->remote_ent);
sim_lookup_set(&accel->remote_lookup, key, ent->handle);
}
if (sim_ent_has_prop(ent, SIM_ENT_PROP_CLIENT)) { if (sim_ent_has_prop(ent, SIM_ENT_PROP_CLIENT)) {
struct sim_lookup_key key = sim_lookup_key_from_client_handle(ent->client_handle); struct sim_lookup_key key = sim_lookup_key_from_client_handle(ent->client_handle);
sim_lookup_set(&accel->client_lookup, key, ent->handle); sim_lookup_set(&accel->client_lookup, key, ent->handle);
@ -561,40 +578,24 @@ void sim_step(struct sim_step_ctx *step_ctx)
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
struct sim_snapshot *world = step_ctx->world; struct sim_snapshot *world = step_ctx->world;
struct sim_accel *accel = step_ctx->accel; i64 sim_dt_ns = step_ctx->sim_dt_ns;
struct sim_snapshot_list *cmd_snapshots = step_ctx->cmd_snapshots;
i64 real_dt_ns = step_ctx->real_dt_ns;
/* ========================== * /* ========================== *
* Begin frame * Begin frame
* ========================== */ * ========================== */
sim_accel_rebuild(world, step_ctx->accel);
//sys_sleep_precise(rng_rand_f32(0, 0.050)); //sys_sleep_precise(rng_rand_f32(0, 0.050));
//sys_sleep_precise(0.050); //sys_sleep_precise(0.050);
world->real_dt_ns = max_i64(0, real_dt_ns); world->sim_dt_ns = max_i64(0, sim_dt_ns);
world->real_time_ns += world->real_dt_ns; world->sim_time_ns += world->sim_dt_ns;
f32 sim_dt = SECONDS_FROM_NS(world->sim_dt_ns);
world->world_timescale = SIM_TIMESCALE;
world->world_dt_ns = max_i64(0, real_dt_ns * world->world_timescale);
world->world_time_ns += world->world_dt_ns;
f64 real_dt = SECONDS_FROM_NS(world->real_dt_ns);
f64 real_time = SECONDS_FROM_NS(world->real_time_ns);
f64 world_dt = SECONDS_FROM_NS(world->world_dt_ns);
f64 world_time = SECONDS_FROM_NS(world->world_time_ns);
(UNUSED)real_dt;
(UNUSED)real_time;
(UNUSED)world_dt;
(UNUSED)world_time;
struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); struct sprite_scope *sprite_frame_scope = sprite_scope_begin();
struct sim_ent *root = sim_ent_from_handle(world, SIM_ENT_ROOT_HANDLE); struct sim_ent *root = sim_ent_from_handle(world, SIM_ENT_ROOT_HANDLE);
if (world->is_master) { if (step_ctx->is_master) {
/* ========================== * /* ========================== *
* Spawn test entities * Spawn test entities
* ========================== */ * ========================== */
@ -651,6 +652,67 @@ void sim_step(struct sim_step_ctx *step_ctx)
* Process client cmds * Process client cmds
* ========================== */ * ========================== */
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
struct sim_ent *cmd_ent = &world->ents[ent_index];
if (!sim_ent_is_valid_and_active(cmd_ent)) continue;
if (sim_ent_has_prop(cmd_ent, SIM_ENT_PROP_CMD_CONTROL)) {
struct sim_ent *client_ent = sim_ent_from_handle(world, cmd_ent->cmd_client);
if (sim_ent_is_valid_and_active(client_ent)) {
/* Process control cmd for client */
struct sim_control old_control = client_ent->client_control;
struct sim_control *control = &client_ent->client_control;
*control = cmd_ent->cmd_control;
{
client_ent->client_dbg_drag_start = false;
client_ent->client_dbg_drag_stop = false;
if (v2_len_sq(control->move) > 1) {
/* Cap movement vector magnitude at 1 */
control->move = v2_norm(control->move);
}
/* Determine cursor pos from focus */
{
struct sim_ent_handle client_control_ent_handle = client_ent->client_control_ent;
struct sim_ent *client_control_ent = sim_ent_from_handle(world, client_control_ent_handle);
if (client_control_ent->valid || sim_ent_handle_eq(client_control_ent_handle, SIM_ENT_NIL_HANDLE)) {
/* Only update cursor pos if focus ent is valid (or nil) */
client_ent->client_cursor_pos = v2_add(sim_ent_get_xform(client_control_ent).og, client_ent->client_control.focus);
}
}
u32 flags = control->flags;
if (flags & SIM_CONTROL_FLAG_DRAG) {
if (!(old_control.flags & SIM_CONTROL_FLAG_DRAG)) {
client_ent->client_dbg_drag_start = true;
}
} else {
if (old_control.flags & SIM_CONTROL_FLAG_DRAG) {
client_ent->client_dbg_drag_stop = true;
}
}
if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) {
if (!(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) {
test_clear_level(world);
}
}
if (flags & SIM_CONTROL_FLAG_SPAWN_TEST) {
if (!(old_control.flags & SIM_CONTROL_FLAG_SPAWN_TEST)) {
logf_info("Spawning (test)");
u32 count = 1;
f32 spread = 1;
for (u32 j = 0; j < count; ++j) {
spawn_test_entities(world, V2(0, (((f32)j / (f32)count) - 0.5) * spread));
}
}
}
}
}
}
}
#if 0
for (struct sim_snapshot_list_node *n = cmd_snapshots->first; n; n = n->next) { for (struct sim_snapshot_list_node *n = cmd_snapshots->first; n; n = n->next) {
struct sim_snapshot *cmd_snapshot = n->ss; struct sim_snapshot *cmd_snapshot = n->ss;
struct sim_client_handle client_handle = cmd_snapshot->producer_client; struct sim_client_handle client_handle = cmd_snapshot->producer_client;
@ -698,12 +760,12 @@ void sim_step(struct sim_step_ctx *step_ctx)
} }
u32 flags = control->flags; u32 flags = control->flags;
if (flags & SIM_CONTROL_FLAG_DRAGGING) { if (flags & SIM_CONTROL_FLAG_DRAG) {
if (!(old_control.flags & SIM_CONTROL_FLAG_DRAGGING)) { if (!(old_control.flags & SIM_CONTROL_FLAG_DRAG)) {
client_ent->client_dbg_drag_start = true; client_ent->client_dbg_drag_start = true;
} }
} else { } else {
if (old_control.flags & SIM_CONTROL_FLAG_DRAGGING) { if (old_control.flags & SIM_CONTROL_FLAG_DRAG) {
client_ent->client_dbg_drag_stop = true; client_ent->client_dbg_drag_stop = true;
} }
} }
@ -724,6 +786,10 @@ void sim_step(struct sim_step_ctx *step_ctx)
} }
} }
} }
#endif
/* ========================== * /* ========================== *
* Create client player ents * Create client player ents
@ -762,7 +828,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
if (client_ent->valid) { if (client_ent->valid) {
ent->control = client_ent->client_control; ent->control = client_ent->client_control;
/* TODO: Move this */ /* TODO: Move this */
if (ent->control.flags & SIM_CONTROL_FLAG_FIRING) { if (ent->control.flags & SIM_CONTROL_FLAG_FIRE) {
sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED);
} else { } else {
sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED);
@ -786,10 +852,10 @@ void sim_step(struct sim_step_ctx *step_ctx)
{ {
struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name); struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name);
if (ent->animation_last_frame_change_time_ns == 0) { if (ent->animation_last_frame_change_time_ns == 0) {
ent->animation_last_frame_change_time_ns = world_time; ent->animation_last_frame_change_time_ns = SECONDS_FROM_NS(world->sim_time_ns);
} }
f64 time_in_frame = SECONDS_FROM_NS(world->world_time_ns - ent->animation_last_frame_change_time_ns); f64 time_in_frame = SECONDS_FROM_NS(world->sim_time_ns - ent->animation_last_frame_change_time_ns);
u64 frame_index = ent->animation_frame; u64 frame_index = ent->animation_frame;
if (frame_index < span.start || frame_index > span.end) { if (frame_index < span.start || frame_index > span.end) {
frame_index = span.start; frame_index = span.start;
@ -805,7 +871,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
frame_index = span.start; frame_index = span.start;
} }
frame = sprite_sheet_get_frame(sheet, frame_index); frame = sprite_sheet_get_frame(sheet, frame_index);
ent->animation_last_frame_change_time_ns = world->world_time_ns; ent->animation_last_frame_change_time_ns = world->sim_time_ns;
} }
} }
@ -971,9 +1037,9 @@ void sim_step(struct sim_step_ctx *step_ctx)
struct sim_ent *ent = &world->ents[ent_index]; struct sim_ent *ent = &world->ents[ent_index];
if (!sim_ent_is_valid_and_active(ent)) continue; if (!sim_ent_is_valid_and_active(ent)) continue;
if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) continue; if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) continue;
if ((world_time - ent->last_triggered < ent->trigger_delay) && ent->last_triggered != 0) continue; if ((world->sim_time_ns - ent->last_triggered_ns < NS_FROM_SECONDS(ent->trigger_delay)) && ent->last_triggered_ns != 0) continue;
ent->last_triggered = world_time; ent->last_triggered_ns = world->sim_time_ns;
/* Fire weapon */ /* Fire weapon */
if (sim_ent_has_prop(ent, SIM_ENT_PROP_WEAPON)) { if (sim_ent_has_prop(ent, SIM_ENT_PROP_WEAPON)) {
@ -1092,7 +1158,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
} }
/* Set correction rate dynamically since motor velocity is only set for one frame */ /* Set correction rate dynamically since motor velocity is only set for one frame */
joint_ent->motor_joint_data.correction_rate = 10 * world_dt; joint_ent->motor_joint_data.correction_rate = 10 * sim_dt;
/* Solve for final angle using law of sines */ /* Solve for final angle using law of sines */
@ -1145,7 +1211,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
f32 diff = math_unwind_angle(new_angle - xform_get_rotation(joint_xf)); f32 diff = math_unwind_angle(new_angle - xform_get_rotation(joint_xf));
if (math_fabs(diff) > angle_error_allowed) { if (math_fabs(diff) > angle_error_allowed) {
/* Instantly snap joint ent to new angle */ /* Instantly snap joint ent to new angle */
new_vel = diff / real_dt; new_vel = diff / sim_dt;
} }
} }
sim_ent_set_angular_velocity(joint_ent, new_vel); sim_ent_set_angular_velocity(joint_ent, new_vel);
@ -1260,7 +1326,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
struct phys_step_ctx phys = ZI; struct phys_step_ctx phys = ZI;
phys.sim_step_ctx = step_ctx; phys.sim_step_ctx = step_ctx;
phys.pre_solve_callback = on_collision; phys.pre_solve_callback = on_collision;
phys_step(&phys, world_dt); phys_step(&phys, sim_dt);
} }
/* ========================== * /* ========================== *
@ -1274,7 +1340,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
struct v2 end = sim_ent_get_xform(ent).og; struct v2 end = sim_ent_get_xform(ent).og;
struct v2 tick_velocity = v2_mul(ent->tracer_start_velocity, world_dt); struct v2 tick_velocity = v2_mul(ent->tracer_start_velocity, sim_dt);
struct v2 gradient_start = v2_add(ent->tracer_gradient_start, tick_velocity); struct v2 gradient_start = v2_add(ent->tracer_gradient_start, tick_velocity);
struct v2 gradient_end = v2_add(ent->tracer_gradient_end, tick_velocity); struct v2 gradient_end = v2_add(ent->tracer_gradient_end, tick_velocity);
@ -1374,7 +1440,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
/* Lerp camera */ /* Lerp camera */
if (ent->camera_applied_lerp_continuity_gen_plus_one == ent->camera_lerp_continuity_gen + 1) { if (ent->camera_applied_lerp_continuity_gen_plus_one == ent->camera_lerp_continuity_gen + 1) {
f32 t = 1 - math_pow(2.f, -20.f * (f32)world_dt); f32 t = 1 - math_pow(2.f, -20.f * (f32)sim_dt);
xf = xform_lerp(xf, ent->camera_xform_target, t); xf = xform_lerp(xf, ent->camera_xform_target, t);
} else { } else {
/* Skip lerp */ /* Skip lerp */
@ -1407,7 +1473,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
if (!sim_ent_is_valid_and_active(ent)) continue; if (!sim_ent_is_valid_and_active(ent)) continue;
if (!sim_ent_has_prop(ent, SIM_ENT_PROP_QUAKE)) continue; if (!sim_ent_has_prop(ent, SIM_ENT_PROP_QUAKE)) continue;
ent->quake_intensity = max_f32(0, ent->quake_intensity - (ent->quake_fade * world_dt)); ent->quake_intensity = max_f32(0, ent->quake_intensity - (ent->quake_fade * sim_dt));
if (ent->quake_intensity <= 0) { if (ent->quake_intensity <= 0) {
sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE_NEXT_TICK); sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE_NEXT_TICK);
} }

View File

@ -44,6 +44,8 @@ struct sim_lookup_entry *sim_lookup_get(struct sim_lookup *l, struct sim_lookup_
void sim_lookup_set(struct sim_lookup *l, struct sim_lookup_key key, struct sim_ent_handle handle); void sim_lookup_set(struct sim_lookup *l, struct sim_lookup_key key, struct sim_ent_handle handle);
void sim_lookup_remove(struct sim_lookup *l, struct sim_lookup_entry *entry); void sim_lookup_remove(struct sim_lookup *l, struct sim_lookup_entry *entry);
struct sim_lookup_key sim_lookup_key_from_client_and_ent_handles(struct sim_client_handle client_handle, struct sim_ent_handle ent_handle);
struct sim_lookup_key sim_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1); struct sim_lookup_key sim_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1);
struct sim_lookup_key sim_lookup_key_from_client_handle(struct sim_client_handle handle); struct sim_lookup_key sim_lookup_key_from_client_handle(struct sim_client_handle handle);
@ -56,6 +58,7 @@ struct sim_lookup_key sim_lookup_key_from_client_handle(struct sim_client_handle
struct sim_accel { struct sim_accel {
struct space *space; struct space *space;
struct sim_lookup remote_lookup;
struct sim_lookup client_lookup; struct sim_lookup client_lookup;
struct sim_lookup contact_lookup; struct sim_lookup contact_lookup;
#if COLLIDER_DEBUG #if COLLIDER_DEBUG
@ -72,10 +75,10 @@ void sim_accel_rebuild(struct sim_snapshot *ss, struct sim_accel *accel);
* ========================== */ * ========================== */
struct sim_step_ctx { struct sim_step_ctx {
b32 is_master;
struct sim_accel *accel; struct sim_accel *accel;
struct sim_snapshot *world; struct sim_snapshot *world;
struct sim_snapshot_list *cmd_snapshots; i64 sim_dt_ns;
i64 real_dt_ns;
}; };
void sim_step(struct sim_step_ctx *step_ctx); void sim_step(struct sim_step_ctx *step_ctx);

View File

@ -414,7 +414,7 @@ struct sock_read_result sock_read(struct sock *sock, struct string read_buff)
res.address = sock_address_from_win32_address(ws_addr); res.address = sock_address_from_win32_address(ws_addr);
if (size >= 0) { if (size >= 0) {
atomic_u64_eval_add_u64(&app_statistics()->sock_bytes_sent, size); atomic_u64_eval_add_u64(&app_statistics()->sock_bytes_received, size);
res.data.text = read_buff.text; res.data.text = read_buff.text;
res.data.len = size; res.data.len = size;
res.valid = true; res.valid = true;

View File

@ -49,14 +49,14 @@ GLOBAL struct {
struct string connect_address_str; struct string connect_address_str;
struct sim_client_store *user_client_store; struct sim_client_store *user_client_store;
struct sim_client *user_unblended_snapshots_client; /* Contains buffered snapshots received from sim */ struct sim_client *user_unblended_client; /* Contains snapshots received from local sim */
struct sim_client *user_blended_snapshots_client; /* Contains single world snapshot from result of blending sim snapshots */ struct sim_client *user_blended_client; /* Contains single snapshot from result of blending local sim snapshots */
struct sim_snapshot *ss_blended; /* Points to blended snapshot in blended snapshots client */ struct sim_snapshot *ss_blended; /* Points to blended snapshot contained in blended client */
/* Usage stats */ /* Usage stats */
i64 last_second_reset_ns; i64 last_second_reset_ns;
struct second_stat client_bytes_read; struct second_stat net_bytes_read;
struct second_stat client_bytes_sent; struct second_stat net_bytes_sent;
/* Render targets */ /* Render targets */
struct renderer_texture final_texture; struct renderer_texture final_texture;
@ -88,23 +88,21 @@ GLOBAL struct {
struct sim_control user_sim_cmd_control; struct sim_control user_sim_cmd_control;
u64 last_user_sim_cmd_gen; u64 last_user_sim_cmd_gen;
u64 user_sim_cmd_gen; u64 user_sim_cmd_gen;
u64 user_sim_cmd_ack;
/* Local sim -> user */ /* Local sim -> user */
struct sys_mutex local_sim_to_user_mutex; struct sys_mutex local_to_user_client_mutex;
struct sim_client_store *local_sim_to_user_client_store; struct sim_client_store *local_to_user_client_store;
struct sim_client *local_sim_to_user_client; struct sim_client *local_to_user_client;
/* Rolling window of local sim publish times */ /* Rolling window of local sim -> user publish time deltas */
i64 last_snapshot_published_at_ns; i64 last_local_to_user_snapshot_published_at_ns;
i64 snapshot_publish_dts_ns[50]; i64 local_to_user_snapshot_publish_dts_ns[50];
i64 snapshot_publish_dts_index; i64 local_to_user_snapshot_publish_dts_index;
i64 average_snapshot_publish_dt_ns; i64 average_local_to_user_snapshot_publish_dt_ns;
/* Calculated from <last snapshot receive time + time since packet receive> */ i64 local_sim_predicted_time_ns; /* Calculated from <last local sim to user pubilsh time> + <time since last local sim to user publish> */
i64 local_sim_predicted_time_ns; i64 render_time_target_ns; /* Claculated from <local_sim_rpedicted_time_ns> - <render interp delay> */
i64 render_time_target_ns; i64 render_time_ns; /* Incremented at a constant rate based on average local to user publish delta, but snaps to render_time_target_ns if it gets too distant */
i64 render_time_ns;
u64 local_sim_last_known_tick; u64 local_sim_last_known_tick;
i64 local_sim_last_known_time_ns; i64 local_sim_last_known_time_ns;
@ -205,16 +203,16 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
G.sys_events_mutex = sys_mutex_alloc(); G.sys_events_mutex = sys_mutex_alloc();
G.sys_events_arena = arena_alloc(GIGABYTE(64)); G.sys_events_arena = arena_alloc(GIGABYTE(64));
/* Snapshot store */ /* User blend clients */
G.user_client_store = sim_client_store_alloc(); G.user_client_store = sim_client_store_alloc();
G.user_unblended_snapshots_client = sim_client_alloc(G.user_client_store, SIM_CLIENT_KIND_USER); G.user_unblended_client = sim_client_alloc(G.user_client_store);
G.user_blended_snapshots_client = sim_client_alloc(G.user_client_store, SIM_CLIENT_KIND_USER); G.user_blended_client = sim_client_alloc(G.user_client_store);
G.ss_blended = sim_snapshot_nil(); G.ss_blended = sim_snapshot_nil();
/* Local sim snapshot store */ /* Local to user client */
G.local_sim_to_user_mutex = sys_mutex_alloc(); G.local_to_user_client_mutex = sys_mutex_alloc();
G.local_sim_to_user_client_store = sim_client_store_alloc(); G.local_to_user_client_store = sim_client_store_alloc();
G.local_sim_to_user_client = sim_client_alloc(G.local_sim_to_user_client_store, SIM_CLIENT_KIND_LOCAL_SIM); G.local_to_user_client = sim_client_alloc(G.local_to_user_client_store);
/* User sim control */ /* User sim control */
G.user_sim_cmd_mutex = sys_mutex_alloc(); G.user_sim_cmd_mutex = sys_mutex_alloc();
@ -391,16 +389,16 @@ INTERNAL void user_update(void)
* ========================== */ * ========================== */
{ {
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_to_user_mutex); struct sys_lock lock = sys_mutex_lock_e(&G.local_to_user_client_mutex);
u64 old_last_tick = G.user_unblended_snapshots_client->last_tick; u64 old_last_tick = G.user_unblended_client->last_tick;
u64 last_tick = G.local_sim_to_user_client->last_tick; u64 last_tick = G.local_to_user_client->last_tick;
if (last_tick > old_last_tick) { if (last_tick > old_last_tick) {
struct sim_snapshot *src = sim_snapshot_from_tick(G.local_sim_to_user_client, last_tick); struct sim_snapshot *src = sim_snapshot_from_tick(G.local_to_user_client, last_tick);
sim_snapshot_alloc(G.user_unblended_snapshots_client, src, src->tick); sim_snapshot_alloc(G.user_unblended_client, src, src->tick);
G.last_snapshot_published_at_ns = src->publish_time_ns; G.last_local_to_user_snapshot_published_at_ns = src->publish_time_ns;
G.snapshot_publish_dts_ns[G.snapshot_publish_dts_index++] = src->publish_dt_ns; G.local_to_user_snapshot_publish_dts_ns[G.local_to_user_snapshot_publish_dts_index++] = src->publish_dt_ns;
if (G.snapshot_publish_dts_index >= (i64)ARRAY_COUNT(G.snapshot_publish_dts_ns)) { if (G.local_to_user_snapshot_publish_dts_index >= (i64)ARRAY_COUNT(G.local_to_user_snapshot_publish_dts_ns)) {
G.snapshot_publish_dts_index = 0; G.local_to_user_snapshot_publish_dts_index = 0;
} }
} }
sys_mutex_unlock(&lock); sys_mutex_unlock(&lock);
@ -414,8 +412,8 @@ INTERNAL void user_update(void)
i64 average_publish_dt_ns = 0; i64 average_publish_dt_ns = 0;
{ {
i64 num_dts = 0; i64 num_dts = 0;
for (u64 i = 0; i < ARRAY_COUNT(G.snapshot_publish_dts_ns); ++i) { for (u64 i = 0; i < ARRAY_COUNT(G.local_to_user_snapshot_publish_dts_ns); ++i) {
i64 dt_ns = G.snapshot_publish_dts_ns[i]; i64 dt_ns = G.local_to_user_snapshot_publish_dts_ns[i];
if (dt_ns != 0) { if (dt_ns != 0) {
average_publish_dt_ns += dt_ns; average_publish_dt_ns += dt_ns;
++num_dts; ++num_dts;
@ -427,7 +425,7 @@ INTERNAL void user_update(void)
average_publish_dt_ns /= num_dts; average_publish_dt_ns /= num_dts;
} }
} }
G.average_snapshot_publish_dt_ns = average_publish_dt_ns; G.average_local_to_user_snapshot_publish_dt_ns = average_publish_dt_ns;
} }
/* ========================== * /* ========================== *
@ -435,26 +433,26 @@ INTERNAL void user_update(void)
* ========================== */ * ========================== */
{ {
/* How along are we between sim ticks (float in range [0, 1]) */ /* How along are we between sim ticks (0 = start of tick, 1 = end of tick) */
f64 tick_progress = 0; f64 tick_progress = 0;
i64 next_tick_expected_ns = G.last_snapshot_published_at_ns + G.average_snapshot_publish_dt_ns; i64 next_tick_expected_ns = G.last_local_to_user_snapshot_published_at_ns + G.average_local_to_user_snapshot_publish_dt_ns;
if (next_tick_expected_ns > G.last_snapshot_published_at_ns) { if (next_tick_expected_ns > G.last_local_to_user_snapshot_published_at_ns) {
tick_progress = (f64)(G.real_time_ns - G.last_snapshot_published_at_ns) / (f64)(next_tick_expected_ns - G.last_snapshot_published_at_ns); tick_progress = (f64)(G.real_time_ns - G.last_local_to_user_snapshot_published_at_ns) / (f64)(next_tick_expected_ns - G.last_local_to_user_snapshot_published_at_ns);
} }
/* Predict local sim time based on average snapshot publish dt. */ /* Predict local sim time based on average snapshot publish dt. */
struct sim_snapshot *newest_snapshot = sim_snapshot_from_tick(G.user_unblended_snapshots_client, G.user_unblended_snapshots_client->last_tick); struct sim_snapshot *newest_snapshot = sim_snapshot_from_tick(G.user_unblended_client, G.user_unblended_client->last_tick);
G.local_sim_last_known_time_ns = newest_snapshot->real_time_ns; G.local_sim_last_known_time_ns = newest_snapshot->sim_time_ns;
G.local_sim_last_known_tick = newest_snapshot->tick; G.local_sim_last_known_tick = newest_snapshot->tick;
u64 keep_unblended_tick = newest_snapshot->tick; u64 keep_unblended_tick = newest_snapshot->tick;
G.local_sim_predicted_time_ns = newest_snapshot->real_time_ns + (newest_snapshot->real_dt_ns * tick_progress); G.local_sim_predicted_time_ns = newest_snapshot->sim_time_ns + (newest_snapshot->sim_dt_ns * tick_progress);
#if USER_INTERP_ENABLED #if USER_INTERP_ENABLED
/* Determine render time */ /* Determine render time */
G.render_time_target_ns = G.local_sim_predicted_time_ns - (USER_INTERP_RATIO * G.average_snapshot_publish_dt_ns); G.render_time_target_ns = G.local_sim_predicted_time_ns - (USER_INTERP_RATIO * G.average_local_to_user_snapshot_publish_dt_ns);
if (G.average_snapshot_publish_dt_ns > 0) { if (G.average_local_to_user_snapshot_publish_dt_ns > 0) {
/* Increase render time based on average publish dt */ /* Increment render time based on average publish dt */
f64 sim_publish_timescale = (f64)newest_snapshot->real_dt_ns / (f64)G.average_snapshot_publish_dt_ns; f64 sim_publish_timescale = (f64)newest_snapshot->sim_dt_ns / (f64)G.average_local_to_user_snapshot_publish_dt_ns;
G.render_time_ns += G.real_dt_ns * sim_publish_timescale; G.render_time_ns += G.real_dt_ns * sim_publish_timescale;
} }
i64 render_time_target_diff_ns = G.render_time_target_ns - G.render_time_ns; i64 render_time_target_diff_ns = G.render_time_target_ns - G.render_time_ns;
@ -467,17 +465,17 @@ INTERNAL void user_update(void)
struct sim_snapshot *left_snapshot = sim_snapshot_nil(); struct sim_snapshot *left_snapshot = sim_snapshot_nil();
struct sim_snapshot *right_snapshot = newest_snapshot; struct sim_snapshot *right_snapshot = newest_snapshot;
{ {
struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_unblended_snapshots_client, G.user_unblended_snapshots_client->first_tick); struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_unblended_client, G.user_unblended_client->first_tick);
while (ss->valid) { while (ss->valid) {
u64 next_tick = ss->next_tick; u64 next_tick = ss->next_tick;
i64 ss_time_ns = ss->real_time_ns; i64 ss_time_ns = ss->sim_time_ns;
if (ss_time_ns < G.render_time_ns && ss_time_ns > left_snapshot->real_time_ns) { if (ss_time_ns < G.render_time_ns && ss_time_ns > left_snapshot->sim_time_ns) {
left_snapshot = ss; left_snapshot = ss;
} }
if (ss_time_ns > G.render_time_ns && ss_time_ns < right_snapshot->real_time_ns) { if (ss_time_ns > G.render_time_ns && ss_time_ns < right_snapshot->sim_time_ns) {
right_snapshot = ss; right_snapshot = ss;
} }
ss = sim_snapshot_from_tick(G.user_unblended_snapshots_client, next_tick); ss = sim_snapshot_from_tick(G.user_unblended_client, next_tick);
} }
} }
@ -487,39 +485,39 @@ INTERNAL void user_update(void)
/* Create world from blended snapshots */ /* Create world from blended snapshots */
if (left_snapshot->valid && right_snapshot->valid) { if (left_snapshot->valid && right_snapshot->valid) {
f64 blend = (f64)(G.render_time_ns - left_snapshot->real_time_ns) / (f64)(right_snapshot->real_time_ns - left_snapshot->real_time_ns); f64 blend = (f64)(G.render_time_ns - left_snapshot->sim_time_ns) / (f64)(right_snapshot->sim_time_ns - left_snapshot->sim_time_ns);
G.ss_blended = sim_snapshot_alloc_from_lerp(G.user_blended_snapshots_client, left_snapshot, right_snapshot, blend); G.ss_blended = sim_snapshot_alloc_from_lerp(G.user_blended_client, left_snapshot, right_snapshot, blend);
} else if (left_snapshot->valid) { } else if (left_snapshot->valid) {
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, left_snapshot, left_snapshot->tick); G.ss_blended = sim_snapshot_alloc(G.user_blended_client, left_snapshot, left_snapshot->tick);
} else if (right_snapshot->valid) { } else if (right_snapshot->valid) {
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, right_snapshot, right_snapshot->tick); G.ss_blended = sim_snapshot_alloc(G.user_blended_client, right_snapshot, right_snapshot->tick);
} }
#else #else
/* Interp disabled, just copy latest snapshot */ /* Interp disabled, just copy latest snapshot */
G.render_time_target_ns = newest_snapshot->real_time_ns; G.render_time_target_ns = newest_snapshot->sim_time_ns;
G.render_time_ns = newest_snapshot->real_time_ns; G.render_time_ns = newest_snapshot->sim_time_ns;
if (G.ss_blended->tick != newest_snapshot->tick) { if (G.ss_blended->tick != newest_snapshot->tick) {
if (G.ss_blended->valid) { if (G.ss_blended->valid) {
sim_snapshot_release(G.ss_blended); sim_snapshot_release(G.ss_blended);
} }
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, newest_snapshot, newest_snapshot->tick); G.ss_blended = sim_snapshot_alloc(G.user_blended_client, newest_snapshot, newest_snapshot->tick);
} }
#endif #endif
/* Release unneeded unblended sim snapshots */ /* Release unneeded unblended sim snapshots */
if (keep_unblended_tick > 0) { if (keep_unblended_tick > 0) {
sim_snapshot_release_ticks_in_range(G.user_unblended_snapshots_client, 0, keep_unblended_tick - 1); sim_snapshot_release_ticks_in_range(G.user_unblended_client, 0, keep_unblended_tick - 1);
} }
/* Release unused blended snapshots */ /* Release unused blended snapshots */
{ {
struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_blended_snapshots_client, G.user_blended_snapshots_client->first_tick); struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_blended_client, G.user_blended_client->first_tick);
while (ss->valid) { while (ss->valid) {
u64 next_tick = ss->next_tick; u64 next_tick = ss->next_tick;
if (ss != G.ss_blended) { if (ss != G.ss_blended) {
sim_snapshot_release(ss); sim_snapshot_release(ss);
} }
ss = sim_snapshot_from_tick(G.user_blended_snapshots_client, next_tick); ss = sim_snapshot_from_tick(G.user_blended_client, next_tick);
} }
} }
} }
@ -648,7 +646,7 @@ INTERNAL void user_update(void)
f32 shake = ent->shake; f32 shake = ent->shake;
if (shake > 0) { if (shake > 0) {
u64 basis = hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&ent->handle)); u64 basis = hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&ent->handle));
u64 angle_seed0 = basis + (u64)(G.ss_blended->world_time_ns / frequency_ns); u64 angle_seed0 = basis + (u64)(G.ss_blended->sim_time_ns / frequency_ns);
u64 angle_seed1 = angle_seed0 + 1; u64 angle_seed1 = angle_seed0 + 1;
f32 angle0 = rng_noise_f32(angle_seed0, 0, TAU); f32 angle0 = rng_noise_f32(angle_seed0, 0, TAU);
f32 angle1 = rng_noise_f32(angle_seed1, 0, TAU); f32 angle1 = rng_noise_f32(angle_seed1, 0, TAU);
@ -658,7 +656,7 @@ INTERNAL void user_update(void)
struct v2 vec1 = v2_with_len(v2_from_angle(angle1), shake); struct v2 vec1 = v2_with_len(v2_from_angle(angle1), shake);
/* TODO: Cubic interp? */ /* TODO: Cubic interp? */
f32 blend = (f32)(G.ss_blended->world_time_ns % frequency_ns) / (f32)frequency_ns; f32 blend = (f32)(G.ss_blended->sim_time_ns % frequency_ns) / (f32)frequency_ns;
struct v2 vec = v2_lerp(vec0, vec1, blend); struct v2 vec = v2_lerp(vec0, vec1, blend);
struct xform xf = sim_ent_get_xform(ent); struct xform xf = sim_ent_get_xform(ent);
@ -1402,10 +1400,10 @@ INTERNAL void user_update(void)
struct bind_state spawn_state = G.bind_states[USER_BIND_KIND_DEBUG_SPAWN]; struct bind_state spawn_state = G.bind_states[USER_BIND_KIND_DEBUG_SPAWN];
if (fire_state.num_presses || fire_state.is_held) { if (fire_state.num_presses || fire_state.is_held) {
control.flags |= SIM_CONTROL_FLAG_FIRING; control.flags |= SIM_CONTROL_FLAG_FIRE;
} }
if (drag_state.num_presses || drag_state.is_held) { if (drag_state.num_presses || drag_state.is_held) {
control.flags |= SIM_CONTROL_FLAG_DRAGGING; control.flags |= SIM_CONTROL_FLAG_DRAG;
} }
if (clear_state.num_presses) { if (clear_state.num_presses) {
control.flags |= SIM_CONTROL_FLAG_CLEAR_ALL; control.flags |= SIM_CONTROL_FLAG_CLEAR_ALL;
@ -1433,7 +1431,6 @@ INTERNAL void user_update(void)
u32 old_flags = G.user_sim_cmd_control.flags; u32 old_flags = G.user_sim_cmd_control.flags;
G.user_sim_cmd_control = control; G.user_sim_cmd_control = control;
G.user_sim_cmd_control.flags |= old_flags; G.user_sim_cmd_control.flags |= old_flags;
G.user_sim_cmd_ack = G.local_sim_last_known_tick;
sys_mutex_unlock(&lock); sys_mutex_unlock(&lock);
} }
} }
@ -1450,6 +1447,20 @@ INTERNAL void user_update(void)
#endif #endif
} }
{
/* Update network usage stats */
i64 stat_now_ns = sys_time_ns();
G.net_bytes_read.last_second_end = atomic_u64_eval(&app_statistics()->sock_bytes_received);
G.net_bytes_sent.last_second_end = atomic_u64_eval(&app_statistics()->sock_bytes_sent);
if (stat_now_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) {
G.last_second_reset_ns = stat_now_ns;
G.net_bytes_read.last_second = G.net_bytes_read.last_second_end - G.net_bytes_read.last_second_start;
G.net_bytes_sent.last_second = G.net_bytes_sent.last_second_end - G.net_bytes_sent.last_second_start;
G.net_bytes_read.last_second_start = G.net_bytes_read.last_second_end;
G.net_bytes_sent.last_second_start = G.net_bytes_sent.last_second_end;
}
}
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
@ -1467,24 +1478,17 @@ INTERNAL void user_update(void)
pos.y += spacing; pos.y += spacing;
#endif #endif
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Read from host: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1000 / 1000, 3)));
pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Sent from host: %F mbit/s"), FMT_FLOAT_P((f64)G.client_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("blended world entities: %F/%F"), FMT_UINT(G.ss_blended->num_ents_allocated), FMT_UINT(G.ss_blended->num_ents_reserved))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world entities: %F/%F"), FMT_UINT(G.ss_blended->num_ents_allocated), FMT_UINT(G.ss_blended->num_ents_reserved)));
pos.y += spacing; pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world tick: %F"), FMT_UINT(G.ss_blended->tick))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world tick: %F"), FMT_UINT(G.ss_blended->tick)));
pos.y += spacing; pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world time: %F"), FMT_FLOAT_P(SECONDS_FROM_NS(G.ss_blended->world_time_ns), 3))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world time: %F"), FMT_FLOAT_P(SECONDS_FROM_NS(G.ss_blended->sim_time_ns), 3)));
pos.y += spacing; pos.y += spacing;
pos.y += spacing; pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("average local sim publish dt: %F"), FMT_FLOAT_P(SECONDS_FROM_NS(G.average_snapshot_publish_dt_ns), 3))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("average local sim publish dt: %F"), FMT_FLOAT_P(SECONDS_FROM_NS(G.average_local_to_user_snapshot_publish_dt_ns), 3)));
pos.y += spacing; pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("local sim last known tick: %F"), FMT_UINT(G.local_sim_last_known_tick))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("local sim last known tick: %F"), FMT_UINT(G.local_sim_last_known_tick)));
@ -1503,6 +1507,13 @@ INTERNAL void user_update(void)
pos.y += spacing; pos.y += spacing;
pos.y += spacing; pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Network read: %F mbit/s"), FMT_FLOAT_P((f64)G.net_bytes_read.last_second * 8 / 1000 / 1000, 3)));
pos.y += spacing;
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("Memory usage: %F MiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->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)atomic_u64_eval(&app_statistics()->memory_committed) / 1024 / 1024, 3)));
pos.y += spacing; pos.y += spacing;
@ -1696,6 +1707,155 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg)
* Local sim thread * Local sim thread
* ========================== */ * ========================== */
/* TODO: Move this */
#define translate(fieldname) local->fieldname = _translate(remote_lookup, old.fieldname, remote_client_handle, remote->fieldname)
INTERNAL struct sim_ent_handle _translate(struct sim_lookup *remote_lookup, struct sim_ent_handle old_local_handle, struct sim_client_handle remote_client_handle, struct sim_ent_handle remote_ent_handle)
{
struct sim_lookup_key key = sim_lookup_key_from_client_and_ent_handles(remote_client_handle, remote_ent_handle);
struct sim_lookup_entry *entry = sim_lookup_get(remote_lookup, key);
return entry ? entry->ent : old_local_handle;
}
INTERNAL void sim_ent_sync_with_remote(struct sim_accel *accel, struct sim_ent *local, struct sim_ent *remote, struct sim_ent *client_ent)
{
struct sim_client_handle remote_client_handle = remote->ss->client->handle;
struct sim_lookup *remote_lookup = &accel->remote_lookup;
struct sim_ent old = *local;
MEMCPY_STRUCT(local, remote);
local->ss = old.ss;
local->handle = old.handle;
local->remote_client = remote_client_handle;
local->remote_ent = remote->handle;
sim_ent_enable_prop(local, SIM_ENT_PROP_REMOTE);
sim_ent_set_xform(local, sim_ent_get_xform(remote));
/* Translate remote handles */
translate(top);
translate(parent);
translate(next);
translate(prev);
translate(first);
translate(last);
translate(next_free);
translate(client_control_ent);
translate(client_camera_ent);
translate(client_dbg_drag_joint_ent);
translate(controlling_client);
translate(move_joint);
translate(aim_joint);
translate(ground_friction_joint);
translate(equipped);
translate(bullet_src);
translate(bullet_tracer);
translate(camera_follow);
if (sim_ent_has_prop(local, SIM_ENT_PROP_CMD_CONTROL)) {
local->cmd_client = client_ent->handle;
}
}
INTERNAL void sim_snapshot_sync_with_remote(struct sim_accel *accel, struct sim_snapshot *local_ss, struct sim_snapshot *remote_ss)
{
__prof;
/* FIXME: Only sync cmds from non-master remote */
struct sim_ent *local_root = sim_ent_from_handle(local_ss, SIM_ENT_ROOT_HANDLE);
struct sim_client_handle remote_client_handle = remote_ss->client->handle;
struct sim_lookup *remote_lookup = &accel->remote_lookup;
struct sim_lookup *client_lookup = &accel->client_lookup;
struct sim_ent *client_ent;
{
struct sim_lookup_key key = sim_lookup_key_from_client_handle(remote_client_handle);
struct sim_lookup_entry *client_entry = sim_lookup_get(client_lookup, key);
client_ent = client_entry ? sim_ent_from_handle(local_ss, client_entry->ent) : sim_ent_nil();
}
/* Build remote lookup table */
for (u64 i = 0; i < local_ss->num_ents_reserved; ++i) {
struct sim_ent *ent = &local_ss->ents[i];
if (ent->valid && sim_ent_has_prop(ent, SIM_ENT_PROP_REMOTE) && sim_client_handle_eq(ent->remote_client, remote_client_handle)) {
struct sim_lookup_key key = sim_lookup_key_from_client_and_ent_handles(remote_client_handle, ent->remote_ent);
sim_lookup_set(remote_lookup, key, ent->handle);
}
}
/* Create new ents from remote */
/* Skipping root entity */
for (u64 i = SIM_ENT_ROOT_HANDLE.idx + 1; i < remote_ss->num_ents_reserved; ++i) {
struct sim_ent *remote_ent = &remote_ss->ents[i];
if (remote_ent->valid) {
struct sim_lookup_key key = sim_lookup_key_from_client_and_ent_handles(remote_client_handle, remote_ent->handle);
struct sim_ent *local_ent = sim_ent_nil();
{
struct sim_lookup_entry *entry = sim_lookup_get(remote_lookup, key);
if (entry) {
local_ent = sim_ent_from_handle(local_ss, entry->ent);
}
}
if (!local_ent->valid) {
local_ent = sim_ent_alloc(local_root);
sim_ent_enable_prop(local_ent, SIM_ENT_PROP_REMOTE);
local_ent->remote_client = remote_client_handle;
local_ent->remote_ent = remote_ent->handle;
sim_lookup_set(remote_lookup, key, local_ent->handle);
}
}
}
/* Sync ents with remote */
for (u64 i = 0; i < local_ss->num_ents_reserved; ++i) {
struct sim_ent *local_ent = &local_ss->ents[i];
if (local_ent->valid && sim_ent_has_prop(local_ent, SIM_ENT_PROP_REMOTE) && sim_client_handle_eq(local_ent->remote_client, remote_client_handle)) {
struct sim_ent *remote_ent = sim_ent_from_handle(remote_ss, local_ent->remote_ent);
if (remote_ent->valid) {
/* Copy all ent data from remote */
sim_ent_sync_with_remote(accel, local_ent, remote_ent, client_ent);
} else {
/* Remote ent is no longer valid / networked, release it */
sim_ent_enable_prop(local_ent, SIM_ENT_PROP_RELEASE_THIS_TICK);
sim_ent_disable_prop(local_ent, SIM_ENT_PROP_REMOTE);
}
}
}
}
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
{ {
#if 0 #if 0
@ -1723,9 +1883,10 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
struct sim_accel accel = sim_accel_alloc(); struct sim_accel accel = sim_accel_alloc();
struct sim_client_store *store = sim_client_store_alloc(); struct sim_client_store *store = sim_client_store_alloc();
struct sim_client *local_client = sim_client_alloc(store, SIM_CLIENT_KIND_LOCAL_SIM); struct sim_client *user_input_client = sim_client_alloc(store); /* Stores snapshots containing commands to be published to both local & master clients */
struct sim_client *user_client = sim_client_alloc(store, SIM_CLIENT_KIND_USER); struct sim_client *local_client = sim_client_alloc(store); /* Stores snapshots produced locally */
struct sim_client *master_client = sim_client_nil(); struct sim_client *publish_client = sim_client_alloc(store); /* Stores versions of local snapshots that will be published to remote sims */
struct sim_client *master_client = sim_client_nil(); /* Stores snapshots received from master (if relevant) */
u64 step_tick = 0; u64 step_tick = 0;
i64 last_publish_ns = 0; i64 last_publish_ns = 0;
@ -1733,6 +1894,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
i64 step_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND; i64 step_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND;
i64 compute_dt_ns = step_dt_ns; i64 compute_dt_ns = step_dt_ns;
while (!atomic_i32_eval(&G.local_sim_thread_shutdown)) { while (!atomic_i32_eval(&G.local_sim_thread_shutdown)) {
__profscope(local_sim_loop);
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
{ {
__profscope(local_sim_sleep); __profscope(local_sim_sleep);
@ -1741,22 +1903,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
} }
++step_tick; ++step_tick;
/* Create user cmd snapshot */ /* Read net messages */
{
struct sim_snapshot *prev_user_cmd_ss = sim_snapshot_from_tick(user_client, user_client->last_tick);
struct sim_snapshot *user_cmd_ss = sim_snapshot_alloc(user_client, prev_user_cmd_ss, step_tick);
struct sys_lock lock = sys_mutex_lock_e(&G.user_sim_cmd_mutex);
user_cmd_ss->producer_client = user_client->handle;
user_cmd_ss->producer_client_is_local = true;
user_cmd_ss->control = G.user_sim_cmd_control;
user_client->ack = G.user_sim_cmd_ack;
user_client->reverse_ack = step_tick;
++G.user_sim_cmd_gen;
sys_mutex_unlock(&lock);
}
/* Read net snapshots */
{ {
host_update(host); host_update(host);
struct host_event_array host_events = host_pop_events(scratch.arena, host); struct host_event_array host_events = host_pop_events(scratch.arena, host);
@ -1767,16 +1914,14 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
switch (event->kind) { switch (event->kind) {
case HOST_EVENT_KIND_CHANNEL_OPENED: case HOST_EVENT_KIND_CHANNEL_OPENED:
{ {
/* Create sim client */ /* Create remote client */
if (!client->valid) { if (!client->valid) {
/* TODO: Master challenge */ /* TODO: Master challenge */
if (is_master) { client = sim_client_alloc(store);
client = sim_client_alloc(store, SIM_CLIENT_KIND_SLAVE_SIM); sim_client_set_channel_id(client, channel_id);
} else { if (!is_master) {
client = sim_client_alloc(store, SIM_CLIENT_KIND_MASTER_SIM);
master_client = client; master_client = client;
} }
sim_client_set_channel_id(client, channel_id);
} }
} break; } break;
@ -1787,57 +1932,39 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
struct bitbuff_reader msg_br = br_from_bitbuff(&msg_bb); struct bitbuff_reader msg_br = br_from_bitbuff(&msg_bb);
u64 ack = br_read_uv(&msg_br); u64 ack = br_read_uv(&msg_br);
u64 reverse_ack = br_read_uv(&msg_br); u64 double_ack = br_read_uv(&msg_br);
if (ack > client->ack) { if (ack > client->ack) {
client->ack = ack; client->ack = ack;
} }
if (reverse_ack > client->reverse_ack) { if (double_ack > client->double_ack) {
client->reverse_ack = reverse_ack; client->double_ack = double_ack;
} }
/* Read snapshots */ /* Read snapshots */
struct string encoded_tmp = ZI; u64 tmp_encoded_len = br_read_uv(&msg_br);
encoded_tmp.len = br_read_uv(&msg_br); u8 *tmp_encoded_bytes = br_read_bytes_raw(&msg_br, tmp_encoded_len);
encoded_tmp.text = br_read_bytes_raw(&msg_br, encoded_tmp.len); if (!tmp_encoded_bytes) tmp_encoded_len = 0;
if (!encoded_tmp.text) { while (tmp_encoded_len > 0) {
encoded_tmp.len = 0; struct bitbuff decoder_bb = bitbuff_from_string(STRING(tmp_encoded_len, tmp_encoded_bytes));
}
while (encoded_tmp.len > 0) {
struct bitbuff decoder_bb = bitbuff_from_string(encoded_tmp);
struct bitbuff_reader decoder_br = br_from_bitbuff(&decoder_bb); struct bitbuff_reader decoder_br = br_from_bitbuff(&decoder_bb);
u64 base_tick = br_read_uv(&decoder_br); u64 base_tick = br_read_uv(&decoder_br);
u64 tick = br_read_uv(&decoder_br); u64 tick = br_read_uv(&decoder_br);
if (client->kind == SIM_CLIENT_KIND_MASTER_SIM) {
if (tick > client->last_tick) { if (tick > client->last_tick) {
struct sim_snapshot *base_ss = sim_snapshot_from_tick(client, base_tick); struct sim_snapshot *base_ss = sim_snapshot_from_tick(client, base_tick);
if (base_ss->tick == base_tick) { if (base_ss->tick == base_tick) {
if (client == master_client || (tick == client->last_tick + 1 || tick > client->last_tick + 100)) {
/* Alloc & decode snapshot */
struct sim_snapshot *ss = sim_snapshot_alloc(client, base_ss, tick); struct sim_snapshot *ss = sim_snapshot_alloc(client, base_ss, tick);
sim_snapshot_decode(&decoder_br, ss); sim_snapshot_decode(&decoder_br, ss);
ss->producer_client = client->handle; }
} else { } else {
/* We do not have the base tick that the incoming tick is delta encoded from */ /* We do not have the tick that the incoming delta is based from */
ASSERT(false); ASSERT(false);
} }
} }
} else { tmp_encoded_len = br_read_uv(&msg_br);
/* TODO: Limit range of incoming cmd ticks */ tmp_encoded_bytes = br_read_bytes_raw(&msg_br, tmp_encoded_len);
if (tick == client->last_tick + 1) { if (!tmp_encoded_bytes) tmp_encoded_len = 0;
struct sim_snapshot *base_ss = sim_snapshot_from_tick(client, base_tick);
if (base_ss->tick == base_tick) {
struct sim_snapshot *ss = sim_snapshot_alloc(client, base_ss, tick);
sim_snapshot_decode(&decoder_br, ss);
ss->producer_client = client->handle;
} else {
/* We do not have the base tick that the incoming tick is delta encoded from */
ASSERT(false);
}
}
}
encoded_tmp.len = br_read_uv(&msg_br);
encoded_tmp.text = br_read_bytes_raw(&msg_br, encoded_tmp.len);
if (!encoded_tmp.text) {
encoded_tmp.len = 0;
}
} }
} }
} break; } break;
@ -1847,7 +1974,89 @@ 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, step_tick);
struct sim_ent *user_input_root = sim_ent_from_handle(user_input_ss, SIM_ENT_ROOT_HANDLE);
/* 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(user_input_root);
sim_ent_enable_prop(control_cmd_ent, SIM_ENT_PROP_CMD_CONTROL);
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;
++G.user_sim_cmd_gen;
sys_mutex_unlock(&lock);
}
}
/* Allocate local snapshot */
struct sim_snapshot *prev_local_ss = sim_snapshot_from_tick(local_client, local_client->last_tick);
struct sim_snapshot *local_ss = sim_snapshot_alloc(local_client, prev_local_ss, step_tick);
/* Rebuild acceleration tables */
sim_accel_rebuild(local_ss, &accel);
/* Sync clients */
{
struct sim_ent *local_root = sim_ent_from_handle(local_ss, SIM_ENT_ROOT_HANDLE);
for (u64 client_index = 0; client_index < store->num_clients_reserved; ++client_index) {
struct sim_client *client = &store->clients[client_index];
if (client->valid) {
/* Create client ent if necessary */
if (is_master && client != publish_client && client != local_client && client != master_client) {
struct sim_lookup_key key = sim_lookup_key_from_client_handle(client->handle);
struct sim_lookup_entry *client_entry = sim_lookup_get(&accel.client_lookup, key);
struct sim_ent *client_ent = client_entry ? sim_ent_from_handle(local_ss, client_entry->ent) : sim_ent_nil();
if (!client_ent->valid) {
/* FIXME: Client ent never released upon disconnect */
client_ent = sim_ent_alloc(local_root);
client_ent->client_handle = client->handle;
sim_ent_enable_prop(client_ent, SIM_ENT_PROP_CLIENT);
sim_ent_enable_prop(client_ent, SIM_ENT_PROP_ACTIVE);
sim_lookup_set(&accel.client_lookup, key, client_ent->handle);
if (client == user_input_client) {
local_ss->local_client = user_input_client->handle;
}
}
}
/* Sync w/ client */
if (client != publish_client && client != local_client) {
struct sim_snapshot *client_ss = sim_snapshot_from_tick(client, step_tick);
if (client_ss->valid) {
sim_snapshot_sync_with_remote(&accel, local_ss, client_ss);
}
}
}
}
}
/* Release unneeded snapshots */ /* Release unneeded snapshots */
for (u64 i = 0; i < store->num_clients_reserved; ++i) {
struct sim_client *client = &store->clients[i];
if (client->valid) {
u64 keep_count = 100;
if (client->last_tick > keep_count) {
u64 keep_tick = client->last_tick - keep_count;
sim_snapshot_release_ticks_in_range(client, 0, keep_tick - 1);
}
}
}
/* Release unneeded snapshots */
#if 0
#if 0 #if 0
u64 oldest_client_ack = 0; u64 oldest_client_ack = 0;
for (u64 i = 0; i < store->num_clients_reserved; ++i) { for (u64 i = 0; i < store->num_clients_reserved; ++i) {
@ -1855,9 +2064,9 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
enum sim_client_kind kind = client->kind; enum sim_client_kind kind = client->kind;
if (client->valid && kind != SIM_CLIENT_KIND_LOCAL_SIM) { if (client->valid && kind != SIM_CLIENT_KIND_LOCAL_SIM) {
u64 ack = client->ack; u64 ack = client->ack;
u64 reverse_ack = client->reverse_ack; u64 double_ack = client->double_ack;
if (reverse_ack > 1) { if (double_ack > 1) {
sim_snapshot_release_ticks_in_range(client, 0, reverse_ack - 1); sim_snapshot_release_ticks_in_range(client, 0, double_ack - 1);
} }
if (ack < oldest_client_ack || oldest_client_ack == 0) { if (ack < oldest_client_ack || oldest_client_ack == 0) {
oldest_client_ack = ack; oldest_client_ack = ack;
@ -1870,184 +2079,127 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
#else #else
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];
enum sim_client_kind kind = client->kind; if (client->valid) {
if (client->valid && kind != SIM_CLIENT_KIND_LOCAL_SIM) { if (client->is_remote) {
u64 reverse_ack = client->reverse_ack; u64 double_ack = client->double_ack;
if (reverse_ack > 1) { if (double_ack > 1) {
sim_snapshot_release_ticks_in_range(client, 0, reverse_ack - 1); sim_snapshot_release_ticks_in_range(client, 0, double_ack - 1);
}
} else {
u64 keep_ticks_count = 500;
if (step_tick > keep_ticks_count) {
u64 keep_tick = client->last_tick - keep_ticks_count;
sim_snapshot_release_ticks_in_range(client, 0, keep_tick);
} }
} }
} }
u64 local_keep_ticks_count = 100;
if (step_tick > local_keep_ticks_count) {
u64 local_keep_tick = step_tick - local_keep_ticks_count;
sim_snapshot_release_ticks_in_range(local_client, 0, local_keep_tick);
} }
#endif #endif
#endif
/* Pick client snapshot cmds to feed to sim step */
struct sim_snapshot_list client_step_cmds = ZI;
for (u64 i = 0; i < store->num_clients_reserved; ++i) {
struct sim_client *client = &store->clients[i];
enum sim_client_kind kind = client->kind;
if (client->valid && kind != SIM_CLIENT_KIND_LOCAL_SIM && kind != SIM_CLIENT_KIND_MASTER_SIM) {
struct sim_snapshot *cmd_snapshot = sim_snapshot_from_closest_tick_lte(client, step_tick);
struct sim_snapshot_list_node *n = arena_push_zero(scratch.arena, struct sim_snapshot_list_node);
n->ss = cmd_snapshot;
if (client_step_cmds.last) {
client_step_cmds.last->next = n;
} else {
client_step_cmds.first = n;
}
client_step_cmds.last = n;
}
}
/* Create step snapshot */
struct sim_snapshot *step_ss;
if (is_master) {
struct sim_snapshot *prev_step_ss = sim_snapshot_from_tick(local_client, step_tick - 1);
step_ss = sim_snapshot_alloc(local_client, prev_step_ss, step_tick);
step_ss->is_master = true;
} else {
struct sim_snapshot *newest_master_ss = sim_snapshot_from_tick(master_client, master_client->last_tick);
struct sim_snapshot *local_copy = sim_snapshot_from_tick(local_client, newest_master_ss->tick);
if (!local_copy->valid) {
sim_snapshot_alloc(local_client, newest_master_ss, newest_master_ss->tick);
}
step_ss = sim_snapshot_from_tick(local_client, step_tick);
if (!step_ss->valid) {
step_ss = sim_snapshot_from_tick(local_client, 1);
if (!step_ss->valid) {
step_ss = sim_snapshot_alloc(local_client, sim_snapshot_nil(), 1);
}
}
step_ss->is_master = false;
}
/* Step */ /* Step */
{ {
struct sim_step_ctx step_ctx = ZI; struct sim_step_ctx step_ctx = ZI;
step_ctx.is_master = is_master;
step_ctx.accel = &accel; step_ctx.accel = &accel;
step_ctx.world = step_ss; step_ctx.world = local_ss;
step_ctx.cmd_snapshots = &client_step_cmds; step_ctx.sim_dt_ns = step_dt_ns;
step_ctx.real_dt_ns = step_dt_ns;
sim_step(&step_ctx); sim_step(&step_ctx);
} }
/* Publish snapshot to clients */
/* Construct publishable snapshot */
/* Publish snapshot to remote clients */
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 != user_input_client && client != local_client && client != publish_client) {
struct bitbuff_writer msg_bw = bw_from_bitbuff(&msg_writer_bb); struct bitbuff_writer msg_bw = bw_from_bitbuff(&msg_writer_bb);
if (client->valid) {
switch (client->kind) {
case SIM_CLIENT_KIND_SLAVE_SIM:
{
struct sim_snapshot *base_ss = sim_snapshot_from_tick(local_client, client->ack);
/* Encode single master state sanpshot snapshot */ bw_write_uv(&msg_bw, client->last_tick); /* ack */
struct bitbuff_writer snapshot_bw = bw_from_bitbuff(&snapshot_writer_bb); bw_write_uv(&msg_bw, client->ack); /* double ack */
bw_write_uv(&snapshot_bw, base_ss->tick);
bw_write_uv(&snapshot_bw, step_ss->tick);
sim_snapshot_encode(&snapshot_bw, client, base_ss, step_ss);
struct string encoded_tmp = ZI; struct sim_snapshot *base_ss = sim_snapshot_from_tick(publish_client, client->ack);
encoded_tmp.len = bw_num_bytes_written(&snapshot_bw); struct sim_snapshot *publish_ss;
encoded_tmp.text = bw_get_written_raw(&snapshot_bw); if (client == master_client) {
/* If sending to master, start sending all snapshots since last ack */
u64 ack = client->last_tick; publish_ss = sim_snapshot_from_tick(publish_client, base_ss->tick + 1);
u64 reverse_ack = client->ack;
bw_write_uv(&msg_bw, ack);
bw_write_uv(&msg_bw, reverse_ack);
bw_write_uv(&msg_bw, encoded_tmp.len);
bw_write_bytes(&msg_bw, encoded_tmp);
bw_write_uv(&msg_bw, 0);
struct string encoded = ZI;
encoded.len = bw_num_bytes_written(&msg_bw);
encoded.text = bw_get_written_raw(&msg_bw);
host_queue_write(host, client->channel_id, encoded, 0);
} break;
case SIM_CLIENT_KIND_MASTER_SIM:
{
u64 base_tick = client->last_tick;
struct sim_snapshot *base_ss = sim_snapshot_from_tick(client, base_tick);
if (base_ss->tick == base_tick) {
u64 ack = client->last_tick;
u64 reverse_ack = client->ack;
bw_write_uv(&msg_bw, ack);
bw_write_uv(&msg_bw, reverse_ack);
/* Write all user cmd snapshots since last client ack */
struct sim_snapshot *send_ss = sim_snapshot_from_tick(local_client, base_ss->tick + 1);
while (send_ss->valid) {
struct bitbuff_writer snapshot_bw = bw_from_bitbuff(&snapshot_writer_bb);
bw_write_uv(&snapshot_bw, base_ss->tick);
bw_write_uv(&snapshot_bw, step_ss->tick);
sim_snapshot_encode(&snapshot_bw, client, base_ss, step_ss);
struct string encoded_tmp = ZI;
encoded_tmp.len = bw_num_bytes_written(&snapshot_bw);
encoded_tmp.text = bw_get_written_raw(&snapshot_bw);
bw_write_uv(&msg_bw, encoded_tmp.len);
bw_write_bytes(&msg_bw, encoded_tmp);
send_ss = sim_snapshot_from_tick(local_client, send_ss->tick + 1);
}
bw_write_uv(&msg_bw, 0);
struct string encoded = ZI;
encoded.len = bw_num_bytes_written(&msg_bw);
encoded.text = bw_get_written_raw(&msg_bw);
host_queue_write(host, client->channel_id, encoded, 0);
} else { } else {
/* We do not have the client's ack tick to delta encode from */ /* If sending to slave, only send latest snapshot */
ASSERT(false); publish_ss = sim_snapshot_from_tick(publish_client, publish_client->last_tick);
} }
} break;
case SIM_CLIENT_KIND_USER: while (publish_ss->valid) {
struct bitbuff_writer snapshot_bw = bw_from_bitbuff(&snapshot_writer_bb);
struct string tmp_snapshot_encoded = ZI;
{
bw_write_uv(&snapshot_bw, base_ss->tick);
bw_write_uv(&snapshot_bw, publish_ss->tick);
sim_snapshot_encode(&snapshot_bw, client, base_ss, publish_ss);
tmp_snapshot_encoded.len = bw_num_bytes_written(&snapshot_bw);
tmp_snapshot_encoded.text = bw_get_written_raw(&snapshot_bw);
}
bw_write_uv(&msg_bw, tmp_snapshot_encoded.len);
bw_write_bytes(&msg_bw, tmp_snapshot_encoded);
publish_ss = sim_snapshot_from_tick(publish_client, publish_ss->tick + 1);
}
bw_write_uv(&msg_bw, 0);
struct string encoded = ZI;
encoded.len = bw_num_bytes_written(&msg_bw);
encoded.text = bw_get_written_raw(&msg_bw);
host_queue_write(host, client->channel_id, encoded, 0);
}
}
/* Copy local snapshot to user client */
{ {
/* TODO: Double buffer */ /* TODO: Double buffer */
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_to_user_mutex); struct sys_lock lock = sys_mutex_lock_e(&G.local_to_user_client_mutex);
struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_sim_to_user_client, step_ss, step_ss->tick); struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_to_user_client, local_ss, local_ss->tick);
if (is_master) { pub_ss->local_client = local_ss->local_client;
pub_ss->local_client = client->handle;
}
i64 publish_ns = sys_time_ns(); i64 publish_ns = sys_time_ns();
pub_ss->publish_dt_ns = publish_ns - last_publish_ns; pub_ss->publish_dt_ns = publish_ns - last_publish_ns;
pub_ss->publish_time_ns = publish_ns; pub_ss->publish_time_ns = publish_ns;
last_publish_ns = publish_ns; last_publish_ns = publish_ns;
sim_snapshot_release_ticks_in_range(G.local_sim_to_user_client, 0, step_ss->tick - 1); sim_snapshot_release_ticks_in_range(G.local_to_user_client, 0, local_ss->tick - 1);
sys_mutex_unlock(&lock); sys_mutex_unlock(&lock);
} break; }
default: break;
}
}
}
/* Send host messages */ /* Send host messages */
host_update(host); host_update(host);
__profframe("Local sim"); __profframe("Local sim");
{
/* Update network usage stats */
i64 stat_now_ns = sys_time_ns();
G.client_bytes_read.last_second_end = host->bytes_received;
G.client_bytes_sent.last_second_end = host->bytes_sent;
if (stat_now_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) {
G.last_second_reset_ns = stat_now_ns;
G.client_bytes_read.last_second = G.client_bytes_read.last_second_end - G.client_bytes_read.last_second_start;
G.client_bytes_sent.last_second = G.client_bytes_sent.last_second_end - G.client_bytes_sent.last_second_start;
G.client_bytes_read.last_second_start = G.client_bytes_read.last_second_end;
G.client_bytes_sent.last_second_start = G.client_bytes_sent.last_second_end;
}
}
scratch_end(scratch); scratch_end(scratch);
} }